Browse Source

'合并销售管理'

周杰伦 4 years ago
parent
commit
13590b0f9b

+ 5 - 1
package.json

@@ -17,6 +17,8 @@
     "axios": "^0.19.0",
     "core-js": "^3.1.2",
     "enquire.js": "^2.1.6",
+    "echarts": "4.2.1",
+    "echarts-gl": "^1.1.1",
     "form-making": "^1.2.10",
     "k-form-design": "^3.7.2",
     "lodash.clonedeep": "^4.5.0",
@@ -29,6 +31,7 @@
     "store": "^2.0.12",
     "viser-vue": "^2.4.6",
     "vue": "^2.6.10",
+    "vue-echarts": "^5.0.0-beta.0",
     "vue-clipboard2": "^0.2.1",
     "vue-cropper": "0.4.9",
     "vue-i18n": "^8.17.4",
@@ -36,7 +39,8 @@
     "vue-router": "^3.1.2",
     "vue-svg-component-runtime": "^1.0.1",
     "vuex": "^3.1.1",
-    "wangeditor": "^3.1.1"
+    "wangeditor": "^3.1.1",
+    "vue-count-to": "^1.0.13"
   },
   "devDependencies": {
     "@ant-design/colors": "^3.2.1",

+ 1 - 1
src/api/role.js

@@ -34,7 +34,7 @@ export function putDepart (data) {
     })
   }
 
-// 查询部门
+// 查询人员详情
 export function getUser (id) {
     return request({
       url: `/config/user/${id}`,

+ 28 - 4
src/config/router.config.js

@@ -280,26 +280,48 @@ export const asyncRouterMap = [
             path: '/markting/active',
             name: 'Active',
             component: () => import('@/views/markting/active/list'),
-            meta: { title: '市场活动', hideHeader: true, permission: [ 'user' ] },
+            meta: { title: '市场活动', hideHeader: true, permission: [ 'user' ] }
           },
           {
             path: '/markting/active/info',
             name: 'ActiveInfo',
             hidden: true,
             component: () => import('@/views/markting/active/info'),
-            meta: { title: '活动详情', keepAlive: true, hideHeader: true, permission: [ 'user' ] },
+            meta: { title: '活动详情', keepAlive: true, hideHeader: true, permission: [ 'user' ] }
           },
           {
             path: '/markting/seas',
             name: 'Seas',
             component: () => import('@/views/markting/seas/list'),
-            meta: { title: '公海池', hideHeader: true, permission: [ 'user' ] },
+            meta: { title: '公海池', hideHeader: true, permission: [ 'user' ] }
           },
           {
             path: '/markting/leads',
             name: 'Leads',
             component: () => import('@/views/markting/leads/list'),
-            meta: { title: '线索管理', hideHeader: true, permission: [ 'user' ] },
+            meta: { title: '线索管理', hideHeader: true, permission: [ 'user' ] }
+          }
+        ]
+      },
+      // 数据分析datum
+      {
+        path: '/datum',
+        component: RouteView,
+        redirect: '/datum/sell',
+        name: 'basics',
+        meta: { title: '数据统计', icon: 'user', keepAlive: true, permission: [ 'datum' ] },
+        children: [
+          {
+            path: '/datum/sell',
+            name: 'datumSell',
+            component: () => import('@/views/datum/sell/index'),
+            meta: { title: '销售统计', hideHeader: true, permission: [ 'datum' ] }
+          },
+          {
+            path: '/datum/removeCourse',
+            name: 'removeCourse',
+            component: () => import('@/views/datum/removeCourse/index'),
+            meta: { title: '消课统计', hideHeader: true, permission: [ 'datum' ] }
           }
         ]
       },
@@ -331,6 +353,7 @@ export const asyncRouterMap = [
           }
         ]
       },
+      // 权限设置
       {
         path: '/roleIng',
         component: RouteView,
@@ -352,6 +375,7 @@ export const asyncRouterMap = [
           }
         ]
       }
