Browse Source

被服详情

seimin 10 months ago
parent
commit
046a5838c7

+ 1 - 2
index.html

@@ -2,9 +2,8 @@
2 2
 <html lang="en">
3 3
   <head>
4 4
     <meta charset="UTF-8">
5
-    <link rel="icon" href="/favicon.ico">
6 5
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
-    <title>Vite App</title>
6
+    <title>被服详情</title>
8 7
   </head>
9 8
   <body>
10 9
     <div id="app"></div>

File diff suppressed because it is too large
+ 1255 - 43
package-lock.json


+ 8 - 1
package.json

@@ -2,22 +2,29 @@
2 2
   "name": "zy-spreadsheet",
3 3
   "version": "0.0.0",
4 4
   "private": true,
5
-  "type": "module",
6 5
   "scripts": {
7 6
     "dev": "vite",
8 7
     "build": "vite build",
8
+    "development": "node ./upload/development.js",
9
+    "production": "node ./upload/production.js",
9 10
     "preview": "vite preview"
10 11
   },
11 12
   "dependencies": {
12 13
     "@antv/s2": "^2.0.0-next.21",
13 14
     "@antv/s2-vue": "^2.0.0-next.13",
14 15
     "ant-design-vue": "^3.2.20",
16
+    "axios": "^0.28.1",
17
+    "lodash": "^4.17.21",
15 18
     "pinia": "^2.1.7",
16 19
     "vue": "^3.4.21",
17 20
     "vue-router": "^4.3.0"
18 21
   },
19 22
   "devDependencies": {
20 23
     "@vitejs/plugin-vue": "^5.0.4",
24
+    "less": "^4.2.0",
25
+    "shelljs": "^0.8.5",
26
+    "ssh2-sftp-client": "^10.0.3",
27
+    "unplugin-vue-components": "^0.27.0",
21 28
     "vite": "^5.2.8"
22 29
   }
23 30
 }

+ 0 - 74
src/App.vue

@@ -1,85 +1,11 @@
1 1
 <script setup>
2 2
 import { RouterLink, RouterView } from 'vue-router'
3
-import HelloWorld from './components/HelloWorld.vue'
4 3
 </script>
5 4
 
6 5
 <template>
7
-  <header>
8
-    <img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
9
-
10
-    <div class="wrapper">
11
-      <HelloWorld msg="You did it!" />
12
-
13
-      <nav>
14
-        <RouterLink to="/">Home</RouterLink>
15
-        <RouterLink to="/about">About</RouterLink>
16
-      </nav>
17
-    </div>
18
-  </header>
19
-
20 6
   <RouterView />
21 7
 </template>
22 8
 
23 9
 <style scoped>
24
-header {
25
-  line-height: 1.5;
26
-  max-height: 100vh;
27
-}
28
-
29
-.logo {
30
-  display: block;
31
-  margin: 0 auto 2rem;
32
-}
33
-
34
-nav {
35
-  width: 100%;
36
-  font-size: 12px;
37
-  text-align: center;
38
-  margin-top: 2rem;
39
-}
40
-
41
-nav a.router-link-exact-active {
42
-  color: var(--color-text);
43
-}
44
-
45
-nav a.router-link-exact-active:hover {
46
-  background-color: transparent;
47
-}
48
-
49
-nav a {
50
-  display: inline-block;
51
-  padding: 0 1rem;
52
-  border-left: 1px solid var(--color-border);
53
-}
54
-
55
-nav a:first-of-type {
56
-  border: 0;
57
-}
58
-
59
-@media (min-width: 1024px) {
60
-  header {
61
-    display: flex;
62
-    place-items: center;
63
-    padding-right: calc(var(--section-gap) / 2);
64
-  }
65
-
66
-  .logo {
67
-    margin: 0 2rem 0 0;
68
-  }
69
-
70
-  header .wrapper {
71
-    display: flex;
72
-    place-items: flex-start;
73
-    flex-wrap: wrap;
74
-  }
75
-
76
-  nav {
77
-    text-align: left;
78
-    margin-left: -1rem;
79
-    font-size: 1rem;
80 10
 
81
-    padding: 1rem 0;
82
-    margin-top: 1rem;
83
-  }
84
-}
85 11
 </style>

+ 0 - 1
src/assets/logo.svg

@@ -1 +0,0 @@
1
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

+ 0 - 34
src/assets/main.css

@@ -1,35 +1 @@
1 1
 @import './base.css';
