Bläddra i källkod

添加小课学员管理页面

lex-xin 3 år sedan
förälder
incheckning
31843277e0

+ 2 - 0
src/router/index.js

@@ -548,6 +548,8 @@ export const asyncRoutes = {
   liveClassDetail:()=>import('@/views/liveClassManager/liveClassDetail'),
   // 平台员工管理
   platformAdminManger:()=>import('@/views/platformManager/paltformAdmin'),
+  // 小课学员管理
+  smallStudentManager:()=>import('@/views/smallStudentManager'),
 }
 
 export default router

+ 47 - 47
src/views/accompanyManager/index.vue

@@ -1,47 +1,47 @@
-<!--  -->
-<template>
-  <div class="m-container">
-    <h2>
-      <div class="squrt"></div>
-      网管课管理
-    </h2>
-    <div class="m-core">
-      <tab-router
-        v-model.trim="activeIndex"
-        type="card"
-        @tab-click="handleClick"
-      >
-        <el-tab-pane label="网管课管理" lazy name="1">
-          <accompanyList v-if="activeIndex === '1'" />
-        </el-tab-pane>
-        <el-tab-pane label="网管课购买" lazy name="2">
-          <accompanyBuys v-if="activeIndex === '2'" />
-        </el-tab-pane>
-      </tab-router>
-    </div>
-  </div>
-</template>
-
-<script>
-import accompanyList from "@/views/accompanyManager/accompanyList";
-import accompanyBuys from "@/views/accompanyManager/accompanyBuys";
-export default {
-  components: { accompanyList, accompanyBuys },
-  data() {
-    return {
-      activeIndex: "1",
-    };
-  },
-  //生命周期 - 创建完成(可以访问当前this实例)
-  created() {},
-  //生命周期 - 挂载完成(可以访问DOM元素)
-  mounted() {},
-  methods: {
-    handleClick(evt) {
-      // this.changeHash(evt.name);
-    },
-  },
-};
-</script>
-<style lang='scss' scoped>
-</style>
+<!--  -->
+<template>
+  <div class="m-container">
+    <h2>
+      <div class="squrt"></div>
+      网管课管理
+    </h2>
+    <div class="m-core">
+      <tab-router
+        v-model.trim="activeIndex"
+        type="card"
+        @tab-click="handleClick"
+      >
+        <el-tab-pane label="网管课管理" lazy name="1">
+          <accompanyList v-if="activeIndex === '1'" />
+        </el-tab-pane>
+        <el-tab-pane label="网管课购买" lazy name="2">
+          <accompanyBuys v-if="activeIndex === '2'" />
+        </el-tab-pane>
+      </tab-router>
+    </div>
+  </div>
+</template>
+
+<script>
+import accompanyList from "@/views/accompanyManager/accompanyList";
+import accompanyBuys from "@/views/accompanyManager/accompanyBuys";
+export default {
+  components: { accompanyList, accompanyBuys },
+  data() {
+    return {
+      activeIndex: "1",
+    };
+  },
+  //生命周期 - 创建完成(可以访问当前this实例)
+  created() {},
+  //生命周期 - 挂载完成(可以访问DOM元素)
+  mounted() {},
+  methods: {
+    handleClick(evt) {
+      // this.changeHash(evt.name);
+    },
+  },
+};
+</script>
+<style lang='scss' scoped>
+</style>

+ 243 - 243
src/views/main/baseinfo/business.vue

