فهرست منبع

Merge remote-tracking branch 'origin/master'

yusm 4 ماه پیش
والد
کامیت
23fcf47b63
24فایلهای تغییر یافته به همراه898 افزوده شده و 17 حذف شده
  1. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/package.json
  2. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/kehuhz.png
  3. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitdingdan.png
  4. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitjer.png
  5. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitkehu.png
  6. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlianxir.png
  7. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlishi.png
  8. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitshangji.png
  9. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitxianshuo.png
  10. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/shangjihz.png
  11. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/xiansuohz.png
  12. 64 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/dataView/reRcharts.vue
  13. 5 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js
  14. 146 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/businessEcharts.vue
  15. 111 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataAnalysis.vue
  16. 165 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataSummary.vue
  17. 176 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/filterItem.vue
  18. 184 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/salesBriefings.vue
  19. 0 9
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue
  20. 14 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/defaultData.js
  21. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/vite.config.js
  22. 20 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/yarn.lock
  23. 5 1
      fhKeeper/formulahousekeeper/timesheet/src/components/selectPersonnel.vue
  24. 5 1
      fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/package.json

@@ -13,6 +13,7 @@
   "dependencies": {
     "axios": "^1.6.7",
     "dayjs": "^1.11.13",
+    "echarts": "^5.5.1",
     "pinia": "^2.1.7",
     "pinia-plugin-persist": "^1.0.0",
     "vant": "^4.5.0",

BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/kehuhz.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitdingdan.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitjer.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitkehu.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlianxir.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlishi.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitshangji.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitxianshuo.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/shangjihz.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/xiansuohz.png


+ 64 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/dataView/reRcharts.vue

@@ -0,0 +1,64 @@
+<script setup lang="ts">
+import { ECharts, EChartsOption, init } from 'echarts';
+import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
+
+const props = defineProps({
+  width: {
+    type: String,
+    default: '100%'
+  },
+  height: {
+    type: String,
+    default: '100%'
+  },
+  option: {
+    type: Object,
+    default: () => ({})
+  }
+});
+
+const myChartsRef = ref();
+let myChart;
+let timer;
+
+// 初始化echarts
+const initChart = () => {
+  if (myChart !== undefined) {
+    myChart.dispose();
+  }
+  myChart = init(myChartsRef.value);
+  // 拿到option配置项,渲染echarts
+  myChart?.setOption(props.option, true);
+  resizeChart()
+};
+// 重新渲染echarts
+const resizeChart = () => {
+  timer = setTimeout(() => {
+    if (myChart) {
+      myChart.resize();
+    }
+  }, 500);
+};
+
+onMounted(() => {
+  initChart();
+  window.addEventListener('resize', resizeChart);
+});
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', resizeChart);
+  clearTimeout(timer);
+  timer = null;
+});
+
+watch(() => props.option, () => {
+  initChart();
+}, {
+  deep: true
+}
+);
+</script>
+
+<template>
+  <div ref="myChartsRef" :style="{ height: height, width: width }" :option="option"></div>
+</template>

+ 5 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js

@@ -99,4 +99,8 @@ export const TOP_DATA_OF_CONTRACT_LIST = `/contract/pageContractByPin` // 合同
 
 export const SALES_ORDER_TOP_PLACEMENT = `/order/pinOrder` // 销售订单顶置
 export const CANCEL_THE_TOP_PLACEMENT_OF_THE_SALES_ORDER = `/order/undoPin` // 取消顶置
-export const TOP_DATA_OF_SALES_ORDER_LIST = `/order/pageOrderByPin` // 销售订单列表顶置数据
+export const TOP_DATA_OF_SALES_ORDER_LIST = `/order/pageOrderByPin` // 销售订单列表顶置数据
+
+export const GET_SALES_BRIEFINGS = `/order/salesKit` // 获取销售简报
+export const OBTAIN_DATA_SUMMARY = `/order/dataSummary` // 获取数据汇总
+export const STAGE_OF_OBTAINING_BUSINESS_OPPORTUNITIES = `/order/businessOpportunityStage` // 获取商机阶段

+ 146 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/businessEcharts.vue