2
-
3
-#app {
4
-  max-width: 1280px;
5
-  margin: 0 auto;
6
-  padding: 2rem;
7
-  font-weight: normal;
8
-}
9
-
10
-a,
11
-.green {
12
-  text-decoration: none;
13
-  color: hsla(160, 100%, 37%, 1);
14
-  transition: 0.4s;
15
-  padding: 3px;
16
-}
17
-
18
-@media (hover: hover) {
19
-  a:hover {
20
-    background-color: hsla(160, 100%, 37%, 0.2);
21
-  }
22
-}
23
-
24
-@media (min-width: 1024px) {
25
-  body {
26
-    display: flex;
27
-    place-items: center;
28
-  }
29
-
30
-  #app {
31
-    display: grid;
32
-    grid-template-columns: 1fr 1fr;
33
-    padding: 0 2rem;
34
-  }
35
-}

+ 18 - 0
src/assets/theme.less

@@ -0,0 +1,18 @@
1
+/**
2
+  ant design vue 主题样式全局变量
3
+*/
4
+
5
+@primary-color: #49b856; // 全局主色
6
+@link-color: #1890ff; // 链接色
7
+@success-color: #52c41a; // 成功色
8
+@warning-color: #faad14; // 警告色
9
+@error-color: #f5222d; // 错误色
10
+@font-size-base: 14px; // 主字号
11
+@heading-color: rgba(0, 0, 0, 0.85); // 标题色
12
+@text-color: rgba(0, 0, 0, 0.65); // 主文本色
13
+@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
14
+@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
15
+@border-radius-base: 2px; // 组件/浮层圆角
16
+@border-color-base: #d9d9d9; // 边框色
17
+@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层阴影
18
+

+ 0 - 44
src/components/HelloWorld.vue

@@ -1,44 +0,0 @@
1
-<script setup>
2
-defineProps({
3
-  msg: {
4
-    type: String,
5
-    required: true
6
-  }
7
-})
8
-</script>
9
-
10
-<template>
11
-  <div class="greetings">
12
-    <h1 class="green">{{ msg }}</h1>
13
-    <h3>
14
-      You’ve successfully created a project with
15
-      <a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
16
-      <a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
17
-    </h3>
18
-  </div>
19
-</template>
20
-
21
-<style scoped>
22
-h1 {
23
-  font-weight: 500;
24
-  font-size: 2.6rem;
25
-  position: relative;
26
-  top: -10px;
27
-}
28
-
29
-h3 {
30
-  font-size: 1.2rem;
31
-}
32
-
33
-.greetings h1,
34
-.greetings h3 {
35
-  text-align: center;
36
-}
37
-
38
-@media (min-width: 1024px) {
39
-  .greetings h1,
40
-  .greetings h3 {
41
-    text-align: left;
42
-  }
43
-}
44
-</style>

+ 0 - 88
src/components/TheWelcome.vue

@@ -1,88 +0,0 @@
1
-<script setup>
2
-import WelcomeItem from './WelcomeItem.vue'
3
-import DocumentationIcon from './icons/IconDocumentation.vue'
4
-import ToolingIcon from './icons/IconTooling.vue'
5
-import EcosystemIcon from './icons/IconEcosystem.vue'
6
-import CommunityIcon from './icons/IconCommunity.vue'
7
-import SupportIcon from './icons/IconSupport.vue'
8
-</script>
9
-
10
-<template>
11
-  <WelcomeItem>
12
-    <template #icon>
13
-      <DocumentationIcon />
14
-    </template>
15
-    <template #heading>Documentation</template>
16
-
17
-    Vue’s
18
-    <a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
19
-    provides you with all information you need to get started.
20
-  </WelcomeItem>
21
-
22
-  <WelcomeItem>
23
-    <template #icon>
24
-      <ToolingIcon />
25
-    </template>
26
-    <template #heading>Tooling</template>
27
-
28
-    This project is served and bundled with
29
-    <a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
30
-    recommended IDE setup is
31
-    <a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
32
-    <a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
33
-    you need to test your components and web pages, check out
34
-    <a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
35
-    <a href="https://on.cypress.io/component" target="_blank" rel="noopener"
36
-      >Cypress Component Testing</a
37
-    >.
38
-
39
-    <br />
40
-
41
-    More instructions are available in <code>README.md</code>.
42
-  </WelcomeItem>
43
-
44
-  <WelcomeItem>
45
-    <template #icon>
46
-      <EcosystemIcon />
47
-    </template>
48
-    <template #heading>Ecosystem</template>
49
-
50
-    Get official tools and libraries for your project:
51
-    <a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
52
-    <a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
53
-    <a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
54
-    <a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
55
-    you need more resources, we suggest paying
56
-    <a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
57
-    a visit.
58
-  </WelcomeItem>
59
-
60
-  <WelcomeItem>
61
-    <template #icon>
62
-      <CommunityIcon />
63
-    </template>
64
-    <template #heading>Community</template>
65
-
66
-    Got stuck? Ask your question on
67
-    <a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
68
-    Discord server, or
69
-    <a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
70
-      >StackOverflow</a
71
-    >. You should also subscribe to
72
-    <a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
73
-    the official
74
-    <a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
75
-    twitter account for latest news in the Vue world.
76
-  </WelcomeItem>
77
-
78
-  <WelcomeItem>
79
-    <template #icon>
80
-      <SupportIcon />
81
-    </template>
82
-    <template #heading>Support Vue</template>
83
-
84
-    As an independent project, Vue relies on community backing for its sustainability. You can help
85
-    us by
86
-    <a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
87
-  </WelcomeItem>
88
-</template>

