فهرست منبع

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper into master

seyason 1 سال پیش
والد
کامیت
c43af857af
36فایلهای تغییر یافته به همراه86794 افزوده شده و 452 حذف شده
  1. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/index.html
  2. 159 0
      fhKeeper/formulahousekeeper/customerBuler-crm/package-lock.json
  3. 3 0
      fhKeeper/formulahousekeeper/customerBuler-crm/package.json
  4. 18 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/App.vue
  5. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/404.png
  6. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/background.png
  7. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/login_logo.png
  8. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/qiyeweixin.png
  9. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/ReEcharts/index.vue
  10. 11 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/main.ts
  11. 11 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/404.vue
  12. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/api.ts
  13. 54 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/home.vue
  14. 0 8
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/index.vue
  15. 89 245
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue
  16. 179 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/register.vue
  17. 18 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/config.ts
  18. 144 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/echarts.vue
  19. 20 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/index.vue
  20. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/api.ts
  21. 9 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue
  22. 80 26
      fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts
  23. 14 8
      fhKeeper/formulahousekeeper/customerBuler-crm/src/store/index.ts
  24. 0 80
      fhKeeper/formulahousekeeper/customerBuler-crm/src/styles.css
  25. 13 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/styles/global.scss
  26. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts
  27. 22 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts
  28. 2 1
      fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.json
  29. 49 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ClueController.java
  30. 18 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Clue.java
  31. 6 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/ClueService.java
  32. 58 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ClueServiceImpl.java
  33. 85730 0
      fhKeeper/formulahousekeeper/management-crm/workTime.log
  34. 60 54
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  35. 18 16
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  36. 1 0
      fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/index.html

@@ -4,7 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Vite + Vue + TS</title>
+    <title>客户管家</title>
   </head>
   <body>
     <div id="app"></div>

+ 159 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/package-lock.json

@@ -8,10 +8,13 @@
       "name": "customerbuler-crm",
       "version": "0.0.0",
       "dependencies": {
+        "@element-plus/icons-vue": "^2.3.1",
+        "@zmjs/form-design": "^0.1.16",
         "axios": "^1.6.7",
         "echarts": "^5.5.0",
         "element-plus": "^2.5.6",
         "pinia": "^2.1.7",
+        "pinia-plugin-persistedstate": "^3.2.1",
         "vue": "^3.4.19",
         "vue-router": "^4.3.0"
       },
@@ -1022,6 +1025,119 @@
         }
       }
     },
+    "node_modules/@zmjs/form-design": {
+      "version": "0.1.16",
+      "resolved": "https://registry.npmjs.org/@zmjs/form-design/-/form-design-0.1.16.tgz",
+      "integrity": "sha512-RCjB1JbV1BKJrQ14jxez8WI72oKN3JXsMkLskZyni7tVEPjmTP8s/R3ZfFzchV0FVuZ4FZnRYNYrSDxBnpqmuw==",
+      "dependencies": {
+        "@vueuse/core": "^8.2.3",
+        "ace-builds": "^1.4.14",
+        "element-plus": "^2.1.10",
+        "lodash-es": "^4.17.21",
+        "prismjs": "^1.28.0",
+        "vue": "^3.2.31",
+        "vue-prism-editor": "^2.0.0-alpha.2",
+        "vuedraggable": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/zhiyuanzmj"
+      }
+    },
+    "node_modules/@zmjs/form-design/node_modules/@types/web-bluetooth": {
+      "version": "0.0.14",
+      "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.14.tgz",
+      "integrity": "sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A=="
+    },
+    "node_modules/@zmjs/form-design/node_modules/@vueuse/core": {
+      "version": "8.9.4",
+      "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-8.9.4.tgz",
+      "integrity": "sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q==",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.14",
+        "@vueuse/metadata": "8.9.4",
+        "@vueuse/shared": "8.9.4",
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.1.0",
+        "vue": "^2.6.0 || ^3.2.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@zmjs/form-design/node_modules/@vueuse/core/node_modules/@vueuse/shared": {
+      "version": "8.9.4",
+      "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-8.9.4.tgz",
+      "integrity": "sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==",
+      "dependencies": {
+        "vue-demi": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.1.0",
+        "vue": "^2.6.0 || ^3.2.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        },
+        "vue": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@zmjs/form-design/node_modules/@vueuse/core/node_modules/vue-demi": {
+      "version": "0.14.7",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
+      "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@zmjs/form-design/node_modules/@vueuse/metadata": {
+      "version": "8.9.4",
+      "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-8.9.4.tgz",
+      "integrity": "sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==",
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/ace-builds": {
+      "version": "1.32.7",
+      "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.32.7.tgz",
+      "integrity": "sha512-ziv35kaYELFw4suWlotz/Xsl1/1LhWAbwFoD3zIgCgP9gXGECEsAM4GhiB0T0xZdmQjyv6hmAzO280g0+n4vGw=="
+    },
     "node_modules/ansi-regex": {
       "version": "6.0.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -2065,6 +2181,14 @@
         }
       }
     },
+    "node_modules/pinia-plugin-persistedstate": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz",
+      "integrity": "sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ==",
+      "peerDependencies": {
+        "pinia": "^2.0.0"
+      }
+    },
     "node_modules/pinia/node_modules/vue-demi": {
       "version": "0.14.7",
       "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
@@ -2247,6 +2371,14 @@
       "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
       "dev": true
     },
+    "node_modules/prismjs": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
+      "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/proxy-from-env": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -2480,6 +2612,11 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/sortablejs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
+      "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w=="
+    },
     "node_modules/source-map-js": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -2841,6 +2978,17 @@
         }
       }
     },
+    "node_modules/vue-prism-editor": {
+      "version": "2.0.0-alpha.2",
+      "resolved": "https://registry.npmjs.org/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz",
+      "integrity": "sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==",
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.3.0",
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.0.tgz",
@@ -2882,6 +3030,17 @@
         "typescript": "*"
       }
     },
+    "node_modules/vuedraggable": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
+      "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+      "dependencies": {
+        "sortablejs": "1.14.0"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.1"
+      }
+    },
     "node_modules/which": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

+ 3 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/package.json

@@ -10,10 +10,13 @@
     "preview": "vite preview"
   },
   "dependencies": {
+    "@element-plus/icons-vue": "^2.3.1",
+    "@zmjs/form-design": "^0.1.16",
     "axios": "^1.6.7",
     "echarts": "^5.5.0",
     "element-plus": "^2.5.6",
     "pinia": "^2.1.7",
+    "pinia-plugin-persistedstate": "^3.2.1",
     "vue": "^3.4.19",
     "vue-router": "^4.3.0"
   },

+ 18 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/App.vue

@@ -2,6 +2,22 @@
   <router-view></router-view>
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { useStore } from '@/store/index'
+const { setAsyncRoutesMark } = useStore()
+window.addEventListener('beforeunload', () => beforeunloadFn())
+const beforeunloadFn = (() => {
+  setAsyncRoutesMark(false)
+})
+</script>
 