@@ -1,243 +1,243 @@
-<template
->
-  <el-card header="">
-    <div slot="header" class="clearfix">
-      <searchHeader
-        v-if="mdate.length > 0"
-        :dates="mdate"
-        :title="'业务数据'"
-        :isShowQuert="false"
-        @changeValue="changeValue"
-      />
-    </div>
-
-    <div class="wall" style="height:68px;" v-if="JSON.stringify(items) == '{}'"></div>
-    <statistic class="statistic" :cols="0">
-      <statistic-item
-        v-for="(item, key) in items"
-        :key="key"
-        :class="{ active: active === key }"
-        @click="active = key"
-      >
-        <span>
-          {{ item.title+'(%)'}}
-          <el-tooltip
-            v-if="item.desc"
-            :content="item.desc"
-            :open-delay="0.3"
-            placement="top"
-          >
-            <i
-              style="margin-left: 5px; cursor: pointer"
-              class="el-icon-warning-outline"
-            />
-          </el-tooltip>
-        </span>
-        <span> <count-to :endVal="item.percent" :decimals="2" /></span>
-      </statistic-item>
-    </statistic>
-    <!--   :data-zoom="dataZoom" -->
-    <ve-line
-      style="width: 100%"
-      height="350px"
-      :data="chartData"
-      :data-empty="dataEmpty"
-      :extend="chartExtend"
-      :legend="legend"
-    ></ve-line>
-  </el-card>
-</template>
-<script>
-import "v-charts/lib/style.css";
-import "echarts/lib/component/dataZoom";
-import countTo from "vue-count-to";
-import veLine from "v-charts/lib/line.common";
-import searchHeader from "./modals/searchHeader";
-import { getTimes } from "@/utils";
-import { getIndex } from "../api";
-import { descs, chioseNum } from "../constant";
-import { getNowDateAndSunday, getNowDateAndMonday } from "@/utils/date";
-export default {
-  props: ["data", "search"],
-  components: {
-    "count-to": countTo,
-    "ve-line": veLine,
-    searchHeader,
-  },
-  computed: {
-    legend() {
-      return {
-        left: "10px",
-      };
-    },
-    items() {
-      let obj = {}
-      let arr = ["HOMEWORK_CREATE_RATE","HOMEWORK_SUBMIT_RATE","HOMEWORK_COMMENT_RATE"]
-      arr.forEach(str=>{
-        if(this.data[str]){
-          obj[str]=this.data[str]
-        }
-      })
-      /**
-       *  {
-        HOMEWORK_CREATE_RATE:this.data["HOMEWORK_CREATE_RATE"] || {},
-        HOMEWORK_SUBMIT_RATE: this.data["HOMEWORK_SUBMIT_RATE"] || {},
-        HOMEWORK_COMMENT_RATE: this.data["HOMEWORK_COMMENT_RATE"] || {},
-      };
-       */
-      return obj
-    },
-    chartExtend() {
-      return {
-        series: {
-           type: 'line',
-          smooth: false,
-        },
-        yAxis: {
-          //纵轴标尺固定
-          minInterval: 1,
-          type: "value",
-          scale: true,
-          min: 0,
-          max: 100,
-          axisLabel: {
-            formatter: "{value}%",
-          },
-        },
-        tooltip: {
-          axisPointer: {
-            type: "shadow",
-            shadowStyle: {
-              color: "rgba(150,150,150,0.2)",
-            },
-          },
-
-          formatter: (item) => {
-            return [
-              item[0].axisValueLabel,
-              ...item.map(
-                (d) => `<br/>${d.marker}${d.seriesName}: ${d.value[1]} %`
-              ),
-            ].join("");
-          },
-        },
-      };
-    },
-    dataZoom() {
-      return [
-        {
-          grid: {
-            left: "0%",
-          },
-          type: "slider",
-          start: 40,
-          end: 100,
-        },
-      ];
-    },
-    chartData() {
-      const values = Object.values(this.items);
-      const months = {};
-      for (const item of values) {
-        for (const row of item.indexMonthData || []) {
-          const key = this.$helpers.dayjs(row.month).format("YYYY-MM-DD");
-          if (!months[key]) {
-            months[key] = {
-              日期: key + "/" + getNowDateAndSunday(key),
-            };
-          }
-          months[key][item.title] = row.percent;
-        }
-      }
-
-      return {
-        columns: [
-          "日期",
-          ...values.map((item) => {
-            return item.title;
-          }),
-        ],
-        rows: Object.values(months),
-      };
-    },
-    dataEmpty() {
-      return !this.chartData.rows.length;
-    },
-  },
-  data() {
-    return {
-      active: "ACTIVATION_RATE",
-      mdate: [],
-      loading: false,
-    };
-  },
-  mounted() {
-    this.init()
-  },
-  methods: {
-    init() {
-      let nowTiem = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
-      let startTime = this.$helpers
-        .dayjs(getNowDateAndMonday(nowTiem))
-        .subtract(49, "day")
-        .format("YYYY-MM-DD");
-      let endTime = getNowDateAndSunday(nowTiem);
-      this.mdate = [startTime, endTime];
-      this.FetchDetail();
-    },
-    changeValue(date) {
-      // 请求更改数据
-      this.mdate = date;
-      //  this.isDayOrMoth(date)
-      this.FetchDetail();
-    },
-    async FetchDetail() {
-      this.loading = true;
-      let data = [];
-      try {
-        const { dates, ...rest } = this.search;
-        const res = await getIndex({
-          ...rest,
-          ...getTimes(this.mdate, ["startDate", "endDate"]),
-          dataTypes:
-            "HOMEWORK_CREATE_RATE,HOMEWORK_SUBMIT_RATE,HOMEWORK_COMMENT_RATE",
-        });
-        for (const item of res.data) {
-          // 再循环一遍
-          for (const key in this.items) {
-            if (item.dataType == key) {
-              data[item.dataType] = {
-                ...item,
-                desc: descs[item.dataType],
-              };
-            }
-          }
-        }
-      } catch (error) {
-        console.log(error);
-      }
-      this.loading = false;
-      // this.dataInfo = data;
-      this.$emit("resetDate", data);
-    },
-  },
-};
-</script>
-<style lang="less" scoped>
-// .statistic{
-//   /deep/ .statistic-content{
-//     cursor: pointer;
-//     &.active > span{
-//       color: var(--color-primary) !important;
-//     }
-//   }
-// }
-.chioseBox {
-  position: absolute;
-  right: 20px;
-  z-index: 1000;
-}
-.wrap {
-  position: relative;
-}
-</style>
+<template
+>
+  <el-card header="">
+    <div slot="header" class="clearfix">
+      <searchHeader
+        v-if="mdate.length > 0"
+        :dates="mdate"
+        :title="'业务数据'"
+        :isShowQuert="false"
+        @changeValue="changeValue"
+      />
+    </div>
+
+    <div class="wall" style="height:68px;" v-if="JSON.stringify(items) == '{}'"></div>
+    <statistic class="statistic" :cols="0">
+      <statistic-item
+        v-for="(item, key) in items"
+        :key="key"
+        :class="{ active: active === key }"
+        @click="active = key"
+      >
+        <span>
+          {{ item.title+'(%)'}}
+          <el-tooltip
+            v-if="item.desc"
+            :content="item.desc"
+            :open-delay="0.3"
+            placement="top"
+          >
+            <i
+              style="margin-left: 5px; cursor: pointer"
+              class="el-icon-warning-outline"
+            />
+          </el-tooltip>
+        </span>
+        <span> <count-to :endVal="item.percent" :decimals="2" /></span>
+      </statistic-item>
+    </statistic>
+    <!--   :data-zoom="dataZoom" -->
+    <ve-line
+      style="width: 100%"
+      height="350px"
+      :data="chartData"
+      :data-empty="dataEmpty"
+      :extend="chartExtend"
+      :legend="legend"
+    ></ve-line>
+  </el-card>
+</template>
+<script>
+import "v-charts/lib/style.css";
+import "echarts/lib/component/dataZoom";
+import countTo from "vue-count-to";
+import veLine from "v-charts/lib/line.common";
+import searchHeader from "./modals/searchHeader";
+import { getTimes } from "@/utils";
+import { getIndex } from "../api";
+import { descs, chioseNum } from "../constant";
+import { getNowDateAndSunday, getNowDateAndMonday } from "@/utils/date";
+export default {
+  props: ["data", "search"],
+  components: {
+    "count-to": countTo,
+    "ve-line": veLine,
+    searchHeader,
+  },
+  computed: {
+    legend() {
+      return {
+        left: "10px",
+      };
+    },
+    items() {
+      let obj = {}
+      let arr = ["HOMEWORK_CREATE_RATE","HOMEWORK_SUBMIT_RATE","HOMEWORK_COMMENT_RATE"]
+      arr.forEach(str=>{
+        if(this.data[str]){
+          obj[str]=this.data[str]
+        }
+      })
+      /**
+       *  {
+        HOMEWORK_CREATE_RATE:this.data["HOMEWORK_CREATE_RATE"] || {},
+        HOMEWORK_SUBMIT_RATE: this.data["HOMEWORK_SUBMIT_RATE"] || {},
+        HOMEWORK_COMMENT_RATE: this.data["HOMEWORK_COMMENT_RATE"] || {},
+      };
+       */
+      return obj
+    },
+    chartExtend() {
+      return {
+        series: {
+           type: 'line',
+          smooth: false,
+        },
+        yAxis: {
+          //纵轴标尺固定
+          minInterval: 1,
+          type: "value",
+          scale: true,
+          min: 0,
+          max: 100,
+          axisLabel: {
+            formatter: "{value}%",
+          },
+        },
+        tooltip: {
+          axisPointer: {
+            type: "shadow",
+            shadowStyle: {
+              color: "rgba(150,150,150,0.2)",
+            },
+          },
+
+          formatter: (item) => {
+            return [
+              item[0].axisValueLabel,
+              ...item.map(
+                (d) => `<br/>${d.marker}${d.seriesName}: ${d.value[1]} %`
+              ),
+            ].join("");
+          },
+        },
+      };
+    },
+    dataZoom() {
+      return [
+        {
+          grid: {
+            left: "0%",
+          },
+          type: "slider",
+          start: 40,
+          end: 100,
+        },
+      ];
+    },
+    chartData() {
+      const values = Object.values(this.items);
+      const months = {};
+      for (const item of values) {
+        for (const row of item.indexMonthData || []) {
+          const key = this.$helpers.dayjs(row.month).format("YYYY-MM-DD");
+          if (!months[key]) {
+            months[key] = {
+              日期: key + "/" + getNowDateAndSunday(key),
+            };
+          }
+          months[key][item.title] = row.percent;
+        }
+      }
+
+      return {
+        columns: [
+          "日期",
+          ...values.map((item) => {
+            return item.title;
+          }),
+        ],
+        rows: Object.values(months),
+      };
+    },
+    dataEmpty() {
+      return !this.chartData.rows.length;
+    },
+  },
+  data() {
+    return {
+      active: "ACTIVATION_RATE",
+      mdate: [],
+      loading: false,
+    };
+  },
+  mounted() {
+    this.init()
+  },
+  methods: {
+    init() {
+      let nowTiem = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
+      let startTime = this.$helpers
+        .dayjs(getNowDateAndMonday(nowTiem))
+        .subtract(49, "day")
+        .format("YYYY-MM-DD");
+      let endTime = getNowDateAndSunday(nowTiem);
+      this.mdate = [startTime, endTime];
+      this.FetchDetail();
+    },
+    changeValue(date) {
+      // 请求更改数据
+      this.mdate = date;
+      //  this.isDayOrMoth(date)
+      this.FetchDetail();
+    },
+    async FetchDetail() {
+      this.loading = true;
+      let data = [];
+      try {
+        const { dates, ...rest } = this.search;
+        const res = await getIndex({
+          ...rest,
+          ...getTimes(this.mdate, ["startDate", "endDate"]),
+          dataTypes:
+            "HOMEWORK_CREATE_RATE,HOMEWORK_SUBMIT_RATE,HOMEWORK_COMMENT_RATE",
+        });
+        for (const item of res.data) {
+          // 再循环一遍
+          for (const key in this.items) {
+            if (item.dataType == key) {
+              data[item.dataType] = {
+                ...item,
+                desc: descs[item.dataType],
+              };
+            }
+          }
+        }
+      } catch (error) {
+        console.log(error);
+      }
+      this.loading = false;
+      // this.dataInfo = data;
+      this.$emit("resetDate", data);
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+// .statistic{
+//   /deep/ .statistic-content{
+//     cursor: pointer;
+//     &.active > span{
+//       color: var(--color-primary) !important;
+//     }
+//   }
+// }
+.chioseBox {
+  position: absolute;
+  right: 20px;
+  z-index: 1000;
+}
+.wrap {
+  position: relative;
+}
+</style>

+ 306 - 306
src/views/main/baseinfo/index.vue

@@ -1,306 +1,306 @@
-<template>
-  <div class="container main">
-    <save-form
-      inline
-      :model="search"
-      @submit="FetchDetail"
-      @reset="reset"
-      saveKey="/main/main/baseInfo"
-    >
-      <!-- <el-form-item prop="year">
-        <el-date-picker
-          v-model="search.dates"
-          type="daterange"
-          align="right"
-          unlink-panels
-          range-separator="至"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :picker-options="pickerOptions">
-        </el-date-picker>
-      </el-form-item> -->
-      <el-form-item prop="organId">
-        <el-select
-          clearable
-          filterable
-          placeholder="请选择分部"
-          v-model="search.organId"
-        >
-          <el-option
-            v-for="(item, index) in selects.branchs"
-            :key="index"
-            :label="item.name"
-            :value="item.id"
-          ></el-option>
-        </el-select>
-      </el-form-item>
-      <el-button native-type="submit" type="primary">搜索</el-button>
-      <el-button native-type="reset" type="danger">重置</el-button>
-    </save-form>
-    <!-- <el-alert type="info" :closable="false" style="margin-bottom: 20px;">
-      每日0点更新前一日数据
-    </el-alert> -->
-    <!-- 这里显示选项卡 -->
-    <empty desc="暂无统计数据" v-if="isEmpty" />
-    <el-row v-else class="rows" :gutter="20">
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
-        <studentbaseinfo :data="dataInfo" />
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
-        <operate :data="dataInfo" />
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="8">
-        <hrdata :data="dataInfo" />
-      </el-col>
-      <!-- <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <surplusCourse :data="dataInfo" />
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
-        <useCourse :data="dataInfo" />
-      </el-col> -->
-      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-        <management
-          ref="management"
-          :data="dataInfo"
-          :search="search"
-          @resetDate="resetDate"
-        />
-      </el-col>
-      <el-col
-        :xs="24"
-        :sm="24"
-        :md="24"
-        :lg="24"
-        :xl="24"
-        v-if="$helpers.tenantId == 1"
-      >
-        <business
-          ref="business"
-          :data="dataInfo"
-          :search="search"
-          @resetDate="resetDate"
-        />
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-        <curriculum
-          :data="dataInfo"
-          ref="curriculum"
-          :search="search"
-          @resetDate="resetDate"
-        />
-      </el-col>
-      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
-        <student
-          :data="dataInfo"
-          ref="student"
-          :search="search"
-          @resetDate="resetDate"
-        />
-      </el-col>
-
-      <!-- <el-col :xs="24" :sm="24" :md="12">
-        <operate :data="dataInfo"/>
-      </el-col> -->
-      <!-- <el-col :xs="24" :sm="24" :md="12">
-        <hrdata :data="dataInfo"/>
-      </el-col> -->
-    </el-row>
-  </div>
-</template>
-<script>
-import { getIndex } from "../api";
-import operate from "./operate";
-import business from "./business";
-import management from "./management";
-import hrdata from "./hr";
-import student from "./student";
-import curriculum from "./curriculum";
-import studentbaseinfo from "./studentBaseinfo";
-import surplusCourse from "./surplusCourse";
-import useCourse from "./useCourse";
-import { getTimes } from "@/utils";
-import { descs } from "../constant";
-
-export default {
-  components: {
-    operate,
-    business,
-    management,
-    hrdata,
-    student,
-    curriculum,
-    studentbaseinfo,
-    surplusCourse,
-    useCourse,
-  },
-  data() {
-    return {
-      pickerOptions: {
-        firstDayOfWeek: 1,
-        disabledDate: (a) => {
-          const { dayjs } = this.$helpers;
-          return dayjs(a).isAfter(dayjs().subtract(1, "day"));
-        },
-        shortcuts: [
-          {
-            text: "最近一周",
-            onClick(picker) {
-              const end = new Date();
-              const start = new Date();
-              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
-              start.setTime(start.getTime() - 3600 * 1000 * 24 * 8);
-              picker.$emit("pick", [start, end]);
-            },
-          },
-          {
-            text: "最近一个月",
-            onClick(picker) {
-              const end = new Date();
-              const start = new Date();
-              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
-              start.setTime(start.getTime() - 3600 * 1000 * 24 * 31);
-              picker.$emit("pick", [start, end]);
-            },
-          },
-          {
-            text: "最近三个月",
-            onClick(picker) {
-              const end = new Date();
-              const start = new Date();
-              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
-              start.setTime(start.getTime() - 3600 * 1000 * 24 * 91);
-              picker.$emit("pick", [start, end]);
-            },
-          },
-        ],
-      },
-      search: {
-        dates: [],
-        organId: null,
-      },
-      dataInfo: {},
-      business: {},
-      loading: false,
-    };
-  },
-  computed: {
-    isEmpty() {
-      return !Object.keys(this.dataInfo).length;
-    },
-  },
-  created() {},
-  async mounted() {
-    this.$set(this.search, "dates", this.getInitDate());
-   await this.$store.dispatch("setBranchs");
-    this.FetchDetail();
-  },
-  methods: {
-    getInitDate() {
-      const end = this.$helpers
-        .dayjs(new Date())
-        .subtract(1, "day")
-        .format("YYYY-MM-DD");
-      const start = this.$helpers
-        .dayjs(new Date())
-        .subtract(1, "day")
-        .set("date", 1)
-        .format("YYYY-MM-DD");
-      return [start, end];
-    },
-    reset() {
-      this.$set(this.search, "dates", this.getInitDate());
-      this.$set(this.search, "organId", null);
-      this.FetchDetail();
-    },
-    async FetchDetail() {
-      // 这里改变就通知子组件重置
-
-      const data = {};
-      try {
-        const { dates, ...rest } = this.search;
-        const res = await getIndex({
-          ...rest,
-          ...getTimes(dates, ["startDate", "endDate"]),
-        });
-        for (const item of res.data) {
-          data[item.dataType] = {
-            ...item,
-            desc: descs[item.dataType],
-          };
-        }
-      } catch (error) {
-        console.log(error);
-      }
-      this.dataInfo = data;
-      if (this.$refs.business) {
-        this.$refs["business"].init();
-      }
-      if (this.$refs.management) {
-        this.$refs["management"].init();
-      }
-    },
-    resetDate(data) {
-      let arr = null;
-      for (let item in data) {
-        // console.log(item)
-        if (item == "VIP_GROUP_COURSE") {
-          arr = data[item].indexMonthData;
-        }
-        this.dataInfo[item] = data[item];
-      }
-      this.dataInfo = { ...this.dataInfo };
-    },
-  },
-};
-</script>
-<style lang="less" scoped>
-.container {
-  overflow: hidden;
-  .rows {
-    > div {
-      margin-bottom: 20px;
-    }
-  }
-  /deep/ .el-card__body .statistic {
-    margin-bottom: 15px;
-    padding: 0;
-  }
-}
-/deep/.el-card__header {
-  padding: 0 20px !important;
-}
-</style>
-<style lang="scss">
-.main {
-  .statistic {
-    .statistic-content > span {
-      font-size: 22px !important;
-      &:first-child {
-        font-size: 14px !important;
-      }
-    }
-  }
-}
-
-.box {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  height: 55px;
-  line-height: 55px;
-  .shape {
-    margin-right: 10px;
-    height: 18px;
-    width: 4px;
-    background-color: var(--color-primary);
-  }
-}
-.wall {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: center;
-  color: #888;
-  font-size: 14px;
-}
-</style>
+<template>
+  <div class="container main">
+    <save-form
+      inline
+      :model="search"
+      @submit="FetchDetail"
+      @reset="reset"
+      saveKey="/main/main/baseInfo"
+    >
+      <!-- <el-form-item prop="year">
+        <el-date-picker
+          v-model="search.dates"
+          type="daterange"
+          align="right"
+          unlink-panels
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :picker-options="pickerOptions">
+        </el-date-picker>
+      </el-form-item> -->
+      <el-form-item prop="organId">
+        <el-select
+          clearable
+          filterable
+          placeholder="请选择分部"
+          v-model="search.organId"
+        >
+          <el-option
+            v-for="(item, index) in selects.branchs"
+            :key="index"
+            :label="item.name"
+            :value="item.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-button native-type="submit" type="primary">搜索</el-button>
+      <el-button native-type="reset" type="danger">重置</el-button>
+    </save-form>
+    <!-- <el-alert type="info" :closable="false" style="margin-bottom: 20px;">
+      每日0点更新前一日数据
+    </el-alert> -->
+    <!-- 这里显示选项卡 -->
+    <empty desc="暂无统计数据" v-if="isEmpty" />
+    <el-row v-else class="rows" :gutter="20">
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
+        <studentbaseinfo :data="dataInfo" />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="8">
+        <operate :data="dataInfo" />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="8">
+        <hrdata :data="dataInfo" />
+      </el-col>
+      <!-- <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+        <surplusCourse :data="dataInfo" />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+        <useCourse :data="dataInfo" />
+      </el-col> -->
+      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+        <management
+          ref="management"
+          :data="dataInfo"
+          :search="search"
+          @resetDate="resetDate"
+        />
+      </el-col>
+      <el-col
+        :xs="24"
+        :sm="24"
+        :md="24"
+        :lg="24"
+        :xl="24"
+        v-if="$helpers.tenantId == 1"
+      >
+        <business
+          ref="business"
+          :data="dataInfo"
+          :search="search"
+          @resetDate="resetDate"
+        />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+        <curriculum
+          :data="dataInfo"
+          ref="curriculum"
+          :search="search"
+          @resetDate="resetDate"
+        />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
+        <student
+          :data="dataInfo"
+          ref="student"
+          :search="search"
+          @resetDate="resetDate"
+        />
+      </el-col>
+
+      <!-- <el-col :xs="24" :sm="24" :md="12">
+        <operate :data="dataInfo"/>
+      </el-col> -->
+      <!-- <el-col :xs="24" :sm="24" :md="12">
+        <hrdata :data="dataInfo"/>
+      </el-col> -->
+    </el-row>
+  </div>
+</template>
+<script>
+import { getIndex } from "../api";
+import operate from "./operate";
+import business from "./business";
+import management from "./management";
+import hrdata from "./hr";
+import student from "./student";
+import curriculum from "./curriculum";
+import studentbaseinfo from "./studentBaseinfo";
+import surplusCourse from "./surplusCourse";
+import useCourse from "./useCourse";
+import { getTimes } from "@/utils";
+import { descs } from "../constant";
+
+export default {
+  components: {
+    operate,
+    business,
+    management,
+    hrdata,
+    student,
+    curriculum,
+    studentbaseinfo,
+    surplusCourse,
+    useCourse,
+  },
+  data() {
+    return {
+      pickerOptions: {
+        firstDayOfWeek: 1,
+        disabledDate: (a) => {
+          const { dayjs } = this.$helpers;
+          return dayjs(a).isAfter(dayjs().subtract(1, "day"));
+        },
+        shortcuts: [
+          {
+            text: "最近一周",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 8);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近一个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 31);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "最近三个月",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 91);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+        ],
+      },
+      search: {
+        dates: [],
+        organId: null,
+      },
+      dataInfo: {},
+      business: {},
+      loading: false,
+    };
+  },
+  computed: {
+    isEmpty() {
+      return !Object.keys(this.dataInfo).length;
+    },
+  },
+  created() {},
+  async mounted() {
+    this.$set(this.search, "dates", this.getInitDate());
+   await this.$store.dispatch("setBranchs");
+    this.FetchDetail();
+  },
+  methods: {
+    getInitDate() {
+      const end = this.$helpers
+        .dayjs(new Date())
+        .subtract(1, "day")
+        .format("YYYY-MM-DD");
+      const start = this.$helpers
+        .dayjs(new Date())
+        .subtract(1, "day")
+        .set("date", 1)
+        .format("YYYY-MM-DD");
+      return [start, end];
+    },
+    reset() {
+      this.$set(this.search, "dates", this.getInitDate());
+      this.$set(this.search, "organId", null);
+      this.FetchDetail();
+    },
+    async FetchDetail() {
+      // 这里改变就通知子组件重置
+
+      const data = {};
+      try {
+        const { dates, ...rest } = this.search;
+        const res = await getIndex({
+          ...rest,
+          ...getTimes(dates, ["startDate", "endDate"]),
+        });
+        for (const item of res.data) {
+          data[item.dataType] = {
+            ...item,
+            desc: descs[item.dataType],
+          };
+        }
+      } catch (error) {
+        console.log(error);
+      }
+      this.dataInfo = data;
+      if (this.$refs.business) {
+        this.$refs["business"].init();
+      }
+      if (this.$refs.management) {
+        this.$refs["management"].init();
+      }
+    },
+    resetDate(data) {
+      let arr = null;
+      for (let item in data) {
+        // console.log(item)
+        if (item == "VIP_GROUP_COURSE") {
+          arr = data[item].indexMonthData;
+        }
+        this.dataInfo[item] = data[item];
+      }
+      this.dataInfo = { ...this.dataInfo };
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+.container {
+  overflow: hidden;
+  .rows {
+    > div {
+      margin-bottom: 20px;
+    }
+  }
+  /deep/ .el-card__body .statistic {
+    margin-bottom: 15px;
+    padding: 0;
+  }
+}
+/deep/.el-card__header {
+  padding: 0 20px !important;
+}
+</style>
+<style lang="scss">
+.main {
+  .statistic {
+    .statistic-content > span {
+      font-size: 22px !important;
+      &:first-child {
+        font-size: 14px !important;
+      }
+    }
+  }
+}
+
+.box {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  height: 55px;
+  line-height: 55px;
+  .shape {
+    margin-right: 10px;
+    height: 18px;
+    width: 4px;
+    background-color: var(--color-primary);
+  }
+}
+.wall {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  color: #888;
+  font-size: 14px;
+}
+</style>

+ 506 - 506
src/views/main/baseinfo/management.vue

@@ -1,506 +1,506 @@
-<template >
-  <el-card id="management">
-    <div slot="header" class="clearfix">
-      <searchHeader
-        :dates="mdate"
-        :title="'经营数据'"
-        @changeValue="changeValue"
-        :isShowQuert="true"
-        :endDate="endDate"
-        ref="searchHeader"
-      />
-    </div>
-    <div
-      class="wall"
-      style="height: 68px"
-      v-if="JSON.stringify(items) == '{}'"
-    ></div>
-    <statistic :col="5" class="statistic" :cols="12">
-      <statistic-item @click="active = 0">
-        <span>
-          {{ items["TOTAL_AMOUNT"].title + "(元)" }}
-          <el-tooltip
-            v-if="items['TOTAL_AMOUNT'].desc"
-            :content="items['TOTAL_AMOUNT'].desc"
-            :open-delay="0.3"
-            placement="top"
-          >
-            <i
-              style="margin-left: 5px; cursor: pointer"
-              class="el-icon-warning-outline"
-            />
-          </el-tooltip>
-        </span>
-        <!--  12345678901.23 -->
-        <span>
-          <count-to
-            :endVal="items['TOTAL_AMOUNT'].percent"
-            :decimals="2"
-            :class="'blod'"
-        /></span>
-      </statistic-item>
-      <template v-for="(item, key) in items">
-        <statistic-item
-          v-if="item.dataType != 'TOTAL_AMOUNT'"
-          :key="key"
-          :class="{ active: active === key }"
-          @click="active = key"
-        >
-          <span>
-            {{ item.title + "(元)" }}
-            <el-tooltip
-              v-if="item.desc"
-              :content="item.desc"
-              :open-delay="0.3"
-              placement="top"
-            >
-              <i
-                style="margin-left: 5px; cursor: pointer"
-                class="el-icon-warning-outline"
-              />
-            </el-tooltip>
-          </span>
-          <!--  12345678901.23 -->
-          <span>
-            <count-to
-              :endVal="item.percent"
-              :decimals="2"
-              :class="item.dataType == 'TOTAL_AMOUNT' ? 'blod' : ''"
-          /></span>
-        </statistic-item>
-      </template>
-
-      <statistic-item></statistic-item>
-    </statistic>
-
-    <div class="statisticWrap">
-      <statistic :col="5" class="statistic" :cols="0" :isNoLine='true'>
-        <statistic-item
-          v-for="(item, key) in items2"
-          :key="key"
-          :class="{ active: active === key }"
-          @click="active = key"
-        >
-          <template v-if="item.dataType != 'OTHER_AMOUNT'">
-            <span>
-              {{ item.title + "(元)" }}
-              <!-- {{ item.dataType == 'OTHER_AMOUNT' ? 'other' : null }} -->
-              <el-tooltip
-                v-if="item.desc"
-                :content="item.desc"
-                :open-delay="0.3"
-                placement="top"
-              >
-                <i
-                  style="margin-left: 5px; cursor: pointer"
-                  class="el-icon-warning-outline"
-                />
-              </el-tooltip>
-            </span>
-            <!--  12345678901.23 -->
-            <span> <count-to :endVal="item.percent" :decimals="2" /></span>
-          </template>
-          <el-popover
-            placement="top"
-            width="400"
-            v-else
-            trigger="hover">
-            <descriptions :column="1" v-if="(item.indexMonthDataDetail || []).length > 0">
-              <template v-for="(item, index) in item.indexMonthDataDetail || []">
-                <descriptions-item :key="index" :label="orderType[item.orderType]">{{ item.totalNum | moneyFormat }}元
-                </descriptions-item>
-              </template>
-            </descriptions>
-            <div slot="reference">
-              <span>
-                {{ item.title + "(元)" }}
-                <el-tooltip
-                  v-if="item.desc"
-                  :content="item.desc"
-                  :open-delay="0.3"
-                  placement="top"
-                >
-                  <i
-                    style="margin-left: 5px; cursor: pointer"
-                    class="el-icon-warning-outline"
-                  />
-                </el-tooltip>
-              </span>
-              <span style="font-size: 18px; display: block; color: rgba(0, 0, 0, 0.85);"> <count-to :endVal="item.percent" :decimals="2" /></span>
-            </div>
-          </el-popover>
-        </statistic-item>
-      </statistic>
-    </div>
-
-    <!-- 按月/按天 -->
-
-    <div class="wrap">
-      <div class="chioseBox">
-        <el-radio-group v-model="timer" >
-          <el-radio-button label="day">按天</el-radio-button>
-          <el-radio-button label="month">按月</el-radio-button>
-        </el-radio-group>
-      </div>
-      <!--   v-if="timer == 'day'"     :settings="chartSettings"   DD-->
-      <ve-line
-        :data="timer == 'day' ? chartData : chartDataForMoth"
-        height="350px"
-        :data-empty="dataEmpty"
-        :extend="chartExtend"
-        :legend="legend"
-      />
-      <!-- <ve-line
-      v-else
-        :data-zoom="dataZoom"
-        :settings="{
-          area: true,
-        }"
-        :data="chartDataForMoth"
-        height="350px"
-        :data-empty="dataEmpty"
-        :data-zoom="dataZoom"
-        :extend="chartExtend"
-        :legend="legend"
-      /> -->
-    </div>
-  </el-card>
-</template>
-<script>
-import "echarts/lib/component/dataZoom";
-import countTo from "vue-count-to";
-import veLine from "v-charts/lib/line.common";
-import histogram from "v-charts/lib/histogram.common";
-import searchHeader from "./modals/searchHeader";
-import { getIndex } from "../api";
-import { getTimes } from "@/utils";
-import { descs, chioseNum } from "../constant";
-import { orderType } from '@/constant'
-
-export default {
-  props: ["data", "search"],
-  components: {
-    "ve-line": veLine,
-    "count-to": countTo,
-    "ve-histogram": histogram,
-    searchHeader,
-  },
-  computed: {
-    legend() {
-      return {
-        left: "10px",
-      };
-    },
-    items() {
-      let obj = {};
-      let arr = [
-        "TOTAL_AMOUNT",
-        "FINANCE_AMOUNT",
-        "FINANCE_BALANCE_AMOUNT",
-        "FINANCE_PAY",
-      ];
-      arr.forEach((str) => {
-        if (this.data[str]) {
-          obj[str] = this.data[str];
-        }
-      });
-      // console.log(obj);
-      return obj;
-    },
-    items2() {
-      let obj = {};
-      let arr = [
-        "APPLY_AMOUNT",
-        "RENEW_AMOUNT",
-        "VIP_AMOUNT",
-        "PRACTICE_AMOUNT",
-        "OTHER_AMOUNT",
-      ];
-      arr.forEach((str) => {
-        if (this.data[str]) {
-          obj[str] = this.data[str];
-        }
-      });
-      return obj;
-    },
-    chartExtend() {
-      return {
-        series: {
-          smooth: false,
-        },
-
-        yAxis: {
-          //纵轴标尺固定
-          minInterval: 1,
-          type: "value",
-          scale: true,
-          min: 0,
-          axisLabel: {
-            formatter: "{value}元",
-          },
-        },
-
-        tooltip: {
-          axisPointer: {
-            type: "shadow",
-            shadowStyle: {
-              color: "rgba(150,150,150,0.2)",
-            },
-          },
-
-          formatter: (item) => {
-            return [
-              item[0].axisValueLabel,
-              ...item.map(
-                (d) => `<br/>${d.marker}${d.seriesName}: ${d.value[1]} 元`
-              ),
-            ].join("");
-          },
-        },
-      };
-    },
-    dataZoom() {
-      return [
-        {
-          type: "slider",
-          start: 50,
-          end: 100,
-          filterMode: "empty",
-        },
-      ];
-    },
-    chartData() {
-      const values = Object.values({ ...this.items, ...this.items2 });
-      const months = {};
-      for (const item of values) {
-        for (const row of item.indexMonthData || []) {
-          const key = this.$helpers.dayjs(row.month).format("YYYY-MM-DD");
-          if (!months[key]) {
-            months[key] = {
-              日期: key,
-            };
-          }
-          months[key][item.title] = row.percent;
-        }
-      }
-      // console.log(values);
-      return {
-        columns: [
-          "日期",
-          "总收入",
-          "现金收入",
-          "余额收入",
-          "财务支出",
-          "报名缴费收入",
-          "网管课收入",
-          "其他收入",
-          "乐团续费收入",
-          "VIP课收入",
-        ],
-        rows: Object.values(months),
-        loading: true,
-      };
-    },
-    chartDataForMoth() {
-      const values = Object.values({ ...this.items, ...this.items2 });
-      const months = {};
-      for (const item of values) {
-        for (const row of item.indexMonthData || []) {
-          const key = this.$helpers.dayjs(row.month).format("YYYY-MM");
-
-          if (!months[key]) {
-            months[key] = {
-              月份: key,
-            };
-            months[key][item.title] = row.percent;
-          } else {
-            if (months[key][item.title]) {
-              months[key][item.title] = (
-                parseFloat(months[key][item.title]) + parseFloat(row.percent)
-              ).toFixed(2);
-            } else {
-              months[key][item.title] = parseFloat(row.percent).toFixed(2);
-            }
-          }
-        }
-      }
-      return {
-        columns: [
-          "月份",
-          "总收入",
-          "现金收入",
-          "余额收入",
-          "财务支出",
-          "报名缴费收入",
-          "网管课收入",
-          "其他收入",
-          "乐团续费收入",
-          "VIP课收入",
-        ],
-        rows: Object.values(months),
-        loading: true,
-      };
-    },
-    dataEmpty() {
-      return !this.chartData.rows.length;
-    },
-  },
-  data() {
-    // this.chartSettings = {
-    //   stack: { 总收入: [`现金收入`, `余额收入`] },
-    // };
-    return {
-      orderType,
-      active: "SHOULD_INCOME_MONEY",
-      timer: "day",
-      mdate: [],
-      loading: false,
-      endDate: "",
-      show: true,
-    };
-  },
-  mounted() {
-    this.init();
-  },
-  methods: {
-    init() {
-      this.$refs.searchHeader.initStatue('month');
-      this.mdate = this.getInitDate();
-      this.endDate = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
-      // this.$refs.searchHeader.initStatue()
-      this.changeValue(this.mdate);
-    },
-    changeValue(date) {
-      // 请求更改数据
-      this.mdate = date;
-      this.isDayOrMoth(date);
-      this.FetchDetail();
-    },
-    async FetchDetail() {
-      this.loading = true;
-      let data = [];
-      try {
-        const { dates, ...rest } = this.search;
-        const res = await getIndex({
-          ...rest,
-          ...getTimes(this.mdate, ["startDate", "endDate"]),
-          //
-          dataTypes:
-            "FINANCE_AMOUNT,FINANCE_BALANCE_AMOUNT,FINANCE_PAY,TOTAL_AMOUNT",
-        });
-        for (const item of res.data) {
-          // 再循环一遍
-          for (const key in { ...this.items, ...this.items2 }) {
-            // console.log(key);
-            if (item.dataType == key) {
-              data[item.dataType] = {
-                ...item,
-                desc: descs[item.dataType],
-              };
-            }
-          }
-        }
-      } catch (error) {
-        console.log(error);
-      }
-      this.loading = false;
-      this.$emit("resetDate", data);
-    },
-
-    isDayOrMoth(arr) {
-      if (!arr || arr.length < 1) {
-        this.timer = "day";
-      } else {
-        const count = this.$helpers
-          .dayjs(arr[0])
-          .diff(this.$helpers.dayjs(arr[1]), "day");
-        Math.abs(count) > chioseNum
-          ? (this.timer = "month")
-          : (this.timer = "day");
-      }
-    },
-    getInitDate() {
-      const end = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
-      const start = this.$helpers
-        .dayjs(new Date())
-        .set("date", 1)
-        .format("YYYY-MM-DD");
-      return [start, end];
-    },
-  },
-};
-</script>
-<style lang="less" scoped>
-/deep/.description-title {
-  margin-bottom: 0;
-}
-#management .statistic .statistic-content > span {
-  &:first-child {
-    font-size: 14px !important;
-  }
-  font-size: 18px !important;
-}
-.chioseBox {
-  position: absolute;
-  right: 20px;
-  z-index: 1000;
-}
-.wrap {
-  position: relative;
-}
-.blod {
-  font-weight: bold;
-  color: var(--color-primary);
-  font-size: 22px !important;
-}
-// .chioseBox {
-//   position: absolute;
-//   right: 20px;
-//   z-index: 1000;
-// }
-// .wrap {
-//   position: relative;
-// }
-.statisticWrap {
-  // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
-  border-radius: 4px;
-  border: 1px solid #ebeef5;
-  padding-top: 10px;
-  margin-bottom: 10px;
-  position: relative;
-  &:after {
-    top: -16px;
-    margin-left:calc(10% - 14px) ;
-    border-top-width: 0;
-    border-color: transparent;
-    border-bottom-color: #fff;
-    content: " ";
-    border-width: 8px;
-    position: absolute;
-    display: block;
-    width: 0;
-    height: 0;
-
-    border-style: solid;
-    z-index: 101;
-
-  }
-      &:before {
-      top: -20px;
-       margin-left:calc(10% - 16px) ;
-
-      border-top-width: 0;
-      border-color: transparent;
-      border-bottom-color: #ebeef5;
-      content: " ";
-      border-width: 10px;
-      position: absolute;
-      display: block;
-      width: 0;
-      height: 0;
-
-      border-style: solid;
-      z-index: 100;
-    }
-}
-</style>
+<template >
+  <el-card id="management">
+    <div slot="header" class="clearfix">
+      <searchHeader
+        :dates="mdate"
+        :title="'经营数据'"
+        @changeValue="changeValue"
+        :isShowQuert="true"
+        :endDate="endDate"
+        ref="searchHeader"
+      />
+    </div>
+    <div
+      class="wall"
+      style="height: 68px"
+      v-if="JSON.stringify(items) == '{}'"
+    ></div>
+    <statistic :col="5" class="statistic" :cols="12">
+      <statistic-item @click="active = 0">
+        <span>
+          {{ items["TOTAL_AMOUNT"].title + "(元)" }}
+          <el-tooltip
+            v-if="items['TOTAL_AMOUNT'].desc"
+            :content="items['TOTAL_AMOUNT'].desc"
+            :open-delay="0.3"
+            placement="top"
+          >
+            <i
+              style="margin-left: 5px; cursor: pointer"
+              class="el-icon-warning-outline"
+            />
+          </el-tooltip>
+        </span>
+        <!--  12345678901.23 -->
+        <span>
+          <count-to
+            :endVal="items['TOTAL_AMOUNT'].percent"
+            :decimals="2"
+            :class="'blod'"
+        /></span>
+      </statistic-item>
+      <template v-for="(item, key) in items">
+        <statistic-item
+          v-if="item.dataType != 'TOTAL_AMOUNT'"
+          :key="key"
+          :class="{ active: active === key }"
+          @click="active = key"
+        >
+          <span>
+            {{ item.title + "(元)" }}
+            <el-tooltip
+              v-if="item.desc"
+              :content="item.desc"
+              :open-delay="0.3"
+              placement="top"
+            >
+              <i
+                style="margin-left: 5px; cursor: pointer"
+                class="el-icon-warning-outline"
+              />
+            </el-tooltip>
+          </span>
+          <!--  12345678901.23 -->
+          <span>
+            <count-to
+              :endVal="item.percent"
+              :decimals="2"
+              :class="item.dataType == 'TOTAL_AMOUNT' ? 'blod' : ''"
+          /></span>
+        </statistic-item>
+      </template>
+
+      <statistic-item></statistic-item>
+    </statistic>
+
+    <div class="statisticWrap">
+      <statistic :col="5" class="statistic" :cols="0" :isNoLine='true'>
+        <statistic-item
+          v-for="(item, key) in items2"
+          :key="key"
+          :class="{ active: active === key }"
+          @click="active = key"
+        >
+          <template v-if="item.dataType != 'OTHER_AMOUNT'">
+            <span>
+              {{ item.title + "(元)" }}
+              <!-- {{ item.dataType == 'OTHER_AMOUNT' ? 'other' : null }} -->
+              <el-tooltip
+                v-if="item.desc"
+                :content="item.desc"
+                :open-delay="0.3"
+                placement="top"
+              >
+                <i
+                  style="margin-left: 5px; cursor: pointer"
+                  class="el-icon-warning-outline"
+                />
+              </el-tooltip>
+            </span>
+            <!--  12345678901.23 -->
+            <span> <count-to :endVal="item.percent" :decimals="2" /></span>
+          </template>
+          <el-popover
+            placement="top"
+            width="400"
+            v-else
+            trigger="hover">
+            <descriptions :column="1" v-if="(item.indexMonthDataDetail || []).length > 0">
+              <template v-for="(item, index) in item.indexMonthDataDetail || []">
+                <descriptions-item :key="index" :label="orderType[item.orderType]">{{ item.totalNum | moneyFormat }}元
+                </descriptions-item>
+              </template>
+            </descriptions>
+            <div slot="reference">
+              <span>
+                {{ item.title + "(元)" }}
+                <el-tooltip
+                  v-if="item.desc"
+                  :content="item.desc"
+                  :open-delay="0.3"
+                  placement="top"
+                >
+                  <i
+                    style="margin-left: 5px; cursor: pointer"
+                    class="el-icon-warning-outline"
+                  />
+                </el-tooltip>
+              </span>
+              <span style="font-size: 18px; display: block; color: rgba(0, 0, 0, 0.85);"> <count-to :endVal="item.percent" :decimals="2" /></span>
+            </div>
+          </el-popover>
+        </statistic-item>
+      </statistic>
+    </div>
+
+    <!-- 按月/按天 -->
+
+    <div class="wrap">
+      <div class="chioseBox">
+        <el-radio-group v-model="timer" >
+          <el-radio-button label="day">按天</el-radio-button>
+          <el-radio-button label="month">按月</el-radio-button>
+        </el-radio-group>
+      </div>
+      <!--   v-if="timer == 'day'"     :settings="chartSettings"   DD-->
+      <ve-line
+        :data="timer == 'day' ? chartData : chartDataForMoth"
+        height="350px"
+        :data-empty="dataEmpty"
+        :extend="chartExtend"
+        :legend="legend"
+      />
+      <!-- <ve-line
+      v-else
+        :data-zoom="dataZoom"
+        :settings="{
+          area: true,
+        }"
+        :data="chartDataForMoth"
+        height="350px"
+        :data-empty="dataEmpty"
+        :data-zoom="dataZoom"
+        :extend="chartExtend"
+        :legend="legend"
+      /> -->
+    </div>
+  </el-card>
+</template>
+<script>
+import "echarts/lib/component/dataZoom";
+import countTo from "vue-count-to";
+import veLine from "v-charts/lib/line.common";
+import histogram from "v-charts/lib/histogram.common";
+import searchHeader from "./modals/searchHeader";
+import { getIndex } from "../api";
+import { getTimes } from "@/utils";
+import { descs, chioseNum } from "../constant";
+import { orderType } from '@/constant'
+
+export default {
+  props: ["data", "search"],
+  components: {
+    "ve-line": veLine,
+    "count-to": countTo,
+    "ve-histogram": histogram,
+    searchHeader,
+  },
+  computed: {
+    legend() {
+      return {
+        left: "10px",
+      };
+    },
+    items() {
+      let obj = {};
+      let arr = [
+        "TOTAL_AMOUNT",
+        "FINANCE_AMOUNT",
+        "FINANCE_BALANCE_AMOUNT",
+        "FINANCE_PAY",
+      ];
+      arr.forEach((str) => {
+        if (this.data[str]) {
+          obj[str] = this.data[str];
+        }
+      });
+      // console.log(obj);
+      return obj;
+    },
+    items2() {
+      let obj = {};
+      let arr = [
+        "APPLY_AMOUNT",
+        "RENEW_AMOUNT",
+        "VIP_AMOUNT",
+        "PRACTICE_AMOUNT",
+        "OTHER_AMOUNT",
+      ];
+      arr.forEach((str) => {
+        if (this.data[str]) {
+          obj[str] = this.data[str];
+        }
+      });
+      return obj;
+    },
+    chartExtend() {
+      return {
+        series: {
+          smooth: false,
+        },
+
+        yAxis: {
+          //纵轴标尺固定
+          minInterval: 1,
+          type: "value",
+          scale: true,
+          min: 0,
+          axisLabel: {
+            formatter: "{value}元",
+          },
+        },
+
+        tooltip: {
+          axisPointer: {
+            type: "shadow",
+            shadowStyle: {
+              color: "rgba(150,150,150,0.2)",
+            },
+          },
+
+          formatter: (item) => {
+            return [
+              item[0].axisValueLabel,
+              ...item.map(
+                (d) => `<br/>${d.marker}${d.seriesName}: ${d.value[1]} 元`
+              ),
+            ].join("");
+          },
+        },
+      };
+    },
+    dataZoom() {
+      return [
+        {
+          type: "slider",
+          start: 50,
+          end: 100,
+          filterMode: "empty",
+        },
+      ];
+    },
+    chartData() {
+      const values = Object.values({ ...this.items, ...this.items2 });
+      const months = {};
+      for (const item of values) {
+        for (const row of item.indexMonthData || []) {
+          const key = this.$helpers.dayjs(row.month).format("YYYY-MM-DD");
+          if (!months[key]) {
+            months[key] = {
+              日期: key,
+            };
+          }
+          months[key][item.title] = row.percent;
+        }
+      }
+      // console.log(values);
+      return {
+        columns: [
+          "日期",
+          "总收入",
+          "现金收入",
+          "余额收入",
+          "财务支出",
+          "报名缴费收入",
+          "网管课收入",
+          "其他收入",
+          "乐团续费收入",
+          "VIP课收入",
+        ],
+        rows: Object.values(months),
+        loading: true,
+      };
+    },
+    chartDataForMoth() {
+      const values = Object.values({ ...this.items, ...this.items2 });
+      const months = {};
+      for (const item of values) {
+        for (const row of item.indexMonthData || []) {
+          const key = this.$helpers.dayjs(row.month).format("YYYY-MM");
+
+          if (!months[key]) {
+            months[key] = {
+              月份: key,
+            };
+            months[key][item.title] = row.percent;
+          } else {
+            if (months[key][item.title]) {
+              months[key][item.title] = (
+                parseFloat(months[key][item.title]) + parseFloat(row.percent)
+              ).toFixed(2);
+            } else {
+              months[key][item.title] = parseFloat(row.percent).toFixed(2);
+            }
+          }
+        }
+      }
+      return {
+        columns: [
+          "月份",
+          "总收入",
+          "现金收入",
+          "余额收入",
+          "财务支出",
+          "报名缴费收入",
+          "网管课收入",
+          "其他收入",
+          "乐团续费收入",
+          "VIP课收入",
+        ],
+        rows: Object.values(months),
+        loading: true,
+      };
+    },
+    dataEmpty() {
+      return !this.chartData.rows.length;
+    },
+  },
+  data() {
+    // this.chartSettings = {
+    //   stack: { 总收入: [`现金收入`, `余额收入`] },
+    // };
+    return {
+      orderType,
+      active: "SHOULD_INCOME_MONEY",
+      timer: "day",
+      mdate: [],
+      loading: false,
+      endDate: "",
+      show: true,
+    };
+  },
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      this.$refs.searchHeader.initStatue('month');
+      this.mdate = this.getInitDate();
+      this.endDate = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
+      // this.$refs.searchHeader.initStatue()
+      this.changeValue(this.mdate);
+    },
+    changeValue(date) {
+      // 请求更改数据
+      this.mdate = date;
+      this.isDayOrMoth(date);
+      this.FetchDetail();
+    },
+    async FetchDetail() {
+      this.loading = true;
+      let data = [];
+      try {
+        const { dates, ...rest } = this.search;
+        const res = await getIndex({
+          ...rest,
+          ...getTimes(this.mdate, ["startDate", "endDate"]),
+          //
+          dataTypes:
+            "FINANCE_AMOUNT,FINANCE_BALANCE_AMOUNT,FINANCE_PAY,TOTAL_AMOUNT",
+        });
+        for (const item of res.data) {
+          // 再循环一遍
+          for (const key in { ...this.items, ...this.items2 }) {
+            // console.log(key);
+            if (item.dataType == key) {
+              data[item.dataType] = {
+                ...item,
+                desc: descs[item.dataType],
+              };
+            }
+          }
+        }
+      } catch (error) {
+        console.log(error);
+      }
+      this.loading = false;
+      this.$emit("resetDate", data);
+    },
+
+    isDayOrMoth(arr) {
+      if (!arr || arr.length < 1) {
+        this.timer = "day";
+      } else {
+        const count = this.$helpers
+          .dayjs(arr[0])
+          .diff(this.$helpers.dayjs(arr[1]), "day");
+        Math.abs(count) > chioseNum
+          ? (this.timer = "month")
+          : (this.timer = "day");
+      }
+    },
+    getInitDate() {
+      const end = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
+      const start = this.$helpers
+        .dayjs(new Date())
+        .set("date", 1)
+        .format("YYYY-MM-DD");
+      return [start, end];
+    },
+  },
+};
+</script>
+<style lang="less" scoped>
+/deep/.description-title {
+  margin-bottom: 0;
+}
+#management .statistic .statistic-content > span {
+  &:first-child {
+    font-size: 14px !important;
+  }
+  font-size: 18px !important;
+}
+.chioseBox {
+  position: absolute;
+  right: 20px;
+  z-index: 1000;
+}
+.wrap {
+  position: relative;
+}
+.blod {
+  font-weight: bold;
+  color: var(--color-primary);
+  font-size: 22px !important;
+}
+// .chioseBox {
+//   position: absolute;
+//   right: 20px;
+//   z-index: 1000;
+// }
+// .wrap {
+//   position: relative;
+// }
+.statisticWrap {
+  // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
+  border-radius: 4px;
+  border: 1px solid #ebeef5;
+  padding-top: 10px;
+  margin-bottom: 10px;
+  position: relative;
+  &:after {
+    top: -16px;
+    margin-left:calc(10% - 14px) ;
+    border-top-width: 0;
+    border-color: transparent;
+    border-bottom-color: #fff;
+    content: " ";
+    border-width: 8px;
+    position: absolute;
+    display: block;
+    width: 0;
+    height: 0;
+
+    border-style: solid;
+    z-index: 101;
+
+  }
+      &:before {
+      top: -20px;
+       margin-left:calc(10% - 16px) ;
+
+      border-top-width: 0;
+      border-color: transparent;
+      border-bottom-color: #ebeef5;
+      content: " ";
+      border-width: 10px;
+      position: absolute;
+      display: block;
+      width: 0;
+      height: 0;
+
+      border-style: solid;
+      z-index: 100;
+    }
+}
+</style>