+ 0 - 86
src/components/WelcomeItem.vue

@@ -1,86 +0,0 @@
1
-<template>
2
-  <div class="item">
3
-    <i>
4
-      <slot name="icon"></slot>
5
-    </i>
6
-    <div class="details">
7
-      <h3>
8
-        <slot name="heading"></slot>
9
-      </h3>
10
-      <slot></slot>
11
-    </div>
12
-  </div>
13
-</template>
14
-
15
-<style scoped>
16
-.item {
17
-  margin-top: 2rem;
18
-  display: flex;
19
-  position: relative;
20
-}
21
-
22
-.details {
23
-  flex: 1;
24
-  margin-left: 1rem;
25
-}
26
-
27
-i {
28
-  display: flex;
29
-  place-items: center;
30
-  place-content: center;
31
-  width: 32px;
32
-  height: 32px;
33
-  color: var(--color-text);
34
-}
35
-
36
-h3 {
37
-  font-size: 1.2rem;
38
-  font-weight: 500;
39
-  margin-bottom: 0.4rem;
40
-  color: var(--color-heading);
41
-}
42
-
43
-@media (min-width: 1024px) {
44
-  .item {
45
-    margin-top: 0;
46
-    padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
47
-  }
48
-
49
-  i {
50
-    top: calc(50% - 25px);
51
-    left: -26px;
52
-    position: absolute;
53
-    border: 1px solid var(--color-border);
54
-    background: var(--color-background);
55
-    border-radius: 8px;
56
-    width: 50px;
57
-    height: 50px;
58
-  }
59
-
60
-  .item:before {
61
-    content: ' ';
62
-    border-left: 1px solid var(--color-border);
63
-    position: absolute;
64
-    left: 0;
65
-    bottom: calc(50% + 25px);
66
-    height: calc(50% - 25px);
67
-  }
68
-
69
-  .item:after {
70
-    content: ' ';
71
-    border-left: 1px solid var(--color-border);
72
-    position: absolute;
73
-    left: 0;
74
-    top: calc(50% + 25px);
75
-    height: calc(50% - 25px);
76
-  }
77
-
78
-  .item:first-of-type:before {
79
-    display: none;
80
-  }
81
-
82
-  .item:last-of-type:after {
83
-    display: none;
84
-  }
85
-}
86
-</style>

File diff suppressed because it is too large
+ 0 - 7
src/components/icons/IconCommunity.vue


File diff suppressed because it is too large
+ 0 - 7
src/components/icons/IconDocumentation.vue


File diff suppressed because it is too large
+ 0 - 7
src/components/icons/IconEcosystem.vue


+ 0 - 7
src/components/icons/IconSupport.vue

@@ -1,7 +0,0 @@
1
-<template>
2
-  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
3
-    <path
4
-      d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
5
-    />
6
-  </svg>
7
-</template>

+ 0 - 19
src/components/icons/IconTooling.vue

@@ -1,19 +0,0 @@
1
-<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
2
-<template>
3
-  <svg
4
-    xmlns="http://www.w3.org/2000/svg"
5
-    xmlns:xlink="http://www.w3.org/1999/xlink"
6
-    aria-hidden="true"
7
-    role="img"
8
-    class="iconify iconify--mdi"
9
-    width="24"
10
-    height="24"
11
-    preserveAspectRatio="xMidYMid meet"
12
-    viewBox="0 0 24 24"
13
-  >
14
-    <path
15
-      d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
16
-      fill="currentColor"
17
-    ></path>
18
-  </svg>
19
-</template>

