Selaa lähdekoodia

经销商拜访

zhujindu 9 kuukautta sitten
commit
0cbf1af2ef

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+VUE_APP_PublicTitle=SFA
+VUE_APP_publicPath=/order/
+

+ 14 - 0
.env.development

@@ -0,0 +1,14 @@
+# 页面标题
+VUE_APP_TITLE = 门店拜访
+
+# 开发环境配置
+ENV = 'development'
+VUE_APP_Target=https://ssbsfatest.nipponpaint.com.cn
+VUE_APP_SSB_LINK=http://suishenbangtest.nipponpaint.com.cn
+VUE_APP_XD_LINK=http://b2btest.nipponpaint.com.cn
+# 门店拜访/开发环境
+#VUE_APP_BASE_API = '/dev-api/'
+VUE_APP_BASE_API = '/ssbsfa/'
+# VUE_APP_BASE_API = '/'
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 17 - 0
.env.production

@@ -0,0 +1,17 @@
+
+# 页面标题
+VUE_APP_TITLE = 门店拜访
+
+# 开发环境配置
+ENV = 'production'
+
+# 正式环境
+VUE_APP_Target=https://ssbsfa.nipponpaint.com.cn
+VUE_APP_SSB_LINK=http://suishenbang.nipponpaint.com.cn
+VUE_APP_XD_LINK=http://b2b.nipponpaint.com.cn
+# 测试环境
+# VUE_APP_Target=https://ssbsfatest.nipponpaint.com.cn 
+# VUE_APP_SSB_LINK=http://suishenbangtest.nipponpaint.com.cn
+# VUE_APP_XD_LINK=http://b2btest.nipponpaint.com.cn
+VUE_APP_BASE_API='/ssbsfa/'
+devtool=cheap-module-source-map

+ 22 - 0
.env.test

@@ -0,0 +1,22 @@
+# 页面标题
+VUE_APP_TITLE = 门店拜访
+ENV = 'test'
+
+# 本地开发环境配置
+# VUE_APP_Target=http://192.168.100.191:9560/
+# VUE_APP_SSB_LINK=http://suishenbangtest.nipponpaint.com.cn
+# VUE_APP_XD_LINK=http://b2btest.nipponpaint.com.cn
+# VUE_APP_BASE_API = '/'
+#测试环境
+VUE_APP_Target=https://ssbsfatest.nipponpaint.com.cn
+VUE_APP_SSB_LINK=http://suishenbangtest.nipponpaint.com.cn
+VUE_APP_XD_LINK=http://b2btest.nipponpaint.com.cn
+VUE_APP_BASE_API = '/ssbsfa/'
+
+# 正式环境
+# VUE_APP_Target=https://ssbsfa.nipponpaint.com.cn
+# VUE_APP_SSB_LINK=http://suishenbang.nipponpaint.com.cn
+# VUE_APP_XD_LINK=http://b2b.nipponpaint.com.cn
+# VUE_APP_BASE_API='/ssbsfa/'
+# 路由懒加载
+VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 18 - 0
.eslintrc.js

@@ -0,0 +1,18 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  'extends': [
+    'plugin:vue/essential',
+    'eslint:recommended'
+  ],
+  rules: {
+	  "no-mixed-spaces-and-tabs": "off",
+    "no-console": 0,
+    "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
+  },
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+};

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+.DS_Store
+node_modules
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+/agent/

+ 28 - 0
README.md

@@ -0,0 +1,28 @@
+# storevisit-ui-mobile-agent
+
+# 环境
+
+node > 16
+
+## Project setup
+
+```
+## 建议修改下载源为淘宝镜像,镜像地址可能会有变化请自行百度
+npm install
+```
+
+### Compiles and hot-reloads
+
+```
+npm run test // 本地开发环境,为了跳过本地开发不能定位的问题,自定义定位坐标为当前位置
+npm run dev // 测试环境
+npm run prod // 生产环境
+```
+
+### Compiles and minifies
+
+```
+npm run build:dev  // 测试环境
+npm run build:prod // 生产环境
+
+```

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/cli-plugin-babel/preset'
+  ]
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 27798 - 0
package-lock.json


+ 64 - 0
package.json

@@ -0,0 +1,64 @@
+{
+  "name": "storevisit-ui-mobile-agent",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "test": "vue-cli-service serve --mode test",
+    "prod": "vue-cli-service serve --mode production",
+    "build:dev": "vue-cli-service build --mode development",
+    "build:prod": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@vant/touch-emulator": "^1.4.0",
+    "axios": "^0.24.0",
+    "axios-jsonp": "^1.0.4",
+    "clipboard": "^2.0.11",
+    "coordtransform": "^2.1.2",
+    "core-js": "^3.6.5",
+    "element-ui": "^2.15.6",
+    "vant": "^2.12.37",
+    "vconsole": "^3.15.1",
+    "vue": "^2.6.11",
+    "vue-baidu-map": "^0.21.22",
+    "vue-jsonp": "^2.0.0",
+    "vue-router": "^3.5.3",
+    "vuex": "^3.6.0",
+    "watermark-dom": "^2.3.0",
+    "weixin-js-sdk": "^1.6.5"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-eslint": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-prettier": "^3.1.1",
+    "eslint-plugin-vue": "^6.2.2",
+    "sass": "^1.23.7",
+    "sass-loader": "^8.0.0",
+    "vue-template-compiler": "^2.6.11"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "eslint:recommended"
+    ],
+    "parserOptions": {
+      "parser": "babel-eslint"
+    },
+    "rules": {
+      "no-unused-vars": "off"
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

BIN
public/favicon.ico


+ 46 - 0
public/index.html

@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <meta
+      name="viewport"
+      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
+    <title><%= webpackConfig.name %></title>
+  </head>
+  <body class="bgcolor">
+    <noscript>
+      <strong
+        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without
+        JavaScript enabled. Please enable it to continue.</strong
+      >
+    </noscript>
+    <div id="app"></div>
+    <script>
+      if (GetQueryValue('loginName') != null) {
+        localStorage.removeItem('loginType');
+        localStorage.setItem('loginName', GetQueryValue('loginName'));
+        location.href = location.href.split('?')[0] + '?v=' + new Date().getTime();
+      }
+      function GetQueryValue(queryName) {
+        var query = decodeURI(window.location.search.substring(1));
+        var vars = query.split('&');
+        for (var i = 0; i < vars.length; i++) {
+          var pair = vars[i].split('=');
+          if (pair[0] == queryName) {
+            return pair[1];
+          }
+        }
+        return null;
+      }
+    </script>
+    <script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
+    <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
+    <!--生产、uat-->
+    <script
+      charset="utf-8"
+      src="https://map.qq.com/api/gljs?v=1.exp&libraries=service,tools&key=Y7SBZ-PI5K5-FOJIT-ILHLY-PN66T-HKB4A"></script>
+  </body>
+</html>

+ 2 - 0
public/robots.txt

@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /

+ 56 - 0
src/App.vue

@@ -0,0 +1,56 @@
+<template>
+  <div id="app" class="bgcolor">
+    <router-view />
+  </div>
+</template>
+<script>
+import watermark from 'watermark-dom';
+export default {
+  name: 'App',
+  created() {
+    // 当前设备:PC/mobile
+    let isDevice = window.navigator.userAgent.match(
+      /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
+    );
+    localStorage.setItem('isDevice', !isDevice ? 'PC' : 'mobile'); // !isDevice: true:PC false:mobile
+  },
+  mounted() {
+    setTimeout(() => {
+      let username = localStorage.getItem('nickName');
+      if (username) {
+        let now = new Date();
+        let year = now.getFullYear();
+        let month = now.getMonth() + 1;
+        let day = now.getDate();
+        month = month < 10 ? '0' + month : month;
+        day = day < 10 ? '0' + day : day;
+        let date = year + '-' + month + '-' + day;
+        watermark.load({
+          watermark_txt: username + '@立邦' + date,
+          watermark_fontsize: '13px',
+          watermark_width: 100,
+          watermark_rows: 0,
+          watermark_cols: 3,
+          watermark_height: 50,
+          watermark_x_space: 10,
+          watermark_y_space: 60,
+          watermark_alpha: 0.1,
+        });
+      }
+    }, 1000);
+  },
+  methods: {},
+};
+</script>
+<style lang="scss">
+#wm_div_id {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 9999999;
+}
+</style>

+ 28 - 0
src/api/AIImage.js