+ 29 - 0
src/views/smallStudentManager/api.js

@@ -0,0 +1,29 @@
+import request2 from '@/utils/request2'
+
+// 获取首页数据
+export const studentSmallClassStatisticsSum = data => request2({
+  url: '/api-web/studentStatistics/studentSmallClassStatisticsSum',
+  params: data,
+  method: 'get',
+})
+
+
+// 获取首页数据
+export const studentSmallClassStatisticsSumForDate = data => request2({
+  url: '/api-web/studentStatistics/studentSmallClassStatisticsSumForDate',
+  data: data,
+  requestType: 'form',
+  method: 'post',
+})
+
+
+// 获取首页数据
+export const queryPage = data => request2({
+  url: '/api-web/studentStatistics/queryPage',
+  data: data,
+  requestType: 'form',
+  method: 'post',
+})
+
+// studentStatistics/queryPage分页查询
+// studentStatistics/studentSmallClassStatisticsSum?groupType=VIP汇总

+ 52 - 0
src/views/smallStudentManager/auditionClass.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="auditionClass">
+    <Statistics :groupType='groupType' />
+
+    <TableList :groupType='groupType' />
+  </div>
+</template>
+
+<script>
+import Statistics from './components/index';
+import TableList from './components/tableList';
+export default {
+  name: "auditionClass",
+  components: {
+    Statistics,
+    TableList
+  },
+  data() {
+    return {
+      groupType: 'PRACTICE'
+    }
+  },
+  async mounted() {
+
+  },
+  methods: {
+    __init() {
+
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.rows {
+    > div {
+      margin-bottom: 20px;
+    }
+  }
+/deep/ .el-card__body .statistic {
+  margin-bottom: 15px;
+  padding: 0;
+}
+.statistic {
+  .statistic-content > span {
+    font-size: 22px !important;
+    &:first-child {
+      font-size: 14px !important;
+    }
+  }
+}
+</style>

+ 67 - 0
src/views/smallStudentManager/components/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="Statistics">
+    <el-row class="rows" :gutter="20">
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+        <sleep :data="statistic" />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+        <studyStudent :data="statistic" />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+        <remainder :data="statistic" />
+      </el-col>
+      <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
+        <studentChange :groupType="groupType" />
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import sleep from './sleep';
+import studyStudent from './studyStudent';
+import remainder from './remainder';
+import studentChange from './studentChange';
+import { studentSmallClassStatisticsSum } from '../api'
+export default {
+  name: 'Statistics',
+  props: ['groupType'],
+  components: {
+    sleep,
+    studyStudent,
+    remainder,
+    studentChange
+  },
+  data() {
+    return {
+      statistic: {}
+    }
+  },
+  async mounted() {
+    try {
+      let res = await studentSmallClassStatisticsSum({ groupType: this.groupType })
+      this.statistic = res.data || {}
+    } catch {}
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.rows {
+    > div {
+      margin-bottom: 20px;
+    }
+  }
+/deep/ .el-card__body .statistic {
+  margin-bottom: 15px;
+  padding: 0;
+}
+.statistic {
+  .statistic-content > span {
+    font-size: 22px !important;
+    &:first-child {
+      font-size: 14px !important;
+    }
+  }
+}
+</style>

+ 96 - 0
src/views/smallStudentManager/components/remainder.vue

@@ -0,0 +1,96 @@
+<template>
+  <div>
+    <el-card>
+      <div slot="header" class="clearfix">
+        <div class="box">
+          <span class="shape"></span>
+          <span>剩余时间</span>
+        </div>
+      </div>
+      <!-- <div
+        class="wall"
+        style="height: 68px"
+        v-if="JSON.stringify(items) == '{}'&&!data['CHARGE_STUDENT_CHANGE_RATE']&&!data['ACTIVATION_RATE']"
+      >
+        暂无数据
+      </div> -->
+      <statistic :col="5" class="statistic" :cols="0">
+        <statistic-item
+          v-for="(item, key) in items"
+          :key="key"
+          :class="{ active: active === key }"
+          @click="active = key"
+        >
+          <span>
+            {{ item.text }}
+          </span>
+          <span> <count-to :endVal="item.value" /> </span>
+        </statistic-item>
+      </statistic>
+    </el-card>
+  </div>
+</template>
+<script>
+import countTo from "vue-count-to";
+export default {
+  props: ["data"],
+  components: {
+    "count-to": countTo,
+  },
+  data() {
+    return {
+      active: "",
+    };
+  },
+  computed: {
+    dataEmpty() {
+      return !this.chartData.rows.length;
+    },
+    items() {
+      let tempArr = [{
+        text: '待续费人数',
+        value: 0,
+        id: 'waitRenewNum'
+      }, {
+        text: '未开始课时',
+        value: 0,
+        id: 'subCourseNum'
+      }, {
+        text: '未排课课时',
+        value: 0,
+        id: 'noScheduleNum'
+      }]
+      tempArr.forEach(item => {
+        if(this.data[item.id]) {
+          item.value = this.data[item.id]
+        }
+      })
+      return tempArr
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+/deep/ .el-card__body .statistic {
+    margin-bottom: 15px;
+    padding: 0;
+  }
+/deep/.el-card__header {
+    padding: 0 20px!important;
+}
+.box {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  height: 55px;
+  line-height: 55px;
+  .shape {
+    margin-right: 10px;
+    height: 18px;
+    width: 4px;
+    background-color: var(--color-primary);
+  }
+}
+</style>
+

+ 96 - 0
src/views/smallStudentManager/components/sleep.vue

@@ -0,0 +1,96 @@
+<template>
+  <div>
+    <el-card>
+      <div slot="header" class="clearfix">
+        <div class="box">
+          <span class="shape"></span>
+          <span>沉睡学员</span>
+        </div>
+      </div>
+      <div
+        class="wall"
+        style="height: 68px"
+        v-if="JSON.stringify(items) == '{}'&&!data['CHARGE_STUDENT_CHANGE_RATE']&&!data['ACTIVATION_RATE']"
+      >
+        暂无数据
+      </div>
+      <statistic :col="5" class="statistic" :cols="0">
+        <statistic-item
+          v-for="(item, key) in items"
+          :key="key"
+          :class="{ active: active === key }"
+          @click="active = key"
+        >
+          <span>
+            {{ item.text }}
+          </span>
+          <span> <count-to :endVal="item.value" /> </span>
+        </statistic-item>
+      </statistic>
+    </el-card>
+  </div>
+</template>
+<script>
+import countTo from "vue-count-to";
+export default {
+  props: ["data"],
+  components: {
+    "count-to": countTo,
+  },
+  data() {
+    return {
+      active: "",
+    };
+  },
+  computed: {
+    dataEmpty() {
+      return !this.chartData.rows.length;
+    },
+    items() {
+      let tempArr = [{
+        text: '沉睡学员',
+        value: 0,
+        id: '待续费人数'
+      }, {
+        text: '有未排课',
+        value: 0,
+        id: 'sleepStudentHasNotSchedule'
+      }, {
+        text: '无未排课',
+        value: 0,
+        id: 'sleepStudentNoNotSchedule'
+      }]
+      tempArr.forEach(item => {
+        if(this.data[item.id]) {
+          item.value = this.data[item.id]
+        }
+      })
+      return tempArr
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+/deep/ .el-card__body .statistic {
+    margin-bottom: 15px;
+    padding: 0;
+  }
+/deep/.el-card__header {
+    padding: 0 20px!important;
+}
+.box {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  height: 55px;
+  line-height: 55px;
+  .shape {
+    margin-right: 10px;
+    height: 18px;
+    width: 4px;
+    background-color: var(--color-primary);
+  }
+}
+</style>
+

+ 155 - 0
src/views/smallStudentManager/components/studentChange.vue

@@ -0,0 +1,155 @@
+<template>
+  <div>
+    <el-card>
+      <div slot="header" class="clearfix">
+        <searchHeader
+          :dates="mdate"
+          :title="'经营数据'"
+          @changeValue="changeValue"
+          :isShowQuert="false"
+          :endDate="endDate"
+          ref="searchHeader"
+        />
+      </div>
+      <!-- <div
+        class="wall"
+        style="height: 68px"
+        v-if="JSON.stringify(items) == '{}'&&!data['CHARGE_STUDENT_CHANGE_RATE']&&!data['ACTIVATION_RATE']"
+      >
+        暂无数据
+      </div> -->
+      <statistic :col="5" class="statistic" :cols="0">
+        <statistic-item
+          v-for="(item, key) in items"
+          :key="key"
+          :class="{ active: active === key }"
+          @click="active = key"
+        >
+          <span>
+            {{ item.text }}
+          </span>
+          <span> <count-to :endVal="item.value" /> </span>
+        </statistic-item>
+      </statistic>
+    </el-card>
+  </div>
+</template>
+<script>
+import countTo from "vue-count-to";
+import searchHeader from "@/views/main/baseinfo/modals/searchHeader";
+import { studentSmallClassStatisticsSumForDate } from '../api';
+import { getTimes } from "@/utils";
+export default {
+  props: ["groupType"],
+  components: {
+    "count-to": countTo,
+    searchHeader
+  },
+  data() {
+    return {
+      active: "",
+      loading: false,
+      mdate: [],
+      endDate: '',
+      data: {},
+    };
+  },
+  computed: {
+    dataEmpty() {
+      return !this.chartData.rows.length;
+    },
+    items() {
+      let tempArr = [{
+        text: '流失人数',
+        value: 0,
+        id: 'lostNum'
+      }, {
+        text: '新增人数',
+        value: 0,
+        id: 'addNum'
+      }, {
+        text: '续费人数',
+        value: 0,
+        id: 'renewNum'
+      }, {
+        text: '回访人数',
+        value: 0,
+        id: 'visitNum'
+      }]
+      tempArr.forEach(item => {
+        if(this.data[item.id]) {
+          item.value = this.data[item.id]
+        }
+      })
+      return tempArr
+    },
+  },
+  mounted() {
+    this.init()
+  },
+  methods: {
+    init() {
+      let startTime = this.$helpers
+          .dayjs(new Date())
+          .set("date", 1)
+          .format("YYYY-MM-DD");
+      let endTime = this.$helpers
+              .dayjs(new Date())
+              .subtract(1, "day")
+              .format("YYYY-MM-DD");
+      this.mdate = [startTime, endTime];
+      this.FetchDetail();
+    },
+    getInitDate() {
+      const end = this.$helpers.dayjs(new Date()).format("YYYY-MM-DD");
+      const start = this.$helpers
+        .dayjs(new Date())
+        .set("date", 1)
+        .format("YYYY-MM-DD");
+      return [start, end];
+    },
+    changeValue(date) {
+      // 请求更改数据
+      this.mdate = date;
+      this.FetchDetail();
+    },
+    async FetchDetail() {
+      this.loading = true;
+      try {
+        const res = await studentSmallClassStatisticsSumForDate({
+          groupType: this.groupType,
+          ...getTimes(this.mdate, ["startDate", "endDate"]),
+        });
+        this.data = res.data || {};
+      } catch (error) {
+        console.log(error);
+      }
+      this.loading = false;
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+/deep/ .el-card__body .statistic {
+    margin-bottom: 15px;
+    padding: 0;
+  }
+/deep/.el-card__header {
+    padding: 0 20px!important;
+}
+.box {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  height: 55px;
+  line-height: 55px;
+  .shape {
+    margin-right: 10px;
+    height: 18px;
+    width: 4px;
+    background-color: var(--color-primary);
+  }
+}
+</style>
+

+ 100 - 0
src/views/smallStudentManager/components/studyStudent.vue

@@ -0,0 +1,100 @@
+<template>
+  <div>
+    <el-card>
+      <div slot="header" class="clearfix">
+        <div class="box">
+          <span class="shape"></span>
+          <span>在读学员</span>
+        </div>
+      </div>
+      <!-- <div
+        class="wall"
+        style="height: 68px"
+        v-if="JSON.stringify(items) == '{}'&&!data['CHARGE_STUDENT_CHANGE_RATE']&&!data['ACTIVATION_RATE']"
+      >
+        暂无数据
+      </div> -->
+      <statistic :col="5" class="statistic" :cols="0">
+        <statistic-item
+          v-for="(item, key) in items"
+          :key="key"
+          :class="{ active: active === key }"
+          @click="active = key"
+        >
+          <span>
+            {{ item.text }}
+          </span>
+          <span> <count-to :endVal="item.value" /> </span>
+        </statistic-item>
+      </statistic>
+    </el-card>
+  </div>
+</template>
+<script>
+import countTo from "vue-count-to";
+export default {
+  props: ["data"],
+  components: {
+    "count-to": countTo,
+  },
+  data() {
+    return {
+      active: "",
+    };
+  },
+  computed: {
+    dataEmpty() {
+      return !this.chartData.rows.length;
+    },
+    items() {
+      let tempArr = [{
+        text: '在读学员总数',
+        value: 0,
+        id: 'normalStudentNum'
+      }, {
+        text: '进行中',
+        value: 0,
+        id: 'normalStudentHasNormalGroupNum'
+      }, {
+        text: '暂停',
+        value: 0,
+        id: 'hasCourseBalanceAndNotSubCourseNum'
+      }, {
+        text: '未排课',
+        value: 0,
+        id: 'normalStudentHasNoScheduleNum'
+      }]
+      tempArr.forEach(item => {
+        if(this.data[item.id]) {
+          item.value = this.data[item.id]
+        }
+      })
+      return tempArr
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+/deep/ .el-card__body .statistic {
+    margin-bottom: 15px;
+    padding: 0;
+  }
+/deep/.el-card__header {
+    padding: 0 20px!important;
+}
+.box {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  height: 55px;
+  line-height: 55px;
+  .shape {
+    margin-right: 10px;
+    height: 18px;
+    width: 4px;
+    background-color: var(--color-primary);
+  }
+}
+</style>
+

+ 298 - 0
src/views/smallStudentManager/components/tableList.vue

@@ -0,0 +1,298 @@
+<template>
+  <div class="tableList">
+    <save-form
+      :inline="true"
+      class="searchForm"
+      @submit="onSearch"
+      @reset="onReSet"
+      ref="searchForm"
+      :model.sync="searchForm"
+    >
+      <el-form-item prop="search">
+        <el-input
+          clearable
+          placeholder="学生姓名或电话"
+          @keyup.enter.native="onSearch"
+          v-model.trim="searchForm.search"
+        ></el-input>
+      </el-form-item>
+      <el-form-item prop="organId">
+        <el-select
+          class="multiple"
+          filterable
+          v-model.trim="searchForm.organId"
+          multiple
+          collapse-tags
+          clearable
+          placeholder="请选择分部"
+        >
+          <el-option
+            v-for="(item, index) in selects.branchs"
+            :key="index"
+            :label="item.name"
+            :value="item.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <el-form-item prop="teacherId">
+        <remote-search :commit="'setTeachers'" v-model="searchForm.teacherId" />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button native-type="submit" type="danger">搜索</el-button>
+        <el-button native-type="reset" type="primary">重置</el-button>
+      </el-form-item>
+    </save-form>
+
+    <div class="tableWrap">
+      <el-table
+        :data="tableList"
+        :header-cell-style="{ background: '#EDEEF0', color: '#444' }"
+      >
+        <el-table-column type="expand">
+            <template slot-scope="props">
+              <el-form label-position="left" class="demo-table-expand" :inline='true'>
+                <el-row>
+                  <el-col :span="6">
+                    <el-form-item label="指导老师信息">
+                      <div class="schoolWrap">
+                        <span>{{ props.row.teacherName }}</span>
+                        <span>{{ props.row.id }}</span>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="乐团主管">
+                      <div class="schoolWrap">
+                        <span>{{ props.row.teacherName }}</span>
+                        <span>{{ props.row.id }}</span>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="声部课老师">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.cooperationOrganName"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="年级">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.cooperationOrganName"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="6">
+                    <el-form-item label="近30天课耗">
+                      <div class="schoolWrap">
+                        <span>{{ props.row.teacherName }}</span>
+                        <span>{{ props.row.id }}</span>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="回访次数">
+                      <div class="schoolWrap">
+                        <span>{{ props.row.visitNum }}</span>
+                        <span>{{ props.row.id }}</span>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="回访状态">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.lastVisitStatus"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="回访日期">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.lastVisitTime"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                </el-row>
+                <el-row>
+                  <el-col :span="6">
+                    <el-form-item label="第一次课时间">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.firstCourseTime"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="最近上课时间">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.lastCourseTime"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                  <el-col :span="6">
+                    <el-form-item label="原因">
+                      <div class="schoolWrap">
+                        <overflow-text
+                          width="100%"
+                          :text="props.row.visitReason"
+                        ></overflow-text>
+                      </div> </el-form-item
+                  ></el-col>
+                </el-row>
+              </el-form>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="所属分部">
+            <template slot-scope="scope">
+              <copy-text>{{ scope.row.studentBasicInfo.organName }}</copy-text>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            prop="userId"
+            label="学员信息"
+            width="140px"
+          >
+            <template slot-scope="scope">
+              <div style="color: var(--color-primary)">
+                {{ scope.row.studentBasicInfo.userName }}<br />
+              {{ scope.row.studentBasicInfo.userId }}
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="studentBasicInfo.phone" label="联系电话">
+          </el-table-column>
+          <el-table-column align="center" prop="studentBasicInfo.subjectName" label="声部">
+          </el-table-column>
+          <el-table-column align="center" prop="studentBasicInfo.cooperationOrganName" label="学校">
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="学员状态">
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="未排课时">
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="总课时">
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="已完成课时">
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="剩余课时">
+          </el-table-column>
+          <el-table-column align="center" prop="organName" label="预收金额">
+          </el-table-column>
+          <el-table-column align="center" width="180px" label="操作">
+            <template slot-scope="scope">
+              <el-button
+                type="text"
+                @click="setStudent(scope.row)"
+                >回访记录</el-button>
+            </template>
+          </el-table-column>
+      </el-table>
+      <pagination
+          sync
+          :total.sync="pageInfo.total"
+          :page.sync="pageInfo.page"
+          :limit.sync="pageInfo.limit"
+          :page-sizes="pageInfo.page_size"
+          @pagination="getList"
+        />
+    </div>
+  </div>
+</template>
+
+<script>
+import pagination from "@/components/Pagination/index";
+import { queryPage } from '../api'
+export default {
+  name: "tableList",
+  props: ['groupType'],
+  components: {
+    pagination
+  },
+  data() {
+    return {
+      searchForm: {
+        search: "",
+        organId: "",
+        teacherId: ""
+      },
+      tableList: [{
+        name: "",
+        phone: "",
+        organName: "",
+        teacherName: "",
+        createTime: "",
+        status: "",
+        id: ""
+      }],
+      pageInfo: {
+        // 分页规则
+        limit: 10, // 限制显示条数
+        page: 1, // 当前页
+        total: 0, // 总条数
+        page_size: [10, 20, 40, 50], // 选择限制显示条数
+      },
+    };
+  },
+  async mounted() {
+    this.$store.dispatch("setBranchs");
+    this.$store.dispatch("setTeachers");
+
+    await this.getList();
+  },
+  methods: {
+    onSearch() {
+      this.pageInfo.page = 1;
+      this.getList();
+    },
+    onReSet() {
+      this.$refs.searchForm.resetFields();
+      this.onSearch();
+    },
+    async getList() {
+      let params = {...this.searchForm};
+      params.rows = this.pageInfo.limit;
+      params.page = this.pageInfo.page;
+      try {
+        let res = await queryPage({
+          ...params,
+          groupType: this.groupType,
+        });
+        this.tableList = res.data.rows || [];
+        this.pageInfo.total = res.data.total;
+        console.log(res)
+      } catch {}
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.rows {
+  > div {
+    margin-bottom: 20px;
+  }
+}
+/deep/ .el-card__body .statistic {
+  margin-bottom: 15px;
+  padding: 0;
+}
+.statistic {
+  .statistic-content > span {
+    font-size: 22px !important;
+    &:first-child {
+      font-size: 14px !important;
+    }
+  }
+}
+</style>

+ 47 - 0
src/views/smallStudentManager/index.vue

@@ -0,0 +1,47 @@
+<!--  -->
+<template>
+  <div class="m-container">
+    <h2>
+      <div class="squrt"></div>
+      小课学员管理
+    </h2>
+    <div class="m-core">
+      <tab-router
+        v-model.trim="activeIndex"
+        type="card"
+        @tab-click="handleClick"
+      >
+        <el-tab-pane label="VIP课" lazy name="1">
+          <vipClass v-if="activeIndex === '1'" />
+        </el-tab-pane>
+        <el-tab-pane label="乐理课" lazy name="2">
+          <theoryClass v-if="activeIndex === '2'" />
+        </el-tab-pane>
+        <el-tab-pane label="网管课" lazy name="3">
+          <auditionClass v-if="activeIndex === '3'" />
+        </el-tab-pane>
+      </tab-router>
+    </div>
+  </div>
+</template>
+
+<script>
+import vipClass from './vipClass';
+import theoryClass from './theoryClass';
+import auditionClass from './auditionClass';
+export default {
+  components: { vipClass, theoryClass, auditionClass },
+  data() {
+    return {
+      activeIndex: "1",
+    };
+  },
+  methods: {
+    handleClick(evt) {
+      // this.changeHash(evt.name);
+    },
+  },
+};
+</script>
+<style lang='scss' scoped>
+</style>

+ 52 - 0
src/views/smallStudentManager/theoryClass.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="theoryClass">
+    <Statistics :groupType='groupType' />
+
+    <TableList :groupType='groupType' />
+  </div>
+</template>
+
+<script>
+import Statistics from './components/index';
+import TableList from './components/tableList';
+export default {
+  name: "theoryClass",
+  components: {
+    Statistics,
+    TableList
+  },
+  data() {
+    return {
+      groupType: 'THEORY'
+    }
+  },
+  async mounted() {
+    
+  },
+  methods: {
+    __init() {
+
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.rows {
+    > div {
+      margin-bottom: 20px;
+    }
+  }
+/deep/ .el-card__body .statistic {
+  margin-bottom: 15px;
+  padding: 0;
+}
+.statistic {
+  .statistic-content > span {
+    font-size: 22px !important;
+    &:first-child {
+      font-size: 14px !important;
+    }
+  }
+}
+</style>

+ 52 - 0
src/views/smallStudentManager/vipClass.vue

@@ -0,0 +1,52 @@
+<template>
+  <div class="vipClass">
+    <Statistics :groupType='groupType' />
+
+    <TableList :groupType='groupType' />
+  </div>
+</template>
+
+<script>
+import Statistics from './components/index';
+import TableList from './components/tableList';
+export default {
+  name: "vipClass",
+  components: {
+    Statistics,
+    TableList
+  },
+  data() {
+    return {
+      groupType: 'VIP'
+    }
+  },
+  async mounted() {
+    
+  },
+  methods: {
+    __init() {
+
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.rows {
+    > div {
+      margin-bottom: 20px;
+    }
+  }
+/deep/ .el-card__body .statistic {
+  margin-bottom: 15px;
+  padding: 0;
+}
+.statistic {
+  .statistic-content > span {
+    font-size: 22px !important;
+    &:first-child {
+      font-size: 14px !important;
+    }
+  }
+}
+</style>