+ 15 - 0
src/http/api.js

@@ -0,0 +1,15 @@
1
+import { get, post, postExport } from "@/http/http.js"
2
+
3
+/**
4
+ * 全院统计详情/科室统计详情
5
+ */
6
+export function api_clothes_details(model, data){
7
+  return post("/report/clothes/details/" + model, data);
8
+}
9
+
10
+/**
11
+ * 全院统计导出/科室统计导出
12
+ */
13
+export function api_export(model, data){
14
+  return postExport("/report/export/" + model, data);
15
+}

+ 44 - 0
src/http/http.js

@@ -0,0 +1,44 @@
1
+import axios from 'axios';
2
+
3
+export const path = `${location.origin}/service`
4
+
5
+const http = axios.create({
6
+  baseURL: path,
7
+});
8
+
9
+// 添加响应拦截器
10
+http.interceptors.response.use(function (response) {
11
+  return response.data;
12
+}, function (error) {
13
+  return Promise.reject(error);
14
+});
15
+
16
+// get方法
17
+export function get(url) {
18
+  url = path + url;
19
+  return http({
20
+    method: 'get',
21
+    url,
22
+  });
23
+}
24
+
25
+// post方法
26
+export function post(url, data = {}) {
27
+  url = path + url;
28
+  return http({
29
+    method: 'post',
30
+    url,
31
+    data,
32
+  });
33
+}
34
+
35
+// postExport方法
36
+export function postExport(url, data = {}) {
37
+  url = path + url;
38
+  return http({
39
+    method: 'post',
40
+    url,
41
+    data,
42
+    responseType:'arraybuffer'
43
+  });
44
+}

+ 9 - 0
src/main.js

@@ -6,8 +6,17 @@ import { createPinia } from 'pinia'
6 6
 import App from './App.vue'
7 7
 import router from './router'
8 8
 
9
+import { Modal, message } from 'ant-design-vue';
10
+import 'ant-design-vue/es/message/style/css';
11
+import 'ant-design-vue/es/modal/style/css';
12
+
13
+
14
+
9 15
 const app = createApp(App)
10 16
 
17
+app.config.globalProperties.$Modal = Modal;
18
+app.config.globalProperties.$message = message;
19
+
11 20
 app.use(createPinia())
12 21
 app.use(router)
13 22
 

+ 16 - 8
src/router/index.js

@@ -1,22 +1,30 @@
1
-import { createRouter, createWebHistory } from 'vue-router'
2
-import HomeView from '../views/HomeView.vue'
1
+import { createRouter, createWebHashHistory } from 'vue-router'
2
+import Home from '../views/Home.vue'
3 3
 
4 4
 const router = createRouter({
5
-  history: createWebHistory(import.meta.env.BASE_URL),
5
+  history: createWebHashHistory(import.meta.env.BASE_URL),
6 6
   routes: [
7 7
     {
8 8
       path: '/',
9 9
       name: 'home',
10
-      component: HomeView
10
+      component: Home
11 11
     },
12 12
     {
13
-      path: '/about',
14
-      name: 'about',
13
+      path: '/quiltWashingDepartment/:deptId/:type/:startTime/:endTime',
14
+      name: 'quiltWashingDepartment',
15 15
       // route level code-splitting
16 16
       // this generates a separate chunk (About.[hash].js) for this route
17 17
       // which is lazy-loaded when the route is visited.
18
-      component: () => import('../views/AboutView.vue')
19
-    }
18
+      component: () => import('../views/QuiltWashingDepartment.vue')
19
+    },
20
+    {
21
+      path: '/quiltWashingHospital/:dateStr/:type',
22
+      name: 'quiltWashingHospital',
23
+      // route level code-splitting
24
+      // this generates a separate chunk (About.[hash].js) for this route
25
+      // which is lazy-loaded when the route is visited.
26
+      component: () => import('../views/QuiltWashingHospital.vue')
27
+    },
20 28
   ]
21 29
 })
22 30
 

+ 0 - 15
src/views/AboutView.vue

@@ -1,15 +0,0 @@
1
-<template>
2
-  <div class="about">
3
-    <h1>This is an about page</h1>
4
-  </div>
5
-</template>
6
-
7
-<style>
8
-@media (min-width: 1024px) {
9
-  .about {
10
-    min-height: 100vh;
11
-    display: flex;
12
-    align-items: center;
13
-  }
14
-}
15
-</style>

+ 8 - 0
src/views/Home.vue

