Jelajahi Sumber

销售目标

zhaoxiaohai 2 tahun lalu
induk
melakukan
1e8199b6dc

+ 147 - 5
packageA/target/index.js

@@ -1,17 +1,159 @@
+const _Http = getApp().globalData.http;
+
 Page({
     data: {
-        active: "业绩目标"
+        active: "业绩目标",
+        targetYear: null,
+        "year": new Date().getFullYear().toString(),
+        "content": {
+            "nocache": true,
+            "year": new Date().getFullYear().toString(), //年
+            "targettype": "人员目标", //1
+            "type": 1, //
+            "where": {
+                "condition": ""
+            }
+        },
+        target: null, //目标
+        showActions: false,
+        actionSheet: "开票金额",
+        actions: [{
+            name: '开票金额',
+            value: "1"
+        }, {
+            name: '订单金额',
+            value: "2"
+        }, {
+            name: '出货金额',
+            value: "3"
+        }],
     },
     onLoad(options) {
+        this.getData();
+    },
+    toDetail() {
+        if (this.data.active == "业绩目标") {
+            wx.navigateTo({
+                url: './person',
+            })
+        } else {
 
+        }
+    },
+    getData() {
+        _Http.basic({
+            "id": 20220920133102,
+            "content": this.data.content
+        }).then(res => {
+            if (res.msg != '成功') return wx.showToast({
+                title: res.data,
+                icon: "none"
+            })
+            let lineData = [],
+                histogram = [];
+            res.data.month.forEach(v => {
+                lineData = lineData.concat([{
+                        label: v.month + '月',
+                        value: v.l,
+                        type: "基本目标金额"
+                    },
+                    {
+                        label: v.month + '月',
+                        value: v.h,
+                        type: "挑战目标金额"
+                    },
+                    {
+                        label: v.month + '月',
+                        value: v.a,
+                        type: "实际订单金额"
+                    }
+                ])
+                histogram = histogram.concat([{
+                        label: v.month + '月',
+                        value: v.pl,
+                        type: "基础目标实际完成率"
+                    },
+                    {
+                        label: v.month + '月',
+                        value: v.ph,
+                        type: "挑战目标实际完成率"
+                    }
+                ])
+            });
+            //绘制线图
+            this.selectComponent("#line").render(lineData);
+            this.selectComponent("#histogram").render(histogram);
+            this.setData({
+                targetYear: {
+                    yl: res.data.y1l,
+                    yh: res.data.y1h,
+                    ya: res.data.y1a,
+                }
+            });
+            if (this.data.year == this.data.content.year) {
+                const m = new Date().getMonth() + 1;
+                let s = [
+                    [1, 2, 3],
+                    [4, 5, 6],
+                    [7, 8, 9],
+                    [10, 11, 12]
+                ].findIndex(v => v.some(va => va == m)) + 1;
+                this.setData({
+                    targetSeason: {
+                        sl: res.data[`s${s}l`],
+                        sh: res.data[`s${s}h`],
+                        sa: res.data[`s${s}a`],
+                    },
+                    targetMonth: {
+                        ml: res.data[`m${m}l`],
+                        mh: res.data[`m${m}h`],
+                        ma: res.data[`m${m}a`]
+                    }
+                })
+            }
+        })
     },
-    tabsChange({detail}){
+    /* 弹出选择 */
+    select({
+        detail
+    }) {
+        if (this.data.actionSheet == detail.name) return;
         this.setData({
-            active:detail.title
+            actionSheet: detail.name,
+            "content.type": detail.value,
+            showActions: false
+        });
+        this.getData();
+    },
+    cancelActions() {
+        this.setData({
+            showActions: false
         })
     },
-    onReady() {
-
+    openActions() {
+        this.setData({
+            showActions: true
+        })
+    },
+    /* 选择年份 */
+    bindDateChange({
+        detail
+    }) {
+        if (this.data.content.year == detail.value) return;
+        this.setData({
+            "content.year": detail.value
+        });
+        this.getData();
+    },
+    /* tabs切换 */
+    tabsChange({
+        detail
+    }) {
+        this.setData({
+            "content.targettype": detail.title == '业绩目标' ? '人员目标' : '项目目标',
+            active: detail.title
+        });
+        this.getData();
     },
     onShareAppMessage() {}
 })

+ 5 - 2
packageA/target/index.json

@@ -1,4 +1,7 @@
 {
-    "usingComponents": {},
-    "navigationBarTitleText": "销售目标"
+  "usingComponents": {
+    "brokenLine": "./modules/brokenLine/index",
+    "histogram": "./modules/histogram/index"
+  },
+  "navigationBarTitleText": "销售目标"
 }

+ 122 - 13
packageA/target/index.scss

@@ -1,3 +1,124 @@
+.tab-class {
+    font-size: 28rpx;
+    font-family: PingFang SC-Bold, PingFang SC;
+    font-weight: bold;
+    color: #999999;
+}
+
+.tab-active-class {
+    font-family: PingFang SC-Bold, PingFang SC;
+    font-weight: bold !important;
+    color: #3874F6 !important;
+}
+
+/* 过滤 */
+.filtrate {
+    display: flex;
+    width: 100vw;
+    height: 86rpx;
+    box-sizing: border-box;
+    align-items: center;
+
+    >view,
+    >picker,
+    .picker {
+        flex: 1;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 28rpx;
+        font-family: PingFang SC-Regular, PingFang SC;
+        color: #333333;
+
+        .icon-daoruxialajiantou {
+            display: block;
+            color: #999999;
+            font-size: 16rpx;
+            margin-left: 10rpx;
+            transform: rotateX(180deg);
+        }
+
+    }
+}
+
+/* 内容 */
+.box {
+    width: 100vw;
+    background-color: #fff;
+    margin-bottom: 20rpx;
+    padding-top: 20rpx;
+    box-sizing: border-box;
+
+    .label {
+        display: flex;
+        height: 40rpx;
+        padding-left: 30rpx;
+        align-items: center;
+        font-family: PingFang SC-Regular, PingFang SC;
+        color: #333333;
+        font-size: 28rpx;
+
+        .iconfont {
+            color: #3874F6;
+            font-size: 32rpx;
+            margin-right: 10rpx;
+        }
+    }
+
+    .content {
+        display: flex;
+        margin-top: 20rpx;
+
+        .col1 {
+            padding: 0 30rpx;
+            width: 60rpx;
+            font-size: 20rpx;
+            font-family: PingFang SC-Regular, PingFang SC;
+            color: #666666;
+
+            >view {
+                margin-top: 42rpx;
+            }
+        }
+
+        .col2 {
+            flex: 1;
+            height: 100%;
+
+            .title {
+                display: flex;
+                font-size: 20rpx;
+                font-family: PingFang SC-Regular, PingFang SC;
+                color: #666666;
+
+                >view {
+                    width: 33.33%;
+                    text-align: center;
+                }
+            }
+
+            .row {
+                display: flex;
+                height: 50rpx;
+                font-size: 36rpx;
+                font-family: PingFang SC-Bold, PingFang SC;
+                font-weight: bold;
+                color: #444444;
+                padding: 10rpx 0;
+
+                >view {
+                    width: 33.33%;
+                    text-align: center;
+
+                    text {
+                        color: #3874F6;
+                    }
+                }
+            }
+        }
+    }
+}
+
 .footer {
     position: fixed;
     width: 100vw;
@@ -7,6 +128,7 @@
     align-items: center;
     bottom: 0;
     background-color: #fff;
+    z-index: 999999999999999999999999999999999999999;
 
     .but {
         width: 500rpx;
@@ -18,17 +140,4 @@
         font-weight: bold;
         color: #FFFFFF;
     }
-}
-
-.tab-class {
-    font-size: 28rpx;
-    font-family: PingFang SC-Bold, PingFang SC;
-    font-weight: bold;
-    color: #999999;
-}
-
-.tab-active-class {
-    font-family: PingFang SC-Bold, PingFang SC;
-    font-weight: bold !important;
-    color: #3874F6 !important;
 }

+ 70 - 5
packageA/target/index.wxml

@@ -1,12 +1,77 @@
-<van-tabs active="{{ active }}" sticky color='#3874F6' tab-class='tab-class' tab-active-class='tab-active-class' bind:change="tabsChange">
+<van-tabs active="{{ active }}" color='#3874F6' tab-class='tab-class' tab-active-class='tab-active-class' bind:change="tabsChange">
     <van-tab title="业绩目标" />
     <van-tab title="业绩项目目标" />
 </van-tabs>
+<!-- 过滤 -->
+<view class="filtrate">
+    <view>全部<text class="iconfont icon-daoruxialajiantou" /></view>
+    <view catchtap="openActions">
+        {{actionSheet}}<text class="iconfont icon-daoruxialajiantou" />
+    </view>
+    <picker mode="date" value="{{content.year}}" fields='year' start="2015" end="2023" bindchange="bindDateChange">
+        <view class="picker"><text class="iconfont icon-daoruxialajiantou" />{{content.year}}<text class="iconfont icon-daoruxialajiantou" /></view>
+    </picker>
+</view>
+<van-action-sheet show="{{ showActions }}" actions="{{ actions }}" bind:select='select' bind:cancel='cancelActions' cancel-text="取消" />
 
+<view class="box">
+    <view class="label">
+        <text class="iconfont icon-a-shouyeshujugaikuangzhanshishuju" />
+        <text>目标完成情况(金额:万元)</text>
+    </view>
+    <view class="content">
+        <view class="col1">
+            <view style="margin-top: 62rpx;">年度:</view>
+            <block wx:if="{{content.year==year}}">
+                <view>本季:</view>
+                <view>本月:</view>
+            </block>
+        </view>
+        <view class="col2">
+            <view class="title">
+                <view>基本目标</view>
+                <view>挑战目标</view>
+                <view>实际完成</view>
+            </view>
+            <view class="row">
+                <view>{{targetYear.yl}}</view>
+                <view>{{targetYear.yh}}</view>
+                <view><text>{{targetYear.ya}}</text></view>
+            </view>
+            <block wx:if="{{content.year==year}}">
+                <view class="row">
+                    <view>{{targetSeason.sl}}</view>
+                    <view>{{targetSeason.sh}}</view>
+                    <view><text>{{targetSeason.sa}}</text></view>
+                </view>
+                <view class="row">
+                    <view>{{targetMonth.ml}}</view>
+                    <view>{{targetMonth.mh}}</view>
+                    <view><text>{{targetMonth.ma}}</text></view>
+                </view>
+            </block>
+        </view>
+    </view>
+    <view style="height:20rpx" />
+</view>
+<!-- 目标完成情况 -->
+<view class="box">
+    <view class="label">
+        <text class="iconfont icon-a-shouyeshujugaikuangzhanshishuju" />
+        <text>目标完成情况(金额:万元)</text>
+    </view>
+    <brokenLine id='line' />
+</view>
+<!-- 目标完成率 -->
+<view class="box">
+    <view class="label">
+        <text class="iconfont icon-a-shouyeshujugaikuangzhanshishuju" />
+        <text>目标完成率(实际相对目标的完成率)</text>
+    </view>
+    <histogram id='histogram' />
+</view>
 
-
-
-
+<view style="height: 130rpx;" />
 <view class="footer">
-    <van-button custom-class='but'>{{active=='业绩目标'?'我的业绩目标':'我的项目目标'}}</van-button>
+    <van-button custom-class='but' bindtap="toDetail">{{active=='业绩目标'?'我的业绩目标':'我的项目目标'}}</van-button>
 </view>

+ 59 - 0
packageA/target/modules/brokenLine/chart.js

@@ -0,0 +1,59 @@
+import {
+    Chart,
+    Axis,
+    Line,
+    Legend
+} from '@antv/f2';
+import {
+    jsx as _jsx
+} from "@antv/f2/jsx-runtime";
+import {
+    jsxs as _jsxs
+} from "@antv/f2/jsx-runtime";
+export default (({
+    data
+}) => {
+    return _jsxs(Chart, {
+        data,
+        children: [
+            _jsx(Axis, {
+                field: "label",
+                tickCount: 12,
+                style: {
+                    label: {
+                        align: 'between'
+                    },
+                }
+            }),
+            _jsx(Axis, {
+                field: "value",
+                tickCount: 5
+            }),
+            _jsx(Line, {
+                x: "label",
+                y: "value",
+                lineWidth: "4px",
+                color: "type"
+            }),
+            _jsx(Legend, {
+                position: "top",
+                style: {
+                    justifyContent: 'space-around',
+                },
+                triggerMap: {
+                    press: (items, records, legend) => {
+                        const map = {};
+                        items.forEach((item) => (map[item.name] = _.clone(item)));
+                        records.forEach((record) => {
+                            map[record.type].value = record.value;
+                        });
+                        legend.setItems(_.values(map));
+                    },
+                    pressend: (items, records, legend) => {
+                        legend.setItems(items);
+                    },
+                },
+            })
+        ]
+    });
+});

+ 26 - 0
packageA/target/modules/brokenLine/index.js

@@ -0,0 +1,26 @@
+import {
+  createElement
+} from '@antv/f2';
+import Chart from './chart';
+import {
+  jsx as _jsx
+} from "@antv/f2/jsx-runtime";
+Component({
+  data: {
+    onRenderChart: () => {}
+  },
+  methods: {
+    render(data) {
+      this.setData({
+        onRenderChart: () => {
+          return this.renderChart(data);
+        }
+      });
+    },
+    renderChart(data) {
+      return createElement(Chart, {
+        data
+      });
+    }
+  }
+})

+ 6 - 0
packageA/target/modules/brokenLine/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "f2": "@antv/f2-wx"
+  }
+}