@@ -0,0 +1,28 @@
+import request from '@/utils/request';
+
+// 获取拜访照片AI识别异常,待审批列表。 失败异常照片需要部主管审批
+export function getPhotoApproveList(query) {
+  return request({
+    url: 'mobile/visit/getPhotoApproveList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 获取拜访照片AI识别异常详情
+export function getPhotoApproveDetail(query) {
+  return request({
+    url: 'mobile/visit/getPhotoApproveDetail',
+    method: 'get',
+    params: query,
+  });
+}
+
+//部主管反馈图片识别异常
+export function savePhotoApprove(data) {
+  return request({
+    url: 'mobile/visit/savePhotoApprove',
+    method: 'post',
+    data,
+  });
+}

+ 73 - 0
src/api/assignAwait.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request';
+
+// 家装客资-查询待分配客资列表接口
+export function selectNotAllocationList(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectNotAllocationList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 家装客资-根据客资类型查询分配人岗位列表和用户列表接口
+export function selectUserListByCustomerType(data) {
+  return request({
+    url: 'mobile/customerClueInfo/selectUserListByCustomerType',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-根据岗位列表查询用户接口
+export function selectUserListByPostName(data) {
+  return request({
+    url: 'mobile/customerClueInfo/selectUserListByPostName',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-分配客资归属接口
+export function allocationCustomer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/allocationCustomer',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-根据客资信息,查询家装客资任务接口
+export function selectCustomerClueInfoById(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectCustomerClueInfoById',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 家装客资-查询下属家装销售专员列表接口
+export function selectSubUserList(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectSubUserList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 家装客资-确认转下属接口
+export function allocationSubCustomer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/allocationSubCustomer',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 家装客资-新增客资跟进信息答案接口
+export function insertFollowCustomerClueAnswer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/insertFollowCustomerClueAnswer',
+    method: 'post',
+    data: data,
+  });
+}

+ 60 - 0
src/api/clew.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request';
+
+// 不想写
+export function infolist(query) {
+  return request({
+    url: 'mobile/customerClueInfo/infolist',
+    method: 'get',
+    params: query,
+  });
+}
+export function getCustomerClueInfoById(query) {
+  return request({
+    url: 'mobile/customerClueInfo/getCustomerClueInfoById',
+    method: 'get',
+    params: query,
+  });
+}
+export function insertCustomerClueAnswer(data) {
+  return request({
+    url: 'mobile/customerClueInfo/insertCustomerClueAnswer',
+    method: 'post',
+    data,
+  });
+}
+export function addPhotoK(data) {
+  return request({
+    url: 'mobile/customerClueInfo/addPhoto',
+    method: 'post',
+    data,
+  });
+}
+export function getCustomerClueAnswerById(query) {
+  return request({
+    url: 'mobile/customerClueInfo/getCustomerClueAnswerById',
+    method: 'get',
+    params: query,
+  });
+}
+export function getDeptInfo(query) {
+  return request({
+    url: 'mobile/customerClueInfo/getDeptInfo',
+    method: 'get',
+    params: query,
+  });
+}
+export function updateCustomerClueDept(query) {
+  return request({
+    url: 'mobile/customerClueInfo/updateCustomerClueDept',
+    method: 'get',
+    params: query,
+  });
+}
+//查询客资列表分组
+export function getCustomerClueType(query) {
+  return request({
+    url: 'mobile/customerClueInfo/getCustomerClueType',
+    method: 'get',
+    params: query,
+  });
+}

+ 37 - 0
src/api/complaintDetail.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request';
+
+// 客诉跟进信息详情接口
+export function getComplaintCustomerClueInfoById(query) {
+  return request({
+    url: '/mobile/customerClueInfo/getComplaintCustomerClueInfoById',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 新增客诉跟进信息答案接口
+export function insertCustomerClueAnswerKs(data) {
+  return request({
+    url: '/mobile/customerClueInfo/insertCustomerClueAnswerKs',
+    method: 'post',
+    data,
+  });
+}
+
+// 客资分类字典接口
+export function customerClassify(query) {
+  return request({
+    url: '/mobile/customerClueInfo/type/customerClassify',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 客资子类字典接口
+export function customerSubClassify(query) {
+  return request({
+    url: '/mobile/customerClueInfo/type/customerSubClassify',
+    method: 'get',
+    params: query,
+  });
+}

+ 10 - 0
src/api/home.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request';
+
+// 提示类建店未完工、未结案列表信息
+export function getStoreInfos(query) {
+  return request({
+    url: '/mobile/storeStatistics/getStoreInfos',
+    method: 'get',
+    params: query,
+  });
+}

+ 972 - 0
src/api/index.js

@@ -0,0 +1,972 @@
+import request from '@/utils/request';
+
+// // 查询省市区(县)
+export function getSelectRegionsList(query) {
+  return request({
+    url: 'sfa/store/cover/selectRegionsList',
+    method: 'get',
+    params: query,
+  });
+}
+export function getvisitDeptInfo(query) {
+  return request({
+    url: '/mobile/visit/getDeptInfo',
+    method: 'get',
+    params: query,
+  });
+}
+export function userTodayPlanNum(query) {
+  return request({
+    url: 'mobile/route/userTodayPlanNum',
+    method: 'get',
+    params: query,
+  });
+}
+
+//获取门店
+// 计划内
+export function getUserInPlanList(query) {
+  return request({
+    url: 'mobile/route/userInPlan',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function addVisits(data) {
+  return request({
+    url: 'mobile/storeGroup/addVisits',
+    method: 'post',
+    data,
+  });
+}
+
+export function addCollectionAnswer(data) {
+  return request({
+    url: 'mobile/storeGroup/addCollectionAnswer',
+    method: 'post',
+    data,
+  });
+}
+
+export function endVisits(data) {
+  return request({
+    url: 'mobile/storeGroup/endVisits',
+    method: 'post',
+    data,
+  });
+}
+
+export function removePhoto(data) {
+  return request({
+    url: 'mobile/storeGroup/removePhoto?fileId=' + data.fileId,
+    method: 'post',
+  });
+}
+
+export function editDwellTime(data) {
+  return request({
+    url:
+      'mobile/storeGroup/editDwellTime?dwellTime=' + data.dwellTime + '&visitsId=' + data.visitsId,
+    method: 'post',
+  });
+}
+
+export function getStoreGroupTask(data) {
+  return request({
+    url: 'mobile/storeGroup/getStoreGroupTask',
+    method: 'post',
+    data,
+  });
+}
+export function restartProcess(query) {
+  return request({
+    url: 'mobile/storeGroup/restartProcess',
+    method: 'get',
+    params: query,
+  });
+}
+// 计划外
+export function getUserOutPlaList(query) {
+  return request({
+    url: 'mobile/route/userOutPlan',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getPhotoTypeList(query) {
+  return request({
+    url: 'mobile/storeGroup/sfa_task_photo_type',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 城市等级
+export function getCityLevelList(query) {
+  return request({
+    url: 'mobile/storeGroup/sfa_city_level',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 门店类型
+export function getStoreTypeListlp(query) {
+  return request({
+    url: 'mobile/storeGroup/getStoreTypeList',
+    method: 'get',
+    params: query,
+  });
+}
+export function getStoreTypeList(query) {
+  return request({
+    url: 'mobile/storeGroup/sfa_store_type',
+    method: 'get',
+    params: query,
+  });
+}
+export function getstoreCoverPosition(query) {
+  return request({
+    url: 'mobile/storeGroup/store_cover_position',
+    method: 'get',
+    params: query,
+  });
+}
+export function double_week_type(query) {
+  return request({
+    url: 'mobile/storeGroup/double_week_type',
+    method: 'get',
+    params: query,
+  });
+}
+export function getStoreyslTypeList(query) {
+  return request({
+    url: 'mobile/storeGroup/ysl_store_type',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getPhotoTypeList1(query) {
+  return request({
+    url: 'mobile/storeGroup/sfa_abnormal_reason',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getTCFXList(query) {
+  return request({
+    url: 'mobile/storeGroup/TCFX',
+    method: 'get',
+    params: query,
+  });
+}
+// 客户信息来源
+export function getCustomerInfoList() {
+  return request({
+    url: 'mobile/storeGroup/customer_info_from',
+    method: 'get',
+  });
+}
+export function getpotentialCustomerTypeList() {
+  return request({
+    url: 'mobile/storeGroup/potential_customer_type',
+    method: 'get',
+  });
+}
+// 客户性质
+export function getCustomerNatureList() {
+  return request({
+    url: 'mobile/storeGroup/customer_nature',
+    method: 'get',
+  });
+}
+// 主要项目类型
+export function getMainProjectList() {
+  return request({
+    url: 'mobile/storeGroup/main_project_type',
+    method: 'get',
+  });
+}
+// 	主要关系来源
+export function getMainRelationList() {
+  return request({
+    url: 'mobile/storeGroup/main_relation_from',
+    method: 'get',
+  });
+}
+// 	经营模式
+export function getManagementModelList() {
+  return request({
+    url: 'mobile/storeGroup/management_model',
+    method: 'get',
+  });
+}
+export function getSJSList(query) {
+  return request({
+    url: 'mobile/storeGroup/SJS',
+    method: 'get',
+    params: query,
+  });
+}
+export function getQGJZist(query) {
+  return request({
+    url: 'mobile/storeGroup/QGJZ',
+    method: 'get',
+    params: query,
+  });
+}
+export function getbrands(query) {
+  return request({
+    url: 'mobile/storeGroup/main_competitor_brands',
+    method: 'get',
+    params: query,
+  });
+}
+// 客户信息
+export function getUserOrgStoreList(query) {
+  return request({
+    url: 'mobile/route/userOrgStore',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 历史拜访
+export function getVisitsDetail(query) {
+  return request({
+    url: 'mobile/storeGroup/getVisitsDetail',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getVisitsList(data) {
+  return request({
+    url: 'mobile/storeGroup/getVisitsList',
+    method: 'post',
+    data,
+  });
+}
+export function getVisits(query) {
+  return request({
+    url: 'mobile/visit/list',
+    method: 'get',
+    params: query,
+  });
+}
+export function storeMyList(query) {
+  return request({
+    url: 'mobile/store/myList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 公共
+export function addPhoto(data) {
+  return request({
+    url: 'mobile/storeGroup/addPhoto',
+    method: 'post',
+    data,
+  });
+}
+
+export function loginLog(data) {
+  return request({
+    url: 'mobile/route/loginLog',
+    method: 'post',
+    data,
+  });
+}
+
+export function ticket(query) {
+  return request({
+    url: 'mobile/wx/ticket',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 客户信息
+export function storeList(query) {
+  return request({
+    url: 'mobile/store/list',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getById(query) {
+  return request({
+    url: 'mobile/store/getById',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function addStore(data) {
+  return request({
+    url: 'mobile/store/addStore',
+    method: 'post',
+    data,
+  });
+}
+
+export function updateStore(data) {
+  return request({
+    url: 'mobile/store/updateStore',
+    method: 'post',
+    data,
+  });
+}
+
+export function uploadImage(data) {
+  return request({
+    url: 'mobile/store/uploadImage',
+    method: 'post',
+    data,
+  });
+}
+export function uploadImageaddress(data) {
+  return request({
+    url: 'mobile/store/uploadCheckAddressImage',
+    method: 'post',
+    data,
+  });
+}
+
+export function beforeAdd(query) {
+  return request({
+    url: 'mobile/store/beforeAdd',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function streetQuery(query) {
+  return request({
+    url: 'mobile/store/streetQuery',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function homeImge(query) {
+  return request({
+    url: 'mobile/route/homeImge',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function suishenbangStoreSale(query) {
+  return request({
+    url: 'mobile/store/suishenbangStoreSale',
+    method: 'get',
+    params: query,
+  });
+}
+// 周日报
+export function getReportInfo(query) {
+  return request({
+    url: 'mobile/reportMobile/getReportInfo',
+    method: 'get',
+    params: query,
+  });
+}
+export function approveList(query) {
+  return request({
+    url: 'mobile/reportMobile/approveList',
+    method: 'get',
+    params: query,
+  });
+}
+export function getDetailById(query) {
+  return request({
+    url: 'mobile/reportMobile/getDetailById',
+    method: 'get',
+    params: query,
+  });
+}
+export function queryHistoryList(query) {
+  return request({
+    url: 'mobile/reportMobile/queryHistoryList',
+    method: 'get',
+    params: query,
+  });
+}
+// 报表模块
+export function getDeptInfo(query) {
+  return request({
+    url: 'mobile/reportMobile/getDeptInfo',
+    method: 'get',
+    params: query,
+  });
+}
+export function getDeptInfos(query) {
+  return request({
+    url: 'mobile/summaryMobile/GetDeptInfo',
+    method: 'get',
+    params: query,
+  });
+}
+export function queryRates(query) {
+  return request({
+    url: 'mobile/reportMobile/queryRates',
+    method: 'get',
+    params: query,
+  });
+}
+export function updateReport(data) {
+  return request({
+    url: 'mobile/reportMobile/updateReport',
+    method: 'post',
+    data,
+  });
+}
+// 下午汇报查询
+export function querySubReport(query) {
+  return request({
+    url: 'mobile/reportMobile/querySubReport',
+    method: 'get',
+    params: query,
+  });
+}
+export function mobileLogin(data) {
+  return request({
+    url: 'mobileLogin',
+    method: 'post',
+    data,
+  });
+}
+export function tsContents(data) {
+  return request({
+    url: 'mobile/reportMobile/tsContents',
+    method: 'post',
+    data,
+  });
+}
+
+// 点评
+export function remarkList(query) {
+  return request({
+    url: 'mobile/reportMobile/remarkList',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function insertRemark(data) {
+  return request({
+    url: '/mobile/reportMobile/insertRemark',
+    method: 'post',
+    data,
+  });
+}
+export function insertVisitRemark(data) {
+  return request({
+    url: '/mobile/visit/insertRemark',
+    method: 'post',
+    data,
+  });
+}
+
+// 拜访计划接口
+// 结束拜访
+export function stopVisit(query) {
+  return request({
+    url: 'mobile/storeGroup/stopVisit',
+    method: 'get',
+    params: query,
+  });
+}
+export function checkVisit(query) {
+  return request({
+    url: 'mobile/storeGroup/checkVisit',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function addVisitsPosition(data) {
+  return request({
+    url: '/mobile/visit/addVisitsPosition',
+    method: 'post',
+    data,
+  });
+}
+// 初始化坐标
+export function mobileReposition(query) {
+  return request({
+    url: '/mobile/store/mobileReposition',
+    method: 'get',
+    params: query,
+  });
+}
+
+//客户信息
+// 手机号校验
+export function phoneCheck(query) {
+  return request({
+    url: '/mobile/store/phoneCheck',
+    method: 'get',
+    params: query,
+  });
+}
+//TOP经销商总结
+export function getSummaryDetailById(query) {
+  return request({
+    url: '/mobile/summaryMobile/getDetailById',
+    method: 'get',
+    params: query,
+  });
+}
+export function insertCustomAnswer(data) {
+  return request({
+    url: '/mobile/summaryMobile/insertCustomAnswer',
+    method: 'post',
+    data,
+  });
+}
+export function addSummaryPhoto(data) {
+  return request({
+    url: '/mobile/summaryMobile/addPhoto',
+    method: 'post',
+    data,
+  });
+}
+export function removeSummaryPhoto(data) {
+  return request({
+    url: '/mobile/summaryMobile/removePhoto?fileId=' + data.fileId,
+    method: 'post',
+    data,
+  });
+}
+
+export function queryHistorySummaryList(query) {
+  return request({
+    url: '/mobile/summaryMobile/queryHistorySummaryList',
+    method: 'get',
+    params: query,
+  });
+}
+export function getSummaryMobileDeptInfo(query) {
+  return request({
+    url: '/mobile/summaryMobile/getDeptInfo',
+    method: 'get',
+    params: query,
+  });
+}
+export function getSummaryMobilo(query) {
+  return request({
+    url: '/mobile/summaryMobile/' + query.userSummaryId,
+    method: 'get',
+    params: query,
+  });
+}
+
+// 审批数量
+export function applyNumber(query) {
+  return request({
+    url: '/mobile/activiti/todoItem/applyNumber',
+    method: 'get',
+    params: query,
+  });
+}
+export function getTodoItemList(query) {
+  return request({
+    url: '/mobile/activiti/todoItem/list',
+    method: 'get',
+    params: query,
+  });
+}
+export function getTodoItemMyList(query) {
+  return request({
+    url: '/mobile/activiti/todoItem/myList',
+    method: 'get',
+    params: query,
+  });
+}
+// 厨卫类型
+export function getCWStoreTypeList(query) {
+  return request({
+    url: '/mobile/storeGroup/cw_store_type',
+    method: 'get',
+    params: query,
+  });
+}
+//
+export function completeAll(data) {
+  return request({
+    url: '/mobile/activiti/process/completeAll',
+    method: 'post',
+    data,
+  });
+}
+
+// 大客户列表
+export function getstoreCustomerSignList(query) {
+  return request({
+    url: '/mobile/storeCustomerSign/list',
+    method: 'get',
+    params: query,
+  });
+}
+// 大客户详情
+export function getCustomerSignDetail(query) {
+  return request({
+    url: '/mobile/storeCustomerSign/getCustomerSignDetail',
+    method: 'get',
+    params: query,
+  });
+}
+export function storeCustomerSignList(query) {
+  return request({
+    url: '/mobile/storeCustomerSign/list',
+    method: 'get',
+    params: query,
+  });
+}
+export function addComplete(data) {
+  return request({
+    url: '/mobile/activiti/process/complete',
+    method: 'post',
+    data: data,
+  });
+}
+export function editCustomerSign(data) {
+  return request({
+    url: '/mobile/storeCustomerSign',
+    method: 'put',
+    data,
+  });
+}
+// 审批历史
+export function getListHistoryaList(data) {
+  return request({
+    url: '/mobile/activiti/process/listHistory',
+    method: 'post',
+    data,
+  });
+}
+export function getListHistoryList(query) {
+  return request({
+    url: '/mobile/activiti/process/visitListHistory',
+    method: 'get',
+    params: query,
+  });
+}
+//拜访历史
+export function visitStorelist(query) {
+  return request({
+    url: '/mobile/visit/store/list',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getChainsByDeptCode(query) {
+  return request({
+    url: '/mobile/store/getChainsByDeptId',
+    method: 'get',
+    params: query,
+  });
+}
+// 照片上传
+//门店
+export function uploadImagev(data, signal) {
+  return request({
+    url: '/mobile/store/uploadImage',
+    method: 'post',
+    data: data,
+    signal: signal,
+  });
+}
+//任务
+export function addPhotov(data) {
+  return request({
+    url: '/mobile/summaryMobile/addPhoto',
+    method: 'post',
+    data: data,
+  });
+}
+// 拜访
+export function addstorePhoto(data, signal) {
+  return request({
+    url: '/mobile/storeGroup/addPhoto',
+    method: 'post',
+    data: data,
+    signal: signal,
+  });
+}
+export function buryingPoint(data) {
+  return request({
+    url: '/mobile/buryingPoint',
+    method: 'post',
+    data: data,
+  });
+  // buryingPointType	是	string	埋点类型,字典burying_point_type: 1=门店类型筛选,2=审核日报照片
+  // buryingPointValue	否	string	埋点值
+  // buryingPointName	是	string	埋点名称
+  // buryingPointPosition	是	string	埋点位置
+  // systemModel	是	string	系统模块名称
+}
+export function addstoreRegister(data) {
+  return request({
+    url: '/mobile/store/updateStoreOrder',
+    method: 'post',
+    data: data,
+  });
+}
+//清空任务内容
+export function deleteTaskAnswer(query) {
+  return request({
+    url: '/mobile/storeGroup/deleteTaskAnswer',
+    method: 'get',
+    params: query,
+  });
+}
+//加入计划
+export function joinInPlan(query) {
+  return request({
+    url: '/mobile/route/joinInPlan',
+    method: 'get',
+    params: query,
+  });
+}
+export function getCollectionShowHistory(query) {
+  return request({
+    url: '/mobile/storeGroup/getCollectionShowHistory',
+    method: 'get',
+    params: query,
+  });
+}
+export function getProjectList(query) {
+  return request({
+    url: '/mobile/storeGroup/getProjectList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 标签
+export function getStoreLabels(query) {
+  return request({
+    url: '/mobile/storeGroup/getStoreLabels',
+    method: 'get',
+    params: query,
+  });
+}
+export function getCustomerList(query) {
+  return request({
+    url: '/mobile/storeGroup/getCustomerList',
+    method: 'get',
+    params: query,
+  });
+}
+
+export function getStoreMapInfo(query) {
+  return request({
+    url: '/mobile/store/getStoreMapInfo',
+    method: 'get',
+    params: query,
+  });
+}
+//我来的时候什么也没有,注释我也不写了
+export function ProductItemImge(query) {
+  return request({
+    url: '/mobile/route/ProductItemImge',
+    method: 'get',
+    params: query,
+  });
+}
+export function getItemList(query) {
+  return request({
+    url: '/mobile/store/getItemList',
+    method: 'get',
+    params: query,
+  });
+}
+export function getOptionByResult(query) {
+  return request({
+    url: '/mobile/storeGroup/getOptionByResult',
+    method: 'get',
+    params: query,
+  });
+}
+// 获取金牌店铺,同城店铺种类集合
+export function getStoreGroupCategory() {
+  return request({
+    url: '/mobile/storeGroup/store_address_check_category',
+    method: 'get',
+  });
+}
+
+// 获取签名 ticket
+export function getTicket(query) {
+  return request({
+    url: '/mobile/wx/ticket',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 根据门店code获取下单系统最新的下单数据
+export function getOrderByStoreCode(query) {
+  return request({
+    url: '/mobile/store/getOrderByStoreCode',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 提示类页签-汇报指标查询接口 日报、周报、半月报
+export function getReportTarget(query) {
+  return request({
+    url: '/mobile/reportMobile/getReportTarget',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 部门主管审批新建店铺-通过
+export function getStoreApprovalList(query) {
+  return request({
+    url: '/mobile/store/getStoreApprovalList',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 移动端获取用户信息接口
+export function getMobileUserInfo(query) {
+  return request({
+    url: '/mobile/getMobileUserInfo',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 部门主管审批新建同城店铺
+export function approvalStore(data) {
+  return request({
+    url: '/mobile/store/approvalStore',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 各部门未拜访门店数量
+export function selectNoVisitsInfo(query, signal) {
+  return request({
+    url: '/mobile/storeStatistics/selectNoVisitsInfo',
+    method: 'get',
+    params: query,
+    signal: signal,
+  });
+}
+
+// 提示类页签-色卡查询接口
+export function getReportMaterial(query) {
+  return request({
+    url: '/mobile/reportMobile/getReportMaterial',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 提示类页签-查询下属业务员色卡指标接口
+export function getReportMaterialType(data, signal) {
+  return request({
+    url: '/mobile/reportMobile/getReportMaterialType',
+    method: 'post',
+    data: data,
+    signal: signal,
+  });
+}
+
+// 新建同城店铺提交审核
+export function submitApproval(query) {
+  return request({
+    url: '/mobile/store/submitApproval',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 提示类拜访(实时)接口
+export function selectVisitsRealTime(query) {
+  return request({
+    url: '/mobile/storeStatistics/selectVisitsRealTime',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 审批历史-审批历史
+export function storeApprovaHistory(query) {
+  return request({
+    url: '/mobile/store/storeApprovaHistory',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 提示类建店统计接口
+export function selectBuildingStore(query) {
+  return request({
+    url: '/mobile/storeStatistics/selectBuildingStore',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 提示类色卡未领取-根据门店ID查询门店列表接口
+export function getNotSignStoreList(data) {
+  return request({
+    url: '/mobile/reportMobile/getNotSignStoreList',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 提示类用户拜访(实时)详情接口
+export function selectUserStoreNoVisits(data) {
+  return request({
+    url: '/mobile/storeStatistics/selectUserStoreNoVisits',
+    method: 'post',
+    data: data,
+  });
+}
+
+// 提示类建店详情接口
+export function selectPendingCasesInfo(query, signal) {
+  return request({
+    url: '/mobile/storeStatistics/selectPendingCasesInfo',
+    method: 'get',
+    params: query,
+    signal: signal,
+  });
+}
+
+// 获取字典
+export function getDictOption(query, dictTypr) {
+  return request({
+    url: '/mobile/storeGroup/' + dictTypr,
+    method: 'get',
+    params: query,
+  });
+}
+
+// 仍要上传图片入库
+export function addPhotoToDB(data) {
+  return request({
+    url: 'mobile/storeGroup/addPhotoToDB',
+    method: 'post',
+    data: data,
+  });
+}

+ 82 - 0
src/api/inventory.js

@@ -0,0 +1,82 @@
+import request from '@/utils/request'
+
+// 我的库存
+export function getMyInventoryList(query) {
+    return request({
+        url: '/mobile/materialInventory/list',
+        method: 'get',
+        params: query
+    })
+}
+// 物料
+export function getReceiveList(query) {
+    return request({
+        url: '/mobile/materialInventory/receiveList',
+        method: 'get',
+        params: query
+    })
+}
+// 编辑物料
+export function addMaterialInventory(data) {
+    return request({
+        url: '/mobile/materialInventory/change',
+        method: 'PUT',
+        data
+    })
+}
+
+//
+export function getVisitsParams(query) {
+    return request({
+        url: 'mobile/storeGroup/getVisitsParams',
+        method: 'get',
+        params: query
+    })
+}
+
+export function saveVisitsParams(data) {
+    return request({
+        url: 'mobile/storeGroup/saveVisitsParams',
+        method: 'post',
+        data
+    })
+}
+// 高净值门店列表
+export function topStore(query) {
+    return request({
+        url: '/mobile/top/topStore',
+        method: 'get',
+        params: query
+    })
+}
+export function topjoinInPlan(query) {
+    return request({
+        url: '/mobile/top/joinInPlan',
+        method: 'get',
+        params: query
+    })
+}
+
+export function aiDialogue(data) {
+    return request({
+        url: 'mobile/ai/aiDialogue',
+        method: 'post',
+        data
+    })
+}
+
+export function moduleList(query) {
+    return request({
+        url: '/mobile/activiti/todoItem/moduleList',
+        method: 'get',
+        params: query
+    })
+}
+// 去下单获取下单地址
+export function getOrderUrlByStoreId(query) {
+    return request({
+        url: '/mobile/route/getOrderUrlByStoreId',
+        method: 'get',
+        params: query
+    })
+}

+ 29 - 0
src/api/store.js

@@ -0,0 +1,29 @@
+// 门店相关接口
+import request from '@/utils/request';
+
+// 获取经销商分类
+export function listChainsByCategory(query) {
+  return request({
+    url: 'mobile/store/listChainsByCategory',
+    method: 'get',
+    params: query,
+  });
+}
+
+// 门店~扩展经销商(经销商人员)
+export function addStoreChainContact(data) {
+  return request({
+    url: 'mobile/store/addStoreChainContact',
+    method: 'post',
+    data,
+  });
+}
+
+// 新增门店前校验门店是否重复接口
+export function checkStoreBeforeAdd(data) {
+  return request({
+    url: 'mobile/store/checkStoreBeforeAdd',
+    method: 'post',
+    data,
+  });
+}

+ 59 - 0
src/api/visitstore.js

@@ -0,0 +1,59 @@
+import request from '@/utils/request'
+
+// 不想写
+export function getStoreAddress(query) {
+    return request({
+        url: 'mobile/store/getStoreAddress',
+        method: 'get',
+        params: query
+    })
+}
+export function valid(query) {
+    return request({
+        url: 'mobile/store/valid',
+        method: 'get',
+        params: query
+    })
+}
+export function checkStoreAddressByStoreCode(query) {
+    return request({
+        url: 'mobile/store/checkStoreAddressByStoreCode',
+        method: 'get',
+        params: query
+    })
+}
+export function updateStoreAddress(data) {
+    return request({
+        url: 'mobile/store/updateStoreAddress',
+        method: 'post',
+        data
+    })
+}
+export function addPhotoK(data) {
+    return request({
+        url: 'mobile/customerClueInfo/addPhoto',
+        method: 'post',
+        data
+    })
+}
+export function getCustomerClueAnswerById(query) {
+    return request({
+        url: 'mobile/customerClueInfo/getCustomerClueAnswerById',
+        method: 'get',
+        params: query
+    })
+}
+export function getDeptInfo(query) {
+    return request({
+        url: 'mobile/customerClueInfo/getDeptInfo',
+        method: 'get',
+        params: query
+    })
+}
+export function updateCustomerClueDept(query) {
+    return request({
+        url: 'mobile/customerClueInfo/updateCustomerClueDept',
+        method: 'get',
+        params: query
+    })
+}

+ 10 - 0
src/api/week.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request';
+
+// 家装客资-查询当前用户是否存在家装客资分配权限
+export function selectAllocationPermission(query) {
+  return request({
+    url: 'mobile/customerClueInfo/selectAllocationPermission',
+    method: 'get',
+    params: query,
+  });
+}

BIN
src/assets/err.png


+ 44 - 0
src/layout/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div class="container bgcolor">
+    <template v-if="$route.meta.keepAlive">
+      <keep-alive>
+        <router-view></router-view>
+      </keep-alive>
+    </template>
+    <router-view v-else></router-view>
+  </div>
+</template>
+<script>
+export default {
+  created() {
+    if (this.$route.query.token != undefined) {
+      localStorage.setItem('loginName', this.$route.query.token);
+    }
+    if (process.env.NODE_ENV != 'development') {
+      var ua = window.navigator.userAgent.toLowerCase();
+      if (ua.match(/MicroMessenger/i) == 'micromessenger' && ua.match(/wxwork/i) == 'wxwork') {
+      } else {
+        if (process.env.NODE_ENV == 'production') {
+          this.$router.push('/err');
+        }
+      }
+    }
+  },
+  watch: {
+    $route(to, from) {
+      if (this.$route.query.token != undefined) {
+        localStorage.setItem('loginName', this.$route.query.token);
+      }
+      if (process.env.NODE_ENV != 'development') {
+        var ua = window.navigator.userAgent.toLowerCase();
+        if (ua.match(/MicroMessenger/i) == 'micromessenger' && ua.match(/wxwork/i) == 'wxwork') {
+        } else {
+          if (process.env.NODE_ENV == 'production') {
+            this.$router.push('/err');
+          }
+        }
+      }
+    },
+  },
+};
+</script>

+ 83 - 0
src/main.js

@@ -0,0 +1,83 @@
+import Vue from 'vue';
+import App from './App.vue';
+import store from './store';
+import router from './router';
+import permission from './permission';
+// import '@/assets/styles/index.css';
+import {
+  parseTime,
+  selectDictLabelu,
+  selectDictLabel,
+  selectDictLabels,
+  CJ02BD,
+  twoPointSum,
+  parseTimeParagraph,
+  Micrometer,
+  weeklyTimeDivision,
+  gcj02BD,
+  verifyStoreType,
+} from '@/utils/index';
+import { toastLoading } from '@/utils/commonVant';
+import '@vant/touch-emulator';
+Vue.config.productionTip = false;
+import Vant from 'vant';
+import { Toast, Notify } from 'vant';
+import 'vant/lib/index.css';
+import ClipboardJS from 'clipboard';
+Vue.use(Vant);
+import ElTable from 'element-ui/lib/table';
+import 'element-ui/lib/theme-chalk/table.css';
+import 'element-ui/lib/theme-chalk/icon.css';
+import ElTableColumn from 'element-ui/lib/table-column';
+import ElPopover from 'element-ui/lib/popover';
+import 'element-ui/lib/theme-chalk/table-column.css';
+import 'element-ui/lib/theme-chalk/popover.css';
+import Vconsole from 'vconsole';
+import ElProgress from 'element-ui/lib/progress';
+import 'element-ui/lib/theme-chalk/progress.css';
+import ElDialog from 'element-ui/lib/dialog';
+import 'element-ui/lib/theme-chalk/dialog.css';
+// import wx from 'weixin-js-sdk';
+// jenkins 测试打包前先合并
+
+Vue.use(ElTable);
+Vue.use(ElTableColumn);
+Vue.use(ElPopover);
+Vue.use(ElProgress);
+Vue.use(ElDialog);
+Vue.config.productionTip = false;
+Vue.prototype.parseTime = parseTime;
+Vue.prototype.selectDictLabel = selectDictLabel;
+Vue.prototype.selectDictLabels = selectDictLabels;
+Vue.prototype.selectDictLabelu = selectDictLabelu;
+Vue.prototype.weeklyTimeDivision = weeklyTimeDivision;
+Vue.prototype.toastLoading = toastLoading;
+Vue.prototype.CJ02BD = CJ02BD;
+Vue.prototype.gcj02BD = gcj02BD;
+Vue.prototype.twoPointSum = twoPointSum;
+Vue.prototype.wx = wx;
+Vue.prototype.parseTimeParagraph = parseTimeParagraph;
+Vue.prototype.Micrometer = Micrometer;
+Vue.prototype.verifyStoreType = verifyStoreType;
+Vue.prototype.Toast = Toast;
+Vue.prototype.notify = Notify;
+var clipboard = new ClipboardJS('.btn');
+clipboard.on('success', function (e) {
+  Toast('编码复制成功!');
+});
+
+clipboard.on('error', function (e) {
+  Toast('编码复制失败!');
+});
+
+const isProd = process.env.NODE_ENV !== 'production';
+if (isProd) {
+  const vConsole = new Vconsole();
+  Vue.use(vConsole);
+}
+
+new Vue({
+  router,
+  store,
+  render: (h) => h(App),
+}).$mount('#app');

+ 162 - 0
src/mixin/clew.js

@@ -0,0 +1,162 @@
+export const clewMixins = {
+  data() {
+    return {};
+  },
+  computed: {},
+  created() {},
+  mounted() {},
+  methods: {
+    purchaseSubmit(callback) {
+      this.requiredFlag = true;
+      // 每一个层级都是一道题的题目,子级就是题,被选中和填写的题要带上题目一块上传(题的同级也要上传)
+      // 第一级题目下的题默认都要上传
+      let params = {
+        customerClueItemList: [],
+      };
+      // 复制第一级题目
+      params.customerClueItemList.push(...this.deepClone(this.taskGather, 0));
+      this.filterOption(this.taskGather, params);
+      console.log(JSON.stringify(params));
+      // 必填验证
+      if (this.requiredFlag) {
+        callback && callback(params);
+      } else {
+        this.$toast(this.requiredMessage);
+      }
+    },
+    filterOption(optionList, params) {
+      for (let val = 0; val < optionList.length; val++) {
+        if (
+          optionList[val].isMust == '0' &&
+          optionList[val].searchValue == null &&
+          optionList[val].answerType == 'dx'
+        ) {
+          // 题目必填校验
+          this.requiredFlag = false;
+          this.requiredMessage = '请选择' + optionList[val].customerClueName;
+          return;
+        } else if (optionList[val].isMust == '0') {
+          // 子级题校验
+          let customerClueOptionList = optionList[val].customerClueOptionList;
+          if (customerClueOptionList.length) {
+            for (let i = 0; i < customerClueOptionList.length; i++) {
+              // 选中的题目Y:选中,N:未选中
+              if (customerClueOptionList[i].value == 'Y') {
+                if (customerClueOptionList[i].customerClueItemList) {
+                  // 必填校验
+                  this.isRequiredFlag(customerClueOptionList[i].customerClueItemList);
+                  // 赋值选中题
+                  let customerClueItemList = params.customerClueItemList;
+                  customerClueItemList.push(
+                    ...this.deepClone(customerClueOptionList[i].customerClueItemList, 0)
+                  );
+                  if (customerClueOptionList[i].customerClueItemList.length) {
+                    this.filterOption(customerClueOptionList[i].customerClueItemList, params);
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    },
+    // 深拷贝指定拷贝层级
+    deepClone(obj, num) {
+      // 检查是否为对象或数组
+      if (obj === null || typeof obj !== 'object') {
+        return obj; // 基本类型直接返回
+      }
+      // 创建一个数组或对象
+      const copy = Array.isArray(obj) ? [] : {};
+      // 遍历对象的每个属性
+      for (const key in obj) {
+        // 每个题只复制两层子级
+        if (obj.hasOwnProperty(key) && num < 2) {
+          // 递归调用深拷贝
+          if (key == 'customerClueOptionList' || key == 'customerClueItemList') {
+            num = num + 1;
+          }
+          copy[key] = this.deepClone(obj[key], num);
+        }
+      }
+      return copy;
+    },
+    isRequiredFlag(optionList) {
+      // console.log(optionList);
+      for (let i = 0; i < optionList.length; i++) {
+        // 是否必填
+        if (optionList[i].isMust == 0) {
+          // 输入框
+          if (optionList[i].answerType == 'wb' || optionList[i].answerType == 'sz') {
+            if (!optionList[i].answerValue) {
+              // 必填类型
+              this.requiredFlag = false;
+              this.requiredMessage = optionList[i].remark;
+              return;
+            } else {
+              // 条件校验
+              if (optionList[i].minTextLength) {
+                // 输入内容长度校验
+                if (optionList[i].answerValue.length < optionList[i].minTextLength) {
+                  this.requiredFlag = false;
+                  this.requiredMessage = optionList[i].remark;
+                  return;
+                }
+              }
+            }
+          } else if (optionList[i].answerType == 'zp') {
+            // 照片
+            if (!optionList[i].fileInfoList || !optionList[i].fileInfoList.length) {
+              this.requiredFlag = false;
+              this.requiredMessage = optionList[i].remark;
+              return;
+            }
+          } else if (optionList[i].answerType == 'bg') {
+            // 表格
+            let tableData = optionList[i].tableData;
+            let filterBGData = this.filterBGData(tableData);
+            if (filterBGData.hasAllFilledRow && filterBGData.filledRow == '') {
+              optionList[i].answerValue = JSON.stringify(tableData);
+            } else {
+              this.requiredFlag = false;
+              this.requiredMessage =
+                filterBGData.filledRow != ''
+                  ? filterBGData.filledRow
+                  : '请填写' + optionList[i].customerClueName;
+              return;
+            }
+          }
+        }
+      }
+    },
+    filterBGData(tableData) {
+      // 如果一行中有一个输入框输入了内容,其他输入框没有内容,就弹框提示当前行其它输入框也要填写,
+      // 整个表格必须有一行所有输入框都输入了内容
+      let hasAllFilledRow = false; // 是否有整行都填写
+      let filledRow = ''; // 有内容行
+      tableData.data.forEach((row, rowIndex) => {
+        const hasContent = Object.values(row).some(
+          (cell, index) => index !== 0 && cell.trim() !== ''
+        );
+        const allFilled = Object.values(row).every((cell, index) => {
+          return cell.trim() !== '';
+        });
+        if (hasContent && !allFilled) {
+          filledRow = `${row.typeName}请填写完整`;
+          return;
+        }
+        if (allFilled) {
+          hasAllFilledRow = true;
+        }
+      });
+      return { hasAllFilledRow, filledRow };
+      // let data = [];
+      // const isValid = tableData.data.some((row) => {
+      //   if (Object.values(row).every((value) => value.trim() !== '')) {
+      //     data.push(row);
+      //   }
+      // });
+      // return data;
+    },
+  },
+};

+ 15 - 0
src/mixin/scrollTop.js

@@ -0,0 +1,15 @@
+export const scrollTopMixins = {
+  mounted() {
+    const $content = document.querySelector('#content');
+    $content.addEventListener('scroll', () => {
+      this.$route.meta.scrollTop = $content.scrollTop;
+    });
+  },
+  activated() {
+    const scrollTop = this.$route.meta.scrollTop;
+    const $content = document.querySelector('#content');
+    if (scrollTop && $content) {
+      $content.scrollTop = scrollTop;
+    }
+  },
+};

+ 39 - 0
src/permission.js

@@ -0,0 +1,39 @@
+import router from './router';
+import store from './store';
+
+const whiteList = ['/login', '/auth-redirect', '/bind', '/register'];
+
+router.beforeEach((to, from, next) => {
+  const username = localStorage.getItem('loginName');
+  if (username) {
+    /* has token*/
+    if (!store.state.user.userInfo) {
+      // 获取移动端获取用户信息接口
+      store
+        .dispatch('getUserInfo')
+        .then(() => {
+          next();
+        })
+        .catch(() => {
+          next();
+        });
+    } else {
+      next();
+    }
+  } else {
+    // 测试使用 账户密码登录页面,正式环境禁用
+    if (to.path == '/logincs' && process.env.NODE_ENV == 'production') {
+      next('/');
+    } else {
+      next();
+    }
+    // // 没有token
+    // if (whiteList.indexOf(to.path) !== -1) {
+    //   // 在免登录白名单,直接进入
+    //   next();
+    // } else {
+    //     next(`/login?redirect=${to.fullPath}`); // 否则全部重定向到登录页
+    //     NProgress.done();
+    // }
+  }
+});

+ 39 - 0
src/router/index.js

@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+import layout from '@/layout/index.vue';
+
+const originalPush = VueRouter.prototype.push;
+VueRouter.prototype.push = function push(location) {
+  return originalPush.call(this, location).catch((err) => err);
+};
+Vue.use(VueRouter);
+const router = new VueRouter({
+  mode: 'history',
+  // scrollBehavior(to, from, savedPosition) {
+  //   if (savedPosition) {
+  //     return savedPosition;
+  //   } else {
+  //     return { x: 0, y: 0 };
+  //   }
+  // },
+  base: '/agent/',
+
+  routes: [
+    {
+      path: '/',
+      component: layout,
+      redirect: '/home',
+      children: [
+        {
+          path: '/home',
+          name: 'home',
+          component: () => import('@/views/home/index.vue'),
+          meta: {
+            keepAlive: true,
+          },
+        },
+      ],
+    },
+  ],
+});
+export default router;

+ 9 - 0
src/store/getters.js

@@ -0,0 +1,9 @@
+const getters = {
+  userInfo: (state) => state.user.userInfo,
+  refreshClewPage: (state) => state.isRefreshPage.refreshClewPage,
+  storeType: (state) => state.user.storeType,
+  shotsNum: (state) => state.otheStore.shotsNum,
+  deviceOutsidePage: (state) => state.isRefreshPage.deviceOutsidePage,
+  activaCreateTypeStore: (state) => state.user.activaCreateTypeStore,
+};
+export default getters;

+ 19 - 0
src/store/index.js

@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import user from './modules/user';
+import isRefreshPage from './modules/isRefreshPage';
+import otheStore from './modules/otheStore';
+import getters from './getters';
+
+Vue.use(Vuex);
+
+const store = new Vuex.Store({
+  modules: {
+    user,
+    isRefreshPage,
+    otheStore,
+  },
+  getters,
+});
+
+export default store;

+ 26 - 0
src/store/modules/isRefreshPage.js

@@ -0,0 +1,26 @@
+const isRefreshPage = {
+  state: {
+    refreshClewPage: false, //刷新客资页面
+    deviceOutsidePage: false, //刷新计划外页面
+  },
+
+  mutations: {
+    SET_REFRESH_CLEW_PAGE: (state, value) => {
+      state.refreshClewPage = value;
+    },
+    SET_DEVICE_OUTSIDE_PAGE: (state, value) => {
+      state.deviceOutsidePage = value;
+    },
+  },
+
+  actions: {
+    setRefreshClewPage({ commit }, value) {
+      commit('SET_REFRESH_CLEW_PAGE', value);
+    },
+    setDeviceOutsidePage({ commit }, value) {
+      commit('SET_DEVICE_OUTSIDE_PAGE', value);
+    },
+  },
+};
+
+export default isRefreshPage;

+ 19 - 0
src/store/modules/otheStore.js

@@ -0,0 +1,19 @@
+const otheStore = {
+  state: {
+    shotsNum: 0,
+  },
+
+  mutations: {
+    SET_SHOTS_NUM: (state, value) => {
+      state.shotsNum = value;
+    },
+  },
+
+  actions: {
+    setShotsNum({ commit }, value) {
+      commit('SET_SHOTS_NUM', value);
+    },
+  },
+};
+
+export default otheStore;

+ 65 - 0
src/store/modules/user.js

@@ -0,0 +1,65 @@
+import { getMobileUserInfo, getDictOption } from '@/api/index';
+
+const user = {
+  state: {
+    userInfo: null,
+    activaTypeStore: null, //未拜访门店
+    activaCreateTypeStore: null, //建店未完工、未结案门店
+    storeType: [],
+  },
+
+  mutations: {
+    SET_USER_INFO: (state, userInfo) => {
+      state.userInfo = userInfo;
+    },
+    SET_ACTIVA_TYPE_STORE: (state, value) => {
+      state.activaTypeStore = value;
+    },
+    SET_ASSIGN_FLAG: (state, value) => {
+      state.isAssignFlag = value;
+    },
+    SET_STORE_TYPE: (state, value) => {
+      state.storeType = value;
+    },
+    SET_ACTIVA_CREATE_TYPE_STORE: (state, value) => {
+      state.activaCreateTypeStore = value;
+    },
+  },
+
+  actions: {
+    // 获取用户信息
+    getUserInfo({ dispatch, commit, state }) {
+      return new Promise((resolve, reject) => {
+        getMobileUserInfo()
+          .then((res) => {
+            commit('SET_USER_INFO', res.data);
+            localStorage.setItem('nickName', res.data.nickName);
+            localStorage.setItem('postName', res.data.postName);
+            localStorage.setItem('zipPhoto', res.data.zipPhoto);
+            localStorage.setItem('deptLevel', res.data.depts[0].deptLevel);
+            localStorage.setItem('userId', res.data.userId);
+            localStorage.setItem('deptIds', JSON.stringify(res.data.deptIds));
+            localStorage.setItem('chainUser', res.data.chainUser); //是否经销商用户 true
+            // 门店类型
+            getDictOption({}, 'sfa_store_type').then((res) => {
+              commit('SET_STORE_TYPE', res.data);
+              resolve();
+            });
+          })
+          .catch((error) => {
+            reject(error);
+          });
+      });
+    },
+    // 储存提示类-未拜访-从那个店铺类型进入未拜访列表
+    setActivaTypeStore({ commit }, value) {
+      commit('SET_ACTIVA_TYPE_STORE', value);
+    },
+    // 储存提示类-建店-从那个店铺类型进入未完工、未结案列表
+    activaCreateTypeStore({ commit }, value) {
+      commit('SET_ACTIVA_CREATE_TYPE_STORE', value);
+    },
+  },
+};
+
+export default user;

+ 194 - 0
src/utils/TXApiFun.js

@@ -0,0 +1,194 @@
+import Vue from 'vue';
+import { getTicket } from '@/api/index';
+import { toastLoading } from '@/utils/commonVant';
+import { CJ02BD, gcj02BD } from '@/utils/index';
+import { jsonp } from 'vue-jsonp';
+// 微信JSSDK实例
+const wx = Vue.prototype.wx;
+// 腾讯位置服务 key
+const TxMapKey = 'WLCBZ-HRM6L-YOMPV-ME62B-AQOG6-JUBW6';
+// 当前设备
+const isDevice = localStorage.getItem('isDevice');
+
+/**
+ * 获取当前定位 调用之前确保当前页面已经授权(getTicketFun)
+ * @param isPermit // PC 模式下不能获取定位,是否允许跳过定位
+ * */
+export function getPosition(isPermit = false) {
+  return new Promise((resolve, reject) => {
+    toastLoading(0, '定位中...', true);
+    // PC端没有定位权限依然可以访问
+    if (process.env.NODE_ENV === 'test') {
+      // 本地开发 test 环境时跳过获取定位功能 模拟定位
+      let resData = {
+        latitude: 34.615684509277344,
+        longitude: 112.4474105834961,
+      };
+      // 定位坐标转换 腾讯转百度
+      let TXisBD = CJ02BD(resData.latitude, resData.longitude);
+      localStorage.setItem('lat', TXisBD.lat);
+      localStorage.setItem('lon', TXisBD.lon);
+      toastLoading().clear();
+      resolve({ TXisBD: TXisBD, resData: resData });
+    } else if (isDevice == 'PC') {
+      // PC状态 分允许通过和不允许通过 isPermit: tru允许,false不允许
+      localStorage.setItem('lat', '');
+      localStorage.setItem('lon', '');
+      toastLoading().clear();
+      if (isPermit) {
+        resolve({ TXisBD: { lat: '', lon: '' }, resData: { latitude: '', longitude: '' } });
+      } else {
+        reject('GPS未开启');
+      }
+    } else {
+      wx.ready(() => {
+        wx.getLocation({
+          type: 'gcj02',
+          success: (resData) => {
+            console.log('success');
+            toastLoading().clear();
+            console.log('处理前');
+            console.log(resData.latitude, resData.longitude);
+            // 定位坐标转换 腾讯转百度
+            let TXisBD = CJ02BD(resData.latitude, resData.longitude);
+            console.log('处理后');
+            console.log(TXisBD);
+            localStorage.setItem('lat', TXisBD.lat);
+            localStorage.setItem('lon', TXisBD.lon);
+            resolve({ TXisBD: TXisBD, resData: resData });
+          },
+          fail: () => {
+            console.log('fail');
+            toastLoading().clear();
+            reject('GPS未开启');
+          },
+          complete: () => {
+            console.log('complete');
+            toastLoading().clear();
+          },
+        });
+      });
+      wx.error((err) => {
+        console.log('wx.error');
+        toastLoading().clear();
+        console.log(err);
+        localStorage.setItem('lat', '');
+        localStorage.setItem('lon', '');
+        reject('定位失败,请开启企微定位权限');
+      });
+    }
+  });
+}
+
+/**
+ *当前页面授权 一个url只需要授权一次,wx.config 调用多次,只有第一次会调用成功,后面的都会走失败
+ * @param {*object} jsApiList //授权接口列表
+ * @param {*String} getLocation //获取定位
+ * @param {*String} configType //注入类型
+ * config,agentConfig
+ * config注入的是企业的身份与权限,
+ * 而agentConfig注入的是应用的身份与权限。
+ * 尤其是当调用者为第三方服务商时,通过config无法准确区分出调用者是哪个第三方应用,
+ * 而在部分场景下,又必须严谨区分出第三方应用的身份,此时即需要通过agentConfig来注入应用的身份信息。
+ *
+ */
+export function getTicketFun(jsApiList = ['getLocation'], configType = 'config') {
+  return new Promise((resolve, reject) => {
+    // 当前页面
+    let url = window.location.href;
+    //  获取签名
+    getTicket({ url: url }).then((response) => {
+      console.log(response);
+      toastLoading().clear();
+      if (response.code == 200) {
+        let qiyeData = response.data;
+        if (configType == 'config') {
+          wx.config({
+            beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题
+            debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
+            appId: qiyeData.appId, // 必填,企业微信的corpID
+            timestamp: qiyeData.timestamp, // 必填,生成签名的时间戳
+            nonceStr: qiyeData.nonceStr, // 必填,生成签名的随机串
+            signature: qiyeData.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法
+            jsApiList: ['ready', ...jsApiList], // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
+          });
+          console.log('获取签名成功');
+          resolve('获取签名成功');
+        } else if (configType == 'agentConfig') {
+          wx.agentConfig({
+            corpid: qiyeData.appId, // 必填,企业微信的corpid,必须与当前登录的企业一致
+            agentid: qiyeData.agentId, // 必填,企业微信的应用id (e.g. 1000247)
+            timestamp: qiyeData.timestamp, // 必填,生成签名的时间戳
+            nonceStr: qiyeData.nonceStr, // 必填,生成签名的随机串
+            signature: qiyeData.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
+            jsApiList: [...jsApiList], //必填,传入需要使用的接口名称
+            success: function (res) {
+              console.log('获取签名成功');
+              resolve('获取签名成功');
+            },
+            fail: function (res) {
+              if (res.errMsg.indexOf('function not exist') > -1) {
+                alert('版本过低请升级');
+              }
+            },
+          });
+        }
+      } else {
+        console.log('获取签名失败');
+        reject('获取签名失败');
+        alert('获取签名失败');
+      }
+    });
+  });
+}
+/**
+ * 地点搜索 获取500米范围poi点
+ * @param {*object} location //当前位置
+ * @param {*number} radius //搜索半径
+ * @param {*number} auto_extend //是否自动扩大范围 0 不扩大
+ * @returns
+ */
+export function getMapPoi(location, radius = 500, auto_extend = 0) {
+  return new Promise((resolve, reject) => {
+    let api =
+      'https://apis.map.qq.com/ws/place/v1/search?page_size=10&page_index=1&orderby=_distance&output=jsonp';
+    let boundary = `&boundary=nearby(${location.latitude},${location.longitude},${radius},${auto_extend})`;
+    let key = `&key=${TxMapKey}`;
+    jsonp(api + boundary + key)
+      .then((res) => {
+        console.log(res);
+        resolve(res);
+      })
+      .catch((err) => {
+        console.log(err);
+        reject(err);
+      });
+  });
+}
+
+/**
+ * 关键词搜索 在当前城市范围内搜索
+ * @param {*object} location //当前位置
+ * @param {*string} keywordValue //关键字
+ * @returns
+ */
+export function getkeywordPoi(location, keywordValue) {
+  return new Promise((resolve, reject) => {
+    let keyword = keywordValue ? '&keyword=' + encodeURI(keywordValue) : '';
+    // 关键词搜索
+    let api =
+      'https://apis.map.qq.com/ws/place/v1/suggestion?output=jsonp&page_size=10&region_fix=1';
+    let key = `&key=${TxMapKey}`;
+    let locationPos = `&location=${location.latitude},${location.longitude}`;
+    jsonp(api + locationPos + keyword + key)
+      .then((res) => {
+        console.log(keyword);
+        console.log(res);
+        resolve(res);
+      })
+      .catch((err) => {
+        console.log(err);
+        reject(err);
+      });
+  });
+}

+ 15 - 0
src/utils/commonVant.js

@@ -0,0 +1,15 @@
+import { Toast } from "vant";
+/**
+ *
+ * @param {*number} duration //展示时长(ms),值为 0 时,toast 不会消失
+ * @param {*string} message //文本内容,支持通过\n换行
+ * @param {*boolean} forbidClick //	是否禁止背景点击	boolean
+ * @returns
+ */
+export function toastLoading(duration = 0, message = "加载中...", forbidClick = true) {
+  return Toast.loading({
+    duration: duration,
+    message: message,
+    forbidClick: forbidClick,
+  });
+}

+ 6 - 0
src/utils/errorCode.js

@@ -0,0 +1,6 @@
+export default {
+    '401': '认证失败,无法访问系统资源',
+    '403': '当前操作没有权限',
+    '404': '访问资源不存在',
+    'default': '系统未知错误,请反馈给管理员'
+}

+ 257 - 0
src/utils/index.js

@@ -0,0 +1,257 @@
+import store from '../store';
+
+// 日期格式化
+export function parseTime(time, pattern) {
+  if (time != 'null') {
+    if (arguments.length === 0 || !time) {
+      return null;
+    }
+    const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';
+    let date;
+    if (typeof time === 'object') {
+      date = time;
+    } else {
+      if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
+        time = parseInt(time);
+      } else if (typeof time === 'string') {
+        time = time
+          .replace(new RegExp(/-/gm), '/')
+          .replace('T', ' ')
+          .replace(new RegExp(/\.[\d]{3}/gm), '');
+      }
+      if (typeof time === 'number' && time.toString().length === 10) {
+        time = time * 1000;
+      }
+      date = new Date(time);
+    }
+    const formatObj = {
+      y: date.getFullYear(),
+      m: date.getMonth() + 1,
+      d: date.getDate(),
+      h: date.getHours(),
+      i: date.getMinutes(),
+      s: date.getSeconds(),
+      a: date.getDay(),
+    };
+    const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+      let value = formatObj[key];
+      // Note: getDay() returns 0 on Sunday
+      if (key === 'a') {
+        return ['日', '一', '二', '三', '四', '五', '六'][value];
+      }
+      if (result.length > 0 && value < 10) {
+        value = '0' + value;
+      }
+      return value || 0;
+    });
+    return time_str;
+  } else {
+    return '';
+  }
+}
+// 千分号
+export function Micrometer(num) {
+  if (num != null) {
+    let numt = (num || 0).toString().split('.');
+    if (numt[1] == undefined) {
+      return numt[0].replace(/(\d)(?=(?:\d{3})+$)/g, '$1,');
+    } else {
+      return numt[0].replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') + '.' + numt[1];
+    }
+  } else {
+    return 0.0;
+  }
+}
+export function weeklyTimeDivision(time, type) {
+  if (type == 0) {
+    return time.split(' ')[0] + '  ';
+  } else {
+    return time.split(' ')[1];
+  }
+}
+export function parseTimeParagraph(dayTime) {
+  var currentDate = new Date(dayTime);
+  var timesStamp = currentDate.getTime();
+  var currenDay = currentDate.getDay();
+  var dates = [];
+  var tabTime = [];
+  for (var i = 0; i < 14; i++) {
+    var dataTime = new Date(timesStamp + 24 * 60 * 60 * 1000 * (i - ((currenDay + 6) % 7)))
+      .toLocaleDateString()
+      .replace(/\//g, '-');
+    var dataTimeArr = dataTime.split('-');
+    if (dataTimeArr[1] < 10) {
+      dataTimeArr[1] = '0' + dataTimeArr[1];
+    }
+    if (dataTimeArr[2] < 10) {
+      dataTimeArr[2] = '0' + dataTimeArr[2];
+    }
+    dates.push(
+      dataTimeArr.join('-') +
+        ' ' +
+        ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][
+          new Date(dataTime.toString().replace(/-/g, '/')).getDay()
+        ]
+    );
+  }
+  tabTime.push(dates[0]);
+  tabTime.push(dates[4]);
+  return dates;
+}
+export function weeklay(dataTime) {
+  return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][new Date(dataTime).getDay()];
+}
+
+// 回显数据字典
+export function selectDictLabel(datas, value) {
+  var actions = [];
+  Object.keys(datas).some((key) => {
+    if (datas[key].dictValue == '' + value) {
+      actions.push(datas[key].dictLabel);
+      return true;
+    }
+  });
+  return actions.join('');
+}
+
+export function selectDictLabelu(datas, value) {
+  var actions = [];
+  Object.keys(datas).some((key) => {
+    if (datas[key].dictValue == '' + value) {
+      actions.push(datas[key].text);
+      return true;
+    }
+  });
+  return actions.join('');
+}
+
+// 回显数据字典(字符串数组)
+export function selectDictLabels(datas, value, separator) {
+  var actions = [];
+  var currentSeparator = undefined === separator ? ',' : separator;
+  var temp = value.split(currentSeparator);
+  Object.keys(value.split(currentSeparator)).some((val) => {
+    Object.keys(datas).some((key) => {
+      if (datas[key].dictValue == '' + temp[val]) {
+        actions.push(datas[key].dictLabel + currentSeparator);
+      }
+    });
+  });
+  return actions.join('').substring(0, actions.join('').length - 1);
+}
+
+// 字符串格式化(%s )
+export function sprintf(str) {
+  var args = arguments,
+    flag = true,
+    i = 1;
+  str = str.replace(/%s/g, function () {
+    var arg = args[i++];
+    if (typeof arg === 'undefined') {
+      flag = false;
+      return '';
+    }
+    return arg;
+  });
+  return flag ? str : '';
+}
+
+// 转换字符串,undefined,null等转化为""
+export function praseStrEmpty(str) {
+  if (!str || str == 'undefined' || str == 'null') {
+    return '';
+  }
+  return str;
+}
+
+//
+export function twoPointSum(latA, lonA, latB, lonB) {
+  var coordinate = 6371000;
+  var PI = 3.14159265358979324;
+  var x =
+    Math.cos((latA * PI) / 180) *
+    Math.cos((latB * PI) / 180) *
+    Math.cos(((lonA - lonB) * PI) / 180);
+  var y = Math.sin((latA * PI) / 180) * Math.sin((latB * PI) / 180);
+  var s = x + y;
+  if (s > 1) s = 1;
+  if (s < -1) s = -1;
+  var alphabet = Math.acos(s);
+  var PointSum = alphabet * coordinate;
+  return PointSum;
+}
+
+export function CJ02BD(gcjLat, gcjLon) {
+  var x = gcjLon,
+    y = gcjLat;
+  var x_pi = (3.14159265358979324 * 3000.0) / 180.0;
+  var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
+  var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
+  var bdLon = z * Math.cos(theta) + 0.0065;
+  var bdLat = z * Math.sin(theta) + 0.006;
+
+  return {
+    lat: bdLat,
+    lon: bdLon,
+  };
+}
+export function gcj02BD(gcjLat, gcjLon) {
+  var x = gcjLon - 0.0065,
+    y = gcjLat - 0.006;
+  var x_pi = (3.14159265358979324 * 3000.0) / 180.0;
+  var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
+  var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
+  var bdLon = z * Math.cos(theta);
+  var bdLat = z * Math.sin(theta);
+  return { lat: bdLat, lon: bdLon };
+}
+
+/**
+ *门店类型集合,返回对应的类型
+ *@param {*String} dictValue //类型
+ *  */
+export function verifyStoreType(dictValue) {
+  if (!dictValue) return null;
+  let storeData = null;
+  storeData = store.getters.storeType.find((val) => val.dictValue == dictValue);
+  let remarkType = storeData ? JSON.parse(storeData.remark) : null;
+  return remarkType;
+}
+
+export function getMonthCommon() {
+  let timeData = '';
+  // 获取当前日期
+  var currentDate = new Date();
+  // 获取当前月份
+  var currentMonth = currentDate.getMonth();
+  var previousMonthDate1 = new Date();
+  if (currentDate.getDate() == 1) {
+    previousMonthDate1.setMonth(currentMonth - 1);
+  }
+  var previousMonth1 = previousMonthDate1.getMonth();
+  var previousYear1 = previousMonthDate1.getFullYear();
+  // 计算前三个月的年份和月份
+  var previousMonthDate = new Date();
+  if (currentDate.getDate() == 1) {
+    previousMonthDate.setMonth(currentMonth - 3);
+  } else {
+    previousMonthDate.setMonth(currentMonth - 2);
+  }
+  var previousMonth = previousMonthDate.getMonth();
+  var previousYear = previousMonthDate.getFullYear();
+  //前三个月
+  if (previousYear1 == previousYear) {
+    var formattedPreviousMonth1 = previousYear1 + '-' + (previousMonth1 + 1);
+    // 格式化年份和月份
+    var formattedPreviousMonth = previousYear + '-' + (previousMonth + 1);
+    timeData =
+      formattedPreviousMonth.split('-')[1] + '-' + formattedPreviousMonth1.split('-')[1] + '月';
+  } else {
+    var formattedPreviousMonth1 = previousYear1 + '年' + (previousMonth1 + 1) + '月';
+    // .toString().padStart(2, '0');
+    // 格式化年份和月份
+    var formattedPreviousMonth = previousYear + '年' + (previousMonth + 1) + '月';
+    timeData = formattedPreviousMonth + '-' + formattedPreviousMonth1;
+  }
+  return timeData;
+}

+ 74 - 0
src/utils/request.js

@@ -0,0 +1,74 @@
+/**
+ * 封装的axios的工具类
+ * 负责请求的公共配置,以及请求拦截,响应拦截,错误处理,网络不佳处理
+ */
+import axios from 'axios';
+import { Toast, Dialog } from 'vant';
+import errorCode from '@/utils/errorCode';
+import { toastLoading } from '@/utils/commonVant';
+
+axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
+const service = axios.create({
+  baseURL: process.env.VUE_APP_BASE_API,
+  timeout: 0, //30000
+  withCredentials: true,
+});
+// request拦截器
+service.interceptors.request.use(
+  (config) => {
+    // toastLoading(0, '加载中...', true);
+    config.headers['userId'] = localStorage.getItem('loginName');
+    return config;
+  },
+  (error) => {
+    Promise.reject(error);
+  }
+);
+
+// 响应拦截器
+service.interceptors.response.use(
+  (res) => {
+    // toastLoading().clear();
+    const code = res.data.code || 200;
+    const msg = errorCode[code] || res.data.msg || errorCode['default'];
+    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
+      return res.data;
+    }
+    if (code === 401) {
+      Toast('认证信息未通过');
+    } else if (code === 500) {
+      // Toast({
+      //     message:msg,
+      //     duration:5000
+      // });
+      return res.data;
+    } else if (code == -1) {
+      // 图匠图片校验接口超时
+      return res.data;
+    } else if (code !== 200) {
+      Toast({
+        message: msg,
+        duration: 5000,
+      });
+    } else {
+      return res.data;
+    }
+  },
+  (error) => {
+    let { message } = error;
+    if (message == 'Network Error') {
+      message = '网络异常';
+    } else if (message.includes('timeout')) {
+      message = '请求超时';
+    } else if (message.includes('Request failed with status code')) {
+      message = '系统接口' + message.substr(message.length - 3) + '异常';
+    }
+    if (error.message === 'canceled') {
+      console.log('请求被取消:', error.message);
+    } else {
+      Toast(message);
+    }
+    return Promise.reject(error);
+  }
+);
+export default service;

+ 42 - 0
src/views/home/err.vue

@@ -0,0 +1,42 @@
+<template>
+  <div>
+    <br>
+    <br>
+    <br>
+    <br>
+    <br>
+    <br>
+    <br>
+    <img :src="err" class="errImg">
+    <p @click="login" class="errText">请使用企业微信查看</p>
+  </div>
+</template>
+<script>
+import err from "@/assets/err.png";
+export default {
+  name: "err",
+  data() {
+    return {
+      err: err,
+      num: 0
+    }
+  },
+  methods: {
+    login() {
+      if (this.num > 3) {
+        this.$router.push("/login")
+      } else {
+        this.num = this.num + 1;
+      }
+    }
+  }
+}
+</script>
+<style >
+.errImg{
+  margin: 0 auto;display: block;width: 100px;
+}
+.errText{
+  text-align: center;color:#464646;font-size: 18px
+}
+</style>

+ 21 - 0
src/views/home/index.vue

@@ -0,0 +1,21 @@
+<template>
+  <div class="homePage" ref="homePage">
+    <van-nav-bar class="navBar" left-arrow title="经销商拜访" />
+  </div>
+</template>
+
+<script>
+import { mapState } from 'vuex';
+export default {
+  name: 'home',
+  computed: {
+    ...mapState({
+      userInfo: (state) => state.user.userInfo,
+    }),
+  },
+  data() {
+    return {};
+  },
+};
+</script>
+<style scoped lang="scss"></style>

+ 79 - 0
src/views/home/login.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="logins">
+    <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
+    <div class="avsd">门点拜访登录</div>
+    <br /><br />
+    <br /><br />
+    <van-field v-model="name" label="账号" placeholder="请输入账号" />
+    <br /><br /><br /><br />
+    <van-button type="info" size="small" plain class="Btn100" @click="login" style="margin: 0 auto"
+      >登录</van-button
+    >
+  </div>
+</template>
+<script>
+import store from '@/store';
+export default {
+  name: 'login',
+  data() {
+    return {
+      name: '',
+    };
+  },
+  methods: {
+    login() {
+      localStorage.clear();
+      if (this.name.trim() != '') {
+        store.commit('SET_USER_INFO', null);
+        store.dispatch('setDeviceOutsidePage', true);
+        // 当前设备:PC/mobile
+        let isDevice = window.navigator.userAgent.match(
+          /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
+        );
+        localStorage.setItem('isDevice', !isDevice ? 'PC' : 'mobile');
+        localStorage.setItem('loginName', this.name);
+        localStorage.removeItem('loginType');
+        this.$router.push('/');
+      } else {
+        this.$toast('账号不能为空');
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.logins {
+  padding: 10px;
+}
+.Btn100 {
+  margin: 0 auto 10px;
+  display: block;
+  width: 100%;
+  border-radius: 5px;
+  color: #fff !important;
+  background-color: #0057ba;
+  border: 1px solid #0057ba;
+  height: 44px;
+  font-size: 16px;
+}
+.avsd {
+  margin: 0 auto;
+  width: 112px;
+  font-size: 18px;
+  color: #0057ba;
+  animation: jump 3s ease infinite;
+}
+@keyframes jump {
+  0% {
+    transform: translateY(0) scale(1, 1);
+  }
+  /* 中间状态图片位移并且拉伸 */
+  50% {
+    transform: translateY(-20px) scale(0.97, 1.03);
+  }
+  100% {
+    transform: translateY(0) scale(1, 1);
+  }
+}
+</style>

+ 48 - 0
vue.config.js

@@ -0,0 +1,48 @@
+'use strict';
+const path = require('path');
+function resolve(dir) {
+  return path.join(__dirname, dir);
+}
+const name = process.env.VUE_APP_TITLE || '门店拜访'; // 网页标题
+const port = 9999; // 端口
+module.exports = {
+  // publicPath: process.env.NODE_ENV === "production" ? "/mobile/" : "/",
+  publicPath: '/agent/',
+  outputDir: 'agent',
+  assetsDir: 'static',
+  lintOnSave: false,
+  productionSourceMap: false,
+  devServer: {
+    port: port,
+    open: true,
+    proxy: {
+      [process.env.VUE_APP_BASE_API]: {
+        target: process.env.VUE_APP_Target,
+        changeOrigin: true,
+        pathRewrite: {
+          ['^' + process.env.VUE_APP_BASE_API]: process.env.VUE_APP_BASE_API + '',
+        },
+      },
+    },
+    disableHostCheck: true,
+  },
+  configureWebpack: {
+    name: name,
+    resolve: {
+      alias: {
+        '@': resolve('src'),
+      },
+    },
+  },
+  chainWebpack(config) {
+    config.plugins.delete('preload'); // TODO: need test
+    config.plugins.delete('prefetch'); // TODO: need test
+    config.when(process.env.NODE_ENV !== 'development', (config) => {
+      config.optimization.runtimeChunk('single'),
+        {
+          from: path.resolve(__dirname, './public/robots.txt'), //防爬虫文件
+          to: './', //到根目录下
+        };
+    });
+  },
+};