@@ -0,0 +1,8 @@
1
+<script setup>
2
+</script>
3
+
4
+<template>
5
+  <main>
6
+
7
+  </main>
8
+</template>

+ 0 - 9
src/views/HomeView.vue

@@ -1,9 +0,0 @@
1
-<script setup>
2
-import TheWelcome from '../components/TheWelcome.vue'
3
-</script>
4
-
5
-<template>
6
-  <main>
7
-    <TheWelcome />
8
-  </main>
9
-</template>

+ 177 - 0
src/views/QuiltWashingDepartment.vue

@@ -0,0 +1,177 @@
1
+<script setup>
2
+  import { SheetComponent } from "@antv/s2-vue";
3
+  import { getPalette, generatePalette } from '@antv/s2';
4
+  import { shallowRef, reactive, onMounted, ref, onUnmounted } from "vue";
5
+  import "@antv/s2-vue/dist/style.min.css";
6
+  import { api_clothes_details, api_export } from '@/http/api.js'
7
+
8
+  import { message } from 'ant-design-vue';
9
+
10
+  // import { debounce } from 'lodash';
11
+
12
+  import { useRoute } from 'vue-router';
13
+
14
+  const route = useRoute();
15
+  const deptId = route.params.deptId;
16
+  const type = route.params.type;
17
+  const startTime = route.params.startTime;
18
+  const endTime = route.params.endTime;
19
+
20
+  const rawDataCfg = {
21
+    "fields": {
22
+      "rows": [
23
+        "dateStr"
24
+      ],
25
+      "columns": [
26
+        "type",
27
+        "unitPrice",
28
+      ],
29
+      "values": [
30
+        "count",
31
+      ],
32
+    },
33
+    "meta": [
34
+      {
35
+        "field": "count",
36
+        "name": "数值"
37
+      },
38
+      {
39
+        "field": "dateStr",
40
+        "name": "日期"
41
+      },
42
+      {
43
+        "field": "type",
44
+        "name": "被服类型"
45
+      },
46
+      {
47
+        "field": "unitPrice",
48
+        "name": "单价"
49
+      }
50
+    ],
51
+    "data": [],
52
+  };
53
+
54
+  const rawOptions = {
55
+    width: document.documentElement.clientWidth,
56
+    height: document.documentElement.clientHeight - 50,
57
+    frozen: {
58
+      // 行头冻结数量
59
+      rowCount: 1,
60
+      // 列头冻结数量
61
+      colCount: 1,
62
+      // 列尾冻结数量
63
+      trailingRowCount: 1,
64
+      // 列尾冻结数量
65
+      trailingColCount: 1,
66
+    },
67
+  };
68
+
69
+  // dataCfg 数据字段较多,建议使用 shallow, 如果有数据更改直接替换整个对象
70
+  const dataCfg = shallowRef(rawDataCfg);
71
+  const options = reactive(rawOptions);
72
+  const dept = ref('');
73
+
74
+  const s2 = shallowRef();
75
+
76
+  // 主题色
77
+  function generateTheme(){
78
+    // 主题色
79
+    const themeColor = '#49b856';
80
+
81
+    // 使用内置的 colorful 色板作为参考色板
82
+    // 根据风格差异,你也可以选择 default、gray 作为参考色板
83
+    const palette = getPalette('colorful');
84
+
85
+    // 使用参考色板 & 主题色值生成新色板
86
+    const newPalette = generatePalette({ ...palette, brandColor: themeColor });
87
+
88
+    // 使用新色板设置主题
89
+    s2.value.instance.setThemeCfg({
90
+      palette: newPalette,
91
+    });
92
+  }
93
+
94
+  // 获取全院统计详情
95
+  function clothesHospitalDetail(){
96
+    const hide = message.loading('加载中..');
97
+    let postData = {
98
+      deptId,
99
+      type,
100
+      startTime,
101
+      endTime,
102
+    };
103
+    api_clothes_details('dept', postData).then(res => {
104
+      hide();
105
+      let data = res.list || [];
106
+      dataCfg.value = {...dataCfg.value, ...{ data }};
107
+      dept.value = res.dept || '';
108
+    })
109
+  }
110
+
111
+  // 修改窗口大小
112
+  // const debounceHandleResize = debounce(() => {
113
+  //   s2.value.instance.changeSheetSize(document.documentElement.clientWidth, document.documentElement.clientHeight - 50)
114
+  //   s2.value.instance.render(false)
115
+  // }, 200)
116
+
117
+  // 导出
118
+  const loading = ref(false);
119
+  function exportAll(){
120
+    const hide = message.loading('加载中..');
121
+    loading.value = true;
122
+    let postData = {
123
+      deptId,
124
+      type,
125
+      startTime,
126
+      endTime,
127
+    };
128
+    api_export('clothesDeptDetails', postData).then(res => {
129
+      hide();
130
+      loading.value = false;
131
+      let file = new Blob([res], {
132
+        type: "application/vnd.ms-excel",
133
+      });
134
+      //trick to download store a file having its URL
135
+      let fileURL = URL.createObjectURL(file);
136
+      let a = document.createElement("a");
137
+      a.href = fileURL;
138
+      a.target = "_blank";
139
+      a.download = "详细科室报表.xls";
140
+      document.body.appendChild(a);
141
+      a.click();
142
+    })
143
+  }
144
+
145
+  onUnmounted(() => {
146
+    // window.removeEventListener('resize', debounceHandleResize);
147
+  });
148
+
149
+  onMounted(() => {
150
+    // window.addEventListener('resize', debounceHandleResize);
151
+    generateTheme();
152
+    clothesHospitalDetail();
153
+  })
154
+</script>
155
+
156
+<template>
157
+  <div class="head">{{dept}}<a-button type="primary" class="exportAll" @click="exportAll" :loading="loading">导出</a-button></div>
158
+  <SheetComponent v-show="dataCfg.data.length" :dataCfg="dataCfg" :options="options" ref="s2" />
159
+</template>
160
+
161
+<style scoped lang="less">
162
+.head{
163
+  height: 50px;
164
+  font-size: 22px;
165
+  display: flex;
166
+  justify-content: center;
167
+  align-items: center;
168
+  color: #49b856;
169
+  position: relative;
170
+  .exportAll{
171
+    position: absolute;
172
+    top: 50%;
173
+    right: 8px;
174
+    transform:translateY(-50%);
175
+  }
176
+}
177
+</style>

