xiaohaizhao 11 meses atrás
pai
commit
2e6ac3e445

+ 4 - 4
app.json

@@ -192,7 +192,8 @@
                 "index/index",
                 "panel/detail",
                 "customerBlance/detail",
-                "customerBlance/Pipeline"
+                "customerBlance/Pipeline",
+                "dataOverview/index"
             ]
         }
     ],
@@ -244,8 +245,6 @@
         "van-checkbox": "@vant/weapp/checkbox/index",
         "van-checkbox-group": "@vant/weapp/checkbox-group/index",
         "van-transition": "@vant/weapp/transition/index",
-        "shrink": "/components/shrink/shrink",
-        "timeRange": "/components/timeRange/timeRange",
         "viewDate": "/components/viewDate/index",
         "filtrate": "/components/filtrate/filtrate"
     },
@@ -254,7 +253,8 @@
         "color": "#000000",
         "selectedColor": "#000000",
         "backgroundColor": "#000000",
-        "list": [{
+        "list": [
+            {
                 "pagePath": "pages/tabbar/home/index"
             },
             {

+ 14 - 1
project.private.config.json

@@ -6,5 +6,18 @@
         "compileHotReLoad": true,
         "bigPackageSizeSupport": false
     },
-    "libVersion": "3.8.2"
+    "libVersion": "3.8.2",
+    "condition": {
+        "miniprogram": {
+            "list": [
+                {
+                    "name": "数据总揽",
+                    "pathName": "salesPanel/dataOverview/index",
+                    "query": "",
+                    "scene": null,
+                    "launchMode": "default"
+                }
+            ]
+        }
+    }
 }

+ 222 - 0
salesPanel/dataOverview/Clue/dataOverview.js

@@ -0,0 +1,222 @@
+const _Http = getApp().globalData.http;
+Component({
+    options: {
+        addGlobalClass: true,
+    },
+    properties: {
+        update: {
+            type: Function
+        }
+    },
+    lifetimes: {
+        attached: function () {
+            getApp().globalData.Language.getLanguagePackage(this)
+            this.triggerEvent('update', "Clue")
+
+            this.setData({
+                dates: this.data.dates.map(v => {
+                    v.name = getApp().globalData.Language.getMapText(v.value);
+                    v.color = v.value == this.data.content.dateType ? "#3874F6" : "";
+                    return v
+                })
+            })
+        }
+    },
+    data: {
+        "content": {
+            dateType: "本年",
+            dataid: wx.getStorageSync('userMsg').userid,
+            username: wx.getStorageSync('userMsg').name,
+            "where": {
+                isleave: "1"
+            }
+        },
+        detaShow: false,
+        dates: [{
+            value: "全部"
+        }, {
+            value: "本年"
+        }, {
+            value: "本季"
+        }, {
+            value: "本月"
+        }, {
+            value: "去年"
+        }, ],
+    },
+    methods: {
+        getList(init = false) {
+            let content = this.data.content
+            const {
+                dataid,
+                type,
+                username,
+                isleave
+            } = getCurrentPages()[getCurrentPages().length - 1].data;
+            if (content.dataid != dataid || content.type != type || isleave != isleave) init = true
+            content.dataid = dataid;
+            content.type = type;
+            content.username = username;
+            content.where.isleave = isleave;
+            _Http.basic({
+                "id": 20231014114204,
+                content
+            }).then(res => {
+                console.log("线索数据概况", res)
+                this.selectComponent('#ListBox').RefreshToComplete();
+                if (res.code != '1') return wx.showToast({
+                    title: res.msg,
+                    icon: "none"
+                })
+                const getMapText = getApp().globalData.Language.getMapText;
+                let list = [{
+                        title: '线索总数',
+                        value: res.data.total,
+                        tips: getMapText('线索总数:包含待分配、已分配线索数。'),
+                        link: true
+                    },
+                    {
+                        title: '待分配未过期线索数',
+                        value: res.data.dfp,
+                        tips: getMapText('待分配未过期线索数:所有线索中,待分配未过期的线索数量。'),
+                        link: true
+                    },
+                    {
+                        title: '待跟进线索数',
+                        value: res.data.dgj,
+                        tips: getMapText('待跟进线索数:销售线索应用中,已分配给业务员,待跟进状态的线索数量。'),
+                        link: true
+                    },
+                    {
+                        title: '跟进中线索数',
+                        value: res.data.gjz,
+                        tips: getMapText('跟进中线索数:销售线索应用中,已分配给业务员,跟进中状态的线索数量。'),
+                        link: true
+                    },
+                    {
+                        title: '已转化线索数',
+                        value: res.data.yzh,
+                        tips: getMapText("①已转化线索数:销售线索应用中,已分配给业务员,已转化状态的线索数量。") + '\n\n' + getMapText('②一条线索可进行两次转化:转化客户、转化项目,因此,已转化线索数≠转化客户线索数+转化项目线索数。'),
+                        link: true
+                    },
+                    {
+                        title: '已无效线索数',
+                        value: res.data.ywx,
+                        tips: getMapText("已无效线索数:销售线索应用中,已分配给业务员,已无效状态的线索数量。"),
+                        link: true
+                    },
+                    {
+                        title: '待分配已过期线索数',
+                        value: res.data.ygq,
+                        tips: getMapText('待分配已过期线索数:所有线索中,待分配已过期的线索数量。'),
+                        link: true
+                    },
+                    {
+                        title: '过期比例',
+                        value: Math.round(((res.data.gqbl * 100) * 100) / 100) + '%',
+                        tips: getMapText('过期比例=已过期线索数÷待分配线索数×100%'),
+                        link: false
+                    },
+                    {
+                        title: '转化客户线索数',
+                        value: res.data.covercusomers,
+                        tips: getMapText('转化客户线索数:统计有转化客户操作的线索数'),
+                        link: true
+                    },
+                    {
+                        title: '转化项目线索数',
+                        value: res.data.coverproject,
+                        tips: getMapText('转化客户线索数:统计有转化项目操作的线索数'),
+                        link: true
+                    },
+                    {
+                        title: '线索转化率',
+                        value: Math.round(((res.data.zhl * 100) * 100) / 100) + '%',
+                        tips: getMapText('线索转化率=已转化线索数÷线索总数×100%'),
+                        link: false
+                    },
+                    {
+                        title: '线索成交数',
+                        value: res.data.dealqty,
+                        tips: getMapText('线索成交数:转化后有下订单的线索数量'),
+                        link: true
+                    },
+                    {
+                        title: '线索成交率',
+                        value: Math.round(((res.data.cjl * 100) * 100) / 100) + '%',
+                        tips: getMapText('线索成交率=转化后有下订单的线索数÷线索总数×100%'),
+                        link: false
+                    },
+                    {
+                        title: '参与线索数',
+                        value: res.data.joinordercluesize,
+                        tips: getMapText('参与线索数:参与的并且至少有一次跟进的线索数量'),
+                        link: true
+                    },
+                    {
+                        title: '参与线索转化数',
+                        value: res.data.joincoverorderclue,
+                        tips: getMapText('参与线索转化数:参与的并且至少有一次跟进的有发生转化的线索数量'),
+                        link: true
+                    },
+                    {
+                        title: '参与线索转化率',
+                        value: Math.round(((res.data.joinordercluezhl * 100) * 100) / 100) + '%',
+                        tips: getMapText('参与线索转化率=参与线索转化数 ÷ 参与线索数 ×100%'),
+                        link: false
+                    },
+                    {
+                        title: '参与线索成交数',
+                        value: res.data.joindealorderclue,
+                        tips: getMapText('参与线索成交数:参与的并且至少有一次跟进的转化后有下订单的线索数量'),
+                        link: true
+                    },
+                    {
+                        title: '参与线索成交率',
+                        value: Math.round(((res.data.joinordercluecjl * 100) * 100) / 100) + '%',
+                        tips: getMapText('参与线索成交率=参与线索成交率 ÷ 参与线索数 ×100%'),
+                        link: false
+                    },
+                ]
+                this.setData({
+                    list,
+                })
+                this.onCancel()
+            })
+        },
+        openDateType() {
+            this.setData({
+                detaShow: true
+            })
+        },
+        dateOnSelect(event) {
+            const {
+                value
+            } = event.detail;
+            if (this.data.content.dateType == value) return this.onCancel();
+            this.setData({
+                "content.dateType": value,
+                dates: this.data.dates.map(item => {
+                    item.color = item.value == value ? "#3874F6" : "";
+                    item.loading = item.value == value ? true : false;
+                    return item
+                }),
+            })
+            this.getList(true)
+        },
+        onCancel() {
+            this.setData({
+                actionShow: false,
+                detaShow: false,
+                list: this.data.list.map(item => {
+                    item.loading = false;
+                    return item
+                }),
+                dates: this.data.dates.map(item => {
+                    item.loading = false;
+                    return item
+                })
+            })
+        },
+    }
+})

+ 4 - 0
salesPanel/dataOverview/Clue/dataOverview.json

@@ -0,0 +1,4 @@
+{
+    "component": true,
+    "usingComponents": {}
+}

+ 51 - 0
salesPanel/dataOverview/Clue/dataOverview.scss

@@ -0,0 +1,51 @@
+.head {
+	display: flex;
+	justify-content: space-between;
+	margin-bottom: 0;
+	line-height: 40rpx;
+	font-family: PingFang SC, PingFang SC;
+	font-weight: 700;
+	font-size: 28rpx;
+	color: #333333;
+
+	.right {
+		display: flex;
+		align-items: center;
+
+		.iconfont {
+			margin-left: 10rpx;
+		}
+	}
+}
+
+.list {
+	display: flex;
+	flex-wrap: wrap;
+	justify-content: space-between;
+
+	.item {
+		width: 314rpx;
+		padding: 10rpx 20rpx;
+		box-sizing: border-box;
+		border-radius: 20rpx;
+		border: 1rpx solid #E0E0E0;
+		margin-bottom: 20rpx;
+
+		.title {
+			line-height: 28rpx;
+			font-family: PingFang SC, PingFang SC;
+			font-size: 20rpx;
+			color: #999999;
+			margin-top: 6rpx;
+		}
+
+		.value {
+			line-height: 44rpx;
+			font-family: PingFang SC, PingFang SC;
+			font-weight: bold;
+			font-size: 32rpx;
+			color: #333333;
+		}
+
+	}
+}

+ 18 - 0
salesPanel/dataOverview/Clue/dataOverview.wxml

@@ -0,0 +1,18 @@
+<view class="global-card head" style="margin-bottom: 0;">
+    <view class="left">{{language[content.username]||content.username}}_{{language['数据概况']||'数据概况'}}</view>
+    <view class="right" bind:tap="openDateType">
+        {{language[content.dateType]||content.dateType}}
+        <text class="iconfont icon-xiangxiazhankai" />
+    </view>
+</view>
+<Yl_ListBox id='ListBox' bind:getlist='getList'>
+    <view class="global-card list">
+        <view class="item" wx:for="{{list}}" wx:key="title">
+            <view class="value">{{item.value}}</view>
+            <view class="title">{{language[item.title]||item.title}}</view>
+        </view>
+    </view>
+    <My_empty wx:if="{{list.length==0}}" />
+</Yl_ListBox>
+
+<van-action-sheet show="{{  detaShow }}" actions="{{ dates }}" bind:select='dateOnSelect' bind:cancel='onCancel' bind:click-overlay='onCancel' cancel-text="{{language['取消']||'取消'}}" />

+ 111 - 0
salesPanel/dataOverview/index.js

@@ -0,0 +1,111 @@
+Page({
+    data: {
+        active: "",
+        page: null,
+        ops: [],
+        unfolds: [],
+        showFiltrate: false,
+        dataid: wx.getStorageSync('userMsg').userid,
+        username: wx.getStorageSync('userMsg').name,
+        type: 0,
+        isleave: 1,
+    },
+    onLoad(options) {
+        getApp().globalData.Language.getLanguagePackage(this, '数据总览');
+        let organization = this.selectComponent("#organization");
+        organization.setData({
+            tabs: [{
+                value: 1,
+                name: "在职"
+            }, {
+                value: 2,
+                name: "离职"
+            }],
+        })
+        organization.initDepAndUser()
+        const ops = wx.getStorageSync('auth').wdataoverview.options.filter(v => ["Clue", "Client", "Project", "Cost"].includes(v))
+        this.setData({
+            active: ops[0],
+            unfolds: [ops[0]]
+        })
+        this.setData({
+            ops: ops.map(v => {
+                return {
+                    name: v,
+                    title: {
+                        "Clue": "线索",
+                        "Client": "客户",
+                        "Project": "项目",
+                        "Cost": "营销费用"
+                    } [v]
+                }
+            }),
+        })
+    },
+    onChange(event) {
+        const active = event.detail.name;
+        if (!this.data.unfolds.includes(active)) {
+            this.data.unfolds.push(active);
+        }
+        this.setData({
+            active,
+            unfolds: this.data.unfolds
+        });
+        this.getList({
+            detail: active
+        });
+    },
+    getList({
+        detail
+    }) {
+        if (detail && this.data.active == "") return setTimeout(() => {
+            this.getList({
+                detail: detail
+            })
+        })
+        if (detail && this.data.active != detail) return;
+        const page = this.selectComponent("#" + this.data.active);
+        if (page) {
+            if (page.data.total == null) page.getList();
+            this.data.page = page;
+        }
+    },
+    openFiltrate() {
+        this.setData({
+            showFiltrate: true
+        })
+    },
+    handleFilter({
+        detail
+    }) {
+        if (detail.name == "close") return;
+        if (detail.name == 'reset') {
+            this.selectComponent("#organization").setData({
+                isleave: 1
+            })
+            this.selectComponent("#organization").initDepAndUser()
+            this.setData({
+                dataid: wx.getStorageSync('userMsg').userid,
+                'type': 0,
+                isleave: 1,
+                username: wx.getStorageSync('userMsg').name
+            })
+        } else {
+            let active = this.selectComponent("#organization").data.result,
+                isleave = this.selectComponent("#organization").data.isleave;
+            let type = active.userid ? 0 : 1,
+                dataid = type == 0 ? active.userid : active.departmentid
+            this.setData({
+                isleave,
+                dataid,
+                type,
+                username: active.name
+            })
+        }
+        const page = this.selectComponent("#" + this.data.active);
+        if (page) {
+            if (page.data.total == null) page.getList();
+            this.data.page = page;
+        }
+    },
+})

+ 6 - 0
salesPanel/dataOverview/index.json

@@ -0,0 +1,6 @@
+{
+    "usingComponents": {
+        "Clue": "./list/Clue"
+    },
+    "navigationBarTitleText": "数据总览"
+}

+ 50 - 0
salesPanel/dataOverview/index.scss

@@ -0,0 +1,50 @@
+.tab-box {
+	display: flex;
+
+	.left {
+			width: 0;
+			flex: 1;
+
+			.tab-class {
+					font-family: Microsoft YaHei, Microsoft YaHei;
+					font-weight: bold;
+					font-size: 28rpx;
+					color: #333333;
+			}
+	}
+
+	.right {
+			display: flex;
+			align-items: center;
+			background-color: #fff;
+			flex-shrink: 0;
+
+			.filtrate-box {
+					display: flex;
+					height: 100%;
+					align-items: center;
+					padding: 0 20rpx;
+
+					font-family: PingFang SC, PingFang SC;
+					font-size: 28rpx;
+					color: #666666;
+
+					.iconfont {
+							margin-right: 6rpx;
+							color: #999999;
+					}
+
+			}
+
+			.search-box {
+					display: flex;
+					height: 100%;
+					align-items: center;
+					padding: 0 30rpx;
+
+					.iconfont {
+							font-size: 40rpx;
+					}
+			}
+	}
+}

+ 37 - 0
salesPanel/dataOverview/index.wxml

@@ -0,0 +1,37 @@
+<view class='tab-box'>
+	<view class="left">
+		<van-tabs active="{{ active }}" tab-class='tab-class' color='#3874F6' bind:change="onChange">
+			<van-tab wx:for="{{ops}}" wx:key="name" title="{{language[item.title]||item.title}}" name="{{item.name}}" />
+		</van-tabs>
+	</view>
+	<view class="right">
+		<view class="filtrate-box" hover-class="navigator-hover" bindtap="openFiltrate">
+			<text class="iconfont  icon-shaixuan" />
+			<view wx:if="{{username}}">
+				{{language[username]||username}}_{{language[isleave==1?'在职':'离职']||isleave==1?'在职':'离职'}}
+			</view>
+			{{language['筛选']||'筛选'}}
+		</view>
+	</view>
+</view>
+
+<view hidden="{{active != 'Clue'}}">
+	<Clue id='Clue' wx:if="{{per.query(unfolds,'Clue')}}" bindupdate="getList" />
+</view>
+
+
+<view hidden="{{active != 'Client'}}">
+	<Client id='Client' wx:if="{{per.query(unfolds,'Client')}}" bindupdate="getList" />
+</view>
+<view hidden="{{active != 'Project'}}">
+	<Project id='Project' wx:if="{{per.query(unfolds,'Project')}}" bindupdate="getList" />
+</view>
+
+<view hidden="{{active != 'Contacts'}}">
+	<Contacts id='Contacts' wx:if="{{per.query(unfolds,'Contacts')}}" bindupdate="getList" />
+</view>
+<wxs src='../../utils/wxmlQueryPer.wxs' module="per" />
+
+<Yl_Filtrate1 id="Yl_Filtrate1" show='{{showFiltrate}}' list="{{[]}}" bindhandle="handleFilter">
+	<organization slot='head' defaultIsleave='1' dimissionF id='organization' />
+</Yl_Filtrate1>

+ 45 - 0
salesPanel/dataOverview/list/Clue.js

@@ -0,0 +1,45 @@
+const _Http = getApp().globalData.http;
+Component({
+    options: {
+        addGlobalClass: true,
+    },
+    properties: {
+        update: {
+            type: Function
+        }
+    },
+    lifetimes: {
+        attached: function () {
+            getApp().globalData.Language.getLanguagePackage(this)
+            this.triggerEvent('update', "Clue")
+        }
+    },
+    data: {
+        tabsActive: 0,
+        tabsList: [{
+            label: "数据概况",
+            model: "#DataOverview"
+        }],
+    },
+    methods: {
+        getList() {
+            this.partialRenewal()
+        },
+        tabsChange({
+            detail
+        }) {
+            this.setData({
+                tabsActive: detail
+            });
+            this.partialRenewal();
+        },
+        //局部数据更新 tabs
+        partialRenewal() {
+            const model = this.data.tabsList[this.data.tabsActive].model;
+            if (model) {
+                const Component = this.selectComponent(model);
+                Component.getList(true);
+            }
+        },
+    }
+})

+ 6 - 0
salesPanel/dataOverview/list/Clue.json

@@ -0,0 +1,6 @@
+{
+    "component": true,
+    "usingComponents": {
+        "DataOverview": "../Clue/dataOverview"
+    }
+}

+ 50 - 0
salesPanel/dataOverview/list/Clue.scss

@@ -0,0 +1,50 @@
+.tab-box {
+	display: flex;
+
+	.left {
+			width: 0;
+			flex: 1;
+
+			.tab-class {
+					font-family: Microsoft YaHei, Microsoft YaHei;
+					font-weight: bold;
+					font-size: 28rpx;
+					color: #333333;
+			}
+	}
+
+	.right {
+			display: flex;
+			align-items: center;
+			background-color: #fff;
+			flex-shrink: 0;
+
+			.filtrate-box {
+					display: flex;
+					height: 100%;
+					align-items: center;
+					padding: 0 20rpx;
+
+					font-family: PingFang SC, PingFang SC;
+					font-size: 28rpx;
+					color: #666666;
+
+					.iconfont {
+							margin-right: 6rpx;
+							color: #999999;
+					}
+
+			}
+
+			.search-box {
+					display: flex;
+					height: 100%;
+					align-items: center;
+					padding: 0 30rpx;
+
+					.iconfont {
+							font-size: 40rpx;
+					}
+			}
+	}
+}

+ 3 - 0
salesPanel/dataOverview/list/Clue.wxml

@@ -0,0 +1,3 @@
+<Yl_FunTabs id='Yl_FunTabs' safety='{{false}}' list='{{tabsList}}' mode='buts' active='{{tabsActive}}' bind:onChenge="tabsChange">
+    <DataOverview slot='数据概况' id='DataOverview' />
+</Yl_FunTabs>

+ 3 - 3
salesPanel/trendAnalysis/index.wxml

@@ -3,13 +3,13 @@
 	<checkbox-group bindchange="onCheckboxChange" style="display: flex; align-items: center;">
 		<label style="font-size: 12px; display: flex; align-items: center;">
 			<checkbox value="{{isbooked}}" style="transform: scale(0.8);" color="#296DEF" />
-			是否包含备货数据
+			{{language['是否包含备货数据']||'是否包含备货数据'}}
 		</label>
 	</checkbox-group>
 </view>
 <view class="global-card">
-				<view class="chart"></view>
+	<view class="chart"></view>
 </view>
 <view class="chart2">
-				<ec-canvas id="mychart" canvas-id="chart" ec="{{ ec }}"></ec-canvas>
+	<ec-canvas id="mychart" canvas-id="chart" ec="{{ ec }}"></ec-canvas>
 </view>

+ 4 - 0
utils/work/apps.js

@@ -38,6 +38,10 @@ function getapps() {
 		name: "销售数据",
 		path: "/salesPanel/index/index",
 		icon: "work-xiaoshoushuju"
+	}, {
+		name: "数据总览",
+		path: "/salesPanel/dataOverview/index",
+		icon: "work-shujutongji"
 	}];
 	let app = [...getApp().globalData.queryPer.query(wx.getStorageSync('userauth'), ['通用'], ['通用']), ...getApp().globalData.queryPer.query(wx.getStorageSync('userauth'), ['数据统计'], ['数据分析'])],
 		list = [];