+ 3 - 0
packageA/target/modules/brokenLine/index.wxml

@@ -0,0 +1,3 @@
+<view class="container">
+  <f2 onRender="{{onRenderChart}}" />
+</view>

+ 4 - 0
packageA/target/modules/brokenLine/index.wxss

@@ -0,0 +1,4 @@
+.container {
+  width: 100%;
+  height: 560rpx;
+}

+ 46 - 0
packageA/target/modules/histogram/chart.js

@@ -0,0 +1,46 @@
+import {
+    Chart,
+    Interval,
+    Axis
+} from '@antv/f2';
+
+import {
+    jsx as _jsx
+} from "@antv/f2/jsx-runtime";
+import {
+    jsxs as _jsxs
+} from "@antv/f2/jsx-runtime";
+
+export default (({
+    data
+}) => {
+    return _jsxs(Chart, {
+        data,
+        scale: {
+            tem: {
+                tickCount: 5,
+            },
+        },
+        children: [
+            _jsx(Axis, {
+                field: "value",
+                tickCount: 12
+            }),
+            _jsx(Axis, {
+                field: "label"
+            }),
+            _jsx(Interval, {
+                x: "label",
+                y: "value",
+                color: "value",
+                adjust: "dodge",
+                style: {
+                    field: 'value',
+                    radius: (val) => {
+                        return val > 0 ? [4, 4, 0, 0] : [0, 0, 4, 4];
+                    },
+                }
+            })
+        ]
+    });
+});