+ 171 - 0
src/views/QuiltWashingHospital.vue

@@ -0,0 +1,171 @@
1
+<script setup>
2
+  import { SheetComponent } from "@antv/s2-vue";
3
+  import { getPalette, generatePalette } from '@antv/s2';
4
+  import { shallowRef, reactive, onMounted, ref, onUnmounted } from "vue";
5
+  import "@antv/s2-vue/dist/style.min.css";
6
+  import { api_clothes_details, api_export } from '@/http/api.js'
7
+
8
+  import { message } from 'ant-design-vue';
9
+
10
+  // import { debounce } from 'lodash';
11
+
12
+  import { useRoute } from 'vue-router';
13
+
14
+  const route = useRoute();
15
+  const dateStr = route.params.dateStr;
16
+  const type = route.params.type;
17
+
18
+  const rawDataCfg = {
19
+    "fields": {
20
+      "rows": [
21
+        "dept"
22
+      ],
23
+      "columns": [
24
+        "type",
25
+        "unitPrice",
26
+      ],
27
+      "values": [
28
+        "count",
29
+      ],
30
+    },
31
+    "meta": [
32
+      {
33
+        "field": "count",
34
+        "name": "数值"
35
+      },
36
+      {
37
+        "field": "dept",
38
+        "name": "科室"
39
+      },
40
+      {
41
+        "field": "type",
42
+        "name": "被服类型"
43
+      },
44
+      {
45
+        "field": "unitPrice",
46
+        "name": "单价"
47
+      }
48
+    ],
49
+    "data": [],
50
+  };
51
+
52
+  const rawOptions = {
53
+    width: document.documentElement.clientWidth,
54
+    height: document.documentElement.clientHeight - 50,
55
+    frozen: {
56
+      // 行头冻结数量
57
+      rowCount: 1,
58
+      // 列头冻结数量
59
+      colCount: 1,
60
+      // 列尾冻结数量
61
+      trailingRowCount: 1,
62
+      // 列尾冻结数量
63
+      trailingColCount: 1,
64
+    },
65
+  };
66
+
67
+  // dataCfg 数据字段较多,建议使用 shallow, 如果有数据更改直接替换整个对象
68
+  const dataCfg = shallowRef(rawDataCfg);
69
+  const options = reactive(rawOptions);
70
+  const date = ref('');
71
+
72
+  const s2 = shallowRef();
73
+
74
+  // 主题色
75
+  function generateTheme(){
76
+    // 主题色
77
+    const themeColor = '#49b856';
78
+
79
+    // 使用内置的 colorful 色板作为参考色板
80
+    // 根据风格差异,你也可以选择 default、gray 作为参考色板
81
+    const palette = getPalette('colorful');
82
+
83
+    // 使用参考色板 & 主题色值生成新色板
84
+    const newPalette = generatePalette({ ...palette, brandColor: themeColor });
85
+
86
+    // 使用新色板设置主题
87
+    s2.value.instance.setThemeCfg({
88
+      palette: newPalette,
89
+    });
90
+  }
91
+
92
+  // 获取全院统计详情
93
+  function clothesHospitalDetail(){
94
+    const hide = message.loading('加载中..');
95
+    let postData = {
96
+      dateStr,
97
+      type,
98
+    };
99
+    api_clothes_details('hospital', postData).then(res => {
100
+      hide();
101
+      let data = res.list || [];
102
+      dataCfg.value = {...dataCfg.value, ...{ data }};
103
+      date.value = res.date || '';
104
+    })
105
+  }
106
+
107
+  // 修改窗口大小
108
+  // const debounceHandleResize = debounce(() => {
109
+  //   s2.value.instance.changeSheetSize(document.documentElement.clientWidth, document.documentElement.clientHeight - 50)
110
+  //   s2.value.instance.render(false)
111
+  // }, 200)
112
+
113
+  // 导出
114
+  const loading = ref(false);
115
+  function exportAll(){
116
+    const hide = message.loading('加载中..');
117
+    loading.value = true;
118
+    let postData = {
119
+      dateStr,
120
+      type,
121
+    };
122
+    api_export('clothesHosDetails', postData).then(res => {
123
+      hide();
124
+      loading.value = false;
125
+      let file = new Blob([res], {
126
+        type: "application/vnd.ms-excel",
127
+      });
128
+      //trick to download store a file having its URL
129
+      let fileURL = URL.createObjectURL(file);
130
+      let a = document.createElement("a");
131
+      a.href = fileURL;
132
+      a.target = "_blank";
133
+      a.download = "详细院区报表.xls";
134
+      document.body.appendChild(a);
135
+      a.click();
136
+    })
137
+  }
138
+
139
+  onUnmounted(() => {
140
+    // window.removeEventListener('resize', debounceHandleResize);
141
+  });
142
+
143
+  onMounted(() => {
144
+    // window.addEventListener('resize', debounceHandleResize);
145
+    generateTheme();
146
+    clothesHospitalDetail();
147
+  })
148
+</script>
149
+
150
+<template>
151
+  <div class="head">{{date}}被服详情<a-button type="primary" class="exportAll" @click="exportAll" :loading="loading">导出</a-button></div>
152
+  <SheetComponent v-show="dataCfg.data.length" :dataCfg="dataCfg" :options="options" ref="s2" />
153
+</template>
154
+
155
+<style scoped lang="less">
156
+.head{
157
+  height: 50px;
158
+  font-size: 22px;
159
+  display: flex;
160
+  justify-content: center;
161
+  align-items: center;
162
+  color: #49b856;
163
+  position: relative;
164
+  .exportAll{
165
+    position: absolute;
166
+    top: 50%;
167
+    right: 8px;
168
+    transform:translateY(-50%);
169
+  }
170
+}
171
+</style>