-<style scoped></style>
+<style>
+html,
+body,
+#app,
+.layouts {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+</style>

BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/404.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/background.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/login_logo.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm/src/assets/login/qiyeweixin.png


+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/ReEcharts/index.vue

@@ -6,7 +6,7 @@ import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
 interface Props {
 	width?: string;
 	height?: string;
-	option: EChartsOption;
+	option: Partial<EChartsOption>;
 }
 const props = withDefaults(defineProps<Props>(), {
 	width: '100%',
@@ -18,7 +18,7 @@ const myChartsRef = ref<HTMLDivElement>();
 let myChart: ECharts;
 // eslint-disable-next-line no-undef
 // let timer: string | number | NodeJS.Timeout | undefined;
-let timer: number | undefined;
+let timer: any;
 
 // 初始化echarts
 const initChart = (): void => {
@@ -47,7 +47,7 @@ onMounted(() => {
 onBeforeUnmount(() => {
 	window.removeEventListener('resize', resizeChart);
 	clearTimeout(timer);
-	timer = 0;
+	timer = null;
 });
 
 watch(

+ 11 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/main.ts

@@ -1,18 +1,27 @@
 import { createApp } from 'vue'
 import { createPinia } from 'pinia'
 import ElementPlus from 'element-plus'
+import "./TailWindCss/index.css";
 import 'element-plus/dist/index.css'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
 import App from './App.vue'
 import router from './router/index'
-import "./styles.css"
-import "./TailWindCss/index.css";
 import * as echarts from 'echarts';
 
 const app = createApp(App)
+const pinia = createPinia()
+
+pinia.use(piniaPluginPersistedstate)
+
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component)
+}
 
 app.config.globalProperties.$echarts = echarts;
 app
   .use(ElementPlus)
   .use(createPinia())
   .use(router)
+  .use(pinia)
   .mount('#app')

+ 11 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/404.vue

@@ -0,0 +1,11 @@
+<template>
+  <div>
+    404
+  </div>
+</template>
+
+<script lang="ts" setup>
+
+</script>
+
+<style lang="scss" scoped></style>

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/api.ts

@@ -0,0 +1 @@
+export const SENDVCODE = "/user/sendVcode"; //发送验证码

+ 54 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/home.vue

@@ -0,0 +1,54 @@
+<template>
+    <div class="common-layout layouts">
+        <el-container class="flex flex-col layouts">
+            <!-- 头部 -->
+            <el-header></el-header>
+            <!-- 左侧菜单 -->
+            <el-container class="flex-1 layouts">
+                <el-aside class="home-el-aside" width="220px">
+                    <el-menu default-active="1" class="el-menu-vertical-demo">
+                        <el-menu-item :index="(index + 1) + ''" v-for="(item, index) in 10" :key="index">
+                            <el-icon><setting /></el-icon>
+                            <span>第{{ index + 1 }}个</span>
+                        </el-menu-item>
+
+                        <el-sub-menu index="11">
+                            <template #title>
+                                <el-icon>
+                                    <location />
+                                </el-icon>
+                                <span>第十一个</span>
+                            </template>
+                            <el-menu-item :index="'11'">
+                                <el-icon><icon-menu /></el-icon>
+                                <span>第11-1个</span>
+                            </el-menu-item>
+                        </el-sub-menu>
+                    </el-menu>
+                </el-aside>
+                <!-- 主体 -->
+                <el-main>
+                    <router-view></router-view>
+                </el-main>
+            </el-container>
+        </el-container>
+    </div>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style scoped lang="scss">
+.common-layout {
+    .el-header {
+        background: $darkBlue;
+    }
+
+    .el-menu-vertical-demo {
+        height: 100%;
+    }
+    .el-main {
+        padding: 0 20px;
+        background: $backColor;
+    }
+}
+</style>

+ 0 - 8
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/index.vue

@@ -1,8 +0,0 @@
-<template>
-    <div>
-        indexyemian
-    </div>
-</template>
-<script setup lang="ts">
-</script>
-<style scoped lang="scss"></style>

+ 89 - 245
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue

@@ -1,256 +1,100 @@
 <template>
-    <div @click="tologin">登录</div>
-    <h2> {{ store.name }}</h2>
-    <h1 @click="changeName">修改名称</h1>
-    <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick" />
-    <div @click="networkRequest()">点击发起网络请求</div>
-    <div class="abc fonsSize30">测试sass样式</div>
-    <div class="text-pink-200">1111</div>
-    <div :style="{ width: '1230px', height: '350px' }">
-        <Echarts :option="option" />
+  <div class="loginView w-screen h-screen overflow-hidden flex">
+    <div class="w-96 bg-white m-auto border-t-8 border-blue-400 shadow-xl rounded-md pl-9 pr-9">
+      <div class="m-auto pt-4">
+        <img class="w-1/5 h-1/5 m-auto" :src="loginLogo" alt="">
+      </div>
+      <h2 class="text-xl text-center pt-4 font-bold">客户管家</h2>
+      <el-form class="pt-4" ref="ruleFormRef" :model="ruleForm" :rules="rules">
+        <el-form-item prop="username">
+          <el-input clearable :prefix-icon="UserFilled" size="large" class="mt-2" v-model="ruleForm.username" autocomplete="off"
+            placeholder="账号/手机号" />
+        </el-form-item>
+        <el-form-item prop="password">
+          <el-input clearable :prefix-icon="Lock" show-password size="large" class="mt-4" v-model="ruleForm.password"
+            autocomplete="off" placeholder="密码" />
+        </el-form-item>
+        <div class="pt-4">
+          <el-button type="primary" size="large" class="w-full" :loading="loginLoading"
+            @click="login(ruleFormRef)">登录</el-button>
+        </div>
+      </el-form>
+      <el-divider content-position="center">或</el-divider>
+      <div class="m-auto mb-5">
+        <img class="w-9 m-auto p-1 rounded-full border-blue-300 border-2 cursor-pointer" :src="qiyeweixin" alt="">
+      </div>
+      <div class="flex justify-between pb-5">
+        <div class="cursor-pointer text-blue-400 hover:text-blue-300">联系客服</div>
+        <div class="flex justify-around">
+          <div class="mr-4 cursor-pointer text-blue-400 hover:text-blue-300" @click="useHelp()">使用说明</div>
+          <div class="cursor-pointer text-blue-400 hover:text-blue-300" @click="toRegister()">企业注册</div>
+        </div>
+      </div>
+      <el-dialog v-model="helpDialog" width="30%" title="使用说明">
+        <div class="p-2 text-xl">文档水水水水</div>
+        <div>文档水水水水</div>
+        <div>文档水水水水</div>
+      </el-dialog>
     </div>
+  </div>
 </template>
-<script lang="ts" setup>
-import { reactive } from 'vue';
-import Echarts from '@/components/ReEcharts/index.vue';
-import { useRoute, useRouter } from 'vue-router'
-import { useStore } from "../store/index";
-import { post } from '@/utils/request'
 
-const router = useRouter()
-const store = useStore()
-function tologin() {
-    router.push({
-        name: 'index'
-    })
-}
-function changeName(): void {
-    store.name = '索索'
-}
+<script lang="ts" setup>
+import { reactive, ref } from "vue";
+import { useRouter } from "vue-router";
+import loginLogo from "@/assets/login/login_logo.png";
+import qiyeweixin from "@/assets/login/qiyeweixin.png";
+import { UserFilled, Lock } from '@element-plus/icons-vue'
+import type { FormInstance, FormRules } from 'element-plus'
+import { useStore } from '@/store/index'
+const { setRouters } = useStore()
+const router = useRouter();
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = ref({
+  username: "",
+  password: "",
+});
+const loginLoading = ref(false);
+const rules = reactive<FormRules<typeof ruleForm>>({
+  username: [{ required: true, trigger: "blur", message: "请输入账号/手机号" }],
+  password: [{ required: true, trigger: "blur", message: "请输入密码" }],
+})
+const helpDialog = ref(false);
 
-async function networkRequest(): Promise<void> {
-    console.log('发起请求')
-    const requestUrl = '/user/loginAdmin'
-    const data = {
-        username: '18130408100',
-        password: '220926'
+const login = (formEl: FormInstance | undefined) => {
+  if (!formEl) {
+    return
+  }
+  formEl.validate((valid) => {
+    if (!valid) {
+      return false;
     }
-    // proxy.$post(requestUrl, data)
-    // .then((response: any) => {
-    //     console.log(response, '<=== 请求返回的数据')
-    // })
-    const res = await post(requestUrl, data)
-    console.log(res, '<=== 请求返回的数据')
-}
-
-
-interface Tree {
-    label: string
-    children?: Tree[]
-}
+    loginLoading.value = true;
+    console.log(ruleForm.value);
+    let newRouter = [
+      { 
+        path: '/thread',
+        name: 'thread'
+      }
+    ]
+    setRouters(newRouter)
+    setTimeout(() => {
+      loginLoading.value = false;
+      router.push(newRouter[0].path);
+    }, 1000);
+  })
 
-const handleNodeClick = (data: Tree) => {
-    console.log(data)
+};
+const useHelp = () => {
+  helpDialog.value = true;
 }
-
-const data: Tree[] = [
-    {
-        label: 'Level one 1',
-        children: [
-            {
-                label: 'Level two 1-1',
-                children: [
-                    {
-                        label: 'Level three 1-1-1',
-                    },
-                ],
-            },
-        ],
-    },
-    {
-        label: 'Level one 2',
-        children: [
-            {
-                label: 'Level two 2-1',
-                children: [
-                    {
-                        label: 'Level three 2-1-1',
-                    },
-                ],
-            },
-            {
-                label: 'Level two 2-2',
-                children: [
-                    {
-                        label: 'Level three 2-2-1',
-                    },
-                ],
-            },
-        ],
-    },
-    {
-        label: 'Level one 3',
-        children: [
-            {
-                label: 'Level two 3-1',
-                children: [
-                    {
-                        label: 'Level three 3-1-1',
-                    },
-                ],
-            },
-            {
-                label: 'Level two 3-2',
-                children: [
-                    {
-                        label: 'Level three 3-2-1',
-                    },
-                ],
-            },
-        ],
-    },
-]
-
-const defaultProps = {
-    children: 'children',
-    label: 'label',
+const toRegister = () => {
+  router.push("/register");
 }
-
-const option = reactive({
-    tooltip: {
-        trigger: 'axis',
-        axisPointer: {
-            type: 'shadow',
-            label: {
-                show: true
-            }
-        }
-    },
-    grid: {
-        left: '4%',
-        top: '15%',
-        right: '4%',
-        bottom: '10%'
-    },
-    legend: {
-        data: ['昨日总人数', '今日实时人数'],
-        top: '4%',
-        color: '#1FC3CE',
-        fontSize: 14,
-        selected: { 昨日使用率: false } // 不需要显示的设置为false
-    },
-    xAxis: {
-        data: [
-            '会议室1',
-            '会议室2',
-            '会议室3',
-            '会议室4',
-            '会议室5',
-            '会议室6',
-            '会议室7',
-            '会议室8',
-            '会议室9'
-        ],
-        axisLine: {
-            show: true, //隐藏X轴轴线
-            lineStyle: {
-                color: '#eee',
-                width: 1
-            }
-        },
-        axisTick: {
-            show: true, //隐藏X轴刻度
-            alignWithLabel: true
-        },
-        axisLabel: {
-            show: true,
-            color: '#333', //X轴文字颜色
-            fontSize: 14
-        }
-    },
-    yAxis: [
-        {
-            type: 'value',
-            name: '人数',
-            nameTextStyle: {
-                color: '#333',
-                fontSize: 14
-            },
-            splitLine: {
-                show: true,
-                lineStyle: {
-                    width: 1,
-                    color: '#eee'
-                }
-            },
-            axisTick: {
-                show: false
-            },
-            axisLine: {
-                show: false
-            },
-            axisLabel: {
-                show: true,
-                color: '#333',
-                fontSize: 14
-            }
-        }
-    ],
-    series: [
-        {
-            name: '昨日总人数',
-            type: 'bar',
-            barWidth: 18,
-            itemStyle: {
-                color: {
-                    type: 'linear',
-                    x: 0, // 右
-                    y: 1, // 下
-                    x2: 0, // 左
-                    y2: 0, // 上
-                    colorStops: [
-                        {
-                            offset: 0,
-                            color: '#F89898' // 0% 处的颜色
-                        },
-                        {
-                            offset: 1,
-                            color: '#F56C6C' // 100% 处的颜色
-                        }
-                    ]
-                }
-            },
-            data: [24, 45, 43, 35, 76, 154, 86, 42, 68]
-        },
-        {
-            name: '今日实时人数',
-            type: 'bar',
-            barWidth: 18,
-            itemStyle: {
-                color: {
-                    type: 'linear',
-                    x: 0, // 右
-                    y: 1, // 下
-                    x2: 0, // 左
-                    y2: 0, // 上
-                    colorStops: [
-                        {
-                            offset: 0,
-                            color: '#52A7FF' // 0% 处的颜色
-                        },
-                        {
-                            offset: 1,
-                            color: '#409EFF' // 100% 处的颜色
-                        }
-                    ]
-                }
-            },
-            data: [133, 23, 114, 67, 89, 35, 67, 96, 90]
-        }
-    ]
-});
-
 </script>
 
-<style lang="scss" scoped></style>
-
+<style lang="scss" scoped>
+.loginView {
+  background: #f0f2f5 url("../assets/login/background.png") no-repeat;
+}
+</style>

+ 179 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/register.vue

@@ -0,0 +1,179 @@
+<template>
+  <div class="loginView w-screen h-screen overflow-hidden flex">
+    <div class="w-[32rem] bg-white m-auto border-t-8 border-blue-400 shadow-xl rounded-md pl-9 pr-9">
+      <h2 class="text-xl text-center pt-4 font-bold">注册</h2>
+      <el-form class="pt-4" ref="ruleFormRef" :model="ruleForm" :rules="rules">
+        <el-form-item class="pt-1" prop="companyName">
+          <el-input type="text" size="large" v-model="ruleForm.companyName" autocomplete="off" placeholder="公司名"
+            clearable :prefix-icon="HomeFilled"></el-input>
+        </el-form-item>
+        <el-form-item class="pt-1" prop="name">
+          <el-input type="text" size="large" v-model="ruleForm.name" autocomplete="off" placeholder="姓名" clearable
+            :prefix-icon="UserFilled"></el-input>
+        </el-form-item>
+        <el-form-item class="pt-1" prop="phone">
+          <el-input type="text" size="large" v-model="ruleForm.phone" autocomplete="off" placeholder="手机号" clearable
+            :prefix-icon="Iphone"></el-input>
+        </el-form-item>
+        <el-form-item class="pt-1" prop="vcode">
+          <el-input type="text" size="large" v-model="ruleForm.vcode" autocomplete="off" placeholder="验证码" clearable
+            :prefix-icon="Tickets">
+            <template #append>
+              <el-button @click="sendVcode()" :disabled="ruleForm.phone == '' || showTimer">发送验证码<span
+                  v-if="showTimer">({{ countNum }})</span></el-button>
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item class="pt-1" prop="password">
+          <el-input type="password" size="large" v-model="ruleForm.password" autocomplete="off"
+            placeholder="设置密码,长度不低于6位" clearable :prefix-icon="Lock">
+          </el-input>
+        </el-form-item>
+        <el-form-item class="pt-1" prop="repwd">
+          <el-input type="password" size="large" v-model="ruleForm.repwd" autocomplete="off" placeholder="重复密码"
+            clearable :prefix-icon="Lock">
+          </el-input>
+        </el-form-item>
+        <div class="pt-4 mb-5">
+          <el-button type="primary" size="large" class="w-full" :loading="registerLoading"
+            @click="register(ruleFormRef)">注册</el-button>
+        </div>
+      </el-form>
+      <div class="text-right mb-5 cursor-pointer text-blue-400 hover:text-blue-300" @click="router.push('/login')">
+        已有账号?返回登录
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { reactive, ref } from "vue";
+import { useRouter, useRoute } from "vue-router";
+import { HomeFilled, UserFilled, Lock, Iphone, Tickets } from '@element-plus/icons-vue'
+import { ElNotification, ElMessage, type FormInstance, type FormRules } from 'element-plus'
+import { isValueEmpty } from "@/utils/tools";
+import { post } from "@/utils/request";
+import { SENDVCODE } from "./api";
+const router = useRouter();
+const route = useRoute();
+const ruleFormRef = ref<FormInstance>();
+const ruleForm = ref({
+  companyName: "",
+  name: "",
+  phone: "",
+  vcode: "",
+  password: "",
+  repwd: ""
+});
+const rules = reactive<FormRules<typeof ruleForm>>({
+  companyName: [{ required: true, message: '请输入公司名', trigger: 'blur' },],
+  name: [{ required: true, message: '请输入姓名', trigger: 'blur' },],
+  phone: [
+    { required: true, message: '请输入手机号', trigger: 'blur' },
+    { min: 11, max: 11, message: '请输入正确的手机号', trigger: 'blur' },
+  ],
+  vcode: [
+    { required: true, message: '请输入验证码', trigger: 'blur' },
+  ],
+  password: [
+    { required: true, message: '请设置密码,长度不低于6位', trigger: 'blur' },
+    { min: 6, message: '密码长度不少于6位', trigger: 'blur' },
+  ],
+  repwd: [
+    { required: true, message: '请重复输入密码', trigger: 'blur' },
+    {
+      validator: (_rule, value, callback) => {
+        if (value !== ruleForm.value.password) {
+          callback(new Error('两次密码不一致'));
+        } else {
+          callback();
+        }
+      }, trigger: 'blur'
+    }
+  ]
+})
+
+const registerLoading = ref(false);
+const showTimer = ref(false);
+const countNum = ref(10);
+const countDown = () => {
+  if (countNum.value > 0) {
+    setTimeout(() => {
+      countNum.value -= 1;
+      countDown()
+    }, 1000)
+  } else {
+    showTimer.value = false;
+    countNum.value = 10;
+  }
+}
+const sendVcode = () => {
+  showTimer.value = true;
+  countDown();
+  if (ruleForm.value.phone.trim().length != 11) {
+    ElMessage.error({
+      message: "请输入正确的手机号",
+      type: "error",
+      duration: 2000,
+    })
+    return
+  }
+  // post(SENDVCODE, {
+  //   mobile: ruleForm.value.phone
+  // }).then(res => {
+  //   if (res.code == "ok") { 
+  //     ElMessage.success({
+  //       message: "发送成功",
+  //       type: "success",
+  //       duration: 2000,
+  //     })
+  //     showTimer.value = true;
+  //   countDown();
+  //   }
+  // })
+}
+const register = (formEl: FormInstance | undefined) => {
+  if (!formEl) {
+    return
+  }
+
+  formEl.validate((valid) => {
+    if (!valid) {
+      return false;
+    }
+    registerLoading.value = true;
+    let params = {
+      ...ruleForm.value,
+    }
+    if (!isValueEmpty(route.query)) {
+      params = {
+        ...params,
+        ...route.query
+      }
+    }
+    console.log(params);
+    // if (res.code == "ok") { 
+    //   ElNotification.success({
+    //     title: "注册成功",
+    //     message: "注册成功",
+    //     type: "success",
+    //     duration: 1000,
+    //     onClose: () => {
+    //       router.push('/login')
+    //     }
+    //   })
+    // }
+
+
+    registerLoading.value = false;
+  })
+
+};
+
+</script>
+
+<style lang="scss" scoped>
+.loginView {
+  background: #f0f2f5 url("../assets/login/background.png") no-repeat;
+}
+</style>

+ 18 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/config.ts

@@ -0,0 +1,18 @@
+export const config = {
+  1: {
+    num: 1,
+    show: true,
+  },
+  2: {
+    num: 2,
+    show: true,
+  },
+  3: {
+    num: "",
+    show: false,
+  },
+  4: {
+    num: "少时诵诗书",
+    show: false,
+  },
+};

+ 144 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/echarts.vue

@@ -0,0 +1,144 @@
+<template>
+  <div :style="{ width: '1230px', height: '350px' }">
+    <Echarts :option="option" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import Echarts from '@/components/ReEcharts/index.vue';
+import { EChartsOption } from 'echarts';
+import { reactive } from 'vue';
+const option = reactive<Partial<EChartsOption>>({
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'shadow',
+      label: {
+        show: true
+      }
+    }
+  },
+  grid: {
+    left: '4%',
+    top: '15%',
+    right: '4%',
+    bottom: '10%'
+  },
+  legend: {
+    data: ['昨日总人数', '今日实时人数'],
+    top: '4%',
+    selected: { 昨日使用率: false } // 不需要显示的设置为false
+  },
+  xAxis: {
+    data: [
+      '会议室1',
+      '会议室2',
+      '会议室3',
+      '会议室4',
+      '会议室5',
+      '会议室6',
+      '会议室7',
+      '会议室8',
+      '会议室9'
+    ],
+    axisLine: {
+      show: true, //隐藏X轴轴线
+      lineStyle: {
+        color: '#eee',
+        width: 1
+      }
+    },
+    axisTick: {
+      show: true, //隐藏X轴刻度
+      alignWithLabel: true
+    },
+    axisLabel: {
+      show: true,
+      color: '#f00', //X轴文字颜色
+      fontSize: 14
+    }
+  },
+  yAxis: [
+    {
+      type: 'value',
+      name: '人数',
+      nameTextStyle: {
+        color: '#333',
+        fontSize: 14
+      },
+      splitLine: {
+        show: true,
+        lineStyle: {
+          width: 1,
+          color: '#ccc'
+        }
+      },
+      axisTick: {
+        show: false
+      },
+      axisLine: {
+        show: false
+      },
+      axisLabel: {
+        show: true,
+        color: '#333',
+        fontSize: 14
+      }
+    }
+  ],
+  series: [
+    {
+      name: '昨日总人数',
+      type: 'bar',
+      barWidth: 18,
+      itemStyle: {
+        color: {
+          type: 'linear',
+          x: 0, // 右
+          y: 1, // 下
+          x2: 0, // 左
+          y2: 0, // 上
+          colorStops: [
+            {
+              offset: 0,
+              color: '#F89898' // 0% 处的颜色
+            },
+            {
+              offset: 1,
+              color: '#F56C6C' // 100% 处的颜色
+            }
+          ]
+        }
+      },
+      data: [24, 45, 43, 35, 76, 154, 86, 42, 68]
+    },
+    {
+      name: '今日实时人数',
+      type: 'bar',
+      barWidth: 18,
+      itemStyle: {
+        color: {
+          type: 'linear',
+          x: 0, // 右
+          y: 1, // 下
+          x2: 0, // 左
+          y2: 0, // 上
+          colorStops: [
+            {
+              offset: 0,
+              color: '#52A7FF' // 0% 处的颜色
+            },
+            {
+              offset: 1,
+              color: '#409EFF' // 100% 处的颜色
+            }
+          ]
+        }
+      },
+      data: [133, 23, 114, 67, 89, 35, 67, 96, 90]
+    }
+  ]
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 20 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/index.vue

@@ -0,0 +1,20 @@
+<template>
+  <div class="flex w-96 justify-around">
+    <div class="w-1/6 h-80 bg-cyan-200 line-through">1</div>
+    <div class="w-1/6 h-80 bg-cyan-200 leading-normal">2</div>
+    <div v-if="config[id].show" class="w-1/6 h-80 bg-cyan-200 hover:translate-y-10 duration-500">3</div>
+    <div class="myClass">4</div>
+    <div>{{ config[id].num }}</div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { config } from "./config"
+const id = 2;
+</script>
+
+<style lang="scss" scoped>
+.myClass {
+  @apply w-1/6 bg-red-700 text-white hover:size-20;
+}
+</style>

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/api.ts

@@ -0,0 +1 @@
+export const TEST_API = "/api/test"; //发送验证码

+ 9 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue

@@ -0,0 +1,9 @@
+<template>
+<div id="" class="h100">
+    线索文件
+</div>
+</template>
+<script setup lang="ts">
+</script>
+<style scoped lang="scss">
+</style>

+ 80 - 26
fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts

@@ -1,28 +1,82 @@
-import { createRouter, createWebHistory, } from 'vue-router'
-import Login from '../pages/login.vue';
+import { RouteRecordRaw,createRouter, createWebHistory } from "vue-router";
+import { useStore } from "@/store/index";
+export const routes: RouteRecordRaw[] = [
+  {
+    path: "/",
+    redirect: "/login",
+  },
+  {
+    name: "login",
+    path: "/login",
+    component: () => import("../pages/login.vue"),
+  },
+  {
+    name: "register",
+    path: "/register",
+    component: () => import("../pages/register.vue"),
+  },
+  {
+    name: "home",
+    path: "/home",
+    component: () => import("../pages/home.vue"),
+    children: [
+      // {
+      //     name: 'thread',
+      //     path: '/thread',
+      //     component: () => import("../pages/thread/thread.vue")
+      // },
+    ],
+  },
+  {
+    name: "test",
+    path: "/test",
+    component: () => import("../pages/test/index.vue"),
+  },
+  {
+    name: "testEcharts",
+    path: "/testEcharts",
+    component: () => import("../pages/test/echarts.vue"),
+  },
+];
 
-export const routes = [
-    {
-        path: '/',
-        redirect: '/login'
-    },
-    {
-        name: 'login',
-        path: '/login',
-        component: Login
-    },
-    {
-        name: 'index',
-        path: '/index',
-        component: () => import("../pages/index.vue")
-    }
-]
 const router = createRouter({
-    scrollBehavior: () => ({ left: 0, top: 0 }),
-    history: createWebHistory(),
-    routes,
-})
-router.beforeEach((_to, _from, next) => {
-    next()
-})
-export default router
+  scrollBehavior: () => ({ left: 0, top: 0 }),
+  history: createWebHistory(),
+  routes,
+});
+router.beforeEach((to, _from, next) => {
+  const routerList = useStore().routers;
+  const routers = router.getRoutes();
+  console.log(routerList, routers);
+  const { setAsyncRoutesMark, asyncRoutesMark } = useStore();
+  const token = true;
+  const skipPath = ['/login', '/register', '/test', '/testEcharts']
+  if (skipPath.includes(to.path)) {
+    next();
+  } else {
+    if (token && routerList && routerList.length > 0) {
+      if (asyncRoutesMark) {
+        next();
+      } else {
+        setAsyncRoutesMark(true)
+        const newRouters: any = routers
+        const addNewRouter = newRouters.find((item: any) => item.name == 'home')
+        routerList.forEach((item: any) => {
+          addNewRouter?.children.push({
+            path: item.path,
+            name: item.name,
+            meta: {},
+            component: () => import(`@/pages/${item.name}/index.vue`),
+          })
+        })
+        router.addRoute(addNewRouter);
+        next({ ...to, replace: true });
+      }
+    } else {
+      console.log("无登录信息,跳转到登录页");
+      next(`/login`);
+    }
+  }
+  console.log(routerList);
+});
+export default router;

+ 14 - 8
fhKeeper/formulahousekeeper/customerBuler-crm/src/store/index.ts

@@ -1,17 +1,23 @@
 import { defineStore, acceptHMRUpdate } from "pinia";
 export const useStore = defineStore({
     id: "index",
-    state: () => ({name: "old name",}),
-    getters: {
-        myName: (state) => {
-            return `getters ${state.name}`
-        }
+    state: () => ({
+        userInfo: {}, // 当前的用户信息
+        routers: [], // 返回的所有路由
+        asyncRoutesMark: false, // 是否添加过路由
+    }),
+    getters: { // 取值
+        
     },
-    actions: {
-        changeName(name: string) {
-            this.name = name
+    actions: { // 方法
+        setRouters(arr: any) {
+            this.routers = arr;
+        },
+        setAsyncRoutesMark(val: boolean) {
+            this.asyncRoutesMark = val
         }
     },
+    persist: true, // 是否持久化
 });
 if (import.meta.hot) {
     import.meta.hot.accept(acceptHMRUpdate(useStore, import.meta.hot))

+ 0 - 80
fhKeeper/formulahousekeeper/customerBuler-crm/src/styles.css

@@ -1,80 +0,0 @@
-:root {
-    font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-    line-height: 1.5;
-    font-weight: 400;
-  
-    color-scheme: light dark;
-    color: rgba(255, 255, 255, 0.87);
-    background-color: #242424;
-  
-    font-synthesis: none;
-    text-rendering: optimizeLegibility;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-  }
-  
-  a {
-    font-weight: 500;
-    color: #646cff;
-    text-decoration: inherit;
-  }
-  a:hover {
-    color: #535bf2;
-  }
-  
-  body {
-    margin: 0;
-    display: flex;
-    place-items: center;
-    min-width: 320px;
-    min-height: 100vh;
-  }
-  
-  h1 {
-    font-size: 3.2em;
-    line-height: 1.1;
-  }
-  
-  button {
-    border-radius: 8px;
-    border: 1px solid transparent;
-    padding: 0.6em 1.2em;
-    font-size: 1em;
-    font-weight: 500;
-    font-family: inherit;
-    background-color: #1a1a1a;
-    cursor: pointer;
-    transition: border-color 0.25s;
-  }
-  button:hover {
-    border-color: #646cff;
-  }
-  button:focus,
-  button:focus-visible {
-    outline: 4px auto -webkit-focus-ring-color;
-  }
-  
-  .card {
-    padding: 2em;
-  }
-  
-  #app {
-    max-width: 1280px;
-    margin: 0 auto;
-    padding: 2rem;
-    text-align: center;
-  }
-  
-  @media (prefers-color-scheme: light) {
-    :root {
-      color: #213547;
-      background-color: #ffffff;
-    }
-    a:hover {
-      color: #747bff;
-    }
-    button {
-      background-color: #f9f9f9;
-    }
-  }
-  

+ 13 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/styles/global.scss

@@ -0,0 +1,13 @@
+$darkBlue: #3475C5;
+$ashen: #FEFEFE;
+$backColor: #EEF3F6;
+$black: #000;
+$fontBlack: #333;
+$fontGray: #999;
+
+.text-gray{
+    color : $fontGray
+}
+.text-black {
+    color : $fontBlack
+}

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts

@@ -2,10 +2,10 @@ import axios from "axios";
 import { showMessage } from "./errorStatusCode"; // 引入状态码文件
 import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
 import { ElNotification } from "element-plus";
-
+const baseURL='/api'
 // 创建axios实例
 const instance = axios.create({
-  baseURL: "/api", // 设置API的基础URL
+  baseURL, // 设置API的基础URL
 });
 
 // 请求拦截器

+ 22 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts

@@ -0,0 +1,22 @@
+export function isValueEmpty(value: any): boolean {
+  if (value === null || value === undefined) {
+    return true;
+  }
+  if (typeof value === "string" && value.trim() === "") {
+    return true;
+  }
+  if (Array.isArray(value) && value.length === 0) {
+    return true;
+  }
+  if (
+    typeof value === "object" &&
+    !Array.isArray(value) &&
+    Object.keys(value).length === 0
+  ) {
+    return true;
+  }
+  if (typeof value === "symbol" && value.toString() === "Symbol()") {
+    return true;
+  }
+  return false;
+}

+ 2 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.json

@@ -23,7 +23,8 @@
     "noFallthroughCasesInSwitch": true,
     "paths": {
       "@/*": ["./src/*"]
-    }
+    },
+    "types": ["element-plus/global"]
   },
   "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
   "references": [{ "path": "./tsconfig.node.json" }]

+ 49 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ClueController.java

@@ -1,10 +1,18 @@
 package com.management.platform.controller;
 
 
+import com.management.platform.entity.Clue;
+import com.management.platform.service.ClueService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
+import java.util.List;
+
 /**
  * <p>
  *  前端控制器
@@ -16,6 +24,47 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RequestMapping("/clue")
 public class ClueController {
+    @Resource
+    private ClueService clueService;
+
+//    @RequestMapping("listClue")
+//    public Object list(@RequestBody Clue clue){
+//        clueService.getList(clue);
+//        return
+//    }
+    @RequestMapping("/inserANdUpdate")
+    public Object inserANdUpdate(@RequestBody Clue clue){
+        HttpRespMsg msg = new HttpRespMsg();
+        //操作前校验
+        if (null == clue.getClueName() || "" == clue.getClueName()){
+            msg.setError("线索名称不能为空");
+            return msg;
+        }
+        if (null == clue.getClueSourceId()){
+            msg.setError("线索来源不能为空");
+            return msg;
+        }
+        clue.setIsDelete(0);
+        if (null != clue.getId()){
+            //修改
+            clueService.update(clue);
+            msg.setMsg("操作成功");
+        }else {
+            //新增
+            clueService.insert(clue);
+            msg.setMsg("操作成功");
+        }
+        return  msg;
+    }
+
+
+    @RequestMapping("delete")
+    public Object delete(@RequestBody List<Integer> ids){
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.setMsg("操作成功");
+        clueService.isDelete(ids);
+        return msg;
+    }
 
 }
 

+ 18 - 4
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Clue.java

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.extension.activerecord.Model;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+import java.util.Date;
+
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
@@ -46,10 +48,15 @@ public class Clue extends Model<Clue> {
     private Integer clueSourceId;
 
     /**
-     * 联系方式
+     * 电话
+     */
+    @TableField("phone")
+    private String phone;
+    /**
+     * 邮箱
      */
-    @TableField("contact_information")
-    private String contactInformation;
+    @TableField("email")
+    private String email;
 
     /**
      * 客户行业
@@ -69,11 +76,12 @@ public class Clue extends Model<Clue> {
     @TableField("address")
     private String address;
 
+
     /**
      * 负责人id
      */
     @TableField("incharger_id")
-    private String inchargerId;
+    private Integer inchargerId;
 
     /**
      * 备注
@@ -117,6 +125,12 @@ public class Clue extends Model<Clue> {
     @TableField("plate5")
     private String plate5;
 
+    /**
+     * 创建时间
+     */
+    @TableField("create_time")
+    private Date createTime;
+
 
     @Override
     protected Serializable pkVal() {

+ 6 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/ClueService.java

@@ -3,6 +3,8 @@ package com.management.platform.service;
 import com.management.platform.entity.Clue;
 import com.baomidou.mybatisplus.extension.service.IService;
 
+import java.util.List;
+
 /**
  * <p>
  *  服务类
@@ -13,4 +15,8 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface ClueService extends IService<Clue> {
 
+    void insert(Clue clue);
+    void update(Clue clue);
+
+    void isDelete(List<Integer> ids);
 }

+ 58 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ClueServiceImpl.java

@@ -1,10 +1,19 @@
 package com.management.platform.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.management.platform.entity.Clue;
 import com.management.platform.mapper.ClueMapper;
 import com.management.platform.service.ClueService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.service.SysFunctionService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.beans.Transient;
+import java.util.Date;
+import java.util.List;
 
 /**
  * <p>
@@ -17,4 +26,53 @@ import org.springframework.stereotype.Service;
 @Service
 public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements ClueService {
 
+    @Autowired
+    private SysFunctionService sysFunctionService;
+    @Autowired
+    private ClueMapper clueMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void insert(Clue clue) {
+        setNull(clue);
+        clue.setCreateTime(new Date());
+        clueMapper.insert(clue);
+
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void update(Clue clue) {
+        setNull(clue);
+        clueMapper.updateById(clue);
+    }
+
+    private Clue setNull(Clue clue) {
+        if (clue.getPlate1() == ""){
+            clue.setPlate1(null);
+        }
+        if (clue.getPlate2() == ""){
+            clue.setPlate2(null);
+        }
+        if (clue.getPlate3() == ""){
+            clue.setPlate3(null);
+        }
+        if (clue.getPlate4() == ""){
+            clue.setPlate4(null);
+        }
+        if (clue.getPlate5() == ""){
+            clue.setPlate5(null);
+        }
+    return clue;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void isDelete(List<Integer> ids) {
+        UpdateWrapper<Clue> updateWrapper = new UpdateWrapper<>();
+        updateWrapper.in("id", ids);
+        Clue clue = new Clue();
+        clue.setIsDelete(1);
+        clueMapper.update(clue, updateWrapper);
+    }
 }

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 85730 - 0
fhKeeper/formulahousekeeper/management-crm/workTime.log


+ 60 - 54
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -12778,31 +12778,33 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                             item.put("groupName",groupName);
                             item.put("corpwxDeptId",corpwxDeptid);
                             item.put("department_name",departmentName);
-                            if(!taskGroup.isPresent()){
-                                item.put("planHour",0);
-                                item.put("realHour",0);
-                                item.put("realCost",0);
-                                item.put("overHour",0);
-                                item.put("normalHour",0);
-                                item.put("process","0%");
-                            }else {
-                                Optional<Map<String, Object>> first = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
-                                        && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId()) &&( Integer.valueOf(String.valueOf(r.get("deptId"))).equals(7459)||subDeptIds.contains( Integer.valueOf(String.valueOf(r.get("deptId")))))).findFirst();
-                                if(first.isPresent()){
-                                    item.put("planHour",first.get().get("planHour"));
-                                    item.put("realHour",first.get().get("realHour"));
-                                    item.put("realCost",first.get().get("realCost"));
-                                    item.put("overHour",first.get().get("overHour"));
-                                    item.put("normalHour",first.get().get("normalHour"));
-                                    item.put("process",first.get().get("process"));
+                            if(taskGroup.isPresent()){
+                                List<Map<String, Object>> mapList = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
+                                        && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId()) && (Integer.valueOf(String.valueOf(r.get("deptId"))).equals(7459) || subDeptIds.contains(Integer.valueOf(String.valueOf(r.get("deptId")))))).collect(Collectors.toList());
+                                if(mapList!=null&&mapList.size()>0){
+                                    item.put("planHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("planHour")))).sum());
+                                    item.put("realHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum());
+                                    item.put("realCost",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
+                                    item.put("overHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
+                                    item.put("normalHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum());
+                                    double realHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("realHour")))).sum();
+                                    double planHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("planHour")))).sum();
+                                    item.put("process",percentFormat.format(realHour/planHour));
                                 }else {
                                     item.put("planHour",0);
                                     item.put("realHour",0);
                                     item.put("realCost",0);
                                     item.put("overHour",0);
                                     item.put("normalHour",0);
-                                    item.put("process","0%");
+                                    item.put("process","0.00%");
                                 }
+                            }else {
+                                item.put("planHour",0);
+                                item.put("realHour",0);
+                                item.put("realCost",0);
+                                item.put("overHour",0);
+                                item.put("normalHour",0);
+                                item.put("process","0.00%");
                             }
                             itemList.add(item);
                         }
@@ -12821,31 +12823,33 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                             item.put("groupName",groupName);
                             item.put("corpwxDeptId",corpwxDeptid);
                             item.put("department_name",departmentName);
-                            if(!taskGroup.isPresent()){
-                                item.put("planHour",0);
-                                item.put("realHour",0);
-                                item.put("realCost",0);
-                                item.put("overHour",0);
-                                item.put("normalHour",0);
-                                item.put("process","0%");
-                            }else {
-                                Optional<Map<String, Object>> first = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
-                                        && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId()) &&( Integer.valueOf(String.valueOf(r.get("deptId"))).equals(7460)||subDeptIds.contains( Integer.valueOf(String.valueOf(r.get("deptId")))))).findFirst();
-                                if(first.isPresent()){
-                                    item.put("planHour",first.get().get("planHour"));
-                                    item.put("realHour",first.get().get("realHour"));
-                                    item.put("realCost",first.get().get("realCost"));
-                                    item.put("overHour",first.get().get("overHour"));
-                                    item.put("normalHour",first.get().get("normalHour"));
-                                    item.put("process",first.get().get("process"));
+                            if(taskGroup.isPresent()){
+                                List<Map<String, Object>> mapList = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
+                                        && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId()) && (Integer.valueOf(String.valueOf(r.get("deptId"))).equals(7460) || subDeptIds.contains(Integer.valueOf(String.valueOf(r.get("deptId")))))).collect(Collectors.toList());
+                                if(mapList!=null&&mapList.size()>0){
+                                    item.put("planHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("planHour")))).sum());
+                                    item.put("realHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum());
+                                    item.put("realCost",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
+                                    item.put("overHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
+                                    item.put("normalHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum());
+                                    double realHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("realHour")))).sum();
+                                    double planHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("planHour")))).sum();
+                                    item.put("process",percentFormat.format(realHour/planHour));
                                 }else {
                                     item.put("planHour",0);
                                     item.put("realHour",0);
                                     item.put("realCost",0);
                                     item.put("overHour",0);
                                     item.put("normalHour",0);
-                                    item.put("process","0%");
+                                    item.put("process","0.00%");
                                 }
+                            }else {
+                                item.put("planHour",0);
+                                item.put("realHour",0);
+                                item.put("realCost",0);
+                                item.put("overHour",0);
+                                item.put("normalHour",0);
+                                item.put("process","0.00%");
                             }
                             itemList.add(item);
                         }
@@ -12868,31 +12872,33 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                             item.put("groupName",groupName);
                             item.put("corpwxDeptId",corpwxDeptid);
                             item.put("department_name",departmentName);
-                            if(!taskGroup.isPresent()){
-                                item.put("planHour",0);
-                                item.put("realHour",0);
-                                item.put("realCost",0);
-                                item.put("overHour",0);
-                                item.put("normalHour",0);
-                                item.put("process","0%");
-                            }else {
-                                Optional<Map<String, Object>> first = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
-                                        && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId()) &&( Integer.valueOf(String.valueOf(r.get("deptId"))).equals(7458)||subDeptIds.contains( Integer.valueOf(String.valueOf(r.get("deptId")))))).findFirst();
-                                if(first.isPresent()){
-                                    item.put("planHour",first.get().get("planHour"));
-                                    item.put("realHour",first.get().get("realHour"));
-                                    item.put("realCost",first.get().get("realCost"));
-                                    item.put("overHour",first.get().get("overHour"));
-                                    item.put("normalHour",first.get().get("normalHour"));
-                                    item.put("process",first.get().get("process"));
+                            if(taskGroup.isPresent()){
+                                List<Map<String, Object>> mapList = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
+                                        && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId()) && (Integer.valueOf(String.valueOf(r.get("deptId"))).equals(7458) || subDeptIds.contains(Integer.valueOf(String.valueOf(r.get("deptId")))))).collect(Collectors.toList());
+                                if(mapList!=null&&mapList.size()>0){
+                                    item.put("planHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("planHour")))).sum());
+                                    item.put("realHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum());
+                                    item.put("realCost",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
+                                    item.put("overHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
+                                    item.put("normalHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum());
+                                    double realHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("realHour")))).sum();
+                                    double planHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("planHour")))).sum();
+                                    item.put("process",percentFormat.format(realHour/planHour));
                                 }else {
                                     item.put("planHour",0);
                                     item.put("realHour",0);
                                     item.put("realCost",0);
                                     item.put("overHour",0);
                                     item.put("normalHour",0);
-                                    item.put("process","0%");
+                                    item.put("process","0.00%");
                                 }
+                            }else {
+                                item.put("planHour",0);
+                                item.put("realHour",0);
+                                item.put("realCost",0);
+                                item.put("overHour",0);
+                                item.put("normalHour",0);
+                                item.put("process","0.00%");
                             }
                             itemList.add(item);
                         }

+ 18 - 16
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml

@@ -1890,22 +1890,24 @@
     </select>
 
     <select id="groupExpendProcessList" resultType="java.util.Map">
-        SELECT p.project_name AS projectName,p.id as projectId,tg.id as groupId,d.department_name,d.department_id as deptId,d.corpwx_deptid AS corpwxDeptId,tg.name AS groupName,
-        IFNULL(SUM(CASE WHEN (r.create_date BETWEEN #{startDate} AND #{endDate} AND r.state=1 AND r.project_id IS NOT NULL) THEN r.working_time ELSE NULL END),0) AS realHour,
-        IFNULL(SUM(CASE WHEN (r.create_date BETWEEN #{startDate} AND #{endDate} AND r.state=1 AND r.project_id IS NOT NULL) THEN r.cost ELSE NULL END),0) AS realCost,
-        IFNULL(SUM(CASE WHEN (r.create_date BETWEEN #{startDate} AND #{endDate} AND r.state=1 AND r.project_id IS NOT NULL) THEN r.overtime_hours ELSE NULL END),0) AS overHour,
-        IFNULL(SUM(CASE WHEN (r.create_date BETWEEN #{startDate} AND #{endDate} AND r.state=1 AND r.project_id IS NOT NULL) THEN r.working_time ELSE NULL END)
-              -SUM(CASE WHEN (r.create_date BETWEEN #{startDate} AND #{endDate} AND r.state=1 AND r.project_id IS NOT NULL) THEN r.overtime_hours ELSE NULL END),0) AS normalHour,
-        IFNULL(tg.man_day*8,0) AS planHour,
-        CONCAT(ROUND((IFNULL(SUM(CASE WHEN (r.create_date BETWEEN #{startDate} AND #{endDate} AND r.state=1 AND r.project_id IS NOT NULL) THEN r.working_time ELSE NULL END),0)/IFNULL(tg.man_day*8,0))*100,2),'%') as process
-        FROM  task_group tg
-        LEFT JOIN task t ON t.group_id=tg.id
-        LEFT JOIN report r  ON r.task_id=t.id
-        LEFT JOIN project p ON p.id=r.project_id
-        LEFT JOIN department d ON d.department_id=r.dept_id
-        WHERE r.company_id=#{companyId}  AND tg.name IN ('生产部电气','生产部车间','工程部现场安装施工','工程部配合调试','研发部工艺设计','研发部结构设计','研发部BIM设计','研发部电气设计','研发部工艺调试验收','研发部电气调试验收')
+        SELECT p.project_name AS projectName,p.id AS projectId,tg.id AS groupId,d.department_name,d.department_id AS deptId,d.corpwx_deptid AS corpwxDeptId,tg.name AS groupName,
+        IFNULL((SELECT SUM(working_time) FROM report  WHERE task_id=t.id AND dept_id=d.department_id AND create_date BETWEEN #{startDate} AND #{endDate} AND state=1 AND project_id IS NOT NULL),0) AS realHour,
+        IFNULL((SELECT SUM(cost) FROM report  WHERE task_id=t.id AND dept_id=d.department_id AND create_date BETWEEN #{startDate} AND #{endDate} AND state=1 AND project_id IS NOT NULL) ,0) AS realCost,
+        IFNULL((SELECT SUM(overtime_hours) FROM report  WHERE task_id=t.id AND dept_id=d.department_id AND create_date BETWEEN #{startDate} AND #{endDate} AND state=1 AND project_id IS NOT NULL ),0) AS overHour,
+        (IFNULL((SELECT SUM(working_time) FROM report  WHERE task_id=t.id AND dept_id=d.department_id AND create_date BETWEEN #{startDate} AND #{endDate} AND state=1 AND project_id IS NOT NULL),0)
+        -IFNULL((SELECT SUM(overtime_hours) FROM report  WHERE task_id=t.id AND dept_id=d.department_id AND create_date BETWEEN #{startDate} AND #{endDate} AND state=1 AND project_id IS NOT NULL ),0)) as normalHour,
+        IFNULL(SUM(te.plan_hours),0) AS planHour
+        FROM task_executor te
+        LEFT JOIN task t ON t.id=te.task_id
+        LEFT JOIN USER u ON te.executor_id=u.id
+        LEFT JOIN task_group tg ON tg.id=t.group_id
+        LEFT JOIN project p ON p.id=tg.project_id
+        LEFT JOIN department d ON d.department_id=u.department_id
+        WHERE u.company_id=#{companyId}
+        AND tg.name IN ('生产部电气','生产部车间','工程部现场安装施工','工程部配合调试','研发部工艺设计','研发部结构设计','研发部BIM设计','研发部电气设计','研发部工艺调试验收','研发部电气调试验收')
+        AND d.department_id IN ( 7458, 7459, 7902, 7903, 7460, 7813, 7814, 7815, 7816)
         <if test="userId!=null and userId!=''">
-            and r.creator_id=#{userId}
+            and t.executor_id=#{userId}
         </if>
         <if test="list!=null and list.size()>0">
             and d.department_id in
@@ -1919,7 +1921,7 @@
                 #{item}
             </foreach>
         </if>
-        GROUP BY tg.id ORDER BY p.id,d.department_id
+        GROUP BY p.id,tg.id,d.department_id ORDER BY p.id,d.department_id
     </select>
 
     <select id="projectExpendProcessList" resultType="java.util.Map">

+ 1 - 0
fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue

@@ -29,6 +29,7 @@
 
         <!--列表-->
         <el-table :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
+            <el-table-column prop="id" label="企业ID" min-width="180" align="center"></el-table-column>
             <el-table-column prop="companyName" label="企业名称" min-width="240" align="center">
             </el-table-column>
             <el-table-column prop="setMeal" label="是否签约"  min-width="80" align="center">