+ 26 - 0
packageA/target/modules/histogram/index.js

@@ -0,0 +1,26 @@
+import {
+  createElement
+} from '@antv/f2';
+import Chart from './chart';
+import {
+  jsx as _jsx
+} from "@antv/f2/jsx-runtime";
+Component({
+  data: {
+    onRenderChart: () => {}
+  },
+  methods: {
+    render(data) {
+      this.setData({
+        onRenderChart: () => {
+          return this.renderChart(data);
+        }
+      });
+    },
+    renderChart(data) {
+      return createElement(Chart, {
+        data
+      });
+    }
+  }
+})

+ 6 - 0
packageA/target/modules/histogram/index.json

@@ -0,0 +1,6 @@
+{
+    "component": true,
+    "usingComponents": {
+        "f2": "@antv/f2-wx"
+    }
+}

+ 3 - 0
packageA/target/modules/histogram/index.wxml

@@ -0,0 +1,3 @@
+<view class="container">
+  <f2 onRender="{{onRenderChart}}" />
+</view>

+ 4 - 0
packageA/target/modules/histogram/index.wxss

@@ -0,0 +1,4 @@
+.container {
+    width: 100%;
+    height: 560rpx;
+  }

+ 94 - 0
packageA/target/person.js

@@ -0,0 +1,94 @@
+const _Http = getApp().globalData.http;
+
+Page({
+
+    /**
+     * 页面的初始数据
+     */
+    data: {
+        year: new Date().getFullYear()
+    },
+
+    /**
+     * 生命周期函数--监听页面加载
+     */
+    onLoad(options) {
+        this.getData();
+    },
+    getData() {
+        _Http.basic({
+            "id": 20220920161302,
+            "content": {
+                "year": this.data.year,
+                "targettype": "人员目标",
+                "hrid": wx.getStorageSync('userMsg').hrid
+            },
+        }).then(res => {
+            if (res.msg != '成功') return wx.showToast({
+                title: res.data,
+                icon: "none"
+            })
+            this.setData({
+                person: res.data
+            })
+        })
+    },
+    /* 选择年份 */
+    bindDateChange({
+        detail
+    }) {
+        if (this.data.year == detail.value) return;
+        this.setData({
+            "year": detail.value
+        });
+        this.getData();
+    },
+    /**
+     * 生命周期函数--监听页面初次渲染完成
+     */
+    onReady() {
+
+    },
+
+    /**
+     * 生命周期函数--监听页面显示
+     */
+    onShow() {
+
+    },
+
+    /**
+     * 生命周期函数--监听页面隐藏
+     */
+    onHide() {
+
+    },
+
+    /**
+     * 生命周期函数--监听页面卸载
+     */
+    onUnload() {
+
+    },
+
+    /**
+     * 页面相关事件处理函数--监听用户下拉动作
+     */
+    onPullDownRefresh() {
+
+    },
+
+    /**
+     * 页面上拉触底事件的处理函数
+     */
+    onReachBottom() {
+
+    },
+
+    /**
+     * 用户点击右上角分享
+     */
+    onShareAppMessage() {
+
+    }
+})