+ 50 - 0
upload/development.js

@@ -0,0 +1,50 @@
1
+const shell = require('shelljs')
2
+const path = require('path');
3
+const config = {
4
+  ip: "192.168.3.108", // ssh地址
5
+  username: "root", // ssh 用户名
6
+  port: 22,      //端口
7
+  password: "123456", // ssh 密码
8
+  path: '/home/itsm/project/spreadSheet', // 上传地址,删除地址
9
+  buildPath: '../dist/spreadSheet' // 本地打包后文件地址
10
+}
11
+let Client = require('ssh2-sftp-client');
12
+
13
+function connectSSh() {
14
+  let sftp = new Client();
15
+  sftp.connect({
16
+    host: config.ip,
17
+    port: config.port,
18
+    username: config.username,
19
+    password: config.password
20
+  }).then(() => {
21
+    console.log("-----先执行删除服务器文件-----")
22
+    return sftp.rmdir(config.path, true);
23
+  }).then(() => {
24
+    // 上传文件
25
+    console.log("-----开始上传-----")
26
+    return sftp.uploadDir(path.resolve(__dirname, config.buildPath), config.path);
27
+  }).then((data) => {
28
+    console.log("-----上传完成-----");
29
+    sftp.end();
30
+  }).catch((err) => {
31
+    console.log(err, '-----失败-----');
32
+    sftp.end();
33
+  });
34
+}
35
+function runTask() {
36
+  //打包完成
37
+  console.log(`当前NodeJs版本是${process.version}`);
38
+  const isNodeVersionLt17 = parseInt(process.version.slice(1)) < 17;
39
+  const execStr = isNodeVersionLt17 ? 'npm run build' : 'npm run build';
40
+  if (shell.exec(execStr).code == 0) {
41
+    // if (shell.exec(`find dist -name '*.js' -print0 | xargs -0 gzip -k`).code == 0) {
42
+      // if (shell.exec(`find dist -name '*.css' -print0 | xargs -0 gzip -k`).code == 0) {
43
+        console.log("-----打包成功-----");
44
+        //提交上传
45
+        connectSSh();
46
+      // }
47
+    // }
48
+  }
49
+}
50
+runTask()