+
       // other
       /*
       {

+ 7 - 1
src/main.js

@@ -29,10 +29,16 @@ import '../lib/form-making-advanced/dist/FormMaking.css'
 
 import KFormDesign from 'k-form-design'
 import 'k-form-design/lib/k-form-design.css'
+import CountTo from 'vue-count-to'
 
+import ECharts from 'vue-echarts'
+import 'echarts/lib/chart/pie'
+import 'echarts/lib/chart/funnel'
+import 'echarts/lib/chart/bar'
+Vue.component('vue-chart', ECharts)
 Vue.component(GenerateAntdForm.name, GenerateAntdForm)
 Vue.use(KFormDesign)
-
+Vue.component('count-to', CountTo)
 Vue.config.productionTip = false
 
 // mount axios to `Vue.$http` and `this.$http`

+ 0 - 4
src/utils/request.js

@@ -46,11 +46,7 @@ const errorHandler = (error) => {
 // request interceptor
 request.interceptors.request.use(config => {
   // const token = storage.get(ACCESS_TOKEN)
-<<<<<<< HEAD
   const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b3duSWQiOiIyMTMyMTIxMjMyNDVMIiwiZXhwIjoxNjA5Mzg2OTY4LCJ1c2VySWQiOiI4NTUxMjU0NDE1NTRMIn0.YaE2FsIF1YsolsZOShhJ78d5Z5qdTacAeDTnLInTN-g'
-=======
-  const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b3duSWQiOiIyMTMyMTIxMjMyNDVMIiwiZXhwIjoxNjA5MzgwMzE5LCJ1c2VySWQiOiI4NTUxMjU0NDE1NTRMIn0.-49pBtQAdJ4kA-paxTXDYwyERFny0-o1rYWXZnC_3iM'
->>>>>>> origin/markting
   // 如果 token 存在
   // 让每个请求携带自定义 token 请根据实际情况自行修改
   if (token) {

+ 142 - 0
src/views/datum/echartsOptions.js

@@ -0,0 +1,142 @@
+export default {
+    // 饼图
+    pieEcharts: {
+        color: ['#61b1ff', '#70d391', '#fbda68', '#ffb980', '#62d3d4', '#feba4a'],
+        tooltip: {
+          trigger: 'item',
+          formatter: '{b} ({d}%)'
+        },
+        series: [{
+          type: 'pie',
+          radius: ['50%', '70%'],
+          data: [
+            { value: 335, name: '直接访问' },
+            { value: 310, name: '邮件营销' },
+            { value: 234, name: '联盟广告' },
+            { value: 135, name: '视频广告' },
+            { value: 1548, name: '搜索引擎' }
+        ],
+          emphechartsOptionsasis: {
+            label: {
+              show: true,
+              fontSize: '20',
+              fontWeight: 'bold'
+            }
+          },
+          avoidLabelOverlap: false,
+          label: {
+            position: 'outer',
+            alignTo: 'none',
+            bleedMargin: 5
+          }
+        }]
+    },
+    // 漏斗图
+    funnelEcharts: {
+        color: ['#61b1ff', '#70d391', '#fbda68', '#ffb980', '#62d3d4', '#feba4a'],
+        title: {
+            text: '漏斗图',
+            subtext: '纯属虚构'
+        },
+        tooltip: {
+            trigger: 'item',
+            formatter: '{a} <br/>{b} : {c}%'
+        },
+        toolbox: {
+            feature: {
+                dataView: { readOnly: false },
+                restore: {},
+                saveAsImage: {}
+            }
+        },
+        legend: {
+            data: ['展现', '点击', '访问', '咨询', '订单']
+        },
+
+        series: [
+            {
+                name: '漏斗图',
+                type: 'funnel',
+                left: '10%',
+                top: 60,
+                // x2: 80,
+                bottom: 60,
+                width: '50%',
+
+                height: '70%',
+                min: 0,
+                max: 100,
+                minSize: '0%',
+                maxSize: '100%',
+                sort: 'descending',
+                gap: 14,
+                label: {
+                    show: true,
+                    position: 'inside'
+                },
+                labelLine: {
+                    length: 10,
+                    lineStyle: {
+                        width: 1,
+                        type: 'solid'
+                    }
+                },
+                itemStyle: {
+                    borderColor: '#fff',
+                    borderWidth: 1
+                },
+                emphasis: {
+                    label: {
+                        fontSize: 20
+                    }
+                },
+                data: [
+                    { value: 60, name: '访问' },
+                    { value: 40, name: '咨询' },
+                    { value: 20, name: '订单' },
+                    { value: 80, name: '点击' },
+                    { value: 100, name: '展现' }
+                ]
+            }
+        ]
+    },
+    // 柱状图
+    barEcharts: {
+            color: ['#3398DB'],
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: { // 坐标轴指示器,坐标轴触发有效
+                    type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
+                }
+            },
+            grid: {
+                left: '3%',
+                right: '4%',
+                bottom: '3%',
+                containLabel: true
+            },
+            xAxis: [
+                {
+                    type: 'category',
+                    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+                    axisTick: {
+                        alignWithLabel: true
+                    }
+                }
+            ],
+            yAxis: [
+                {
+                    type: 'value'
+                }
+            ],
+            series: [
+                {
+                    name: '直接访问',
+                    type: 'bar',
+                    barWidth: '60%',
+                    data: [10, 52, 200, 334, 390, 330, 220]
+                }
+            ]
+
+    }
+}

+ 14 - 0
src/views/datum/index.vue

@@ -0,0 +1,14 @@
+<template>
+  <div class="Datum">
+    数据统计
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Datum'
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 44 - 0
src/views/datum/removeCourse/components/course.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="Course">
+    <a-row>
+      <a-col :span="24">
+        <a-card
+          :body-style="{padding: '24px 32px'}"
+          :bordered="false"
+          title="课消金额统计"
+        >
+          <template slot="extra">
+            <a-row :gutter="[16]">
+              <a-col :span="4">
+                <a-button>按月统计</a-button>
+              </a-col>
+              <a-col :span="4">
+                <a-button>按周统计</a-button>
+              </a-col>
+              <a-col :span="12">
+                <a-range-picker @change="onChange" />
+              </a-col>
+            </a-row>
+          </template>
+          <vue-chart style="width: 100%;height: 400px;" :autoresize="true" :options="priceOPtions"></vue-chart>
+        </a-card>
+      </a-col>
+    </a-row>
+
+  </div>
+</template>
+
+<script>
+import echartsOptions from '../../echartsOptions'
+export default {
+  name: 'Course',
+  data () {
+      return {
+        priceOPtions: echartsOptions.barEcharts
+      }
+  }
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 129 - 0
src/views/datum/removeCourse/components/teacher.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="teacher">
+    <a-card
+      :body-style="{padding: '24px 32px'}"
+      :bordered="false"
+    >
+      <a-table :columns="columns" :data-source="data">
+        <a slot="name" slot-scope="text">{{ text }}</a>
+        <span slot="customTitle"><a-icon type="smile-o" /> Name</span>
+        <span slot="tags" slot-scope="tags">
+          <a-tag
+            v-for="tag in tags"
+            :key="tag"
+            :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
+          >
+            {{ tag.toUpperCase() }}
+          </a-tag>
+        </span>
+        <span slot="action" slot-scope="text, record">
+          <a>Invite 一 {{ record.name }}</a>
+          <a-divider type="vertical" />
+          <a>Delete</a>
+        </span>
+      </a-table>
+    </a-card>
+
+  </div>
+</template>
+
+<script>
+const columns = [
+  {
+    dataIndex: 'name',
+    key: 'name',
+    title: '老师姓名'
+  },
+  {
+    title: '上课时长',
+    dataIndex: 'courseTime',
+    key: 'courseTime'
+  },
+  {
+    title: '班级类型',
+    dataIndex: 'classType',
+    key: 'classType'
+  },
+  {
+    title: '课程',
+    key: 'course',
+    dataIndex: 'course'
+  },
+  {
+    title: '老师类型',
+    key: 'teacherType',
+    dataIndex: 'teacherType'
+  },
+  {
+    title: '课节数',
+    key: 'courseCount',
+    dataIndex: 'courseCount'
+  },
+  {
+    title: '实到/总人数',
+    key: 'people',
+    dataIndex: 'people'
+  },
+  {
+    title: '实扣、应扣学员课时',
+    key: 'remoceCourse',
+    dataIndex: 'remoceCourse'
+  },
+  {
+    title: '未扣学员课时',
+    key: 'noRmove',
+    dataIndex: 'noRmove'
+  },
+  {
+    title: '老师课时',
+    key: 'teacherCourse',
+    dataIndex: 'teacherCourse'
+  },
+  {
+    title: '学员出勤率',
+    key: 'attendanceRate',
+    dataIndex: 'attendanceRate'
+  },
+  {
+    title: 'Action',
+    key: 'action',
+    scopedSlots: { customRender: 'action' }
+  }
+]
+
+const data = [
+  {
+    key: '1',
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer']
+  },
+  {
+    key: '2',
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser']
+  },
+  {
+    key: '3',
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher']
+  }
+]
+export default {
+  name: 'Teacher',
+  data () {
+      return {
+        data,
+        columns
+      }
+  }
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 45 - 0
src/views/datum/removeCourse/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="remove-class">
+    <page-header-wrapper
+      :title="false"
+      content="用于老师与学员消课统计"
+      :tab-list="tabList"
+      :tab-active-key="tabActiveKey"
+      @tabChange="handleTabChange"
+    >
+      <course v-if="tabActiveKey === '0'" />
+      <teacher v-if="tabActiveKey === '1'" />
+    </page-header-wrapper>
+  </div>
+</template>
+
+<script>
+import Course from './components/course'
+import Teacher from './components/teacher'
+export default {
+  name: 'RemoveClass',
+  components: {
+    Course,
+    Teacher
+  },
+  data () {
+    return {
+      tabList: [
+            { key: '0', tab: '消课统计分析' },
+            { key: '1', tab: '老师消课统计' },
+            { key: '2', tab: '学员消课统计' }
+        ],
+        tabActiveKey: '0'
+    }
+  },
+  methods: {
+    // 切换统计类型
+    handleTabChange (key) {
+      this.tabActiveKey = key
+    }
+  }
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 137 - 0
src/views/datum/sell/components/adopt.vue

@@ -0,0 +1,137 @@
+<template>
+  <div class="adopt">
+    <a-card
+      :body-style="{padding: '24px 32px'}"
+      :bordered="false"
+    >
+      <a-row>
+        <a-col :span="7">
+          <span>采单时间: &nbsp;&nbsp;</span>
+          <a-range-picker @change="onChange" />
+        </a-col>
+        <a-col :span="6">
+          <span>排序方式: &nbsp;&nbsp;</span>
+          <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+            <a-select-option value="jack">
+              Jack
+            </a-select-option>
+            <a-select-option value="lucy">
+              Lucy
+            </a-select-option>
+            <a-select-option value="disabled" disabled>
+              Disabled
+            </a-select-option>
+            <a-select-option value="Yiminghe">
+              yiminghe
+            </a-select-option>
+          </a-select>
+        </a-col>
+      </a-row>
+
+      <a-row style="margin-top: 20px; margin-bottom: 50px">
+        <a-col :span="2">
+          <span>采单线索数: 6</span>
+        </a-col>
+        <a-col :span="2">
+          <span>采单签约数: 1</span>
+        </a-col>
+        <a-col :span="4">
+          <span>总采单转化率: 16.67%</span>
+        </a-col>
+      </a-row>
+
+      <a-table :columns="columns" :data-source="data" >
+        <a slot="name" slot-scope="text">{{ text }}</a>
+        <span slot="customTitle"><a-icon type="smile-o" /> Name</span>
+        <span slot="tags" slot-scope="tags">
+          <a-tag
+            v-for="tag in tags"
+            :key="tag"
+            :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
+          >
+            {{ tag.toUpperCase() }}
+          </a-tag>
+        </span>
+        <span slot="action" slot-scope="text, record">
+          <a>Invite 一 {{ record.name }}</a>
+          <a-divider type="vertical" />
+          <a>Delete</a>
+          <a-divider type="vertical" />
+          <a class="ant-dropdown-link"> More actions <a-icon type="down" /> </a>
+        </span>
+      </a-table>
+    </a-card>
+  </div>
+</template>
+
+<script>
+const columns = [
+  {
+    dataIndex: 'rank',
+    key: 'rank',
+    title: '排行'
+  },
+  {
+    title: '姓名',
+    dataIndex: 'name',
+    key: 'name'
+  },
+  {
+    title: '签约金额',
+    dataIndex: 'price',
+    key: 'price'
+  },
+  {
+    title: '签约人数',
+    key: 'count',
+    dataIndex: 'count'
+  },
+  {
+    title: '课单价',
+    key: 'classPrice',
+    dataIndex: 'classPrice'
+  }
+]
+
+const data = [
+  {
+    key: '1',
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer']
+  },
+  {
+    key: '2',
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser']
+  },
+  {
+    key: '3',
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher']
+  }
+]
+
+export default {
+    name: 'Performance',
+    data () {
+        return {
+            data,
+            columns
+        }
+    },
+    methods: {
+        onChange (data, dataString) {
+            console.log(data, dataString)
+        }
+    }
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 114 - 0
src/views/datum/sell/components/conversion.vue

@@ -0,0 +1,114 @@
+<template>
+  <div class="Conversion">
+    <a-row :gutter="[16]">
+      <a-col
+        :xs="24"
+        :sm="24"
+        :md="11"
+      >
+        <!-- 渠道-转化分析 -->
+        <a-card
+          :body-style="{padding: '24px 32px'}"
+          :bordered="false"
+          title="渠道-转化分析"
+        >
+          <template slot="extra">
+            <a-row :gutter="[16]">
+              <a-col :span="6">
+                <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+                  <a-select-option value="jack">
+                    Jack
+                  </a-select-option>
+                  <a-select-option value="lucy">
+                    Lucy
+                  </a-select-option>
+                  <a-select-option value="disabled" disabled>
+                    Disabled
+                  </a-select-option>
+                  <a-select-option value="Yiminghe">
+                    yiminghe
+                  </a-select-option>
+                </a-select>
+              </a-col>
+              <a-col :span="18">
+                <a-row class="time-box">
+                  <a-col :span="4" >
+                    签约时间:
+                  </a-col>
+                  <a-col :span="18">
+                    <a-range-picker @change="onChange" />
+                  </a-col>
+                </a-row>
+              </a-col>
+            </a-row>
+
+          </template>
+          <vue-chart style="width: 50%;height: 300px;" :autoresize="true" :options="conversion"></vue-chart>
+        </a-card>
+      </a-col>
+      <a-col
+        :xs="24"
+        :sm="24"
+        :md="11"
+      >
+        <a-card
+          :body-style="{padding: '24px 32px'}"
+          :bordered="false"
+          title="渠道-线索分析"
+        >
+          <template slot="extra">
+            <a-row :gutter="[16]">
+              <a-col :span="6">
+                <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+                  <a-select-option value="jack">
+                    Jack
+                  </a-select-option>
+                  <a-select-option value="lucy">
+                    Lucy
+                  </a-select-option>
+                  <a-select-option value="disabled" disabled>
+                    Disabled
+                  </a-select-option>
+                  <a-select-option value="Yiminghe">
+                    yiminghe
+                  </a-select-option>
+                </a-select>
+              </a-col>
+              <a-col :span="18">
+                <a-row class="time-box">
+                  <a-col :span="4" >
+                    签约时间:
+                  </a-col>
+                  <a-col :span="18">
+                    <a-range-picker @change="onChange" />
+                  </a-col>
+                </a-row>
+              </a-col>
+            </a-row>
+
+          </template>
+          <vue-chart style="width: 100%;height: 300px;" :autoresize="true" :options="thread"></vue-chart>
+        </a-card>
+      </a-col>
+    </a-row>
+  </div>
+</template>
+
+<script>
+import echartsOptions from '../../echartsOptions'
+export default {
+  name: 'Conversion',
+  data () {
+      return {
+          conversion: echartsOptions.funnelEcharts,
+          thread: echartsOptions.barEcharts
+      }
+  }
+}
+</script>
+<style scoped lang="less">
+    .time-box {
+        display: flex;
+        align-items: center;
+    }
+</style>

+ 143 - 0
src/views/datum/sell/components/follow.vue

@@ -0,0 +1,143 @@
+<template>
+  <div class="follow">
+    <a-row :gutter="[16]">
+      <a-col :span="10">
+        <a-card
+          :body-style="{padding: '24px 32px'}"
+          :bordered="false"
+          title="跟进分析"
+        >
+          <a-row>
+            <a-col>
+              <span> 签约时间: &nbsp;&nbsp;&nbsp;</span>
+              <a-range-picker @change="onChange" />
+            </a-col>
+          </a-row>
+          <vue-chart style="width: 100%;height: 600px;" :autoresize="true" :options="pieEcharts"></vue-chart>
+        </a-card>
+      </a-col>
+      <a-col :span="12">
+        <a-card
+          :body-style="{padding: '24px 32px'}"
+          :bordered="false"
+          title="详细数据"
+        >
+          <a-row style="margin-bottom: 20px">
+            <a-col :span="14">
+              <span>签约时间:&nbsp;&nbsp;&nbsp;</span>
+              <a-range-picker @change="onChange" />
+            </a-col>
+            <a-col :span="8">
+              <span>排序顺序:&nbsp;&nbsp;&nbsp;</span>
+              <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+                <a-select-option value="jack">
+                  Jack
+                </a-select-option>
+                <a-select-option value="lucy">
+                  Lucy
+                </a-select-option>
+                <a-select-option value="disabled" disabled>
+                  Disabled
+                </a-select-option>
+                <a-select-option value="Yiminghe">
+                  yiminghe
+                </a-select-option>
+              </a-select>
+            </a-col>
+          </a-row>
+          <a-table :columns="columns" :data-source="data" >
+            <a slot="name" slot-scope="text">{{ text }}</a>
+            <span slot="customTitle"><a-icon type="smile-o" /> Name</span>
+            <span slot="tags" slot-scope="tags">
+              <a-tag
+                v-for="tag in tags"
+                :key="tag"
+                :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
+              >
+                {{ tag.toUpperCase() }}
+              </a-tag>
+            </span>
+            <span slot="action" slot-scope="text, record">
+              <a>Invite 一 {{ record.name }}</a>
+              <a-divider type="vertical" />
+              <a>Delete</a>
+              <a-divider type="vertical" />
+              <a class="ant-dropdown-link"> More actions <a-icon type="down" /> </a>
+            </span>
+          </a-table>
+        </a-card>
+      </a-col>
+    </a-row>
+  </div>
+</template>
+
+<script>
+import echartsOptions from '../../echartsOptions'
+const columns = [
+  {
+    dataIndex: 'rank',
+    key: 'rank',
+    title: '排行'
+  },
+  {
+    title: '姓名',
+    dataIndex: 'name',
+    key: 'name'
+  },
+  {
+    title: '跟进次数',
+    dataIndex: 'price',
+    key: 'price'
+  },
+  {
+    title: '跟进线索数',
+    key: 'count',
+    dataIndex: 'count'
+  },
+  {
+    title: '试听数',
+    key: 'classPrice',
+    dataIndex: 'classPrice'
+  }
+]
+
+const data = [
+  {
+    key: '1',
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer']
+  },
+  {
+    key: '2',
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser']
+  },
+  {
+    key: '3',
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher']
+  }
+]
+export default {
+  name: 'Follow',
+  data () {
+      return {
+          pieEcharts: echartsOptions.pieEcharts,
+          data,
+          columns
+      }
+  }
+}
+</script>
+<style scoped lang="less">
+.echarts-box {
+    // width: 600px;
+    // height: 600px;
+}
+</style>

+ 140 - 0
src/views/datum/sell/components/performance.vue

@@ -0,0 +1,140 @@
+<template>
+  <div class="Performance">
+    <a-card
+      :body-style="{padding: '24px 32px'}"
+      :bordered="false"
+    >
+      <a-row>
+        <a-col :span="7">
+          <span>采单时间: &nbsp;&nbsp;</span>
+          <a-range-picker @change="onChange" />
+        </a-col>
+        <a-col :span="6">
+          <span>排序方式: &nbsp;&nbsp;</span>
+          <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+            <a-select-option value="jack">
+              Jack
+            </a-select-option>
+            <a-select-option value="lucy">
+              Lucy
+            </a-select-option>
+            <a-select-option value="disabled" disabled>
+              Disabled
+            </a-select-option>
+            <a-select-option value="Yiminghe">
+              yiminghe
+            </a-select-option>
+          </a-select>
+        </a-col>
+      </a-row>
+
+      <a-row style="margin-top: 20px; margin-bottom: 50px">
+        <a-col :span="2">
+          <span>总签约金额: 6</span>
+        </a-col>
+        <a-col :span="2">
+          <span>签约人数: 1</span>
+        </a-col>
+        <a-col :span="2">
+          <span>签约量: 16.67%</span>
+        </a-col>
+        <a-col :span="2">
+          <span>平均课单价: 16.67%</span>
+        </a-col>
+      </a-row>
+
+      <a-table :columns="columns" :data-source="data" >
+        <a slot="name" slot-scope="text">{{ text }}</a>
+        <span slot="customTitle"><a-icon type="smile-o" /> Name</span>
+        <span slot="tags" slot-scope="tags">
+          <a-tag
+            v-for="tag in tags"
+            :key="tag"
+            :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
+          >
+            {{ tag.toUpperCase() }}
+          </a-tag>
+        </span>
+        <span slot="action" slot-scope="text, record">
+          <a>Invite 一 {{ record.name }}</a>
+          <a-divider type="vertical" />
+          <a>Delete</a>
+          <a-divider type="vertical" />
+          <a class="ant-dropdown-link"> More actions <a-icon type="down" /> </a>
+        </span>
+      </a-table>
+    </a-card>
+  </div>
+</template>
+
+<script>
+const columns = [
+  {
+    dataIndex: 'rank',
+    key: 'rank',
+    title: '排行'
+  },
+  {
+    title: '姓名',
+    dataIndex: 'name',
+    key: 'name'
+  },
+  {
+    title: '签约金额',
+    dataIndex: 'price',
+    key: 'price'
+  },
+  {
+    title: '签约人数',
+    key: 'count',
+    dataIndex: 'count'
+  },
+  {
+    title: '课单价',
+    key: 'classPrice',
+    dataIndex: 'classPrice'
+  }
+]
+
+const data = [
+  {
+    key: '1',
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer']
+  },
+  {
+    key: '2',
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser']
+  },
+  {
+    key: '3',
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher']
+  }
+]
+
+export default {
+    name: 'Performance',
+    data () {
+        return {
+            data,
+            columns
+        }
+    },
+    methods: {
+        onChange (data, dataString) {
+            console.log(data, dataString)
+        }
+    }
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 189 - 0
src/views/datum/sell/components/school.vue

@@ -0,0 +1,189 @@
+<template>
+  <div class="School">
+    <a-card
+      :body-style="{padding: '24px 32px'}"
+      :bordered="false"
+    >
+      <a-row
+        class="number-list"
+      >
+        <a-col
+          class="number-item"
+          v-for="(item, index) in 9"
+          :key="item"
+          :style="{flex: index === 8 ? '40%' : '20%'}"
+        >
+          <div class="content">
+            <div class="label">
+              签约金额
+            </div>
+            <div class="count">
+
+              <count-to :start-val="0" :end-val="2000 || 0" :duration="2000"/>
+            </div>
+          </div>
+        </a-col>
+      </a-row>
+    </a-card>
+
+    <a-card
+      style="margin-top: 20px"
+      :body-style="{padding: '24px 32px'}"
+      :bordered="false"
+    >
+      <a-row style="margin-bottom: 20px">
+        <a-col :span="7">
+          <span>签约时间:&nbsp;&nbsp;</span>
+          <a-range-picker @change="onChange" />
+        </a-col>
+        <a-col :span="4">
+          <span>选择老师:</span>&nbsp;&nbsp;
+          <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+            <a-select-option value="jack">
+              Jack
+            </a-select-option>
+            <a-select-option value="lucy">
+              Lucy
+            </a-select-option>
+            <a-select-option value="disabled" disabled>
+              Disabled
+            </a-select-option>
+            <a-select-option value="Yiminghe">
+              yiminghe
+            </a-select-option>
+          </a-select>
+        </a-col>
+        <a-col :span="4">
+          <span>排序方式&nbsp;&nbsp;</span>
+          <a-select default-value="lucy" style="width: 120px" @change="handleChange">
+            <a-select-option value="jack">
+              Jack
+            </a-select-option>
+            <a-select-option value="lucy">
+              Lucy
+            </a-select-option>
+            <a-select-option value="disabled" disabled>
+              Disabled
+            </a-select-option>
+            <a-select-option value="Yiminghe">
+              yiminghe
+            </a-select-option>
+          </a-select>
+        </a-col>
+      </a-row>
+      <a-table :columns="columns" :data-source="data" >
+        <a slot="name" slot-scope="text">{{ text }}</a>
+        <span slot="customTitle"><a-icon type="smile-o" /> Name</span>
+        <span slot="tags" slot-scope="tags">
+          <a-tag
+            v-for="tag in tags"
+            :key="tag"
+            :color="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'"
+          >
+            {{ tag.toUpperCase() }}
+          </a-tag>
+        </span>
+        <span slot="action" slot-scope="text, record">
+          <a>Invite 一 {{ record.name }}</a>
+          <a-divider type="vertical" />
+          <a>Delete</a>
+          <a-divider type="vertical" />
+          <a class="ant-dropdown-link"> More actions <a-icon type="down" /> </a>
+        </span>
+      </a-table>
+    </a-card>
+
+  </div>
+</template>
+
+<script>
+const columns = [
+  {
+    dataIndex: 'rank',
+    key: 'rank',
+    title: '排行'
+  },
+  {
+    title: '姓名',
+    dataIndex: 'name',
+    key: 'name'
+  },
+  {
+    title: '跟进次数',
+    dataIndex: 'price',
+    key: 'price'
+  },
+  {
+    title: '跟进线索数',
+    key: 'count',
+    dataIndex: 'count'
+  },
+  {
+    title: '试听数',
+    key: 'classPrice',
+    dataIndex: 'classPrice'
+  }
+]
+
+const data = [
+  {
+    key: '1',
+    name: 'John Brown',
+    age: 32,
+    address: 'New York No. 1 Lake Park',
+    tags: ['nice', 'developer']
+  },
+  {
+    key: '2',
+    name: 'Jim Green',
+    age: 42,
+    address: 'London No. 1 Lake Park',
+    tags: ['loser']
+  },
+  {
+    key: '3',
+    name: 'Joe Black',
+    age: 32,
+    address: 'Sidney No. 1 Lake Park',
+    tags: ['cool', 'teacher']
+  }
+]
+export default {
+  name: 'School',
+  data () {
+      return {
+          data,
+          columns
+      }
+  }
+}
+</script>
+<style scoped lang="less">
+@import '~ant-design-vue/es/style/themes/default.less';
+.number-list {
+    display: flex;
+    flex-wrap: wrap;
+    .number-item {
+        height: 50px;
+        display: flex;
+        margin-top: 20px;
+        .content {
+            width: 150px;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: space-between;
+            .count {
+                font-size: 18px;
+                font-weight: bold;
+                color: @primary-color;
+            }
+        }
+        .line {
+            width: 1px;
+            height: 50px;
+            background-color: #e8e8e8;
+        }
+    }
+}
+</style>

+ 56 - 0
src/views/datum/sell/index.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="sell">
+    <page-header-wrapper
+      :title="false"
+      content="销售统计"
+      :tab-list="tabList"
+      :tab-active-key="tabActiveKey"
+      @tabChange="handleTabChange"
+    >
+      <performance v-if="tabActiveKey === '0'" />
+      <adopt v-else-if="tabActiveKey === '1'"/>
+      <follow v-else-if="tabActiveKey === '2'"/>
+      <conversion v-else-if="tabActiveKey === '3'"/>
+      <school v-else-if="tabActiveKey === '4'"/>
+    </page-header-wrapper>
+  </div>
+</template>
+
+<script>
+import Performance from './components/performance'
+import Adopt from './components/adopt'
+import Follow from './components/follow'
+import Conversion from './components/conversion'
+import School from './components/school'
+export default {
+  name: 'DatumSell',
+  components: {
+      Performance,
+      Adopt,
+      Follow,
+      Conversion,
+      School
+  },
+  data () {
+      return {
+        tabList: [
+            { key: '0', tab: '业绩排行' },
+            { key: '1', tab: '采单排行' },
+            { key: '2', tab: '跟进次数' },
+            { key: '3', tab: '转化流失' },
+            { key: '4', tab: '校区业绩' },
+            { key: '5', tab: '我的业绩' }
+        ],
+        tabActiveKey: '3'
+      }
+  },
+  methods: {
+      handleTabChange (key) {
+          this.tabActiveKey = key
+      }
+  }
+}
+</script>
+<style scoped lang="less">
+
+</style>

+ 169 - 115
src/views/roleIng/staff/edit.vue

@@ -1,119 +1,126 @@
 <template>
-  <a-form
-    :form="form"
-    :label-col="{ span: 5 }"
-    :wrapper-col="{ span: 12 }"
-    @submit="handleSubmit">
-    <a-form-item label="员工姓名">
-      <a-input
-        v-decorator="['label', { rules: [{ required: true, max: 11, message: '请填写员工姓名' }] }]"
-      />
-    </a-form-item>
-    <!--    v-decorator="[
-          'phone',
-          { rules: [{ required: true, message: '请填写员工姓名!' }] },
-        ]" -->
-    <!-- validate-status="false"
-    help="" -->
-    <a-form-item
-      label="员工手机"
-    >
-      <a-input
-        id="error"
-        @blur="getPhoneOrjobnumber('phone')"
-        v-decorator="[
-          'phone',
-          { rules: [{ required: true, message: '请填写员工手机号!' }, {len: 11 , message: '不符合手机号格式'}] },
-        ]"
-      >
-      </a-input>
-    </a-form-item>
-    <a-form-item label="员工工号">
-      <a-input
-        @blur="getPhoneOrjobnumber('jobNumber')"
-        v-model="formData.jobNumber"
-      >
-      </a-input>
-    </a-form-item>
-    <a-form-item label="员工邮箱">
-      <a-input
-        v-model="formData.email"
+  <div class="edit">
+
+    <a-form
+      :form="form"
+      :label-col="{ span: 5 }"
+      :wrapper-col="{ span: 12 }"
+      @submit="handleSubmit">
+      <a-form-item label="员工姓名">
+        <a-input
+          v-decorator="['label', { rules: [{ required: true, max: 11, message: '请填写员工姓名' }] }]"
+        />
+      </a-form-item>
+      <a-form-item
+        label="员工手机"
       >
-      </a-input>
-    </a-form-item>
-    <a-form-item label="初始密码" >
-      <a-input v-if="type === 'add'" v-model="formData.password" disabled>  </a-input>
-      <a-input-password
-        v-else
-        v-model="formData.password"
-        disabled
-      />
-    </a-form-item>
-    <a-form-item label="性别" >
-      <a-radio-group name="radioGroup" :default-value="1" @change="e => formData.gender = e.target.value">
-        <a-radio :value="1">
-          男
-        </a-radio>
-        <a-radio :value="2">
-          女
-        </a-radio>
-      </a-radio-group>
-    </a-form-item>
-    <a-form-item label="简介" >
-      <a-textarea
-        v-model="formData.intro"
-        placeholder="填写员工简介"
-        :auto-size="{ minRows: 3, maxRows: 5 }"
-      />
-    </a-form-item>
-    <a-form-item label="角色" >
-      <a-select default-value="lucy" style="width: 120px" @change="handleChange">
-        <a-select-option value="0">
-          Jack
-        </a-select-option>
-        <a-select-option value="1">
-          Lucy
-        </a-select-option>
-        <a-select-option value="2">
-          Disabled
-        </a-select-option>
-        <a-select-option value="3">
-          yiminghe
-        </a-select-option>
-      </a-select>
-    </a-form-item>
-    <a-form-item label="数据权限" >
-      <a-radio-group name="radioGroup" :default-value="1" @change="e => formData.dataAuthority = e.target.value">
-        <a-radio :value="1">
-          全部(可查看机构全部数据)
-        </a-radio>
-        <a-radio :value="2">
-          个人(团队)(可查看自己和下属数据,若无下属则查看自己的数据)
-        </a-radio>
-      </a-radio-group>
-    </a-form-item>
-    <a-form-item label="所属部门" >
-      <a-tree-select
-        v-model="formData.departmentLabel"
-        @select="selectTreeNode"
-        treeNodeFilterProp="title"
-        show-search
-        style="width: 100%"
-        :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
-        placeholder="请选择上级部门"
-        allow-clear
-        :tree-data="treeData"
-        tree-default-expand-all
-        :replaceFields="replaceFields"
-      />
-    </a-form-item>
+        <a-input
+          id="error"
+          @blur="getPhoneOrjobnumber('phone')"
+          v-decorator="[
+            'phone',
+            { rules: [{ required: true, message: '请填写员工手机号!' }, {len: 11 , message: '不符合手机号格式'}] },
+          ]"
+        >
+        </a-input>
+      </a-form-item>
+      <a-form-item label="员工工号">
+        <a-input
+          @blur="getPhoneOrjobnumber('jobNumber')"
+          v-model="formData.jobNumber"
+        >
+        </a-input>
+      </a-form-item>
+      <a-form-item label="员工邮箱">
+        <a-input
+          v-model="formData.email"
+        >
+        </a-input>
+      </a-form-item>
+      <a-form-item label="初始密码" >
+        <a-input v-if="type === 'add'" v-model="formData.password" disabled>  </a-input>
+        <a-input-password
+          v-else
+          v-model="formData.password"
+          disabled
+        />
+      </a-form-item>
+      <a-form-item label="性别" >
+        <a-radio-group name="radioGroup" :default-value="1" @change="e => formData.gender = e.target.value">
+          <a-radio :value="1">
+            男
+          </a-radio>
+          <a-radio :value="0">
+            女
+          </a-radio>
+        </a-radio-group>
+      </a-form-item>
+      <a-form-item label="简介" >
+        <a-textarea
+          v-model="formData.intro"
+          placeholder="填写员工简介"
+          :auto-size="{ minRows: 3, maxRows: 5 }"
+        />
+      </a-form-item>
+      <a-form-item label="角色" >
+        <a-select v-model="formData.roleId" style="width: 120px" @change="handleChange">
+          <a-select-option
+            :value="item.id"
+            v-for="item in roleList"
+            :key="item.id">
+            {{ item.label }}
+          </a-select-option>
+        </a-select>
+      </a-form-item>
+      <a-form-item label="数据权限" >
+        <a-radio-group name="radioGroup" v-model="formData.dataAuthority" @change="e => formData.dataAuthority = e.target.value">
+          <a-radio :value="1">
+            全部(可查看机构全部数据)
+          </a-radio>
+          <a-radio :value="2">
+            个人(团队)(可查看自己和下属数据,若无下属则查看自己的数据)
+          </a-radio>
+        </a-radio-group>
+      </a-form-item>
+      <a-form-item label="所属部门" >
+        <a-tree-select
+          v-model="formData.departmentLabel"
+          @select="selectTreeNode"
+          treeNodeFilterProp="title"
+          show-search
+          style="width: 100%"
+          :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
+          placeholder="请选择上级部门"
+          allow-clear
+          :tree-data="treeData"
+          tree-default-expand-all
+          :replaceFields="replaceFields"
+        />
+        <div style="opacity: 0.5; ">需先选择所属部门,再选择汇报对象</div>
+      </a-form-item>
 
-  </a-form>
+      <a-form-item label="汇报对象" >
+        <a-select
+          :disabled="formData.departmentId ? false : true"
+          v-model="formData.leadId"
+          style="width: 120px"
+          @change="leadChange">
+          <a-select-option
+            :value="item.id"
+            v-for="item in leadList"
+            :key="item.id"
+          >
+            {{ item.label }}
+          </a-select-option>
+        </a-select>
+      </a-form-item>
 
+    </a-form>
+  </div>
 </template>
 <script>
 import { Tree } from 'ant-design-vue'
-import { getDepart, getPhoneOrjobnumber } from '@/api/role'
+import { getDepart, getPhoneOrjobnumber, getRole, searchUser } from '@/api/role'
 export default {
   name: 'Demo',
   components: {
@@ -133,8 +140,14 @@ export default {
       return 'validate-status = error help = Should be combination of numbers & alphabets'
     }
   },
+  watch: {
+    'formData.departmentId' () {
+      this.searchUser()
+    }
+  },
   created () {
     this.getDepart()
+    this.getRole()
   },
   data () {
     return {
@@ -146,21 +159,54 @@ export default {
         password: '123456',
         phone: '',
         roleId: '',
-        roleLabel: '小王',
+        roleLabel: '',
         departmentId: '',
         departmentLabel: '',
         email: '',
         gender: 1,
         intro: '',
-        dataAuthority: 0
+        dataAuthority: 1,
+        leadId: '',
+        leadLabel: ''
       },
-      treeData: [],
+      // ------- 下拉框需要的字段 ---------------------
+      treeData: [], // 部门数据
+
+      roleList: [], // 角色
+      leadList: [], // 汇报对象
       visablePhoneOrJobnumber: ''
-      // 自定义校验
 
     }
   },
   methods: {
+    // 查询汇报对象
+    async searchUser () {
+      const $par = {
+        state: 1, // 只查当前可用的
+        departmentId: this.formData.departmentId,
+        curPage: 1,
+        pageSize: 1000
+      }
+      const { code, data } = await searchUser($par)
+      if (code === 0) {
+        console.log(data)
+        const { records } = data
+        this.leadList = records
+      }
+    },
+    // 查询角色
+    async getRole () {
+      const $par = {
+        curPage: 1,
+        pageSize: 1000
+      }
+      const { code, data } = await getRole($par)
+      if (code === 0) {
+        console.log(data)
+        const { records } = data
+        this.roleList = records
+      }
+    },
     // 检查手机号
     async getPhoneOrjobnumber (type) {
       console.log(this.formData, 'this.form')
@@ -186,9 +232,15 @@ export default {
         }
       }
     },
+    // 选择汇报对象
+    leadChange (id) {
+      this.formData.leadId = id
+      this.formData.leadLabel = this.leadList.find(item => item.id === id).label
+    },
+    // 选择角色
     handleChange (id) {
-      console.log(id)
       this.formData.roleId = id
+      this.formData.roleLabel = this.roleList.find(item => item.id === id).label
     },
     // 选择树节点
     selectTreeNode (value, node, extra) {
@@ -231,6 +283,8 @@ export default {
 
 <style lang="less" scoped>
 .edit {
-    display: flex;
+   height: 500px;
+   overflow: hidden;
+   overflow-y: auto;
 }
 </style>

+ 1 - 0
src/views/roleIng/staff/organiza.vue

@@ -9,6 +9,7 @@
         <a-button type="primary" @click="openModal">新增</a-button>
       </template>
       <tree
+        v-if="treeData && treeData.length > 0"
         :tree-data="treeData"
         defaultExpandAll
         :replaceFields="replaceFields"

+ 16 - 3
src/views/roleIng/staff/staffAdmin.vue

@@ -53,7 +53,12 @@
         </template>
       </a-table>
     </a-card>
-    <a-modal v-model="visible" :width="700" style="height: 500px" :title="type === 'add' ? '新增员工' : '编辑员工信息'" @ok="handleOk">
+    <a-modal
+      v-model="visible"
+      :width="700"
+      destroyOnClose
+      :title="type === 'add' ? '新增员工' : '编辑员工信息'"
+      @ok="handleOk">
       <edit ref="editForm" :type="type"/>
     </a-modal>
   </div>
@@ -97,6 +102,10 @@ const columns = [
     dataIndex: 'dataAuthority',
     scopedSlots: { customRender: 'dataAuthority' }
   },
+  {
+    title: '汇报对象',
+    dataIndex: 'leadLabel'
+  },
   {
     title: '操作',
     dataIndex: 'action',
@@ -179,6 +188,9 @@ export default {
      const { code, data } = await searchUserDetail(this.searchUserId)
      if (code === 0) {
        console.log(data)
+       if (this.type === 'add') {
+         return
+       }
        this.searchUserDetailData = data || {}
        this.$refs.editForm.formData = this.searchUserDetailData
        this.$refs.editForm.backfillForm({ label: this.searchUserDetailData.label, phone: this.searchUserDetailData.phone })
@@ -224,9 +236,10 @@ export default {
       const { code, data } = await postUser($par)
       if (code === 0) {
         console.log(data)
+         data ? this.$message.success('新增成功') : this.$message.error('新增失败')
+         this.searchUser()
+         this.visible = false
       }
-      this.visible = false
-      console.log($par, '$par')
     },
     // 选中行
     onSelectChange (selectedRowKeys) {