+ 4 - 0
packageA/target/person.json

@@ -0,0 +1,4 @@
+{
+    "usingComponents": {},
+    "navigationBarTitleText": "人员目标"
+}

+ 110 - 0
packageA/target/person.scss

@@ -0,0 +1,110 @@
+.picker {
+    display: flex;
+    align-items: center;
+    height: 86rpx;
+    line-height: 86rpx;
+    font-size: 28rpx;
+    font-family: PingFang SC-Regular, PingFang SC;
+    color: #333333;
+    margin-left: 30rpx;
+
+    .icon-daoruxialajiantou {
+        display: block;
+        color: #999999;
+        font-size: 16rpx;
+        margin-left: 10rpx;
+        transform: rotateX(180deg);
+    }
+}
+
+.person {
+    width: 100vw;
+    height: 158rpx;
+    background-color: #ffffff;
+    box-sizing: border-box;
+    padding: 20rpx 30rpx;
+    border-bottom: 1px solid #ddd;
+
+    >view {
+        font-size: 24rpx;
+        font-family: PingFang SC-Regular, PingFang SC;
+        color: #999999;
+        line-height: 36rpx;
+    }
+
+    .title {
+        font-size: 28rpx;
+        font-family: PingFang SC-Regular, PingFang SC;
+        color: #000000;
+        margin-bottom: 10rpx;
+    }
+}
+
+/* 目标 */
+.target {
+    display: flex;
+    width: 100vw;
+    height: 124rpx;
+    background-color: #ffffff;
+    box-sizing: border-box;
+
+    >view {
+        width: 50%;
+
+        .lable {
+            font-size: 24rpx;
+            font-family: PingFang SC-Regular, PingFang SC;
+            color: #666666;
+            height: 34rpx;
+            line-height: 34rpx;
+            text-align: center;
+            margin-top: 20rpx;
+        }
+
+        .value {
+            font-size: 28rpx;
+            font-family: PingFang SC-Regular, PingFang SC;
+            color: #333333;
+            height: 40rpx;
+            line-height: 40rpx;
+            text-align: center;
+            margin-top: 10rpx;
+        }
+    }
+}
+
+.season {
+    width: 100vw;
+    height: 90rpx;
+    line-height: 90rpx;
+    background-color: #ffffff;
+    padding-left: 30rpx;
+    box-sizing: border-box;
+    font-size: 28rpx;
+    font-family: PingFang SC-Regular, PingFang SC;
+    color: #333333;
+    margin-top: 20rpx;
+}
+
+.m-target {
+    height: 270rpx;
+    width: 100vw;
+    background-color: #ffffff;
+    border-top: 1px solid #ddd;
+    box-sizing: border-box;
+    padding-left: 30rpx;
+
+    >view {
+        display: flex;
+        justify-content: space-between;
+        width: 100%;
+        height: 90rpx;
+        line-height: 90rpx;
+        border-bottom: 1px solid #ddd;
+        box-sizing: border-box;
+        padding-right: 30rpx;
+        font-size: 28rpx;
+        font-family: PingFang SC-Regular, PingFang SC;
+        color: #666666;
+    }
+}