@@ -0,0 +1,146 @@
+<template>
+  <div class="businessOpportunityStage box mt-4">
+    <div class="box-title">
+      <div class="box-title-left text-size-in">商机阶段</div>
+      <div class="box-title-right text-size-small" @click="showPopUpLayer()">
+        {{ scopeText }}/{{ dateText }}
+        <van-icon name="play" class="ml-2 rotate-90" color="#747474" />
+      </div>
+    </div>
+
+    <div class="content w-full overflow-hidden transitionEffect"
+      :style="`height: ${expandAndCollapse ? usePxToVwView(echartsHeight) : '0'}`">
+      <ReRcharts :option="echartsOption" />
+    </div>
+
+    <div class="w-full flex justify-center mt-2" @click="expandAndCollapseSwitch()">
+      <van-icon name="arrow-up" color="#AFAFAF" :class="`${!expandAndCollapse && 'rotate-180'} transitionEffect`" />
+    </div>
+
+    <van-popup v-model:show="filterCriteriaPopUpLayerFlag" position="bottom" :style="{ height: '100%' }" closeable>
+      <FilterItem :dataFilters="filterCriteria" @submit="FilterItemSubmit" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { STAGE_OF_OBTAINING_BUSINESS_OPPORTUNITIES } from '@hooks/useApi.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import requests from "@common/requests"
+import usePxToVwView from "@hooks/usePxTransform.js";
+import ReRcharts from '@components/dataView/reRcharts.vue';
+import FilterItem from './filterItem.vue';
+
+const filterCriteriaPopUpLayerFlag = ref(false);
+const filterCriteria = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+const expandAndCollapse = ref(true);
+const echartsHeight = ref(140)
+const echartsOption = ref({})
+
+const scopeText = computed(() => {
+  return fixedFieldPermissionOptions[filterCriteria.value.scopeSelection].label
+})
+
+const dateText = computed(() => {
+  if(filterCriteria.value.dateSelection == -1) {
+    const { customTime = [] } = filterCriteria.value
+    return ` ${customTime[0]} 至 ${customTime[1]}`
+  }
+  
+  return fixedFieldDateOptions[filterCriteria.value.dateSelection].label
+})
+
+function FilterItemSubmit(val) {
+  filterCriteria.value = val
+
+  filterCriteriaPopUpLayerFlag.value = false;
+  getEchartsData()
+}
+
+function showPopUpLayer() {
+  filterCriteriaPopUpLayerFlag.value = true;
+}
+
+function expandAndCollapseSwitch() {
+  expandAndCollapse.value = !expandAndCollapse.value;
+}
+
+function processChartData(yAxisData = [], seriesData = []) {
+  const dataLength = yAxisData.length
+  if(dataLength > 3) {
+    echartsHeight.value = dataLength * 38
+  }
+  echartsOption.value = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow'
+      }
+    },
+    legend: {
+      show: false
+    },
+    grid: {
+      top: usePxToVwView(60),
+      left: usePxToVwView(250),
+      right: usePxToVwView(60),
+      bottom: usePxToVwView(100),
+    },
+    xAxis: {
+      type: 'value',
+      axisLabel: {
+        fontSize: usePxToVwView(12),
+      }
+    },
+    yAxis: {
+      type: 'category',
+      data: yAxisData,
+      axisLabel: {
+        fontSize: usePxToVwView(14),
+      }
+    },
+    series: [
+      {
+        name: '2011',
+        type: 'bar',
+        data: seriesData,
+        barWidth: usePxToVwView(80),
+      },
+    ]
+  }
+}
+
+function getEchartsData() {
+  const { scopeSelection = 0, dateSelection = 0, customTime = [] } = filterCriteria.value
+  const formVal = {
+    queryType: scopeSelection,
+    ...(dateSelection !== -1 ? { dateType: dateSelection } : { startDate: customTime[0], endDate: customTime[1] })
+  }
+  requests.post(STAGE_OF_OBTAINING_BUSINESS_OPPORTUNITIES, {
+    ...formVal
+  }).then((res) => {
+    const yAxisData = (res.data?.dataMap || []).map(item => item.stageName);
+    const seriesData = (res.data?.dataMap || []).map(item => item.num);
+    processChartData(yAxisData, seriesData)
+  })
+}
+
+useLifecycle({
+  load: () => {
+    getEchartsData()
+  },
+  init: () => {
+    getEchartsData()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+/* 样式代码 */
+</style>

+ 111 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataAnalysis.vue

@@ -1,18 +1,126 @@
 <template>
-  <div>数据统计</div>
+  <div class="dataAnalysis">
+    <!-- 销售简报 -->
+    <SalesBriefings />
+
+    <!-- 数据汇总 -->
+    <DataSummary />
+
+    <!-- 商机阶段 -->
+    <BusinessEcharts />
+  </div>
 </template>
 
 <script setup>
 import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import requests from "@common/requests"
+
+import SalesBriefings from './salesBriefings.vue';
+import DataSummary from './dataSummary.vue';
+import BusinessEcharts from './businessEcharts.vue'
 
 useLifecycle({
   load: () => {
     // 添加加载逻辑
+  },
+  init: () => {
   }
 });
 </script>
 
-<style lang='scss' scoped>
-  /* 样式代码 */
+<style lang='scss'>
+/* 样式代码 */
+.dataAnalysis {
+  margin: 20px 0 16px 0;
+  padding: 0 16px;
+
+  .transitionEffect {
+    transition: all .5s ease-in-out;
+  }
+
+  .salesKit {
+    .content {
+      width: 100%;
+
+      .item {
+        width: 50%;
+        display: flex;
+        margin-top: 20px;
+
+        .item-img {
+          width: 54px;
+          height: 54px;
+          margin-right: 6px;
+        }
+      }
+    }
+  }
+
+  .dataSummary {
+    .data-item {
+      padding: 15px;
+      border-radius: 6px;
+      margin-top: 16px;
+
+      .kehu-img {
+        width: 21px;
+        height: 22px;
+        margin-right: 12.5px;
+      }
+
+      .businessOpportunity-img {
+        width: 23px;
+        height: 23px;
+        margin-right: 12px;
+      }
+
+      .xiansuo-img {
+        width: 23px;
+        height: 23px;
+        margin-right: 12px;
+      }
+    }
+  }
+
+  .businessOpportunityStage {}
+
+  .box {
+    background: #fff;
+    border-radius: 10px;
+    padding: 6px 16px 6px 16px;
+
+    .box-title {
+      height: 40px;
+      border-bottom: 1px solid #E5E4EE;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      position: relative;
+
+      .box-title-left {
+        padding-left: 10px;
+        color: #0E1012;
+
+        &::after {
+          content: '';
+          width: 2px;
+          border-radius: 10px;
+          background: #075985;
+          height: 18px;
+          position: absolute;
+          left: 0;
+          top: 50%;
+          transform: translateY(-50%);
+        }
+      }
+
+      .box-title-right {
+        color: #747474;
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
+}
 </style>

+ 165 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataSummary.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="dataSummary box mt-4">
+    <div class="box-title">
+      <div class="box-title-left text-size-in">数据汇总</div>
+      <div class="box-title-right text-size-small" @click="showPopUpLayer()">
+        {{ scopeText }}/{{ dateText }}
+        <van-icon name="play" class="ml-2 rotate-90" color="#747474" />
+      </div>
+    </div>
+
+    <div class="content w-full overflow-hidden transitionEffect"
+      :style="`height: ${expandAndCollapse ? usePxToVwView(313) : '0'}`">
+      <div class="data-item flex flex-row bg-[#F8F8FA]">
+        <img src="/src/assets/image/kehuhz.png" class="kehu-img">
+        <div class="flex-1">
+          <div class="font-bold text-[#000]">客户汇总</div>
+          <div class="flex mt-2">
+            <div class="text-size-small text-[#999]" style="width: 40%">
+              新增客户 <span class="ml-1 text-[#FFBD27]">
+                {{ dataSummary?.customDataSummary?.newNum || 0 }}
+              </span> 人
+            </div>
+            <div class="text-size-small text-[#999]" style="width: 60%">
+              转成交客户 <span class="ml-1 text-[#FFBD27]">
+                {{ dataSummary?.closeDealNum?.newNum || 0 }}
+              </span> 人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="data-item flex flex-row bg-[#F8F8FA]">
+        <img src="/src/assets/image/shangjihz.png" class="businessOpportunity-img">
+        <div class="flex-1">
+          <div class="font-bold text-[#000]">商机汇总</div>
+          <div class="flex mt-2">
+            <div class="text-size-small text-[#999]" style="width: 40%;">
+              新增商机 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.newNum || 0 }}
+              </span> 人
+            </div>
+            <div class="text-size-small text-[#999]" style="width: 60%;">
+              商机赢单 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.losting || 0 }}
+              </span> 个
+            </div>
+          </div>
+          <div class="flex mt-2">
+            <div class="text-size-small text-[#999]" style="width: 40%;">
+              输单商机 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.winning || 0 }}
+              </span> 人
+            </div>
+            <div class="text-size-small text-[#999]" style="width: 60%;">
+              商机总金额 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.allAmountOfMoney || 0 }}
+              </span> 元
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="data-item flex flex-row bg-[#F8F8FA]">
+        <img src="/src/assets/image/xiansuohz.png" class="xiansuo-img">
+        <div class="flex-1">
+          <div class="font-bold text-[#000]">线索汇总</div>
+          <div class="flex mt-2">
+            <div class="w-1/2 text-size-small text-[#999]">
+              新增线索 <span class="ml-1 text-[#28C67E]">
+                {{ dataSummary?.clueDataSummary?.newNum || 0 }}
+              </span> 人
+            </div>
+            <div class="w-1/2 text-size-small text-[#999]">
+              线索转商机 <span class="ml-1 text-[#28C67E]">
+                {{ dataSummary?.clueDataSummary?.changeNum || 0 }}
+              </span> 人
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="w-full flex justify-center mt-2" @click="expandAndCollapseSwitch()">
+      <van-icon name="arrow-up" color="#AFAFAF"
+        :class="`${!expandAndCollapse && 'rotate-180'} transitionEffect`" />
+    </div>
+
+    <van-popup v-model:show="filterCriteriaPopUpLayerFlag" position="bottom" :style="{ height: '100%' }" closeable>
+      <FilterItem :dataFilters="filterCriteria" @submit="FilterItemSubmit" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { OBTAIN_DATA_SUMMARY } from '@hooks/useApi.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import requests from "@common/requests"
+import usePxToVwView from "@hooks/usePxTransform.js";
+import FilterItem from './filterItem.vue';
+
+const filterCriteriaPopUpLayerFlag = ref(false);
+const expandAndCollapse = ref(true);
+const dataSummary = ref({});
+const filterCriteria = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+
+const scopeText = computed(() => {
+  return fixedFieldPermissionOptions[filterCriteria.value.scopeSelection].label
+})
+
+const dateText = computed(() => {
+  if(filterCriteria.value.dateSelection == -1) {
+    const { customTime = [] } = filterCriteria.value
+    return ` ${customTime[0]} 至 ${customTime[1]}`
+  }
+  
+  return fixedFieldDateOptions[filterCriteria.value.dateSelection].label
+})
+
+function FilterItemSubmit(val) {
+  filterCriteria.value = val
+
+  filterCriteriaPopUpLayerFlag.value = false;
+  obtainDataSummary()
+}
+
+function showPopUpLayer() {
+  filterCriteriaPopUpLayerFlag.value = true;
+}
+
+function expandAndCollapseSwitch() {
+  expandAndCollapse.value = !expandAndCollapse.value;
+}
+
+function obtainDataSummary() {
+  const { scopeSelection = 0, dateSelection = 0, customTime = [] } = filterCriteria.value
+  const formVal = {
+    queryType: scopeSelection,
+    ...(dateSelection !== -1 ? { dateType: dateSelection } : { startDate: customTime[0], endDate: customTime[1] })
+  }
+  requests.post(OBTAIN_DATA_SUMMARY, {
+    ...formVal
+  }).then((res) => {
+    dataSummary.value = res.data
+  })
+}
+
+useLifecycle({
+  load: () => {
+    obtainDataSummary()
+  },
+  init: () => {
+    obtainDataSummary()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+/* 样式代码 */
+</style>

+ 176 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/filterItem.vue

@@ -0,0 +1,176 @@
+<template>
+  <div class="filterCriteria">
+    <div class="flex-1 overflow-x-auto">
+      <div class="filterCriteria-title">范围选择</div>
+      <div class="flex flex-wrap">
+        <template v-for="(item, index) in fixedFieldPermissionOptions">
+          <div :class="`${dataFilter.scopeSelection == item.value && 'selected'} item item-special`"
+            @click="scopeSelectionCli(item)">{{ item.label }}</div>
+        </template>
+      </div>
+      <div class="filterCriteria-title">时间选择</div>
+      <div class="flex flex-wrap">
+        <template v-for="(item, index) in fixedFieldDateOptions">
+          <div :class="`${dataFilter.dateSelection == item.value && 'selected'} item`" @click="timeSelectionCli(item)">
+            {{ item.label }}</div>
+        </template>
+      </div>
+
+      <template v-if="dataFilter.dateSelection == -1">
+        <div class="filterCriteria-title">日期选择</div>
+        <div class="dateSelection flex justify-between flex-row items-center mb-4">
+          <div class="mr-6">开始日期</div>
+          <div class="dateItem flex-1" @click="showDate(0)">
+            <div>{{ dataFilter.customTime[0] ? dataFilter.customTime[0] : '请选择' }}</div>
+            <van-icon name="arrow" />
+          </div>
+        </div>
+        <div class="dateSelection flex justify-between flex-row items-center">
+          <div class="mr-6">结束日期</div>
+          <div class="dateItem flex-1" @click="showDate(1)">
+            <div>{{ dataFilter.customTime[1] ? dataFilter.customTime[1] : '请选择' }}</div>
+            <van-icon name="arrow" />
+          </div>
+        </div>
+      </template>
+    </div>
+
+    <van-button type="primary" class=" mt-6 mb-12 w-full" @click="submitSave">确定</van-button>
+
+    <van-popup v-model:show="showPicker" destroy-on-close position="bottom" :style="{ height: '50%' }">
+      <van-date-picker v-model="current" :min-date="minDate" :max-date="maxDate" @confirm="showPickerConfirm"
+        @cancel="showPicker = false" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue';
+import { defineProps, defineEmits } from 'vue'
+import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import dayjs from 'dayjs';
+import useShowToast from "@hooks/useToast";
+
+const { toastText } = useShowToast();
+const emit = defineEmits()
+const props = defineProps({
+  dataFilters: {
+    typeof: Object,
+    default: () => ({})
+  }
+})
+
+const minDate = ref(new Date(dayjs().add(-2, 'year')))
+const maxDate = ref(new Date(dayjs().add(2, 'year')))
+const showPicker = ref(false)
+const current = ref([])
+const dateFiledIndex = ref(0)
+const dataFilter = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+
+watch(() => props.dataFilters, (newValue) => {
+  dataFilter.value = newValue
+})
+
+function submitSave() {
+  const { dateSelection, customTime = [] } = dataFilter.value
+  if(dateSelection == -1 && customTime.length == 0) {
+    toastText('请选择日期', 1000)
+    return
+  }
+  emit('submit', dataFilter.value)
+}
+function showPickerConfirm({ selectedValues }) {
+  dataFilter.value.customTime[dateFiledIndex.value] = selectedValues.join('-')
+  showPicker.value = false
+}
+
+function showDate(index) {
+  if (index == 0) {
+    maxDate.value = dataFilter.value.customTime[1] ? new Date(dataFilter.value.customTime[1]) : new Date(dayjs().add(2, 'year'))
+  }
+
+  if (index == 1) {
+    minDate.value = dataFilter.value.customTime[0] ? new Date(dataFilter.value.customTime[0]) : new Date(dayjs().add(-2, 'year'))
+  }
+  const dateVal = dataFilter.value.customTime[index] || '';
+  const todaySDate = dayjs().format("YYYY-MM-DD");
+  current.value = dateVal ? dateVal.split("-") : todaySDate.split("-");
+  dateFiledIndex.value = index
+  showPicker.value = true;
+}
+
+function scopeSelectionCli(item) {
+  dataFilter.value.scopeSelection = item.value
+}
+
+function timeSelectionCli(item) {
+  dataFilter.value.dateSelection = item.value
+  if(item.value == -1) {
+    dataFilter.value.customTime = []
+  }
+}
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.filterCriteria {
+  padding: 14px 0;
+  margin: 0 20px;
+  position: relative;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .filterCriteria-title {
+    font-size: 18px;
+    color: #474A56;
+    margin-bottom: 20px;
+  }
+
+  .item {
+    width: 98px;
+    text-align: center;
+    padding: 10px 0;
+    color: #000000;
+    border: 1px solid #F8F8FA;
+    border-radius: 6px;
+    background: #F8F8FA;
+    margin-right: 20px;
+    margin-bottom: 20px;
+
+    &:nth-child(3n) {
+      margin-right: 0;
+    }
+  }
+
+  .item-special:last-child {
+    width: 160px;
+  }
+
+  .selected {
+    border-color: $themeColor;
+    color: $themeColor;
+  }
+
+  .dateSelection {
+    .dateItem {
+      border-radius: 6px;
+      background: #F8F8FA;
+      padding: 10px 14px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+  }
+}
+</style>

+ 184 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/salesBriefings.vue

@@ -0,0 +1,184 @@
+<template>
+  <div class="salesKit box">
+    <div class="box-title">
+      <div class="box-title-left text-size-in">销售简报</div>
+      <div class="box-title-right text-size-small" @click="showPopUpLayer()">
+        {{ scopeText }}/{{ dateText }}
+        <van-icon name="play" class="ml-2 rotate-90" color="#747474" />
+      </div>
+    </div>
+
+    <div class="content overflow-hidden transitionEffect"
+      :style="`height: ${expandAndCollapse ? usePxToVwView(298) : '0'}`">
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitkehu.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增客户</div>
+            <div class="flex items-end">
+              <span class="text-[#3C84F3] text-size-in font-bold mr-1">
+                {{ salesBriefings?.custom?.customCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+        <div class="item">
+          <img src="/src/assets/image/salesKitlianxir.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增联系人</div>
+            <div class="flex items-end">
+              <span class="text-[#42DC9E] text-size-in font-bold mr-1">
+                {{ salesBriefings?.contacts?.contactsCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitlishi.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增商机</div>
+            <div class="flex items-end">
+              <span class="text-[#F3893C] text-size-in font-bold mr-1">
+                {{ salesBriefings?.businessOpportunity?.businessOpportunityCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+        <div class="item">
+          <img src="/src/assets/image/salesKitdingdan.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增销售订单</div>
+            <div class="flex items-end">
+              <span class="text-[#A66AFF] text-size-in font-bold mr-1">
+                {{ salesBriefings?.salesOrder?.salesOrderCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitjer.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>销售订单金额</div>
+            <div class="flex items-end">
+              <span class="text-[#67DF2A] text-size-in font-bold mr-1">
+                {{ salesBriefings?.salesOrdersPrice?.salesOrdersPriceCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+        <div class="item">
+          <img src="/src/assets/image/salesKitshangji.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>商机金额</div>
+            <div class="flex items-end">
+              <span class="text-[#EED116] text-size-in font-bold mr-1">
+                {{ salesBriefings?.businessOpportunityPrice?.businessOpportunityPriceCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitxianshuo.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增线索</div>
+            <div class="flex items-end">
+              <span class="text-[#F44873] text-size-in font-bold mr-1">
+                {{ salesBriefings?.clue?.clueCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="w-full flex justify-center mt-2" @click="expandAndCollapseSwitch()">
+      <van-icon name="arrow-up" color="#AFAFAF" :class="`${!expandAndCollapse && 'rotate-180'} transitionEffect`" />
+    </div>
+    <van-popup v-model:show="filterCriteriaPopUpLayerFlag" position="bottom" :style="{ height: '100%' }" closeable>
+      <FilterItem :dataFilters="filterCriteria" @submit="FilterItemSubmit" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { GET_SALES_BRIEFINGS } from '@hooks/useApi.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+
+import requests from "@common/requests"
+import usePxToVwView from "@hooks/usePxTransform.js";
+import FilterItem from './filterItem.vue';
+
+const filterCriteriaPopUpLayerFlag = ref(false);
+const expandAndCollapse = ref(true);
+const salesBriefings = ref({});
+const filterCriteria = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+
+const scopeText = computed(() => {
+  return fixedFieldPermissionOptions[filterCriteria.value.scopeSelection].label
+})
+
+const dateText = computed(() => {
+  if(filterCriteria.value.dateSelection == -1) {
+    const { customTime = [] } = filterCriteria.value
+    return ` ${customTime[0]} 至 ${customTime[1]}`
+  }
+  
+  return fixedFieldDateOptions[filterCriteria.value.dateSelection].label
+})
+
+function FilterItemSubmit(val) {
+  filterCriteria.value = val
+
+  filterCriteriaPopUpLayerFlag.value = false;
+  getData()
+}
+
+function expandAndCollapseSwitch() {
+  expandAndCollapse.value = !expandAndCollapse.value;
+}
+
+function showPopUpLayer() {
+  filterCriteriaPopUpLayerFlag.value = true;
+}
+
+function getData() {
+  const { scopeSelection = 0, dateSelection = 0, customTime = [] } = filterCriteria.value
+  const formVal = {
+    queryType: scopeSelection,
+    ...(dateSelection !== -1 ? { dateType: dateSelection } : { startDate: customTime[0], endDate: customTime[1] })
+  }
+  requests.post(GET_SALES_BRIEFINGS, {
+    ...formVal
+  }).then((res) => {
+    salesBriefings.value = res.data
+  })
+}
+
+useLifecycle({
+  load: () => {
+    getData()
+  },
+  init: () => {
+    getData()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+/* 样式代码 */
+</style>

+ 0 - 9
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue

@@ -91,11 +91,7 @@ function obtainEnterpriseWeChatParameters(data = {}) {
   const token = data.id
   // const curUrl = window.location.href.split('home')[0]
   const curUrl = window.location.href
-  console.log(wx, '开始调用接口')
-  console.log(token, curUrl)
-  console.log(requests, '<==== requests')
   requests.post('/wxcorp/getCorpWXConfig', { url: curUrl, token }).then((res) => {
-    console.log(res, '<====== 返回的参数 /wxcorp/getCorpWXConfig')
     wx.config({
       beta: true,
       debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
@@ -118,19 +114,16 @@ function obtainEnterpriseWeChatParameters(data = {}) {
           signature: res.data.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
           jsApiList: ['selectExternalContact', 'openThirdAppServiceChat', 'openAppManage'], //必填,传入需要使用的接口名称
           success: function (result) {
-            console.log(result, '《+========== 成功1')
             //  wx.agentConfig成功回调后,WWOpenData 才会注入到 window 对象上面
             window.WWOpenData.bind(document.querySelector('ww-open-data'))
           },
           fail: function (res) {
-            console.log(res, '<===== 失败1')
             if (res.errMsg.indexOf('function not exist') > -1) {
               alert('版本过低请升级')
             }
           },
         })
       }).catch(err => {
-        console.log(err, '<===== 失败2')
         if (err.errMsg.indexOf('function not exist') > -1) {
           alert('版本过低请升级')
         }
@@ -141,7 +134,6 @@ function obtainEnterpriseWeChatParameters(data = {}) {
       // alert('wxConfig发生异常:'+JSON.stringify(res));
       // 企业第一次授权安装进入后会报not in reliable domain的错误,刷新后正常
       // location.reload();
-      console.log(res, '<===== 失败3')
     });
   }).catch(err => {
     alert(err);
@@ -153,7 +145,6 @@ useLifecycle({
 
   },
   init: () => {
-    console.log('执行一次')
     const currentEnvironment = navigator.userAgent.toLowerCase();
     const isCorpWX = currentEnvironment.indexOf("wxwork") > 0 ? true : false
     if(isCorpWX) {

+ 14 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/defaultData.js

@@ -57,4 +57,18 @@ export const fixedFieldPaymentStatus = [
   { value: 0, label: '未回款' },
   { value: 1, label: '已回款' },
   { value: 2, label: '已完全回款' },
+]
+
+export const fixedFieldPermissionOptions = [
+  { value: 0, label: '仅本人' },
+  { value: 1, label: '本人及下属' },
+  { value: 2, label: '仅本部门' },
+  { value: 3, label: '本部门及下属部门' },
+]
+
+export const fixedFieldDateOptions = [
+  { value: 0, label: '本月' },
+  { value: 1, label: '本周' },
+  { value: 2, label: '本年' },
+  { value: -1, label: '自定义' },
 ]

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/vite.config.js

@@ -5,8 +5,8 @@ import Components from "unplugin-vue-components/vite";
 import { VantResolver } from "unplugin-vue-components/resolvers";
 import { postcssConfig } from "./postcss.config.js";
 
-// const target = 'http://192.168.2.7:10010';
-const target = 'http://192.168.2.3:10010';
+const target = 'http://192.168.2.7:10010';
+// const target = 'http://192.168.2.3:10010';
 // const target = 'http://192.168.2.17:10010';
 // const target = 'http://47.101.180.183:10010';
 

+ 20 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/yarn.lock

@@ -658,6 +658,14 @@ eastasianwidth@^0.2.0:
   resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
   integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
 
+echarts@^5.5.1:
+  version "5.5.1"
+  resolved "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz#8dc9c68d0c548934bedcb5f633db07ed1dd2101c"
+  integrity sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.6.0"
+
 electron-to-chromium@^1.5.41:
   version "1.5.71"
   resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz"
@@ -1317,6 +1325,11 @@ ts-interface-checker@^0.1.9:
   resolved "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
   integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
 
+tslib@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
+  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+
 unplugin-vue-components@^0.25.1:
   version "0.25.2"
   resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.25.2.tgz"
@@ -1445,3 +1458,10 @@ yaml@^2.3.4:
   version "2.6.1"
   resolved "https://registry.npmmirror.com/yaml/-/yaml-2.6.1.tgz"
   integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==
+
+zrender@5.6.0:
+  version "5.6.0"
+  resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz#01325b0bb38332dd5e87a8dbee7336cafc0f4a5b"
+  integrity sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==
+  dependencies:
+    tslib "2.3.0"

+ 5 - 1
fhKeeper/formulahousekeeper/timesheet/src/components/selectPersonnel.vue

@@ -1,7 +1,7 @@
 <template>
   <el-select v-model="selectPersonnelValue" filterable @visible-change="toggleSelectPrefix" remote
     :remote-method="userListRemotemethod" :multiple="multiple" @focus="userFocus" :size="size" collapse-tags
-    @change="updateValue" clearable :ref="`select${timeRef}`" :style="`width: ${width}`" :class="`custom-select ${user.userNameNeedTranslate == 1 && selectPrefixFlg ? 'setUpInput' : ''}`">
+    @change="updateValue" clearable :ref="`select${timeRef}`" :style="`width: ${width}`" :class="`custom-select ${user.userNameNeedTranslate == 1 && selectPrefixFlg ? 'setUpInput' : ''}`" :disabled="disabled">
     <template #prefix>
       <div style="height: 100%;display: flex;align-items: center;">
         <!-- 单选 -->
@@ -74,6 +74,10 @@ export default {
     width: { // 宽度
       type: String,
       default: '200px'
+    },
+    disabled: {
+      type: Boolean,
+      default: () => false
     }
   },
   data() {

+ 5 - 1
fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue

@@ -77,7 +77,8 @@
                                         <span style="float: right; color: #8492a6; font-size: 13px;margin-left: 20px" v-if="item.jobNumber">{{ item.jobNumber }}</span>
                                     </el-option>
                                 </el-select>
-                                <selectCat v-if="user.userNameNeedTranslate == 1" :size="'mini'" :filterable="true" :subject="users" :subjectId="executorItem.executorId" :distinction="'1'" @selectCal="selectCal" :index="executorItem.indexList" :disabled="(addForm.id != null && user.id != addForm.createrId && currentProject.inchargerId != user.id) && !permissions.projectManagement && !permissions.editAnyTask && !(groupResponsibleId == user.id)"></selectCat>
+                                <selectCat v-if="user.userNameNeedTranslate == 1" :size="'mini'" :filterable="true" :subject="users" :subjectId="executorItem.executorId" :distinction="'1'" @selectCal="selectCal" :index="executorItem.indexList" :disabled="(addForm.id != null && user.id != addForm.createrId && currentProject.inchargerId != user.id) && !permissions.projectManagement && !permissions.editAnyTask && !(groupResponsibleId == user.id)" :key="executorItem.indexList"></selectCat>
+                                <!-- <selectPersonnel v-if="user.userNameNeedTranslate == 1" v-model="executorItem.executorId" :disabled="(addForm.id != null && user.id != addForm.createrId && currentProject.inchargerId != user.id) && !permissions.projectManagement && !permissions.editAnyTask && !(groupResponsibleId == user.id)"  @change="$forceUpdate()" :key="executorItem.indexList" /> -->
                             </div>
                             <!-- 项目服务 -->
                             <div v-if="user.companyId==3092">
@@ -637,6 +638,7 @@
 
 <script>
 import selectCat from "@/components/select.vue"
+import selectPersonnel from "@/components/selectPersonnel.vue"
 // 富文本样式
 import 'quill/dist/quill.core.css'
 import 'quill/dist/quill.snow.css'
@@ -659,6 +661,7 @@ export default {
   components: {
     quillEditor, // 富文本
     selectCat,
+    selectPersonnel
   },
   data() {
     return {
@@ -882,6 +885,7 @@ export default {
         newList.forEach((item, index) => {
             item.indexList = index
         })
+        console.log(newList.slice(this.startNum, this.endNum), '<==== newList')
         return newList.slice(this.startNum, this.endNum)
     },
     containerHeight() {