+ 50 - 0
upload/production.js

@@ -0,0 +1,50 @@
1
+const shell = require('shelljs')
2
+const path = require('path');
3
+const config = {
4
+  ip: "118.190.89.49", // ssh地址
5
+  username: "root", // ssh 用户名
6
+  port: 22,      //端口
7
+  password: "dstech@123", // ssh 密码
8
+  path: '/home/itsm_v2_/web/pc', // 上传地址,删除地址
9
+  buildPath: '../dist/spreadSheet' // 本地打包后文件地址
10
+}
11
+let Client = require('ssh2-sftp-client');
12
+
13
+function connectSSh() {
14
+  let sftp = new Client();
15
+  sftp.connect({
16
+    host: config.ip,
17
+    port: config.port,
18
+    username: config.username,
19
+    password: config.password
20
+  }).then(() => {
21
+    console.log("-----先执行删除服务器文件-----")
22
+    return sftp.rmdir(config.path, true);
23
+  }).then(() => {
24
+    // 上传文件
25
+    console.log("-----开始上传-----")
26
+    return sftp.uploadDir(path.resolve(__dirname, config.buildPath), config.path);
27
+  }).then((data) => {
28
+    console.log("-----上传完成-----");
29
+    sftp.end();
30
+  }).catch((err) => {
31
+    console.log(err, '-----失败-----');
32
+    sftp.end();
33
+  });
34
+}
35
+function runTask() {
36
+  //打包完成
37
+  console.log(`当前NodeJs版本是${process.version}`);
38
+  const isNodeVersionLt17 = parseInt(process.version) < 17;
39
+  const execStr = isNodeVersionLt17 ? 'npm run build' : 'npm run build:17';
40
+  if (shell.exec(execStr).code == 0) {
41
+    if (shell.exec(`find dist -name '*.js' -print0 | xargs -0 gzip -k`).code == 0) {
42
+      if (shell.exec(`find dist -name '*.css' -print0 | xargs -0 gzip -k`).code == 0) {
43
+        console.log("-----打包成功-----");
44
+        //提交上传
45
+        connectSSh();
46
+      }
47
+    }
48
+  }
49
+}
50
+runTask()

+ 30 - 1
vite.config.js

@@ -1,16 +1,45 @@
1 1
 import { fileURLToPath, URL } from 'node:url'
2 2
 
3 3
 import { defineConfig } from 'vite'
4
+import Components from 'unplugin-vue-components/vite';
5
+import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
4 6
 import vue from '@vitejs/plugin-vue'
5 7
 
6 8
 // https://vitejs.dev/config/
7 9
 export default defineConfig({
10
+  base: '/spreadSheet/',
11
+  build: {
12
+    outDir: 'dist/spreadSheet'
13
+  },
8 14
   plugins: [
9 15
     vue(),
16
+    Components({
17
+      resolvers: [AntDesignVueResolver({ importStyle: 'less' })],
18
+    }),
10 19
   ],
11 20
   resolve: {
12 21
     alias: {
13 22
       '@': fileURLToPath(new URL('./src', import.meta.url))
14 23
     }
15
-  }
24
+  },
25
+  css: {
26
+    preprocessorOptions: {
27
+          less: {
28
+              modifyVars: {
29
+                  hack: 'true; @import "@/assets/theme.less"'
30
+              },
31
+              javascriptEnabled: true
32
+          }
33
+      }
34
+  },
35
+  server: {
36
+    host: '0.0.0.0',
37
+    proxy: {
38
+      '/service': {
39
+        target: 'http://192.168.4.163',
40
+        changeOrigin: true,
41
+        rewrite: path => path.replace(RegExp(`^service`), 'service')
42
+      },
43
+    }
44
+  },
16 45
 })