+ 102 - 0
packageA/target/person.wxml

@@ -0,0 +1,102 @@
+<picker mode="date" value="{{year}}" fields='year' start="2015" end="2023" bindchange="bindDateChange">
+    <view class="picker"><text class="iconfont icon-daoruxialajiantou" />{{year}}<text class="iconfont icon-daoruxialajiantou" /></view>
+</picker>
+
+<view class="person">
+    <view class="title">
+        人员:{{person.name}}
+    </view>
+    <view>
+        部门:{{person.depname}}
+    </view>
+    <view>
+        职位:{{person.position}}
+    </view>
+</view>
+<view class="target">
+    <view>
+        <view class="lable">年度基本目标金额(万元)</view>
+        <view class="value">{{person.y1l}}</view>
+    </view>
+    <view>
+        <view class="lable">年度挑战目标金额(万元)</view>
+        <view class="value">{{person.y1h}}</view>
+    </view>
+</view>
+
+<!-- 季度 -->
+<view class="season">
+    第一季度目标
+</view>
+<view class="target">
+    <view>
+        <view class="lable">基本目标金额(万元)</view>
+        <view class="value">{{person.s1l}}</view>
+    </view>
+    <view>
+        <view class="lable">挑战目标金额(万元)</view>
+        <view class="value">{{person.s1h}}</view>
+    </view>
+</view>
+<view class="m-target">
+    <view>1月目标金额(万元)<text>{{person.m1l}}</text></view>
+    <view>2月目标金额(万元)<text>{{person.m2l}}</text></view>
+    <view>3月目标金额(万元)<text>{{person.m3l}}</text></view>
+</view>
+
+<view class="season">
+    第二季度目标
+</view>
+<view class="target">
+    <view>
+        <view class="lable">基本目标金额(万元)</view>
+        <view class="value">{{person.s2l}}</view>
+    </view>
+    <view>
+        <view class="lable">挑战目标金额(万元)</view>
+        <view class="value">{{person.s2h}}</view>
+    </view>
+</view>
+<view class="m-target">
+    <view>4月目标金额(万元)<text>{{person.m4l}}</text></view>
+    <view>5月目标金额(万元)<text>{{person.m5l}}</text></view>
+    <view>6月目标金额(万元)<text>{{person.m6l}}</text></view>
+</view>
+
+<view class="season">
+    第三季度目标
+</view>
+<view class="target">
+    <view>
+        <view class="lable">基本目标金额(万元)</view>
+        <view class="value">{{person.s3l}}</view>
+    </view>
+    <view>
+        <view class="lable">挑战目标金额(万元)</view>
+        <view class="value">{{person.s3h}}</view>
+    </view>
+</view>
+<view class="m-target">
+    <view>7月目标金额(万元)<text>{{person.m7l}}</text></view>
+    <view>8月目标金额(万元)<text>{{person.m8l}}</text></view>
+    <view>9月目标金额(万元)<text>{{person.m9l}}</text></view>
+</view>
+
+<view class="season">
+    第四季度目标
+</view>
+<view class="target">
+    <view>
+        <view class="lable">基本目标金额(万元)</view>
+        <view class="value">{{person.s4l}}</view>
+    </view>
+    <view>
+        <view class="lable">挑战目标金额(万元)</view>
+        <view class="value">{{person.s4h}}</view>
+    </view>
+</view>
+<view class="m-target">
+    <view>10月目标金额(万元)<text>{{person.m10l}}</text></view>
+    <view>11月目标金额(万元)<text>{{person.m11l}}</text></view>
+    <view>12月目标金额(万元)<text>{{person.m12l}}</text></view>
+</view>