Forráskód Böngészése

创建pc-client-component vue 3.0的版本。

yangzhijie 4 éve
szülő
commit
dafa5f3390
100 módosított fájl, 13036 hozzáadás és 3 törlés
  1. 18 0
      README.md
  2. 7 0
      bat/build-lib.bat
  3. 7 0
      bat/build.bat
  4. 5 0
      bat/debug.bat
  5. 6 0
      bat/install.bat
  6. 5 0
      bat/publish.bat
  7. 194 0
      examples/App.vue
  8. 28 0
      examples/Desktop.vue
  9. 612 0
      examples/Modal.md
  10. 109 0
      examples/components/DateTimeExample.vue
  11. 229 0
      examples/components/Hello.vue
  12. 28 0
      examples/components/Test.vue
  13. 29 0
      examples/main.js
  14. 35 0
      examples/route/index.js
  15. 186 0
      examples/vue-monthly-picker/app.vue
  16. 16 0
      examples/vue-monthly-picker/docs.js
  17. 4 0
      examples/vue-monthly-picker/lib.js
  18. 24 0
      index.html
  19. 9 3
      package.json
  20. 7 0
      packages/checkbox/index.js
  21. 220 0
      packages/checkbox/src/Checkbox.vue
  22. 247 0
      packages/common/Common.js
  23. 67 0
      packages/common/DownloadService.js
  24. 25 0
      packages/common/IFrameUtil.js
  25. 72 0
      packages/common/Language.js
  26. 165 0
      packages/common/Notify.js
  27. 29 0
      packages/common/SqlApi.js
  28. 133 0
      packages/common/UserStorageResource.js
  29. 59 0
      packages/common/Uuid.js
  30. 44 0
      packages/customer-window/src/api/CustomerWindowResource.js
  31. 8 0
      packages/date/index.js
  32. 67 0
      packages/date/src/Date.vue
  33. 61 0
      packages/date/src/vue-datepicker-setting.js
  34. 8 0
      packages/datetime-v2/index.js
  35. 148 0
      packages/datetime-v2/src/DateTimeV2.vue
  36. 8 0
      packages/datetime/index.js
  37. 64 0
      packages/datetime/src/DateTime.vue
  38. 61 0
      packages/datetime/src/vue-datepicker-setting.js
  39. 43 0
      packages/html-window/src/api/HtmlWindowResource.js
  40. 62 0
      packages/i18n/en-US.js
  41. 64 0
      packages/i18n/zh-CN.js
  42. 8 0
      packages/image-preview/index.js
  43. 70 0
      packages/image-preview/src/ImagePreview.vue
  44. 164 0
      packages/index.js
  45. 8 0
      packages/info/doc-generator.js
  46. 8 0
      packages/info/index.js
  47. 8 0
      packages/info/info-search-widget.js
  48. 8 0
      packages/info/search-widget.js
  49. 315 0
      packages/info/src/DocGenerator.vue
  50. 479 0
      packages/info/src/DocGeneratorGrid.vue
  51. 396 0
      packages/info/src/DocGeneratorSelected.vue
  52. 50 0
      packages/info/src/EnumSelectWidgetInfo.vue
  53. 290 0
      packages/info/src/InfoHeader.vue
  54. 261 0
      packages/info/src/InfoMultiSearchWidget.vue
  55. 262 0
      packages/info/src/InfoSearchWidget.vue
  56. 142 0
      packages/info/src/InfoUtil.js
  57. 163 0
      packages/info/src/InfoWindow.vue
  58. 87 0
      packages/info/src/InfoWindowUtil.js
  59. 215 0
      packages/info/src/QueryCondition.vue
  60. 413 0
      packages/info/src/QueryConditionComplex.vue
  61. 122 0
      packages/info/src/QueryConditionSimple.vue
  62. 950 0
      packages/info/src/QueryPage.vue
  63. 92 0
      packages/info/src/QueryPageImage.vue
  64. 301 0
      packages/info/src/SearchAutoCompleteWidget.vue
  65. 278 0
      packages/info/src/SearchWidget.vue
  66. 175 0
      packages/info/src/customer/ClientOrgnizationInfo.vue
  67. 8 0
      packages/loading/index.js
  68. 183 0
      packages/loading/src/Loading.vue
  69. 8 0
      packages/modal/index.js
  70. 197 0
      packages/modal/src/Modal.vue
  71. 34 0
      packages/modal/src/ModalFix.js
  72. 8 0
      packages/navbar/index.js
  73. 62 0
      packages/navbar/src/Navbar.vue
  74. 10 0
      packages/page-size-select/index.js
  75. 44 0
      packages/page-size-select/src/PageSizeSelect.vue
  76. 131 0
      packages/page-size-select/src/SizePagination.vue
  77. 10 0
      packages/pagination/index.js
  78. 210 0
      packages/pagination/src/Pagination.vue
  79. 8 0
      packages/print/print-epc.js
  80. 8 0
      packages/print/print-widget.js
  81. 188 0
      packages/print/src/PrintEpc.vue
  82. 138 0
      packages/print/src/PrintEpcUtil.js
  83. 160 0
      packages/print/src/PrintUtil.js
  84. 121 0
      packages/print/src/PrintWidget.vue
  85. 8 0
      packages/process/index.js
  86. 8 0
      packages/process/process-report-result-preview.js
  87. 8 0
      packages/process/process-report-result.js
  88. 45 0
      packages/process/src/EnumSelectWidget.vue
  89. 455 0
      packages/process/src/MultiSearchWidget.vue
  90. 184 0
      packages/process/src/ProcessReport.vue
  91. 208 0
      packages/process/src/ProcessReportArchive.vue
  92. 409 0
      packages/process/src/ProcessReportDynamic.vue
  93. 109 0
      packages/process/src/ProcessReportResult.vue
  94. 252 0
      packages/process/src/ProcessReportResultPreview.vue
  95. 377 0
      packages/process/src/ProcessReportStatic.vue
  96. 362 0
      packages/process/src/api/ProcessReportResource.js
  97. 8 0
      packages/scanner/index.js
  98. 163 0
      packages/scanner/src/Scanner.vue
  99. 8 0
      packages/switches/index.js
  100. 426 0
      packages/switches/src/Switches.vue

+ 18 - 0
README.md

@@ -0,0 +1,18 @@
+# pc-client-component
+
+> A Vue.js project
+
+## Build Setup
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+```
+
+For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader).

+ 7 - 0
bat/build-lib.bat

@@ -0,0 +1,7 @@
+title build
+set current_path="%~dp0"
+cd %current_path%
+cd ..
+rmdir /s/q dist
+npm run lib
+pause

+ 7 - 0
bat/build.bat

@@ -0,0 +1,7 @@
+title PcClient build
+set current_path="%~dp0"
+cd %current_path%
+cd ..
+rmdir /s/q dist
+npm run build
+pause

+ 5 - 0
bat/debug.bat

@@ -0,0 +1,5 @@
+set current_path="%~dp0"
+cd %current_path%
+cd ..
+npm run dev
+pause

+ 6 - 0
bat/install.bat

@@ -0,0 +1,6 @@
+title install
+set current_path="%~dp0"
+cd %current_path%
+cd ..
+npm install -registry=https://registry.npm.taobao.org
+pause

+ 5 - 0
bat/publish.bat

@@ -0,0 +1,5 @@
+set current_path="%~dp0"
+cd %current_path%
+cd ..
+npm publish
+pause

+ 194 - 0
examples/App.vue

@@ -0,0 +1,194 @@
+<template>
+    <div class="container bs-docs-container">
+        <div class="row">
+            <div class="col-md-9">
+                <router-view />
+            </div>
+
+            <div class="col-md-3"
+                 role="complementary">
+                <nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix-bottom"
+                     style="top: 55943.8px;">
+                    <ul class="nav bs-docs-sidenav">
+                        <li>
+                            <ul class="nav">
+                                <li>
+                                    <router-link :to="{ path: '/desktop/test'}">测试页面</router-link>
+                                </li>
+                                <li>
+                                    <router-link :to="{ path: '/desktop/hello'}">Hello</router-link>
+                                </li>
+                                <li>
+                                    <router-link :to="{ path: '/desktop/date-time'}">日期</router-link>
+                                </li>
+                            </ul>
+                        </li>
+                        <li>
+                            <ul class="nav">
+                               <li>
+                                    <a href="#/desktop/process-report/123456">流程报表</a>
+                                </li>
+                                <li>
+                                    <a href="#/desktop/info/123456">查询窗口</a>
+                                </li>
+                                <li>
+                                    <a href="#/doc-generator-selected">生单界面</a>
+                                </li>
+                            </ul>
+                        </li>
+                    </ul>
+                </nav>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+
+
+export default {
+    name: 'desktop',
+
+    data: function () {
+        return {
+
+        }
+    },
+
+    components: {
+    },
+
+    methods: {
+
+    }
+}
+</script>
+
+<style>
+.bs-docs-sidebar.affix {
+    position: static;
+}
+@media (min-width: 768px) {
+    .bs-docs-sidebar {
+        padding-left: 20px;
+    }
+}
+
+.bs-docs-search {
+    margin-bottom: 20px;
+    margin-left: 20px;
+}
+
+/** First level of nav */
+.bs-docs-sidenav {
+    margin-top: 20px;
+    margin-bottom: 20px;
+}
+
+/** All levels of nav */
+.bs-docs-sidebar .nav > li > a {
+    display: block;
+    padding: 4px 20px;
+    font-size: 13px;
+    font-weight: 500;
+    color: #767676;
+}
+.bs-docs-sidebar .nav > li > a:hover,
+.bs-docs-sidebar .nav > li > a:focus {
+    padding-left: 19px;
+    color: #563d7c;
+    text-decoration: none;
+    background-color: transparent;
+    border-left: 1px solid #563d7c;
+}
+.bs-docs-sidebar .nav > .active > a,
+.bs-docs-sidebar .nav > .active:hover > a,
+.bs-docs-sidebar .nav > .active:focus > a {
+    padding-left: 18px;
+    font-weight: 700;
+    color: #563d7c;
+    background-color: transparent;
+    border-left: 2px solid #563d7c;
+}
+
+/** Nav: second level (shown on .active) */
+.bs-docs-sidebar .nav .nav {
+    display: none; /** Hide by default, but at >768px, show it */
+    padding-bottom: 10px;
+}
+.bs-docs-sidebar .nav .nav > li > a {
+    padding-top: 1px;
+    padding-bottom: 1px;
+    padding-left: 30px;
+    font-size: 12px;
+    font-weight: 400;
+}
+.bs-docs-sidebar .nav .nav > li > a:hover,
+.bs-docs-sidebar .nav .nav > li > a:focus {
+    padding-left: 29px;
+}
+.bs-docs-sidebar .nav .nav > .active > a,
+.bs-docs-sidebar .nav .nav > .active:hover > a,
+.bs-docs-sidebar .nav .nav > .active:focus > a {
+    padding-left: 28px;
+    font-weight: 500;
+}
+
+/** Back to top (hidden on mobile) */
+.back-to-top,
+.bs-docs-theme-toggle {
+    display: none;
+    padding: 4px 10px;
+    margin-top: 10px;
+    margin-left: 10px;
+    font-size: 12px;
+    font-weight: 500;
+    color: #999;
+}
+.back-to-top:hover,
+.bs-docs-theme-toggle:hover {
+    color: #563d7c;
+    text-decoration: none;
+}
+.bs-docs-theme-toggle {
+    margin-top: 0;
+}
+
+@media (min-width: 768px) {
+    .back-to-top,
+    .bs-docs-theme-toggle {
+        display: block;
+    }
+}
+
+/** Show and affix the side nav when space allows it */
+@media (min-width: 992px) {
+    .bs-docs-sidebar .nav > .active > ul {
+        display: block;
+    }
+    /** Widen the fixed sidebar */
+    .bs-docs-sidebar.affix,
+    .bs-docs-sidebar.affix-bottom {
+        width: 213px;
+    }
+    .bs-docs-sidebar.affix {
+        position: fixed;
+        top: 20px;
+    }
+    .bs-docs-sidebar.affix-bottom {
+        position: absolute; 
+    }
+    .bs-docs-sidebar.affix-bottom .bs-docs-sidenav,
+    .bs-docs-sidebar.affix .bs-docs-sidenav {
+        margin-top: 0;
+        margin-bottom: 0;
+    }
+}
+@media (min-width: 1200px) {
+    /** Widen the fixed sidebar again */
+    .bs-docs-sidebar.affix-bottom,
+    .bs-docs-sidebar.affix {
+        width: 263px;
+    }
+}
+</style>

+ 28 - 0
examples/Desktop.vue

@@ -0,0 +1,28 @@
+<template>
+    <div>
+        <router-view />
+    </div>
+</template>
+
+<script>
+
+
+export default {
+
+    data: function () {
+        return {
+
+        }
+    },
+
+    components: {
+    },
+
+    methods: {
+
+    }
+}
+</script>
+
+<style>
+</style>

+ 612 - 0
examples/Modal.md

@@ -0,0 +1,612 @@
+
+
+
+
+
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+
+
+
+  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/frameworks-c4ea0a21580a57091fc76df379ba40e19a614ee3bdca871a3a8a2b38d181c65d.css" integrity="sha256-xOoKIVgKVwkfx23zebpA4ZphTuO9yocaOoorONGBxl0=" media="all" rel="stylesheet" />
+  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/github-dd55541a5eac47b36c42f5ca92a52600859338e7173b55ac8915003cc66aed06.css" integrity="sha256-3VVUGl6sR7NsQvXKkqUmAIWTOOcXO1WsiRUAPMZq7QY=" media="all" rel="stylesheet" />
+  
+  
+  <link crossorigin="anonymous" href="https://assets-cdn.github.com/assets/site-b29e324b8fafaead965049ef224818ef0dccc7384b5cfcad56a56a89c33a9438.css" integrity="sha256-sp4yS4+vrq2WUEnvIkgY7w3MxzhLXPytVqVqicM6lDg=" media="all" rel="stylesheet" />
+  
+
+  <meta name="viewport" content="width=device-width">
+  
+  <title>vue-bootstrap-modal/README.md at master · Coffcer/vue-bootstrap-modal · GitHub</title>
+  <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
+  <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
+  <meta property="fb:app_id" content="1401488693436528">
+
+    
+    <meta content="https://avatars2.githubusercontent.com/u/12846955?v=3&amp;s=400" property="og:image" /><meta content="GitHub" property="og:site_name" /><meta content="object" property="og:type" /><meta content="Coffcer/vue-bootstrap-modal" property="og:title" /><meta content="https://github.com/Coffcer/vue-bootstrap-modal" property="og:url" /><meta content="vue-bootstrap-modal - Bootstrap style modal for vue" property="og:description" />
+
+  <link rel="assets" href="https://assets-cdn.github.com/">
+  
+  <meta name="pjax-timeout" content="1000">
+  
+  <meta name="request-id" content="ADB7:284AD:134EBF:20AC75:58D0F16F" data-pjax-transient>
+  
+
+  <meta name="selected-link" value="repo_source" data-pjax-transient>
+
+  <meta name="google-site-verification" content="KT5gs8h0wvaagLKAVWq8bbeNwnZZK1r1XQysX3xurLU">
+<meta name="google-site-verification" content="ZzhVyEFwb7w3e0-uOTltm8Jsck2F5StVihD0exw2fsA">
+    <meta name="google-analytics" content="UA-3769691-2">
+
+<meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" /><meta content="https://collector.githubapp.com/github-external/browser_event" name="octolytics-event-url" /><meta content="ADB7:284AD:134EBF:20AC75:58D0F16F" name="octolytics-dimension-request_id" />
+<meta content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-pjax-transient="true" name="analytics-location" />
+
+
+
+
+  <meta class="js-ga-set" name="dimension1" content="Logged Out">
+
+
+  
+  
+      <meta name="hostname" content="github.com">
+  <meta name="user-login" content="">
+
+      <meta name="expected-hostname" content="github.com">
+    <meta name="js-proxy-site-detection-payload" content="NDJiNDZjYmQxNTE2MDM4YzZmZGMwZWFlYmM4NzZmM2FiOTJkOTA4ODYwYjY3ODliYTQzNGE4ZTljMDVmNTJhNHx7InJlbW90ZV9hZGRyZXNzIjoiNDUuNzYuMjAzLjExMCIsInJlcXVlc3RfaWQiOiJBREI3OjI4NEFEOjEzNEVCRjoyMEFDNzU6NThEMEYxNkYiLCJ0aW1lc3RhbXAiOjE0OTAwODgzMDMsImhvc3QiOiJnaXRodWIuY29tIn0=">
+
+
+  <meta name="html-safe-nonce" content="941b1b4299c75acdbeaee776d0208776918244cc">
+
+  <meta http-equiv="x-pjax-version" content="014da552f118ca793d54aae5c3dc8f93">
+  
+
+    
+  <meta name="description" content="vue-bootstrap-modal - Bootstrap style modal for vue">
+  <meta name="go-import" content="github.com/Coffcer/vue-bootstrap-modal git https://github.com/Coffcer/vue-bootstrap-modal.git">
+
+  <meta content="12846955" name="octolytics-dimension-user_id" /><meta content="Coffcer" name="octolytics-dimension-user_login" /><meta content="54107008" name="octolytics-dimension-repository_id" /><meta content="Coffcer/vue-bootstrap-modal" name="octolytics-dimension-repository_nwo" /><meta content="true" name="octolytics-dimension-repository_public" /><meta content="false" name="octolytics-dimension-repository_is_fork" /><meta content="54107008" name="octolytics-dimension-repository_network_root_id" /><meta content="Coffcer/vue-bootstrap-modal" name="octolytics-dimension-repository_network_root_nwo" />
+      <link href="https://github.com/Coffcer/vue-bootstrap-modal/commits/master.atom" rel="alternate" title="Recent Commits to vue-bootstrap-modal:master" type="application/atom+xml">
+
+
+    <link rel="canonical" href="https://github.com/Coffcer/vue-bootstrap-modal/blob/master/README.md" data-pjax-transient>
+
+
+  <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">
+
+  <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">
+
+  <link rel="mask-icon" href="https://assets-cdn.github.com/pinned-octocat.svg" color="#000000">
+  <link rel="icon" type="image/x-icon" href="https://assets-cdn.github.com/favicon.ico">
+
+<meta name="theme-color" content="#1e2327">
+
+
+  <meta name="u2f-support" content="true">
+
+  </head>
+
+  <body class="logged-out env-production page-blob">
+    
+
+  <div class="position-relative js-header-wrapper ">
+    <a href="#start-of-content" tabindex="1" class="accessibility-aid js-skip-to-content">Skip to content</a>
+    <div id="js-pjax-loader-bar" class="pjax-loader-bar"><div class="progress"></div></div>
+
+    
+    
+    
+
+
+
+          <header class="site-header js-details-container Details" role="banner">
+  <div class="container-responsive">
+    <a class="header-logo-invertocat" href="https://github.com/" aria-label="Homepage" data-ga-click="(Logged out) Header, go to homepage, icon:logo-wordmark">
+      <svg aria-hidden="true" class="octicon octicon-mark-github" height="32" version="1.1" viewBox="0 0 16 16" width="32"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
+    </a>
+
+    <button class="btn-link float-right site-header-toggle js-details-target" type="button" aria-label="Toggle navigation">
+      <svg aria-hidden="true" class="octicon octicon-three-bars" height="24" version="1.1" viewBox="0 0 12 16" width="18"><path fill-rule="evenodd" d="M11.41 9H.59C0 9 0 8.59 0 8c0-.59 0-1 .59-1H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1h.01zm0-4H.59C0 5 0 4.59 0 4c0-.59 0-1 .59-1H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1h.01zM.59 11H11.4c.59 0 .59.41.59 1 0 .59 0 1-.59 1H.59C0 13 0 12.59 0 12c0-.59 0-1 .59-1z"/></svg>
+    </button>
+
+    <div class="site-header-menu">
+      <nav class="site-header-nav">
+        <a href="/features" class="js-selected-navigation-item nav-item" data-ga-click="Header, click, Nav menu - item:features" data-selected-links="/features /features">
+          Features
+</a>        <a href="/explore" class="js-selected-navigation-item nav-item" data-ga-click="Header, click, Nav menu - item:explore" data-selected-links="/explore /trending /trending/developers /integrations /integrations/feature/code /integrations/feature/collaborate /integrations/feature/ship /showcases /explore">
+          Explore
+</a>        <a href="/pricing" class="js-selected-navigation-item nav-item" data-ga-click="Header, click, Nav menu - item:pricing" data-selected-links="/pricing /pricing">
+          Pricing
+</a>      </nav>
+
+      <div class="site-header-actions">
+          <div class="header-search scoped-search site-scoped-search js-site-search" role="search">
+  <!-- '"` --><!-- </textarea></xmp> --></option></form><form accept-charset="UTF-8" action="/Coffcer/vue-bootstrap-modal/search" class="js-site-search-form" data-scoped-search-url="/Coffcer/vue-bootstrap-modal/search" data-unscoped-search-url="/search" method="get"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /></div>
+    <label class="form-control header-search-wrapper js-chromeless-input-container">
+      <div class="header-search-scope">This repository</div>
+      <input type="text"
+        class="form-control header-search-input js-site-search-focus js-site-search-field is-clearable"
+        data-hotkey="s"
+        name="q"
+        placeholder="Search"
+        aria-label="Search this repository"
+        data-unscoped-placeholder="Search GitHub"
+        data-scoped-placeholder="Search"
+        autocapitalize="off">
+    </label>
+</form></div>
+
+
+          <a class="text-bold site-header-link" href="/login?return_to=%2FCoffcer%2Fvue-bootstrap-modal%2Fblob%2Fmaster%2FREADME.md" data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in">Sign in</a>
+            <span class="text-gray">or</span>
+            <a class="text-bold site-header-link" href="/join?source=header-repo" data-ga-click="(Logged out) Header, clicked Sign up, text:sign-up">Sign up</a>
+      </div>
+    </div>
+  </div>
+</header>
+
+
+  </div>
+
+  <div id="start-of-content" class="accessibility-aid"></div>
+
+    <div id="js-flash-container">
+</div>
+
+
+
+  <div role="main">
+      <div itemscope itemtype="http://schema.org/SoftwareSourceCode">
+    <div id="js-repo-pjax-container" data-pjax-container>
+        
+
+
+<div class="pagehead repohead instapaper_ignore readability-menu experiment-repo-nav">
+  <div class="container repohead-details-container">
+
+
+    <ul class="pagehead-actions">
+  <li>
+      <a href="/login?return_to=%2FCoffcer%2Fvue-bootstrap-modal"
+    class="btn btn-sm btn-with-count tooltipped tooltipped-n"
+    aria-label="You must be signed in to watch a repository" rel="nofollow">
+    <svg aria-hidden="true" class="octicon octicon-eye" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M8.06 2C3 2 0 8 0 8s3 6 8.06 6C13 14 16 8 16 8s-3-6-7.94-6zM8 12c-2.2 0-4-1.78-4-4 0-2.2 1.8-4 4-4 2.22 0 4 1.8 4 4 0 2.22-1.78 4-4 4zm2-4c0 1.11-.89 2-2 2-1.11 0-2-.89-2-2 0-1.11.89-2 2-2 1.11 0 2 .89 2 2z"/></svg>
+    Watch
+  </a>
+  <a class="social-count" href="/Coffcer/vue-bootstrap-modal/watchers"
+     aria-label="3 users are watching this repository">
+    3
+  </a>
+
+  </li>
+
+  <li>
+      <a href="/login?return_to=%2FCoffcer%2Fvue-bootstrap-modal"
+    class="btn btn-sm btn-with-count tooltipped tooltipped-n"
+    aria-label="You must be signed in to star a repository" rel="nofollow">
+    <svg aria-hidden="true" class="octicon octicon-star" height="16" version="1.1" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M14 6l-4.9-.64L7 1 4.9 5.36 0 6l3.6 3.26L2.67 14 7 11.67 11.33 14l-.93-4.74z"/></svg>
+    Star
+  </a>
+
+    <a class="social-count js-social-count" href="/Coffcer/vue-bootstrap-modal/stargazers"
+      aria-label="88 users starred this repository">
+      88
+    </a>
+
+  </li>
+
+  <li>
+      <a href="/login?return_to=%2FCoffcer%2Fvue-bootstrap-modal"
+        class="btn btn-sm btn-with-count tooltipped tooltipped-n"
+        aria-label="You must be signed in to fork a repository" rel="nofollow">
+        <svg aria-hidden="true" class="octicon octicon-repo-forked" height="16" version="1.1" viewBox="0 0 10 16" width="10"><path fill-rule="evenodd" d="M8 1a1.993 1.993 0 0 0-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V6.5l3 3v1.78A1.993 1.993 0 0 0 5 15a1.993 1.993 0 0 0 1-3.72V9.5l3-3V4.72A1.993 1.993 0 0 0 8 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"/></svg>
+        Fork
+      </a>
+
+    <a href="/Coffcer/vue-bootstrap-modal/network" class="social-count"
+       aria-label="36 users forked this repository">
+      36
+    </a>
+  </li>
+</ul>
+
+    <h1 class="public ">
+  <svg aria-hidden="true" class="octicon octicon-repo" height="16" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M4 9H3V8h1v1zm0-3H3v1h1V6zm0-2H3v1h1V4zm0-2H3v1h1V2zm8-1v12c0 .55-.45 1-1 1H6v2l-1.5-1.5L3 16v-2H1c-.55 0-1-.45-1-1V1c0-.55.45-1 1-1h10c.55 0 1 .45 1 1zm-1 10H1v2h2v-1h3v1h5v-2zm0-10H2v9h9V1z"/></svg>
+  <span class="author" itemprop="author"><a href="/Coffcer" class="url fn" rel="author">Coffcer</a></span><!--
+--><span class="path-divider">/</span><!--
+--><strong itemprop="name"><a href="/Coffcer/vue-bootstrap-modal" data-pjax="#js-repo-pjax-container">vue-bootstrap-modal</a></strong>
+
+</h1>
+
+  </div>
+  <div class="container">
+    
+<nav class="reponav js-repo-nav js-sidenav-container-pjax"
+     itemscope
+     itemtype="http://schema.org/BreadcrumbList"
+     role="navigation"
+     data-pjax="#js-repo-pjax-container">
+
+  <span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
+    <a href="/Coffcer/vue-bootstrap-modal" class="js-selected-navigation-item selected reponav-item" data-hotkey="g c" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches /Coffcer/vue-bootstrap-modal" itemprop="url">
+      <svg aria-hidden="true" class="octicon octicon-code" height="16" version="1.1" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M9.5 3L8 4.5 11.5 8 8 11.5 9.5 13 14 8 9.5 3zm-5 0L0 8l4.5 5L6 11.5 2.5 8 6 4.5 4.5 3z"/></svg>
+      <span itemprop="name">Code</span>
+      <meta itemprop="position" content="1">
+</a>  </span>
+
+    <span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
+      <a href="/Coffcer/vue-bootstrap-modal/issues" class="js-selected-navigation-item reponav-item" data-hotkey="g i" data-selected-links="repo_issues repo_labels repo_milestones /Coffcer/vue-bootstrap-modal/issues" itemprop="url">
+        <svg aria-hidden="true" class="octicon octicon-issue-opened" height="16" version="1.1" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"/></svg>
+        <span itemprop="name">Issues</span>
+        <span class="counter">4</span>
+        <meta itemprop="position" content="2">
+</a>    </span>
+
+  <span itemscope itemtype="http://schema.org/ListItem" itemprop="itemListElement">
+    <a href="/Coffcer/vue-bootstrap-modal/pulls" class="js-selected-navigation-item reponav-item" data-hotkey="g p" data-selected-links="repo_pulls /Coffcer/vue-bootstrap-modal/pulls" itemprop="url">
+      <svg aria-hidden="true" class="octicon octicon-git-pull-request" height="16" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M11 11.28V5c-.03-.78-.34-1.47-.94-2.06C9.46 2.35 8.78 2.03 8 2H7V0L4 3l3 3V4h1c.27.02.48.11.69.31.21.2.3.42.31.69v6.28A1.993 1.993 0 0 0 10 15a1.993 1.993 0 0 0 1-3.72zm-1 2.92c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zM4 3c0-1.11-.89-2-2-2a1.993 1.993 0 0 0-1 3.72v6.56A1.993 1.993 0 0 0 2 15a1.993 1.993 0 0 0 1-3.72V4.72c.59-.34 1-.98 1-1.72zm-.8 10c0 .66-.55 1.2-1.2 1.2-.65 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"/></svg>
+      <span itemprop="name">Pull requests</span>
+      <span class="counter">1</span>
+      <meta itemprop="position" content="3">
+</a>  </span>
+
+  <a href="/Coffcer/vue-bootstrap-modal/projects" class="js-selected-navigation-item reponav-item" data-selected-links="repo_projects new_repo_project repo_project /Coffcer/vue-bootstrap-modal/projects">
+    <svg aria-hidden="true" class="octicon octicon-project" height="16" version="1.1" viewBox="0 0 15 16" width="15"><path fill-rule="evenodd" d="M10 12h3V2h-3v10zm-4-2h3V2H6v8zm-4 4h3V2H2v12zm-1 1h13V1H1v14zM14 0H1a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1V1a1 1 0 0 0-1-1z"/></svg>
+    Projects
+    <span class="counter">0</span>
+</a>
+
+
+  <a href="/Coffcer/vue-bootstrap-modal/pulse" class="js-selected-navigation-item reponav-item" data-selected-links="pulse /Coffcer/vue-bootstrap-modal/pulse">
+    <svg aria-hidden="true" class="octicon octicon-pulse" height="16" version="1.1" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M11.5 8L8.8 5.4 6.6 8.5 5.5 1.6 2.38 8H0v2h3.6l.9-1.8.9 5.4L9 8.5l1.6 1.5H14V8z"/></svg>
+    Pulse
+</a>
+  <a href="/Coffcer/vue-bootstrap-modal/graphs" class="js-selected-navigation-item reponav-item" data-selected-links="repo_graphs repo_contributors /Coffcer/vue-bootstrap-modal/graphs">
+    <svg aria-hidden="true" class="octicon octicon-graph" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M16 14v1H0V0h1v14h15zM5 13H3V8h2v5zm4 0H7V3h2v10zm4 0h-2V6h2v7z"/></svg>
+    Graphs
+</a>
+
+</nav>
+
+  </div>
+</div>
+
+<div class="container new-discussion-timeline experiment-repo-nav">
+  <div class="repository-content">
+
+    
+
+<a href="/Coffcer/vue-bootstrap-modal/blob/8d6a12c3ced05cc2d5db2517c1030bebe98282ce/README.md" class="d-none js-permalink-shortcut" data-hotkey="y">Permalink</a>
+
+<!-- blob contrib key: blob_contributors:v21:a4f8bee246e12c8ee3a3650e4a76611b -->
+
+<div class="file-navigation js-zeroclipboard-container">
+  
+<div class="select-menu branch-select-menu js-menu-container js-select-menu float-left">
+  <button class=" btn btn-sm select-menu-button js-menu-target css-truncate" data-hotkey="w"
+    
+    type="button" aria-label="Switch branches or tags" tabindex="0" aria-haspopup="true">
+      <i>Branch:</i>
+      <span class="js-select-button css-truncate-target">master</span>
+  </button>
+
+  <div class="select-menu-modal-holder js-menu-content js-navigation-container" data-pjax aria-hidden="true">
+
+    <div class="select-menu-modal">
+      <div class="select-menu-header">
+        <svg aria-label="Close" class="octicon octicon-x js-menu-close" height="16" role="img" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48z"/></svg>
+        <span class="select-menu-title">Switch branches/tags</span>
+      </div>
+
+      <div class="select-menu-filters">
+        <div class="select-menu-text-filter">
+          <input type="text" aria-label="Filter branches/tags" id="context-commitish-filter-field" class="form-control js-filterable-field js-navigation-enable" placeholder="Filter branches/tags">
+        </div>
+        <div class="select-menu-tabs">
+          <ul>
+            <li class="select-menu-tab">
+              <a href="#" data-tab-filter="branches" data-filter-placeholder="Filter branches/tags" class="js-select-menu-tab" role="tab">Branches</a>
+            </li>
+            <li class="select-menu-tab">
+              <a href="#" data-tab-filter="tags" data-filter-placeholder="Find a tag…" class="js-select-menu-tab" role="tab">Tags</a>
+            </li>
+          </ul>
+        </div>
+      </div>
+
+      <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="branches" role="menu">
+
+        <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring">
+
+
+            <a class="select-menu-item js-navigation-item js-navigation-open selected"
+               href="/Coffcer/vue-bootstrap-modal/blob/master/README.md"
+               data-name="master"
+               data-skip-pjax="true"
+               rel="nofollow">
+              <svg aria-hidden="true" class="octicon octicon-check select-menu-item-icon" height="16" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5z"/></svg>
+              <span class="select-menu-item-text css-truncate-target js-select-menu-filter-text">
+                master
+              </span>
+            </a>
+        </div>
+
+          <div class="select-menu-no-results">Nothing to show</div>
+      </div>
+
+      <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket" data-tab-filter="tags">
+        <div data-filterable-for="context-commitish-filter-field" data-filterable-type="substring">
+
+
+        </div>
+
+        <div class="select-menu-no-results">Nothing to show</div>
+      </div>
+
+    </div>
+  </div>
+</div>
+
+  <div class="BtnGroup float-right">
+    <a href="/Coffcer/vue-bootstrap-modal/find/master"
+          class="js-pjax-capture-input btn btn-sm BtnGroup-item"
+          data-pjax
+          data-hotkey="t">
+      Find file
+    </a>
+    <button aria-label="Copy file path to clipboard" class="js-zeroclipboard btn btn-sm BtnGroup-item tooltipped tooltipped-s" data-copied-hint="Copied!" type="button">Copy path</button>
+  </div>
+  <div class="breadcrumb js-zeroclipboard-target">
+    <span class="repo-root js-repo-root"><span class="js-path-segment"><a href="/Coffcer/vue-bootstrap-modal"><span>vue-bootstrap-modal</span></a></span></span><span class="separator">/</span><strong class="final-path">README.md</strong>
+  </div>
+</div>
+
+
+
+  <div class="commit-tease">
+      <span class="float-right">
+        <a class="commit-tease-sha" href="/Coffcer/vue-bootstrap-modal/commit/3c0167a59f4697b1493d244ff474afd0c6edb7b5" data-pjax>
+          3c0167a
+        </a>
+        <relative-time datetime="2016-03-18T11:39:38Z">Mar 18, 2016</relative-time>
+      </span>
+      <div>
+        <img alt="@Coffcer" class="avatar" height="20" src="https://avatars0.githubusercontent.com/u/12846955?v=3&amp;s=40" width="20" />
+        <a href="/Coffcer" class="user-mention" rel="author">Coffcer</a>
+          <a href="/Coffcer/vue-bootstrap-modal/commit/3c0167a59f4697b1493d244ff474afd0c6edb7b5" class="message" data-pjax="true" title="update">update</a>
+      </div>
+
+    <div class="commit-tease-contributors">
+      <button type="button" class="btn-link muted-link contributors-toggle" data-facebox="#blob_contributors_box">
+        <strong>1</strong>
+         contributor
+      </button>
+      
+    </div>
+
+    <div id="blob_contributors_box" style="display:none">
+      <h2 class="facebox-header" data-facebox-id="facebox-header">Users who have contributed to this file</h2>
+      <ul class="facebox-user-list" data-facebox-id="facebox-description">
+          <li class="facebox-user-list-item">
+            <img alt="@Coffcer" height="24" src="https://avatars2.githubusercontent.com/u/12846955?v=3&amp;s=48" width="24" />
+            <a href="/Coffcer">Coffcer</a>
+          </li>
+      </ul>
+    </div>
+  </div>
+
+<div class="file">
+  <div class="file-header">
+  <div class="file-actions">
+
+    <div class="BtnGroup">
+      <a href="/Coffcer/vue-bootstrap-modal/raw/master/README.md" class="btn btn-sm BtnGroup-item" id="raw-url">Raw</a>
+        <a href="/Coffcer/vue-bootstrap-modal/blame/master/README.md" class="btn btn-sm js-update-url-with-hash BtnGroup-item" data-hotkey="b">Blame</a>
+      <a href="/Coffcer/vue-bootstrap-modal/commits/master/README.md" class="btn btn-sm BtnGroup-item" rel="nofollow">History</a>
+    </div>
+
+        <a class="btn-octicon tooltipped tooltipped-nw"
+           href="https://windows.github.com"
+           aria-label="Open this file in GitHub Desktop"
+           data-ga-click="Repository, open with desktop, type:windows">
+            <svg aria-hidden="true" class="octicon octicon-device-desktop" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M15 2H1c-.55 0-1 .45-1 1v9c0 .55.45 1 1 1h5.34c-.25.61-.86 1.39-2.34 2h8c-1.48-.61-2.09-1.39-2.34-2H15c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm0 9H1V3h14v8z"/></svg>
+        </a>
+
+        <button type="button" class="btn-octicon disabled tooltipped tooltipped-nw"
+          aria-label="You must be signed in to make or propose changes">
+          <svg aria-hidden="true" class="octicon octicon-pencil" height="16" version="1.1" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M0 12v3h3l8-8-3-3-8 8zm3 2H1v-2h1v1h1v1zm10.3-9.3L12 6 9 3l1.3-1.3a.996.996 0 0 1 1.41 0l1.59 1.59c.39.39.39 1.02 0 1.41z"/></svg>
+        </button>
+        <button type="button" class="btn-octicon btn-octicon-danger disabled tooltipped tooltipped-nw"
+          aria-label="You must be signed in to make or propose changes">
+          <svg aria-hidden="true" class="octicon octicon-trashcan" height="16" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M11 2H9c0-.55-.45-1-1-1H5c-.55 0-1 .45-1 1H2c-.55 0-1 .45-1 1v1c0 .55.45 1 1 1v9c0 .55.45 1 1 1h7c.55 0 1-.45 1-1V5c.55 0 1-.45 1-1V3c0-.55-.45-1-1-1zm-1 12H3V5h1v8h1V5h1v8h1V5h1v8h1V5h1v9zm1-10H2V3h9v1z"/></svg>
+        </button>
+  </div>
+
+  <div class="file-info">
+      100 lines (89 sloc)
+      <span class="file-info-divider"></span>
+    1.97 KB
+  </div>
+</div>
+
+  
+  <div id="readme" class="readme blob instapaper_body">
+    <article class="markdown-body entry-content" itemprop="text"><h1><a id="user-content-vue-bootstrap-modal" class="anchor" href="#vue-bootstrap-modal" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>vue-bootstrap-modal</h1>
+<p>Bootstrap style modal component for vue.</p>
+<p><a href="https://github.com/Coffcer/vue-bootstrap-modal/blob/master/modal.gif" target="_blank"><img src="https://github.com/Coffcer/vue-bootstrap-modal/raw/master/modal.gif" style="max-width:100%;"></a></p>
+<h1><a id="user-content-usage" class="anchor" href="#usage" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Usage</h1>
+<p>import Bootstrap.css</p>
+<pre><code>&lt;link href="bootstrap.css"&gt;&lt;/link&gt;
+</code></pre>
+<p><strong>NOTE:</strong>  By default, the modal style is pure bootstrap style, you can use any 3rd party bootstrap framework, like above GIF.</p>
+<p>simple options:</p>
+<div class="highlight highlight-text-html-basic"><pre><span class="pl-c"><span class="pl-c">&lt;!--</span>text content<span class="pl-c">--&gt;</span></span>
+&lt;<span class="pl-ent">modal</span> <span class="pl-e">title</span>=<span class="pl-s"><span class="pl-pds">"</span>Modal Title<span class="pl-pds">"</span></span> :<span class="pl-e">show</span>.<span class="pl-e">sync</span>=<span class="pl-s"><span class="pl-pds">"</span>show<span class="pl-pds">"</span></span> @<span class="pl-e">ok</span>=<span class="pl-s"><span class="pl-pds">"</span>ok<span class="pl-pds">"</span></span> @<span class="pl-e">cancel</span>=<span class="pl-s"><span class="pl-pds">"</span>cancel<span class="pl-pds">"</span></span>&gt;
+    Modal Text
+&lt;/<span class="pl-ent">modal</span>&gt;
+
+<span class="pl-c"><span class="pl-c">&lt;!--</span>custom content<span class="pl-c">--&gt;</span></span>
+&lt;<span class="pl-ent">modal</span> <span class="pl-e">title</span>=<span class="pl-s"><span class="pl-pds">"</span>Modal Title<span class="pl-pds">"</span></span> :<span class="pl-e">show</span>.<span class="pl-e">sync</span>=<span class="pl-s"><span class="pl-pds">"</span>show<span class="pl-pds">"</span></span> @<span class="pl-e">ok</span>=<span class="pl-s"><span class="pl-pds">"</span>ok<span class="pl-pds">"</span></span> @<span class="pl-e">cancel</span>=<span class="pl-s"><span class="pl-pds">"</span>cancel<span class="pl-pds">"</span></span>&gt;
+    &lt;<span class="pl-ent">div</span>&gt;Modal Body&lt;/<span class="pl-ent">div</span>&gt;
+    
+    &lt;<span class="pl-ent">div</span> <span class="pl-e">slot</span>=<span class="pl-s"><span class="pl-pds">"</span>header<span class="pl-pds">"</span></span>&gt;Modal Header&lt;/<span class="pl-ent">div</span>&gt;
+    &lt;<span class="pl-ent">div</span> <span class="pl-e">slot</span>=<span class="pl-s"><span class="pl-pds">"</span>title<span class="pl-pds">"</span></span>&gt;Modal Title&lt;/<span class="pl-ent">div</span>&gt;
+    &lt;<span class="pl-ent">div</span> <span class="pl-e">slot</span>=<span class="pl-s"><span class="pl-pds">"</span>footer<span class="pl-pds">"</span></span>&gt;Modal Footer&lt;/<span class="pl-ent">div</span>&gt;
+&lt;/<span class="pl-ent">modal</span>&gt;
+</pre></div>
+<p>#Props</p>
+<div class="highlight highlight-source-js"><pre>props<span class="pl-k">:</span> {
+    show<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">Boolean</span>,
+        twoWay<span class="pl-k">:</span> <span class="pl-c1">true</span>,
+        default<span class="pl-k">:</span> <span class="pl-c1">false</span>
+    },
+    title<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">String</span>,
+        default<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Modal<span class="pl-pds">'</span></span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> Bootstrap small style modal</span>
+    small<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">Boolean</span>,
+        default<span class="pl-k">:</span> <span class="pl-c1">false</span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> Bootstrap large style modal</span>
+    large<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">Boolean</span>,
+        default<span class="pl-k">:</span> <span class="pl-c1">false</span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> Bootstrap full style modal    </span>
+    full<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">Boolean</span>,
+        default<span class="pl-k">:</span> <span class="pl-c1">false</span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> if it set false, click background will close modal </span>
+    force<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">Boolean</span>,
+        default<span class="pl-k">:</span> <span class="pl-c1">false</span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> vue transition name</span>
+    transition<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">String</span>,
+        default<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>modal<span class="pl-pds">'</span></span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> [OK button] text</span>
+    okText<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">String</span>,
+        default<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>OK<span class="pl-pds">'</span></span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> [Cancel button] text</span>
+    cancelText<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">String</span>,
+        default<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>Cancel<span class="pl-pds">'</span></span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> [OK button] className</span>
+    okClass<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">String</span>,
+        default<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>btn blue<span class="pl-pds">'</span></span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> [Cancel button] className</span>
+    cancelClass<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">String</span>,
+        default<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>btn red btn-outline<span class="pl-pds">'</span></span>
+    },
+    <span class="pl-c"><span class="pl-c">//</span> automatically close when click [OK button]</span>
+    closeWhenOK<span class="pl-k">:</span> {
+        type<span class="pl-k">:</span> <span class="pl-c1">Boolean</span>,
+        default<span class="pl-k">:</span> <span class="pl-c1">false</span>
+    }
+}</pre></div>
+<h1><a id="user-content-license" class="anchor" href="#license" aria-hidden="true"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>License</h1>
+<p>MIT</p>
+</article>
+  </div>
+
+</div>
+
+<button type="button" data-facebox="#jump-to-line" data-facebox-class="linejump" data-hotkey="l" class="d-none">Jump to Line</button>
+<div id="jump-to-line" style="display:none">
+  <!-- '"` --><!-- </textarea></xmp> --></option></form><form accept-charset="UTF-8" action="" class="js-jump-to-line-form" method="get"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /></div>
+    <input class="form-control linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line&hellip;" aria-label="Jump to line" autofocus>
+    <button type="submit" class="btn">Go</button>
+</form></div>
+
+  </div>
+  <div class="modal-backdrop js-touch-events"></div>
+</div>
+
+
+
+
+    </div>
+  </div>
+
+  </div>
+
+      <div class="container site-footer-container">
+  <div class="site-footer" role="contentinfo">
+    <ul class="site-footer-links float-right">
+        <li><a href="https://github.com/contact" data-ga-click="Footer, go to contact, text:contact">Contact GitHub</a></li>
+      <li><a href="https://developer.github.com" data-ga-click="Footer, go to api, text:api">API</a></li>
+      <li><a href="https://training.github.com" data-ga-click="Footer, go to training, text:training">Training</a></li>
+      <li><a href="https://shop.github.com" data-ga-click="Footer, go to shop, text:shop">Shop</a></li>
+        <li><a href="https://github.com/blog" data-ga-click="Footer, go to blog, text:blog">Blog</a></li>
+        <li><a href="https://github.com/about" data-ga-click="Footer, go to about, text:about">About</a></li>
+
+    </ul>
+
+    <a href="https://github.com" aria-label="Homepage" class="site-footer-mark" title="GitHub">
+      <svg aria-hidden="true" class="octicon octicon-mark-github" height="24" version="1.1" viewBox="0 0 16 16" width="24"><path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
+</a>
+    <ul class="site-footer-links">
+      <li>&copy; 2017 <span title="0.05917s from github-fe-bd32a9f.cp1-iad.github.net">GitHub</span>, Inc.</li>
+        <li><a href="https://github.com/site/terms" data-ga-click="Footer, go to terms, text:terms">Terms</a></li>
+        <li><a href="https://github.com/site/privacy" data-ga-click="Footer, go to privacy, text:privacy">Privacy</a></li>
+        <li><a href="https://github.com/security" data-ga-click="Footer, go to security, text:security">Security</a></li>
+        <li><a href="https://status.github.com/" data-ga-click="Footer, go to status, text:status">Status</a></li>
+        <li><a href="https://help.github.com" data-ga-click="Footer, go to help, text:help">Help</a></li>
+    </ul>
+  </div>
+</div>
+
+
+
+  
+
+  <div id="ajax-error-message" class="ajax-error-message flash flash-error">
+    <svg aria-hidden="true" class="octicon octicon-alert" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M8.865 1.52c-.18-.31-.51-.5-.87-.5s-.69.19-.87.5L.275 13.5c-.18.31-.18.69 0 1 .19.31.52.5.87.5h13.7c.36 0 .69-.19.86-.5.17-.31.18-.69.01-1L8.865 1.52zM8.995 13h-2v-2h2v2zm0-3h-2V6h2v4z"/></svg>
+    <button type="button" class="flash-close js-flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
+      <svg aria-hidden="true" class="octicon octicon-x" height="16" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48z"/></svg>
+    </button>
+    You can't perform that action at this time.
+  </div>
+
+
+    
+    <script crossorigin="anonymous" integrity="sha256-9ZVgaDj6gInYIAHGtNp/a6M/Md3kgGXgA5AUO5DatkI=" src="https://assets-cdn.github.com/assets/frameworks-f595606838fa8089d82001c6b4da7f6ba33f31dde48065e00390143b90dab642.js"></script>
+    <script async="async" crossorigin="anonymous" integrity="sha256-/9HdxwBKwBbzfRDYr5x0VA/AwmjrRjr+roYI6ZnMWuE=" src="https://assets-cdn.github.com/assets/github-ffd1ddc7004ac016f37d10d8af9c74540fc0c268eb463afeae8608e999cc5ae1.js"></script>
+    
+    
+    
+    
+  <div class="js-stale-session-flash stale-session-flash flash flash-warn flash-banner d-none">
+    <svg aria-hidden="true" class="octicon octicon-alert" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M8.865 1.52c-.18-.31-.51-.5-.87-.5s-.69.19-.87.5L.275 13.5c-.18.31-.18.69 0 1 .19.31.52.5.87.5h13.7c.36 0 .69-.19.86-.5.17-.31.18-.69.01-1L8.865 1.52zM8.995 13h-2v-2h2v2zm0-3h-2V6h2v4z"/></svg>
+    <span class="signed-in-tab-flash">You signed in with another tab or window. <a href="">Reload</a> to refresh your session.</span>
+    <span class="signed-out-tab-flash">You signed out in another tab or window. <a href="">Reload</a> to refresh your session.</span>
+  </div>
+  <div class="facebox" id="facebox" style="display:none;">
+  <div class="facebox-popup">
+    <div class="facebox-content" role="dialog" aria-labelledby="facebox-header" aria-describedby="facebox-description">
+    </div>
+    <button type="button" class="facebox-close js-facebox-close" aria-label="Close modal">
+      <svg aria-hidden="true" class="octicon octicon-x" height="16" version="1.1" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48z"/></svg>
+    </button>
+  </div>
+</div>
+
+
+  </body>
+</html>
+

+ 109 - 0
examples/components/DateTimeExample.vue

@@ -0,0 +1,109 @@
+<template>
+    <div>
+
+        <h1>时间日期控件V2</h1>
+
+        <h2>基本表单</h2>
+        <div>
+            <div class="form-group">
+                <label for="exampleInputEmail1">Email address</label>
+                <input type="email"
+                       class="form-control"
+                       id="exampleInputEmail1"
+                       placeholder="Email">
+            </div>
+            <div class="form-group">
+                <label for="exampleInputPassword1">Password</label>
+                <input type="password"
+                       class="form-control"
+                       id="exampleInputPassword1"
+                       placeholder="Password">
+            </div>
+            <div class="form-group">
+                <label for="datetime">日期</label>
+                <DateTimeV2 :dateValue="dateValueV2"
+                            v-on:on-value-change="dateValueV2 = $event"></DateTimeV2>
+            </div>
+        </div>
+
+        {{ dateValueV2 }}
+
+        <h2>内联表单</h2>
+        <div class="form-inline">
+            <div class="form-group">
+                <label for="exampleInputName2">Name</label>
+                <input type="text"
+                       class="form-control"
+                       id="exampleInputName2"
+                       placeholder="Jane Doe">
+            </div>
+            <div class="form-group">
+                <label for="exampleInputEmail2">Email</label>
+                <input type="email"
+                       class="form-control"
+                       id="exampleInputEmail2"
+                       placeholder="jane.doe@example.com">
+            </div>
+            <div class="form-group">
+                <label class="sr-only"
+                       for="exampleInputAmount">Amount (in dollars)</label>
+                <div class="input-group">
+                    <div class="input-group-addon">$</div>
+                    <input type="text"
+                           class="form-control"
+                           id="exampleInputAmount"
+                           placeholder="Amount">
+                    <div class="input-group-addon">.00</div>
+                </div>
+            </div>
+            <div class="form-group">
+                <label class="control-label"
+                       for="inputGroupSuccess3">日期</label>
+                <DateTimeV2 :dateValue="dateValueV2"
+                            v-on:on-value-change="dateValueV2 = $event"></DateTimeV2>
+            </div>
+        </div>
+
+        {{ dateValueV2 }}
+
+        <h2>只读</h2>
+        <DateTimeV2 :dateValue="dateValueV2"
+                    :readonly="true"></DateTimeV2>
+        {{ dateValueV2 }}
+
+        <h2>复制/粘贴</h2>
+        <DateTimeV2 :dateValue="dateValueV2_1"
+                    v-on:on-value-change="dateValueV2_1 = $event"></DateTimeV2>
+        {{ dateValueV2_1 }}
+
+    </div>
+</template>
+
+<script>
+
+
+import DateTimeV2 from "../../packages/datetime-v2/index.js";
+
+export default {
+    name: 'app',
+
+    data: function () {
+        return {
+            dateValue: '2021-10-09',
+            dateValueV2: '2021-10-09 14:02:00',
+            dateValueV2_1: '',
+        }
+    },
+
+    components: {
+        DateTimeV2,
+    },
+
+    methods: {
+
+    }
+}
+</script>
+
+<style>
+</style>

+ 229 - 0
examples/components/Hello.vue

@@ -0,0 +1,229 @@
+<template>
+    <div id="app"
+         class="container">
+        <h1>复选框</h1>
+        <Checkbox></Checkbox>
+        <h1>日期控件</h1>
+        <Date :dateValue="dateValue"
+              @on-value-change="dateValue = $event"></Date>
+        {{dateValue}}
+        <h1>日期+时间控件</h1>
+        <DateTime></DateTime>
+
+        <h1>时间日期控件V2</h1>
+
+        <div class="form-inline">
+            <div class="form-group">
+                <DateTimeV2 :dateValue="dateValueV2"
+                            v-on:on-value-change="dateValueV2 = $event"></DateTimeV2>
+            </div>
+        </div>
+
+        <div>
+            <div class="form-group">
+                <DateTimeV2 :dateValue="dateValueV2"
+                            v-on:on-value-change="dateValueV2 = $event"></DateTimeV2>
+            </div>
+        </div>
+
+        {{ dateValueV2 }}
+
+        <h1>时间日期控件V2-只读</h1>
+        <DateTimeV2 :dateValue="dateValueV2"
+                    :readonly="true"></DateTimeV2>
+        {{ dateValueV2 }}
+
+        <h1>时间日期控件V2-复制/粘贴</h1>
+        <DateTimeV2 :dateValue="dateValueV2_1"
+                    v-on:on-value-change="dateValueV2_1 = $event"></DateTimeV2>
+        {{ dateValueV2_1 }}
+
+        <h1>时间控件</h1>
+        <Time></Time>
+        <h1>加载中</h1>
+        <button @click="showHideLoading">打开加载中</button>
+        <Loading ref="loading"></Loading>
+
+        <h1>模态框</h1>
+        <button @click="$refs.modal.showModal()">打开模态框</button>
+        <Modal ref="modal"
+               @ok="closeModal">
+            <template #header>
+                hello1
+            </template>
+            <div>Hello World</div>
+        </Modal>
+
+        <h1>页面大小</h1>
+        <PageSizeSelect v-on:pageSizeChanged="gridSizeSelect"></PageSizeSelect>
+
+        <h1>复选框</h1>
+        <Switches></Switches>
+
+        <h1>上传控件</h1>
+        <UploadWidget></UploadWidget>
+
+        <h1>扫描仪</h1>
+        <Scanner></Scanner>
+
+        <h1>年份选择器</h1>
+        <YearPicker></YearPicker>
+
+        <h1>导航栏</h1>
+        <div style="border: 1px solid">
+            <NavBar :title="'导航栏1'"
+                    :isGoBack="true"></NavBar>
+        </div>
+
+        <div style="border: 1px solid">
+            <NavBar :title="'导航栏2'"
+                    :isGoBack="false"></NavBar>
+        </div>
+
+        <h1>分页组件</h1>
+
+        <Pagination v-model="pagination"
+                    @callback='pageChanged'></Pagination>
+        数据总数
+        <input type="number"
+               v-model.number="pagination.total" />
+        页面大小
+        <input type="number"
+               v-model.number="pagination.perPage" />
+        <div>
+            当前页数 {{ pagination.currentPage }}
+        </div>
+
+    </div>
+</template>
+
+<script>
+
+
+import Checkbox from "../../packages/checkbox/index.js";
+import Date from "../../packages/date/index.js";
+import DateTime from "../../packages/datetime/index.js";
+import DateTimeV2 from "../../packages/datetime-v2/index.js";
+import Time from "../../packages/time/index.js";
+import Loading from "../../packages/loading/index.js";
+import Modal from '../../packages/modal/index.js';
+import PageSizeSelect from '../../packages/page-size-select/index.js';
+import Scanner from '../../packages/scanner/index.js';
+import Switches from '../../packages/switches/index.js';
+import UploadWidget from '../../packages/upload-widget/index.js';
+import YearPicker from "../../packages/year-picker/index.js";
+import NavBar from '../../packages/navbar/index.js';
+import Pagination from "../../packages/pagination/index.js";
+
+export default {
+    name: 'app',
+
+    data: function () {
+        return {
+            msg: 'Welcome to Your Vue.js App',
+            users: [
+                {
+                    id: 1,
+                    name: "张三",
+                },
+                {
+                    id: 2,
+                    name: "李四",
+                }
+            ],
+            selectedUserId: null,
+            selectedUserName: null,
+            suggestStyles: {
+                "defaultInput": "form-control"
+            },
+
+            pagination: {
+                currentPage: 1, // 当前的页数
+                perPage: 12,    // 每页的显示数量
+                total: 100,     // 显示总数
+            },
+
+            paginationOptions: {
+                offset: 4,
+                previousText: 'Prev',
+                nextText: 'Next',
+                alwaysShowPrevNext: true
+            },
+            selectedMonth: null,
+            dateValue: '2021-10-09',
+
+            dateValueV2: '2021-10-09 14:02:00',
+            dateValueV2_1: '',
+        }
+    },
+
+    components: {
+        Checkbox,
+        Date,
+        DateTime,
+        DateTimeV2,
+        Time,
+        Loading,
+        Modal,
+        PageSizeSelect,
+        Scanner,
+        Switches,
+        UploadWidget,
+        YearPicker,
+        NavBar,
+        Pagination
+    },
+
+    methods: {
+        showHideLoading: function () {
+            let _self = this;
+            _self.$refs.loading.show();
+            window.setTimeout(function () {
+                _self.$refs.loading.hide();
+            }, 3000);
+        },
+
+
+        /**
+          * 修改页Size
+          */
+        gridSizeSelect: function (newPageSize) {
+            console.log("page size changed: " + newPageSize);
+        },
+
+
+        /**
+         * 选择了用户
+         */
+        selectUser: function (user) {
+            if (user) {
+                this.selectedUserId = user.id;
+            } else {
+                this.selectedUserId = null;
+            }
+        },
+
+        loadData: function () {
+
+        },
+
+        /**
+         * 关闭模态框
+         */
+        closeModal: function () {
+            alert("准备关闭模态框。");
+            this.$refs.modal.hideModal();
+        },
+
+        /**
+         * 页面发生了改变
+         */
+        pageChanged: function () {
+            console.log('页面发生了改变%o', this.pagination);
+        },
+    }
+}
+</script>
+
+<style>
+</style>

+ 28 - 0
examples/components/Test.vue

@@ -0,0 +1,28 @@
+<template>
+    <div>
+        121212
+    </div>
+</template>
+
+<script>
+
+
+export default {
+
+    data: function () {
+        return {
+
+        }
+    },
+
+    components: {
+    },
+
+    methods: {
+
+    }
+}
+</script>
+
+<style>
+</style>

+ 29 - 0
examples/main.js

@@ -0,0 +1,29 @@
+import {createApp} from 'vue'
+import {createRouter, createWebHashHistory} from 'vue-router'
+import { createI18n } from 'vue-i18n/dist/vue-i18n.cjs.js'
+import {VTooltip} from 'v-tooltip';
+import mRouter from './route/index.js';
+import App from './App.vue'
+
+const i18n = createI18n({
+	locale: 'zh-CN',
+	messages: {
+		'zh-CN': require('../packages/i18n/zh-CN.js'),
+		'en-US': require('../packages/i18n/en-US.js'),
+	}
+})
+
+const router = createRouter({
+	history: createWebHashHistory(),
+	routes: mRouter.routes, // (缩写) 相当于 routes: routes
+})
+
+
+// new Vue({
+// 	el: '#app',
+// 	i18n,
+// 	router,
+// 	render: h => h(App)
+// })
+
+createApp(App).use(i18n).use(router).use(VTooltip).mount('#app')

+ 35 - 0
examples/route/index.js

@@ -0,0 +1,35 @@
+import Desktop from '../Desktop.vue'
+import Test from '../components/Test.vue'
+// import Hello from '../components/Hello.vue'
+// import DateTimeExample from '../components/DateTimeExample.vue'
+// const Hello = () => import(/* webpackChunkName: "group-user" */ '../components/Hello.vue')
+const DateTimeExample = () => import(/* webpackChunkName: "group-user" */ '../components/DateTimeExample.vue')
+const ProcessReport = () => import(/* webpackChunkName: "group-user" */ '../../packages/process/src/ProcessReport.vue')
+const InfoWindow = () => import(/* webpackChunkName: "group-user" */ '../../packages/info/src/InfoWindow.vue')
+const DocGeneratorSelected = () => import(/* webpackChunkName: "group-user" */ '../../packages/info/src/DocGeneratorSelected.vue')
+
+export default {
+	routes: [
+		{
+			path: '/desktop',
+			component: Desktop,
+			children: [
+				{ path: 'test', component: Test },
+				// { path: 'hello', component: Hello },
+				{ path: 'date-time', component: DateTimeExample },
+				// 流程报表
+				{ path: 'process-report/:no', component: ProcessReport },
+				// 查询窗口
+				{ path: 'info/:infoWindowNo', component: InfoWindow },
+				// 查询窗口
+				{ path: 'doc-generator-selected', component: DocGeneratorSelected },
+			],
+
+		},
+		{
+			path: "/:pathMatch(.*)*",
+			name: "notFound",
+			component: Test,  // 引入 组件
+		},
+	]
+}

+ 186 - 0
examples/vue-monthly-picker/app.vue

@@ -0,0 +1,186 @@
+<template>
+  <div id="app">
+    <img src="./assets/logo.png">
+    <h3>Vue Monthly Picker</h3>
+    <div class="demo-component">
+      <vue-monthly-picker
+       :inputClass="{'input': isDisplayInput}"
+       :disabled="isDisable"
+       :monthLabels="locale"
+       :clearOption="clearOption"
+       :min="min"
+       :max="max"
+       @selected="handleSelect"
+       v-model="selectedMonth"
+       :alignment="alignment"
+       :selectedBackgroundColor="selectedBackgroundColor">
+      </vue-monthly-picker>
+    </div>
+    <div class="columns option-list">
+      <div class="column is-3">
+        <b-field label="Disabled" expanded>
+            <b-checkbox v-model="isDisable">
+                {{ isDisable ? 'Disabled': 'Enable'}}
+            </b-checkbox>
+        </b-field>
+      </div>
+      <div class="column is-3">
+        <b-field label="Range" expanded>
+            <b-checkbox v-model="isLimitRange">
+                {{ rangeDisplay }}
+            </b-checkbox>
+        </b-field>
+      </div>
+      <div class="column is-3">
+        <b-field label="Display" expanded position="is-centered">
+            <b-switch v-model="isDisplayInput"
+                true-value="Input"
+                false-value="Label">
+                {{ isDisplayInput? 'Input': 'Label' }}
+            </b-switch>
+        </b-field>
+      </div>
+      <div class="column is-3">
+        <b-field label="Clear icon" expanded>
+            <b-checkbox v-model="clearOption">
+                {{ clearOption ? 'Enable': 'Disabled'}}
+            </b-checkbox>
+        </b-field>
+      </div>
+    </div>
+    <div class="columns">
+      <div class="column is-4">
+        <b-field label="Localization" expanded>
+            <b-select placeholder="Select a language" v-model="locale">
+              <option
+                  v-for="option in options"
+                  :value="option.monthLabels"
+                  :key="option.id">
+                  {{ option.title }}
+              </option>
+            </b-select>
+        </b-field>
+      </div>
+      <div class="column is-4">
+        <b-field label="Alignment" expanded>
+           <b-select placeholder="Select an alignment" v-model="alignment">
+             <option
+                 v-for="alignment in alignments"
+                 :value="alignment"
+                 :key="alignment">
+                 {{ alignment }}
+             </option>
+           </b-select>
+       </b-field>
+      </div>
+      <div class="column is-4">
+        <b-field label="Selected background color" expanded>
+            <b-select placeholder="Select an color" v-model="selectedBackgroundColor">
+              <option
+                  v-for="color in colorExamples"
+                  :value="color"
+                  :key="color">
+                  {{ color }}
+              </option>
+            </b-select>
+        </b-field>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import moment from 'moment'
+import VueMonthlyPicker from './lib'
+
+export default {
+  name: 'app',
+  data () {
+    return {
+      selectedMonth: moment(),
+      isDisable: false,
+      isDisplayInput: true,
+      locale: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+      alignment: 'left',
+      selectedBackgroundColor: 'blue',
+      options: [
+        {
+          id: 1,
+          title: 'English',
+          monthLabels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+        },
+        {
+          id: 2,
+          title: 'Japanese',
+          monthLabels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
+        },
+        {
+          id: 3,
+          title: 'Korean',
+          monthLabels: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']
+        }
+      ],
+      alignments: ['left', 'center', 'right'],
+      colorExamples: ['blue', 'red', 'black'],
+      min: null,
+      max: null,
+      isLimitRange: false,
+      clearOption: true
+    }
+  },
+  computed: {
+    rangeDisplay () {
+      if (!this.min || !this.max) {
+        return 'Disabled'
+      }
+      return this.min.format('YYYY/MM') + '-' + this.max.format('YYYY/MM')
+    }
+  },
+  watch: {
+    isLimitRange (newValue) {
+      if (newValue) {
+        this.setSelectRange(moment().subtract(6, 'months'), moment().add(6, 'months'))
+      } else {
+        this.setSelectRange(null, null)
+      }
+    }
+  },
+  methods: {
+    handleSelect (value) {
+      console.log('Select', value)
+    },
+    setSelectRange (min, max) {
+      this.min = min
+      this.max = max
+    }
+  },
+  components: {
+    VueMonthlyPicker
+  }
+}
+</script>
+
+<style>
+#app {
+  font-family: 'Avenir', Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+  color: #2c3e50;
+  margin-top: 60px;
+}
+
+.demo-component {
+  width: 250px;
+  margin: auto;
+}
+.control-group {
+  margin-top: 100px;
+}
+.control {
+  text-align: center;
+}
+.option-list {
+  margin-top: 20px;
+}
+</style>

+ 16 - 0
examples/vue-monthly-picker/docs.js

@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import Buefy from 'buefy'
+Vue.use(Buefy)
+import App from './app'
+import router from './router'
+
+import './styles/app.scss'
+
+Vue.config.productionTip = false
+
+/* eslint-disable no-new */
+new Vue({
+  el: '#app',
+  router,
+  render: h => h(App)
+})

+ 4 - 0
examples/vue-monthly-picker/lib.js

@@ -0,0 +1,4 @@
+import VueMonthlyPicker from './components/VueMonthlyPicker'
+import './styles/lib.scss'
+
+export default VueMonthlyPicker

+ 24 - 0
index.html

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>pc-client-component</title>
+    
+    <link rel="stylesheet" type="text/css" href="./bootstrap/css/bootstrap.min.css">
+    <link rel="stylesheet" type="text/css" href="./bootstrap/css/bootstrap-dialog.min.css">
+
+    <script src="./bootstrap/js/jquery.min.js"></script>
+    <script src="./bootstrap/js/jquery.cookie.js"></script>
+
+    <script src="./bootstrap/js/bootstrap.js"></script>
+    <script src="./bootstrap/js/bootstrap-dialog.min.js"></script>
+
+
+    <script src="./moment/moment.js"></script>
+    <script src="./moment/moment.zh-cn.js"></script>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script src="/dist/build.js"></script>
+  </body>
+</html>

+ 9 - 3
package.json

@@ -4,9 +4,9 @@
   "description": "",
   "main": "dist/pc-component-v3.js",
   "scripts": {
-    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
+    "dev": "webpack-dev-server --open --hot --config webpack.config.js",
     "build": "webpack --config webpack.prod.js",
-    "lib": "cross-env NODE_ENV=production webpack --progress --hide-modules --config webpack.lib.js"
+    "lib": "webpack --progress --config webpack.lib.js"
   },
   "directories": {
     "dist": "dist"
@@ -17,14 +17,20 @@
     "@babel/core": "^7.17.5",
     "@babel/preset-env": "^7.16.11",
     "babel-loader": "^8.2.3",
+    "css-loader": "^6.7.0",
+    "file-loader": "^6.2.0",
+    "html-webpack-plugin": "^5.5.0",
     "webpack": "^5.70.0",
     "webpack-cli": "^4.9.2",
     "webpack-dev-server": "^4.7.4",
     "webpack-merge": "^5.8.0"
   },
   "dependencies": {
+    "v-tooltip": "^4.0.0-beta.17",
     "vue": "^3.2.31",
+    "vue-i18n": "^9.1.9",
     "vue-loader": "^17.0.0",
-    "vue-router": "^4.0.13"
+    "vue-router": "^4.0.13",
+    "vuedraggable": "^4.1.0"
   }
 }

+ 7 - 0
packages/checkbox/index.js

@@ -0,0 +1,7 @@
+import Checkbox from './src/Checkbox.vue';
+
+Checkbox.install = function(Vue) {
+    Vue.component(Checkbox.name, Checkbox);
+};
+
+export default Checkbox;

+ 220 - 0
packages/checkbox/src/Checkbox.vue

@@ -0,0 +1,220 @@
+<template>
+    <div class="checkbox-component">
+        <input type="checkbox"
+               :id="id"
+               :name="name"
+               :value="value"
+               :class="className"
+               :required="required"
+               :disabled="disabled"
+               @change="onChange"
+               :checked="state">
+        <label :for="id">
+            <slot name="input-box">
+                <span class="input-box">
+                    <svg class="input-box-tick"
+                         viewBox="0 0 16 16">
+                        <path fill="none"
+                              d="M1.7,7.0l3.8,3.4l9-8.8"></path>
+                    </svg>
+                </span>
+            </slot>
+            <slot></slot>
+        </label>
+    </div>
+</template>
+
+<script>
+export default {
+    name: "CheckBox",
+    
+    model: {
+        prop: 'modelValue',
+        event: 'input'
+    },
+
+    props: {
+        id: {
+            type: String,
+            default: function () {
+                return 'checkbox-id-' + this._uid;
+            },
+        },
+        name: {
+            type: String,
+            default: null,
+        },
+        value: {
+            default: null,
+        },
+        modelValue: {
+            default: undefined,
+        },
+        className: {
+            type: String,
+            default: null,
+        },
+        checked: {
+            type: Boolean,
+            default: false,
+        },
+        required: {
+            type: Boolean,
+            default: false,
+        },
+        disabled: {
+            type: Boolean,
+            default: false,
+        },
+        model: {}
+    },
+
+    computed: {
+        state() {
+            if (this.modelValue === undefined) {
+                return this.checked;
+            }
+
+            if (Array.isArray(this.modelValue)) {
+                return this.modelValue.indexOf(this.value) > -1;
+            }
+
+            return !!this.modelValue;
+        }
+    },
+
+    methods: {
+        onChange() {
+            this.toggle();
+        },
+
+        toggle() {
+            let value;
+
+            if (Array.isArray(this.modelValue)) {
+                value = this.modelValue.slice(0);
+
+                if (this.state) {
+                    value.splice(value.indexOf(this.value), 1);
+                } else {
+                    value.push(this.value);
+                }
+            } else {
+                value = !this.state;
+            }
+
+            this.$emit('input', value);
+        }
+    },
+
+    watch: {
+        checked(newValue) {
+            if (newValue !== this.state) {
+                this.toggle();
+            }
+        }
+    },
+
+    mounted() {
+        if (this.checked && !this.state) {
+            this.toggle();
+        }
+    },
+};
+</script>
+
+<style>
+/**
+    .checkbox-component {
+        > input {
+            opacity: 0;
+            position: absolute;
+
+            + label > .input-box {
+                display: inline-block;
+                border: 1px solid #000;
+                border-radius: 14%;
+                margin: 0;
+                padding: 0;
+                width: 1em;
+                height: 1em;
+                background: #fff;
+                overflow: hidden;
+                vertical-align: -5%;
+                user-select: none;
+
+                > .input-box-tick {
+                    width: 100%;
+                    height: 100%;
+
+                    > path {
+                        opacity: 0;
+                        stroke: #000;
+                        stroke-width: 2.3px;
+                        stroke-dashoffset: 20;
+                        stroke-dasharray: 20;
+                        transition: stroke-dashoffset 0.15s ease-in;
+                    }
+                }
+            }
+
+            &:checked + label > .input-box > .input-box-tick > path {
+                opacity: 1;
+                stroke-dashoffset: 0;
+            }
+
+            &:focus + label > .input-box {
+                box-shadow: 0 0 2px 3px rgba(115, 185, 255, 0.69);
+            }
+
+            &:disabled + label {
+                opacity: 0.7;
+            }
+        }
+    }
+    */
+.checkbox-component > input {
+    opacity: 0;
+    position: absolute;
+}
+.checkbox-component > input + label > .input-box {
+    display: inline-block;
+    border: 1px solid #000;
+    border-radius: 14%;
+    margin: 0;
+    padding: 0;
+    width: 1em;
+    height: 1em;
+    background: #fff;
+    overflow: hidden;
+    vertical-align: -5%;
+    user-select: none;
+}
+.checkbox-component > input + label > .input-box > .input-box-tick {
+    width: 100%;
+    height: 100%;
+}
+.checkbox-component > input + label > .input-box > .input-box-tick > path {
+    opacity: 0;
+    stroke: #000;
+    stroke-width: 2.3px;
+    stroke-dashoffset: 20;
+    stroke-dasharray: 20;
+    transition: stroke-dashoffset 0.15s ease-in;
+}
+.checkbox-component
+    > input:checked
+    + label
+    > .input-box
+    > .input-box-tick
+    > path {
+    opacity: 1;
+    stroke-dashoffset: 0;
+}
+.checkbox-component > input:focus + label > .input-box {
+    box-shadow: 0 0 2px 3px rgba(115, 185, 255, 0.69);
+}
+.checkbox-component > input:disabled + label {
+    opacity: 0.7;
+}
+</style>

+ 247 - 0
packages/common/Common.js

@@ -0,0 +1,247 @@
+var Notify = require("./Notify.js");
+
+
+module.exports = {
+  pageSize: 20,
+
+
+  // 异常处理
+  processException: function (XMLHttpRequest, textStatus, errorThrown) {
+    var _self = this;
+    console.log(XMLHttpRequest);
+    if (XMLHttpRequest.status == 400) {
+      // 400 Bad Request
+      Notify.error("400", XMLHttpRequest.responseText, true);
+    } else if (XMLHttpRequest.status == 401) {
+      var currentUrl = window.location;
+      var href = window.location.href;
+      // 当前未处于登陆的界面
+      // 系统未登录
+      if (href.indexOf('login') < 0 && href.indexOf('redirectUrl=') < 0) {
+        // 处理钉钉免登陆
+        const clientId = this.getRouteParam("clientId");
+        const appName = this.getRouteParam("appName");
+        const corpId = this.getRouteParam("corpId");
+
+        let newUrl;
+        if (clientId != null && clientId.length > 0 && appName != null && appName.length > 0 && corpId != null && corpId.length > 0) {
+          newUrl = _self.getRedirectUrl("#/login?clientId=" + clientId + "&appName=" + appName + "&corpId=" + corpId + "&redirectUrl=" + encodeURIComponent(currentUrl))
+        } else {
+          newUrl = _self.getRedirectUrl("#/login?redirectUrl=" + encodeURIComponent(currentUrl));
+        }
+        window.location = newUrl;
+      }
+    } else if (XMLHttpRequest.status == 500) {
+      // 500 Internal Server Error
+      Notify.error("500", XMLHttpRequest.responseText, true);
+      if (XMLHttpRequest.responseText.indexOf("登录超时") > 0) {
+        // 如果异常信息包含“登录超时”,则2秒后跳转到登录页面
+        setTimeout(function () {
+          window.location = _self.getRedirectUrl("#/login");
+        }, 2 * 1000);
+      }
+    } else {
+      Notify.error("服务器异常", XMLHttpRequest.responseText, true);
+    }
+  },
+
+
+  /**
+   * 获取主机地址
+   */
+  getRootPath: function () {
+    var protocol = window.location.protocol;
+    //console.log("protocol:" + protocol);
+    var host = window.location.host;
+    //console.log("host:" + host);
+    var localhostPaht = protocol + "//" + host;
+    //console.log("localhostPaht:" + localhostPaht);
+    return localhostPaht;
+  },
+
+  getHostPageBaseURL: function () {
+    return this.getRootPath() + "/"
+  },
+
+  // 获取图片路径url
+  getFileServerUrl: function () {
+    return this.getRootPath() + "/"
+  },
+
+  // 获取API的地址
+  getApiURL: function (apiName) {
+    return this.getHostPageBaseURL() + "api/" + apiName;
+  },
+
+  /**
+   * 获取API的地址
+   * @param {*} apiName 
+   * @returns 
+   */
+  getApiUrl2: function (apiName) {
+    if (apiName === undefined || apiName === null || apiName.length === 0) {
+      return this.getRootPath();
+    }
+    if (apiName[0] === '/') {
+      return this.getRootPath() + apiName;
+    } else {
+      return this.getRootPath() + "/" + apiName;
+    }
+  },
+
+
+  // 获取测试API的地址
+  getTestApiURL: function (apiName) {
+    return "http://xxx/" + "api/" + apiName;
+  },
+
+  // 获取图片路径
+  getImageUrl: function (imageName) {
+    if (imageName == null || imageName == '') {
+      return this.getFileServerUrl() + 'notFound.png';
+    } else {
+      return this.getFileServerUrl() + imageName;
+    }
+  },
+
+  // 获取图片路径
+  getImageSrc: function (className, imageName) {
+    var accountId = localStorage.getItem("account");
+    if (imageName == null) {
+      return null;
+    }
+    if (imageName != null && imageName[0] == '/') {
+      return this.getFileServerUrl() + "Files/" + accountId + "/Images/" + className + imageName;
+    } else {
+      return this.getFileServerUrl() + "Files/" + accountId + "/Images/" + className + "/" + imageName;
+    }
+  },
+
+
+  // 获取略缩图图片路径
+  getThumbnailImageSrc: function (className, imageName) {
+    var accountId = localStorage.getItem("account");
+    if (imageName == null) {
+      return null;
+    }
+    if (imageName != null && imageName[0] == '/') {
+      return this.getFileServerUrl() + "Files/" + accountId + "/Images/" + className + "/thumbnail" + imageName;
+    } else {
+      return this.getFileServerUrl() + "Files/" + accountId + "/Images/" + className + "/thumbnail/" + imageName;
+    }
+  },
+
+  /**
+   * 获取附件的路径
+   * @param  {[type]} className [description]
+   * @param  {[type]} imageName [description]
+   * @return {[type]}           [description]
+   */
+  getAttachmentsSrc: function (className, imageName) {
+    var accountId = localStorage.getItem("account");
+    return this.getFileServerUrl() + "Files/" + accountId + "/Attachments/" + className + "/" + imageName;
+  },
+
+  // 获取图片路径
+  getVideoSrc: function (className, imageName) {
+    var accountId = localStorage.getItem("account");
+    if (imageName == undefined || imageName == "") {
+      return this.getHostPageBaseURL() + "static/image/noImage.jpg";
+    }
+
+    return this.getFileServerUrl() + "Files/" + accountId + "/Video/" + className + "/" + imageName;
+  },
+
+  // 给请求头中加上account和token信息
+  addTokenToRequest: function (request) {
+    var token = $.cookie('token');
+    var account = $.cookie('account');
+    if (token == undefined) {
+      var localStorageToken = localStorage.getItem('token');
+      if (localStorageToken != undefined) {
+        token = localStorageToken;
+      }
+    }
+    if (account == undefined) {
+      var localStorageAccount = localStorage.getItem('account');
+      if (localStorageAccount != undefined) {
+        account = localStorageAccount;
+      }
+    }
+    request.setRequestHeader("account", account);
+    request.setRequestHeader("token", token);
+
+  },
+
+  /**
+   * 获取Token
+   */
+  getToken: function () {
+    return $.cookie('token');
+  },
+
+  // 清空 Cookie
+  clearCookie: function () {
+    var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
+    if (keys) {
+      for (var i = keys.length; i--;) {
+        // 清除当前域名路径的有限日期
+        document.cookie = keys[i] + '=0;path=/;expires=' + new Date(0).toUTCString();
+        // Domain Name域名 清除当前域名的
+        document.cookie = keys[i] + '=0;path=/;domain=' + document.domain + ';expires=' + new Date(0).toUTCString();
+        // 清除一级域名下的或指定的
+        document.cookie = keys[i] + '=0;path=/;domain=baidu.com;expires=' + new Date(0).toUTCString();
+      }
+    }
+  },
+
+
+  // 获取路由中的参数
+  getRouteParam: function (name) {
+    var reg = new RegExp("(^|\\?|&)" + name + "=([^&]*)(\\s|&|$)", "i");
+    if (reg.test(location.href)) return unescape(RegExp.$2.replace(/\+/g, " "));
+    return "";
+  },
+
+
+  /**
+   * 获取跳转的路径
+   * @param {*} url 
+   */
+  getRedirectUrl: function (url) {
+    var href = window.location.href;
+    if (href.indexOf("pcapp") >= 0) {
+      return this.getRootPath() + "/pcapp/" + url;
+    } else {
+      return this.getRootPath() + "/" + url;
+    }
+  },
+
+  clearLocalStorage: function () {
+    // 清理localStorage时需要保留的参数列表
+    var reserveParams = ["hostPageBaseURL", "workShopId", "resourceInstanceId",
+      "resourceInstanceName", "apsBaseUrl", "cameraBaseURL"];
+    //存放的信息
+    var reserveParamValues = [];
+
+    //获取参数信息 
+    var len = reserveParams.length;
+    for (var i = 0; i < len; i++) {
+      var reserveParam = reserveParams[i];
+      var reserveParamValue = "";
+      if (localStorage.getItem(reserveParam) != undefined) {
+        reserveParamValue = localStorage.getItem(reserveParam);
+      }
+      reserveParamValues.push(reserveParamValue);
+    }
+
+    //清理localStorage
+    window.localStorage.clear();
+
+    //还原参数信息
+    for (var i = 0; i < len; i++) {
+      localStorage.setItem(reserveParams[i], reserveParamValues[i]);
+    }
+  },
+}
+

+ 67 - 0
packages/common/DownloadService.js

@@ -0,0 +1,67 @@
+var Common = require("./Common.js");
+
+/**
+ * 报表下载服务
+ */
+module.exports = {
+
+	/**
+	 * GET 方式下载文件
+	 * @param {*} url 
+	 * @param {*} fileName 
+	 */
+	downloadFile: function (url, fileName) {
+		var a = document.createElement("a");
+		a.download = fileName;
+		a.href = url;
+		$("body").append(a); // 修复firefox中无法触发click
+		a.click();
+		$(a).remove();
+	},
+
+
+	/**
+	 * 报表下载
+	 * @param {Object} fileName
+	 * @author GuoZhiBo 20200410
+	 */
+	reportDownload: function (fileName) {
+		var downloadUrl = Common.getApiURL('file/reportDownload') + "?fileName=" + window.encodeURIComponent(fileName);
+		this.downloadFile(downloadUrl, fileName);
+	},
+
+	/**
+	 * 文件下载
+	 * @param {Object} className 类名称
+	 * @param {Object} fileName 文件名称
+	 * @author GuoZhiBo 20211008
+	 */
+	fileDownload:function(className, fileName){
+	 	var downloadUrl = Common.getApiURL('file/fileDownload') + "?className=" + className
+	                + "&fileName=" + window.encodeURIComponent(fileName);
+	    this.downloadFile(downloadUrl, fileName);
+	},
+
+	/**
+	 * POST 方式下载文件
+	 * @param {http请求的地址} url 
+	 * @param {post请求需要的参数} params 
+	 */
+	postDownloadFile: function (url, params) {
+		var form = document.createElement("form");
+		form.style.display = "none";
+		form.action = url;
+		form.method = "post";
+		document.body.appendChild(form);
+		// 动态创建input并给value赋值
+		for (var key in params) {
+			var input = document.createElement("input");
+			input.type = "hidden";
+			input.name = key;
+			input.value = params[key];
+			form.appendChild(input);
+		}
+		form.submit();
+		form.remove();
+	}
+}

+ 25 - 0
packages/common/IFrameUtil.js

@@ -0,0 +1,25 @@
+module.exports = {
+    /**
+     * 关闭iframe
+     */
+    close: function(){
+        var index = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引
+        parent.layer.close(index); //再执行关闭
+    },
+
+    /**
+     * 获取CURD窗口的数据
+     */
+    getCurdModelData: function(){
+        return parent.getModelData();
+    },
+
+    /**
+     * 设置CURD窗口的数据
+     * @param {*} modelData 
+     */
+    setCurdModelData: function(modelData){
+        modelData.restore = true;
+        parent.modelDataChanged(modelData);
+    }
+}

+ 72 - 0
packages/common/Language.js

@@ -0,0 +1,72 @@
+module.exports = {
+    /**
+     * 获取国际化的菜单名称
+     */
+    getMenuNameTrl: function (locale, menu) {
+        if (menu == null || menu == '') {
+            return null;
+        }
+        if (locale == 'en-US') {
+            return (menu.nameEng == null || menu.nameEng == '') ? menu.name : menu.nameEng;
+        } else {
+            return menu.name;
+        }
+    },
+
+    /**
+     * 获取国际化的名称
+     */
+    getNameTrl: function (locale, data) {
+
+        if (data.fieldName == "ai.name") {
+            let a = 0;
+        }
+        if (data == null || data == '') {
+            return null;
+        }
+        if (locale == 'en-US') {
+            return (data.nameEng == null || data.nameEng == '') ? data.name : data.nameEng;
+        } else {
+            return data.name;
+        }
+    },
+
+    /**
+     * 获取国际化的帮助
+     */
+    getHelpTrl: function (locale, data) {
+        if (data == null || data == '') {
+            return null;
+        }
+        if (locale == 'en-US') {
+            return (data.helpEng == null || data.helpEng == '') ? data.help : data.helpEng;
+        } else {
+            return data.help;
+        }
+    },
+
+    /**
+     * 获取国际化的显示名称
+     */
+    getDisplayNameTrl: function (locale, data) {
+        if (data == null || data == '') {
+            return null;
+        }
+        if (locale == 'en-US') {
+            return (data.displayNameEng == null || data.displayNameEng == '') ? data.displayName : data.displayNameEng;
+        } else {
+            return data.displayName;
+        }
+    },
+
+    getGroupNameTrl: function (locale, data) {
+        if (data == null || data == '') {
+            return null;
+        }
+        if (locale == 'en-US') {
+            return (data.groupNameEng == null || data.groupNameEng == '') ? data.groupName : data.groupNameEng;
+        } else {
+            return data.groupName;
+        }
+    },
+}

+ 165 - 0
packages/common/Notify.js

@@ -0,0 +1,165 @@
+var ModalFix = require("../modal/src/ModalFix.JS");
+
+module.exports = {
+    /**
+     * 显示模态框
+     */
+    show : function(options){
+        var dialog = BootstrapDialog.show(options);
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+    },
+
+	/**
+	 * 显示通知
+	 * @param {title} 标题
+	 * @param {content} 内容
+	 * @param {autoClose} 自动延时关闭时间(毫秒)
+	 * author: 杨志杰
+	 * version: 1.0
+	 */
+	notice : function(title, content, autoClose){
+        var dialog = BootstrapDialog.show({
+            title: title,
+            message: content,
+            type: BootstrapDialog.TYPE_WARNING,
+        });
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+
+        if(autoClose && autoClose > 0){
+            setTimeout(function(){
+                dialog.close();
+            }, autoClose);
+        }
+    },
+
+	/**
+	 * 显示通知
+	 * @param {title} 标题
+	 * @param {content} 内容
+	 * author: 杨志杰
+	 * version: 1.0
+	 */
+	noticeBig : function(title, content){
+        var dialog = BootstrapDialog.show({
+            title: title,
+            message: content,
+            type: BootstrapDialog.TYPE_WARNING,
+    		size: BootstrapDialog.SIZE_WIDE,
+        });
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+    },
+
+
+    /**
+	 * 显示消息
+	 * @param {title} 标题
+	 * @param {content} 内容
+	 * @param {autoClose} 自动延时关闭时间(毫秒)
+	 * author: 杨志杰
+	 * version: 1.0
+	 */
+    info : function(title, content, autoClose){
+        var dialog = BootstrapDialog.show({
+            title: title,
+            message: content,
+            type: BootstrapDialog.TYPE_INFO,
+        });
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+
+        
+        if(autoClose && autoClose > 0){
+            setTimeout(function(){
+                dialog.close();
+            }, autoClose);
+        }
+    },
+
+
+    /**
+	 * 显示消息
+	 * @param {title} 标题
+	 * @param {content} 内容
+	 * @param {autoClose} 自动延时关闭时间(毫秒)
+	 * author: 杨志杰
+	 * version: 1.0
+	 */
+    infoBig : function(title, content, autoClose){
+        var dialog = BootstrapDialog.show({
+            title: title,
+            message: content,
+            type: BootstrapDialog.TYPE_INFO,
+    		size: BootstrapDialog.SIZE_WIDE,
+        });
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+
+        
+        if(autoClose && autoClose > 0){
+            setTimeout(function(){
+                dialog.close();
+            }, autoClose);
+        }
+    },
+
+    /**
+	 * 显示成功信息
+	 * @param {title} 标题
+	 * @param {content} 内容
+	 * @param {autoClose} 自动延时关闭时间(毫秒)
+	 * author: 杨志杰
+	 * version: 1.0
+	 */
+    success : function(title, content, autoClose){
+        var dialog = BootstrapDialog.show({
+            title: title,
+            message: content,
+            type: BootstrapDialog.TYPE_SUCCESS,
+        });
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+
+        
+        if(autoClose && autoClose > 0){
+            setTimeout(function(){
+                dialog.close();
+            }, autoClose);
+        }
+    },
+
+    /**
+     * 显示错误信息
+     * @param {title} 标题
+     * @param {content} 内容
+     * @param {autoClose} 自动延时关闭时间(毫秒)
+     * author: 杨志杰
+     * version: 1.0
+     */
+    error : function(title, content, autoClose){
+        var dialog = BootstrapDialog.show({
+            title: title,
+            message: content,
+            type: BootstrapDialog.TYPE_DANGER,
+        });
+
+        dialog.getModal().on('hidden.bs.modal', {dialog: this}, function (event) {
+            ModalFix.fix();
+        });
+    }
+    
+}

+ 29 - 0
packages/common/SqlApi.js

@@ -0,0 +1,29 @@
+var Common = require("./Common.js");
+
+module.exports = {
+
+    /**
+     * 通过GET方式调用SQL API
+     * @param no 编号
+     * @param data json 数据
+     * @returns 
+     */
+    execute: function (no, data) {
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                type: "get",
+                url: Common.getApiURL("SqlApiResource/execute/" + no),
+                data: data,
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    }
+}

+ 133 - 0
packages/common/UserStorageResource.js

@@ -0,0 +1,133 @@
+var Common = require("./Common.js");
+
+/**
+ * 工具类自动生成的API,请勿做任何修改,请勿做任何修改,请勿做任何修改(重要的事情说3遍)
+ * 工具作者: 杨志杰
+ * UserStorageResource 
+ */
+module.exports = {
+
+	/**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 清空用户userStorage 
+	 */
+	clearUserStorage: function(){
+		var requestUrl = "UserStorageResource/clearUserStorage";
+
+
+		return new Promise((resolve, reject) => {
+			$.ajax({
+				url: Common.getApiURL(requestUrl),
+				type: "get",
+				
+				
+				
+				
+				beforeSend: function(request) {
+					Common.addTokenToRequest(request);
+				},
+				success: function(data) {
+					resolve(data);
+				},
+				error: function(XMLHttpRequest, textStatus, errorThrown) {
+					reject(XMLHttpRequest);
+				}
+			});
+		});
+	},
+
+	/**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 *  
+	 */
+	downloadUserStorage: function(){
+		var requestUrl = "UserStorageResource/downloadUserStorage";
+
+
+		return new Promise((resolve, reject) => {
+			$.ajax({
+				url: Common.getApiURL(requestUrl),
+				type: "get",
+				
+				dataType: "json",
+				
+				
+				beforeSend: function(request) {
+					Common.addTokenToRequest(request);
+				},
+				success: function(data) {
+					resolve(data);
+				},
+				error: function(XMLHttpRequest, textStatus, errorThrown) {
+					reject(XMLHttpRequest);
+				}
+			});
+		});
+	},
+
+	/**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 根据key查询UserStorageDto 
+	 */
+	uniqueByKey: function(key){
+		var requestUrl = "UserStorageResource/uniqueByKey/{key}";
+		if(key != null){
+			requestUrl = requestUrl.replace('{key}', key);
+		}
+
+
+		return new Promise((resolve, reject) => {
+			$.ajax({
+				url: Common.getApiURL(requestUrl),
+				type: "get",
+				
+				dataType: "text",
+				
+				
+				beforeSend: function(request) {
+					Common.addTokenToRequest(request);
+				},
+				success: function(data) {
+					resolve(data);
+				},
+				error: function(XMLHttpRequest, textStatus, errorThrown) {
+					reject(XMLHttpRequest);
+				}
+			});
+		});
+	},
+
+	/**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 保存用户localStorage 
+	 */
+	uploadUserStorage: function(userStorageDtos){
+		var requestUrl = "UserStorageResource/uploadUserStorage";
+
+
+		return new Promise((resolve, reject) => {
+			$.ajax({
+				url: Common.getApiURL(requestUrl),
+				type: "post",
+				contentType: "application/json",
+				
+				dataType: "json",
+				data: JSON.stringify(userStorageDtos),
+				
+				beforeSend: function(request) {
+					Common.addTokenToRequest(request);
+				},
+				success: function(data) {
+					resolve(data);
+				},
+				error: function(XMLHttpRequest, textStatus, errorThrown) {
+					reject(XMLHttpRequest);
+				}
+			});
+		});
+	}
+}

+ 59 - 0
packages/common/Uuid.js

@@ -0,0 +1,59 @@
+module.exports = {
+	createUUID: function () {
+		var _self = this;
+		// Loose interpretation of the specification DCE 1.1: Remote Procedure Call
+		// since JavaScript doesn't allow access to internal systems, the last 48 bits 
+		// of the node section is made up using a series of random numbers (6 octets long).
+		//  
+		var dg = new Date(1582, 10, 15, 0, 0, 0, 0);
+		var dc = new Date();
+		var t = dc.getTime() - dg.getTime();
+		var tl = _self.getIntegerBits(t, 0, 31);
+		var tm = _self.getIntegerBits(t, 32, 47);
+		var thv = _self.getIntegerBits(t, 48, 59) + '1'; // version 1, security version is 2
+		var csar = _self.getIntegerBits(_self.rand(4095), 0, 7);
+		var csl = _self.getIntegerBits(_self.rand(4095), 0, 7);
+		// since detection of anything about the machine/browser is far to buggy, 
+		// include some more random numbers here
+		// if NIC or an IP can be obtained reliably, that should be put in
+		// here instead.
+		var n = _self.getIntegerBits(_self.rand(8191), 0, 7) +
+			_self.getIntegerBits(_self.rand(8191), 8, 15) +
+			_self.getIntegerBits(_self.rand(8191), 0, 7) +
+			_self.getIntegerBits(_self.rand(8191), 8, 15) +
+			_self.getIntegerBits(_self.rand(8191), 0, 15); // this last number is two octets long
+		return tl + tm + thv + csar + csl + n;
+	},
+
+	//Pull out only certain bits from a very large integer, used to get the time
+	//code information for the first part of a UUID. Will return zero's if there 
+	//aren't enough bits to shift where it needs to.
+	getIntegerBits: function (val, start, end) {
+		var base16 = this.returnBase(val, 16);
+		var quadArray = new Array();
+		var quadString = '';
+		var i = 0;
+		for (i = 0; i < base16.length; i++) {
+			quadArray.push(base16.substring(i, i + 1));
+		}
+		for (i = Math.floor(start / 4); i <= Math.floor(end / 4); i++) {
+			if (!quadArray[i] || quadArray[i] == '')
+				quadString += '0';
+			else
+				quadString += quadArray[i];
+		}
+		return quadString;
+	},
+
+	//Replaced from the original function to leverage the built in methods in
+	//JavaScript. Thanks to Robert Kieffer for pointing this one out
+	returnBase: function (number, base) {
+		return (number).toString(base).toUpperCase();
+	},
+
+	//pick a random number within a range of numbers
+	//int b rand(int a); where 0 <= b <= a
+	rand: function (max) {
+		return Math.floor(Math.random() * (max + 1));
+	},
+}

+ 44 - 0
packages/customer-window/src/api/CustomerWindowResource.js

@@ -0,0 +1,44 @@
+var Common = require("../../../common/Common.js");
+
+/**
+ * 工具类自动生成的API,请勿做任何修改,请勿做任何修改,请勿做任何修改(重要的事情说3遍)
+ * 工具作者: 杨志杰
+ *  
+ */
+module.exports = {
+
+	/**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 根据自定义窗口编号查询自定义窗口DTO 
+	 */
+	uniqueByNo: function(no){
+		var requestUrl = "CustomerWindowResource/uniqueByNo";
+		var paramCount = 0;
+		if(no != null){
+			requestUrl += (paramCount > 0) ? '&' : '?';
+			requestUrl += ('no=' + no);
+			paramCount ++;
+		}
+		return new Promise((resolve, reject) => {
+			$.ajax({
+				url: Common.getApiURL(requestUrl),
+				type: "get",
+				
+				dataType: "json",
+				
+				
+				beforeSend: function(request) {
+					Common.addTokenToRequest(request);
+				},
+				success: function(data) {
+					resolve(data);
+				},
+				error: function(XMLHttpRequest, textStatus, errorThrown) {
+					reject(XMLHttpRequest);
+				}
+			});
+		});
+	}
+	
+}

+ 8 - 0
packages/date/index.js

@@ -0,0 +1,8 @@
+
+import Date from './src/Date.vue';
+
+Date.install = function(Vue) {
+    Vue.component(Date.name, Date);
+};
+
+export default Date;

+ 67 - 0
packages/date/src/Date.vue

@@ -0,0 +1,67 @@
+<template>
+    <date-picker :readonly="readonly"
+                 :date="starttime"
+                 :option="option"
+                 style="padding: 0px; border: none;"></date-picker>
+</template>
+
+<script>
+var VueDatepicker = require("../../vue-datepicker/src/vue-datepicker.vue").default;
+var VueDatepickerOption0 = require("./vue-datepicker-setting.js");
+
+module.exports = {
+    name: "date",
+    props: [
+        "dateValue", "readonly"
+    ],
+
+    data: function () {
+        var vueDatepickerOption = VueDatepickerOption0();
+        vueDatepickerOption.option.type = 'day';
+        vueDatepickerOption.option.format = 'YYYY-MM-DD';
+
+        vueDatepickerOption.timeoption.type = 'min';
+        vueDatepickerOption.timeoption.format = 'YYYY-MM-DD';
+
+        vueDatepickerOption.multiOption.type = 'multi-day';
+        vueDatepickerOption.multiOption.format = 'YYYY-MM-DD';
+        return {
+            starttime: {
+                time: this.dateValue
+            },
+            endtime: {
+                time: ''
+            },
+            testTime: '',
+            multiTime: '',
+            option: vueDatepickerOption.option,
+            timeoption: vueDatepickerOption.timeoption,
+            multiOption: vueDatepickerOption.multiOption,
+            limit: vueDatepickerOption.limit,
+        }
+    },
+
+    components: {
+        'date-picker': VueDatepicker
+    },
+
+    watch: {
+        // 'value': function(val){
+        // 	// 监听外部对props属性value的变更,并同步到组件内
+        // 	starttime.time = val;
+        // },
+
+        'starttime.time': function (val) {
+            // 组件内对starttime.time变更后向外部发送事件通知
+            this.$emit("on-value-change", val);
+            this.$emit("input", val);
+            console.log('name has been changed:', val);
+        },
+
+        dateValue: function (val) {
+            this.starttime.time = val;
+        }
+    }
+}
+</script>
+

+ 61 - 0
packages/date/src/vue-datepicker-setting.js

@@ -0,0 +1,61 @@
+module.exports = function () {
+    return {
+        option: {
+            type: '',
+            week: ['一', '二', '三', '四', '五', '六', '日'],
+            month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+            format: 'YYYY-MM-DD',
+            placeholder: '请选择时间',
+            inputStyle: {
+                'display': 'inline-block',
+                'width': '100%',
+                'height': '34px',
+                'padding': '6px',
+                'font-size': '14px',
+                'line-height': '22px',
+                'color': '#555',
+                'background-color': '#fff',
+                'background-image': 'none',
+                'border': '1px solid #ccc',
+                'border-radius': '4px 4px 4px 4px',
+                'box-shadow': 'inset 0 1px 1px rgba(0,0,0,.075)',
+                'transition': 'border-color ease-in-out .15s,box-shadow ease-in-out .15s'
+
+            },
+            color: {
+                checked: '#F50057',
+                header: '#3f51b5',
+                headerText: '#fff'
+            },
+            buttons: {
+                ok: '确定',
+                cancel: '取消'
+            },
+            overlayOpacity: 0.5, // 0.5 as default
+            dismissible: true // as true as default
+        },
+        timeoption: {
+            type: '',
+            week: ['一', '二', '三', '四', '五', '六', '日'],
+            month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+            format: 'YYYY-MM-DD'
+        },
+        multiOption: {
+            type: '',
+            week: ['一', '二', '三', '四', '五', '六', '日'],
+            month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+            format: "YYYY-MM-DD"
+        },
+        limit: [
+            {
+                type: 'weekday',
+                available: [1, 2, 3, 4, 5]
+            },
+            {
+                type: 'fromto',
+                from: '2016-02-01',
+                to: '2016-02-20'
+            }
+        ]
+    }
+}

+ 8 - 0
packages/datetime-v2/index.js

@@ -0,0 +1,8 @@
+
+import DateTimeV2 from './src/DateTimeV2.vue';
+
+DateTimeV2.install = function(Vue) {
+    Vue.component(DateTimeV2.name, DateTimeV2);
+};
+
+export default DateTimeV2;

+ 148 - 0
packages/datetime-v2/src/DateTimeV2.vue

@@ -0,0 +1,148 @@
+<template>
+    <div class="datetime-container input-group" :class="{'has-error' : isValid === false}">
+        <input type="datetime-local"
+               id="datetime"
+               v-bind:readonly="readonly"
+               :value="dateTime"
+               class="datetime-item-1 form-control"
+               @change="datetimeLocalValueChanged($event)" />
+
+        <input type="text"
+               v-bind:readonly="readonly"
+               :value="dateValue"
+               class="datetime-item-2 form-control"
+               @change="textValueChanged($event)" />
+
+        <span v-if="isValid === false" class="glyphicon glyphicon-remove datetime-item-3 form-control-feedback" aria-hidden="true"/>
+
+    </div>
+</template>
+
+<script>
+
+module.exports = {
+
+    name: "date-time-v2",
+
+    props: [
+        "dateValue", "readonly"
+    ],
+
+    data: function () {
+        return {
+            "isValid": true,
+        }
+    },
+
+    components: {
+
+    },
+
+    methods: {
+        /**
+         * 是否是有效的日期时间格式,YYYY-MM-DD HH:mm:ss
+         */
+        isValidDateTime: function (text) {
+            if (text == null || text.length == 0) {
+                return false;
+            }
+            return moment(text, 'YYYY-MM-DD HH:mm:ss', true).isValid();
+        },
+
+        textValueChanged: function (event) {
+            let newValue = event.target.value;
+
+            // 日期时间校验
+            if (this.isValidDateTime(newValue)) {
+                this.isValid = true;
+                console.log('DateTimeV2 input value changed: orginal value: %s, current value: %s', this.dateValue, newValue);
+                this.$emit("on-value-change", newValue);
+            } else {
+                this.isValid = false;
+            }
+        },
+
+
+        /**
+         * 值改变事件
+         */
+        datetimeLocalValueChanged: function (event) {
+            let newValue = event.target.value;
+            let parsedDateTime = '';
+            if (newValue != null && newValue.length > 0) {
+                parsedDateTime = moment(newValue).format('YYYY-MM-DD HH:mm:ss');
+            }
+
+            // 日期时间校验
+            if (this.isValidDateTime(parsedDateTime)) {
+                this.isValid = true;
+                console.log('DateTimeV2 value changed: orginal value: %s, current value: %s', newValue, parsedDateTime);
+                this.$emit("on-value-change", parsedDateTime);
+            } else {
+                this.isValid = false;
+            }
+        },
+    },
+
+    mounted: function () {
+        
+    },
+
+
+    computed: {
+
+        /**
+         * 把(YYYY-MM-DD HH:mm:ss)格式的日期字符串转换成datetime-local(YYYY-MM-DDTHH:mm:ss)
+         */
+        dateTime: function () {
+            if (this.dateValue == null || this.dateValue.length == 0) {
+                return null;
+            } else {
+                return moment(this.dateValue, 'YYYY-MM-DD HH:mm:ss').format('YYYY-MM-DDTHH:mm:ss');
+            }
+        },
+
+    }
+}
+</script>
+
+<style>
+.form-inline .datetime-container {
+    display : inline-flex !important;
+    vertical-align: middle;
+}
+</style>
+
+
+<style scoped>
+.datetime-container {
+    position: relative;
+    height: 34px;
+    width: auto;
+    vertical-align: middle;
+    min-width: 200px;
+    /* display : inline-flex; */
+    display : block;
+    
+}
+
+.datetime-item-1 {
+    position: absolute;
+    display: inline;
+    width: 100%;
+    cursor: pointer;
+    border-radius: 4px !important;
+}
+
+.datetime-item-2 {
+    position: absolute;
+    border-radius: 4px 0px 0px 4px !important;
+    display: inline;
+    width: calc(100% - 45px) !important;
+}
+
+.datetime-item-3 {
+    right: 45px;
+}
+</style>
+

+ 8 - 0
packages/datetime/index.js

@@ -0,0 +1,8 @@
+
+import DateTime from './src/DateTime.vue';
+
+DateTime.install = function(Vue) {
+    Vue.component(DateTime.name, DateTime);
+};
+
+export default DateTime;

+ 64 - 0
packages/datetime/src/DateTime.vue

@@ -0,0 +1,64 @@
+<template>
+    <date-picker :readonly="readonly"
+                 :date="starttime"
+                 :option="option"
+                 style="padding: 0px; border: none;"></date-picker>
+</template>
+
+<script>
+var VueDatepicker = require("../../vue-datepicker/src/vue-datepicker.vue").default;
+var VueDatepickerOption1 = require("./vue-datepicker-setting.js");
+
+module.exports = {
+    name: "date-time",
+    props: [
+        "dateValue", "readonly"
+    ],
+
+    data: function () {
+        var vueDatepickerOption = VueDatepickerOption1();
+        vueDatepickerOption.option.type = 'min';
+        vueDatepickerOption.option.format = 'YYYY-MM-DD HH:mm:ss';
+
+        vueDatepickerOption.timeoption.type = 'min';
+        vueDatepickerOption.timeoption.format = 'YYYY-MM-DD HH:mm:ss';
+
+        vueDatepickerOption.multiOption.type = 'multi-day';
+        vueDatepickerOption.multiOption.format = 'YYYY-MM-DD HH:mm:ss';
+        return {
+            starttime: {
+                time: this.dateValue
+            },
+            endtime: {
+                time: ''
+            },
+            testTime: '',
+            multiTime: '',
+            option: vueDatepickerOption.option,
+            timeoption: vueDatepickerOption.timeoption,
+            multiOption: vueDatepickerOption.multiOption,
+            limit: vueDatepickerOption.limit,
+        }
+    },
+
+    components: {
+        'date-picker': VueDatepicker
+    },
+
+    watch: {
+        // 'value': function(val){
+        // 	// 监听外部对props属性value的变更,并同步到组件内
+        // 	starttime.time = val;
+        // },
+
+        'starttime.time': function (val) {
+            // 组件内对starttime.time变更后向外部发送事件通知
+            this.$emit("on-value-change", val);
+            console.log('name has been changed:', val);
+        },
+        dateValue: function (val) {
+            this.starttime.time = val;
+        }
+    }
+}
+</script>

+ 61 - 0
packages/datetime/src/vue-datepicker-setting.js

@@ -0,0 +1,61 @@
+module.exports = function () {
+    return {
+        option: {
+            type: '',
+            week: ['一', '二', '三', '四', '五', '六', '日'],
+            month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+            format: 'YYYY-MM-DD',
+            placeholder: '请选择时间',
+            inputStyle: {
+                'display': 'block',
+                'width': '100%',
+                'height': '34px',
+                'padding': '6px 12px',
+                'font-size': '14px',
+                'line-height': '1.42857143',
+                'color': '#555',
+                'background-color': '#fff',
+                'background-image': 'none',
+                'border': '1px solid #ccc',
+                'border-radius': '4px 0px 0px 4px',
+                'box-shadow': 'inset 0 1px 1px rgba(0,0,0,.075)',
+                'transition': 'border-color ease-in-out .15s,box-shadow ease-in-out .15s'
+
+            },
+            color: {
+                checked: '#F50057',
+                header: '#3f51b5',
+                headerText: '#fff'
+            },
+            buttons: {
+                ok: '确定',
+                cancel: '取消'
+            },
+            overlayOpacity: 0.5, // 0.5 as default
+            dismissible: true // as true as default
+        },
+        timeoption: {
+            type: '',
+            week: ['一', '二', '三', '四', '五', '六', '日'],
+            month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+            format: 'YYYY-MM-DD'
+        },
+        multiOption: {
+            type: '',
+            week: ['一', '二', '三', '四', '五', '六', '日'],
+            month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
+            format: "YYYY-MM-DD"
+        },
+        limit: [
+            {
+                type: 'weekday',
+                available: [1, 2, 3, 4, 5]
+            },
+            {
+                type: 'fromto',
+                from: '2016-02-01',
+                to: '2016-02-20'
+            }
+        ]
+    }
+}

+ 43 - 0
packages/html-window/src/api/HtmlWindowResource.js

@@ -0,0 +1,43 @@
+var Common = require("../../../common/Common.js");
+
+/**
+ * 工具类自动生成的API,请勿做任何修改,请勿做任何修改,请勿做任何修改(重要的事情说3遍)
+ * 工具作者: 杨志杰
+ * 生成html窗口xml 
+ */
+module.exports = {
+
+	/**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 根据HTML窗口编号查询HTML窗口DTO 
+	 */
+	uniqueByNo: function(no){
+		var requestUrl = "HtmlWindowResource/uniqueByNo";
+		var paramCount = 0;
+		if(no != null){
+			requestUrl += (paramCount > 0) ? '&' : '?';
+			requestUrl += ('no=' + no);
+			paramCount ++;
+		}
+		return new Promise((resolve, reject) => {
+			$.ajax({
+				url: Common.getApiURL(requestUrl),
+				type: "get",
+				
+				dataType: "json",
+				
+				
+				beforeSend: function(request) {
+					Common.addTokenToRequest(request);
+				},
+				success: function(data) {
+					resolve(data);
+				},
+				error: function(XMLHttpRequest, textStatus, errorThrown) {
+					reject(XMLHttpRequest);
+				}
+			});
+		});
+	}
+}

+ 62 - 0
packages/i18n/en-US.js

@@ -0,0 +1,62 @@
+const lang = {    
+    uploadWidget: {
+        uploadFile: "Upload File",
+    },
+    
+    QueryCondition :{
+        simpleQuery: "Simple Query",
+        advancedQuery: "Advanced Query",
+    },
+
+    QueryConditionComplex :{
+        pleaseInputTheContent: "Please Input The Content",
+        pleaseEnterANumber: "Please Enter A Number",
+        all: "All",
+        true: "True",
+        false: "False",
+        filter: "Filter",
+        export: "Export",
+    },
+
+    QueryConditionSimple :{
+        enterSearchCriteria: "Enter Search Criteria",
+        filter: "Filter",
+        refresh: "Refresh",
+        export: "Export",
+    },
+
+    QueryPage :{
+        the : "The",
+        strip: "Strip",
+        total: "Total",
+        displayOnEachPage: "Display On Each Page",
+        resultsOfEnforcement: "Results Of Enforcement",
+        serialNumber: "Serial Number"
+    },
+    
+    QueryPageImage :{
+        viewPicture: "View Picture",
+        downloadPictures: "Download Pictures",
+    },
+
+    PrintWidget :{
+        selectPrinter: "Select Printer",
+        cardIssuingMachine: "Card Issuing Machine",
+        printing: "Printing",
+    },
+
+    DocGenerator: {
+        selectedDatas: "Selected Data"
+    },
+
+    vueBootstrapPagination: {
+        firstPage: "first",
+        lastPage: "last",
+        previous: "«",
+        next: "»",
+    }
+}
+
+export default {
+    lang : lang
+};

+ 64 - 0
packages/i18n/zh-CN.js

@@ -0,0 +1,64 @@
+const lang = {
+    uploadWidget: {
+        uploadFile: "上传文件",
+    },
+
+    QueryConditionSimple: {
+        enterSearchCriteria: "输入搜索条件",
+        filter: "过滤",
+        refresh: "刷新",
+        export: "导出",
+    },
+
+    QueryCondition: {
+        simpleQuery: "简单查询",
+        advancedQuery: "高级查询",
+    },
+
+    QueryConditionComplex: {
+        pleaseInputTheContent: "请输入内容",
+        pleaseEnterANumber: "请输入数字",
+        all: "全部",
+        true: "是",
+        false: "否",
+        filter: "过滤",
+        export: "导出",
+    },
+
+    QueryPage: {
+        the: "第",
+        strip: "条",
+        total: "共计",
+        displayOnEachPage: "每页显示",
+        resultsOfEnforcement: "执行结果",
+        serialNumber:"序号"
+    },
+    
+    QueryPageImage: {
+        viewPicture: "查看图片",
+        downloadPictures: "下载图片",
+    },
+
+    PrintWidget: {
+        selectPrinter: "选择打印机",
+        cardIssuingMachine: "发卡机",
+        printing: "打印",
+    },
+
+    
+    DocGenerator: {
+        selectedDatas: "已选数据"
+    },
+
+    vueBootstrapPagination: {
+        firstPage: "首页",
+        lastPage: "尾页",
+        previous: "«",
+        next: "»",
+    }
+}
+
+
+export default {
+    lang : lang
+};

+ 8 - 0
packages/image-preview/index.js

@@ -0,0 +1,8 @@
+
+import ImagePreview from './src/ImagePreview.vue';
+
+ImagePreview.install = function(Vue) {
+    Vue.component(ImagePreview.name, ImagePreview);
+};
+
+export default ImagePreview;

+ 70 - 0
packages/image-preview/src/ImagePreview.vue

@@ -0,0 +1,70 @@
+/** 图片预览 */
+
+<template>
+    <Modal ref="modal"
+           :large="true">
+        <div class="modal-img-box">
+            <img :src="src"
+                 class="m-img" />
+        </div>
+        <div slot="header">
+            {{$t('lang.imagePreview.imagePreview')}}
+        </div>
+    </Modal>
+</template>
+
+
+<script>
+var Common = require("../../common/Common.js");
+var Modal = require("../../modal/src/Modal.vue").default;
+
+module.exports = {
+    name: "ImagePreview",
+    
+    props: ['className', 'imageName'],
+
+    data: function () {
+        return {
+            "src": null,
+        }
+    },
+
+    components: {
+        Modal
+    },
+
+    methods: {
+        /**
+         * 预览图片
+         */
+        preview: function (className, imageName) {
+            if (imageName != null && imageName != "") {
+                this.$refs.modal.show = true;
+                this.src = Common.getImageSrc(className, imageName);
+            }
+        },
+
+        /**
+         * 预览图片
+         */
+        previewImage: function (imageName) {
+            this.$refs.modal.show = true;
+            this.src = imageName;
+        }
+
+    }
+}
+</script>
+
+
+<style scoped>
+.modal-img-box {
+    width: 100%;
+    text-align: center;
+    overflow: auto;
+}
+
+.m-img {
+    width: 60%;
+}
+</style>

+ 164 - 0
packages/index.js

@@ -0,0 +1,164 @@
+import Loading from './loading/index.js';
+import Modal from './modal/index.js';
+import DateTime from './dateTime/index.js';
+import DateTimeV2 from './dateTime-v2/index.js';
+import Date from './date/index.js';
+import Checkbox from '../packages/checkbox/index.js';
+import PageSizeSelect from './page-size-select/index.js';
+import Time from './time/index.js';
+import Switches from './switches/index.js';
+import YearPicker from './year-picker/index.js';
+import VueBootstrapPagination from './vue-bootstrap-pagination/index.js'
+import VueMonthlyPicker from './vue-monthly-picker/index.js';
+import UploadWidget from './upload-widget/index.js';
+import Scanner from './scanner/index.js';
+import Navbar from './navbar/index.js';
+import ImagePreview from './image-preview/index.js';
+import InfoWindow from './info/index.js';
+import ProcessReport from './process/index.js';
+import ProcessReportResult from './process/process-report-result.js';
+import ProcessReportResultPreview from './process/process-report-result-preview.js';
+
+
+import DocGenerator from "./info/doc-generator.js";
+import InfoSearchWidget from "./info/info-search-widget.js";
+import SearchWidget from "./info/search-widget.js";
+
+import PrintEpc from './print/print-epc.js';
+import PrintWidget from './print/print-widget.js';
+
+// import Pagination from './pagination/index.js';
+
+import TreeViewNode from './tree/index.js';
+
+
+import langZhCn from './i18n/zh-CN.js';
+import langEnUs from './i18n/en-US.js';
+
+import VueI18n from 'vue-i18n';
+
+
+import Uuid from './common/Uuid.js';
+import Common from './common/Common.js';
+import SqlApi from './common/SqlApi.js';
+import Notify from './common/Notify.js';
+import IFrameUtil from './common/IFrameUtil.js';
+import UserStorageResource from './common/UserStorageResource.js';
+import DownloadService from './common/DownloadService.js';
+import ProcessReportResource from './process/src/api/ProcessReportResource.js';
+
+import ModalFix from './modal/src/ModalFix.js';
+import PrintUtil from './print/src/PrintUtil.js';
+
+const components = [
+    Loading,
+    Modal,
+    DateTime,
+    DateTimeV2,
+    Date,
+    Checkbox,
+    PageSizeSelect,
+    Time,
+    Switches,
+    YearPicker,
+    VueBootstrapPagination,
+    VueMonthlyPicker,
+    UploadWidget,
+    Scanner,
+    Navbar,
+    ImagePreview,
+    InfoWindow,
+    ProcessReport,
+    ProcessReportResult,
+    ProcessReportResultPreview,
+    DocGenerator,
+    InfoSearchWidget,
+    SearchWidget,
+    PrintEpc,
+    PrintWidget,
+    TreeViewNode,
+    // Pagination
+];
+
+
+const install = function (Vue, opts = {}) {
+    /* istanbul ignore if */
+    if (install.installed) return;
+
+    
+    Vue.use(VueI18n);
+
+    Vue.mixin({
+        beforeCreate() {
+            var _self = this;
+            // 这里为了防止 beforeCreate 比 vue-i18n 的 beforeCreate 先执行导致 this.$i18n 为空
+            this.$nextTick(() => {
+                // 判断是否为根节点,合并国际化信息
+                //if (this.$root === this && this.$i18n) {
+                if (this.$i18n) {
+                    _self.$i18n.mergeLocaleMessage('zh-CN', langZhCn);
+                    _self.$i18n.mergeLocaleMessage('en-US', langEnUs);
+                }
+            });
+        },
+    });
+
+    // locale.use(opts.locale);
+    // locale.i18n(opts.i18n);
+
+    components.map(component => {
+        console.log("install component: " + component.name);
+        Vue.component(component.name, component);
+    });
+};
+
+
+/* istanbul ignore if */
+if (typeof window !== 'undefined' && window.Vue) {
+    install(window.Vue);
+};
+
+
+module.exports = {
+    version: '1.2.9',
+    // locale: locale.use,
+    // i18n: locale.i18n,
+    install,
+    Loading,
+    Modal,
+    DateTime,
+    DateTimeV2,
+    Date,
+    Checkbox,
+    PageSizeSelect,
+    Time,
+    Switches,
+    YearPicker,
+    VueBootstrapPagination,
+    VueMonthlyPicker,
+    UploadWidget,
+    Scanner,
+    Navbar,
+    ImagePreview,
+    InfoWindow,
+    DocGenerator,
+    ProcessReport,
+    ProcessReportResult,
+    ProcessReportResultPreview,
+    InfoSearchWidget,
+    SearchWidget,
+    PrintEpc,
+    PrintWidget,
+    TreeViewNode,
+    // Pagination,
+    Uuid,
+    Common,
+    SqlApi,
+    Notify,
+    IFrameUtil,
+    ModalFix,
+    PrintUtil,
+    UserStorageResource,
+    DownloadService,
+    ProcessReportResource
+};

+ 8 - 0
packages/info/doc-generator.js

@@ -0,0 +1,8 @@
+
+import DocGenerator from './src/DocGenerator.vue';
+
+DocGenerator.install = function(Vue) {
+    Vue.component(DocGenerator.name, DocGenerator);
+};
+
+export default DocGenerator;

+ 8 - 0
packages/info/index.js

@@ -0,0 +1,8 @@
+
+import InfoWindow from './src/InfoWindow.vue';
+
+InfoWindow.install = function(Vue) {
+    Vue.component(InfoWindow.name, InfoWindow);
+};
+
+export default InfoWindow;

+ 8 - 0
packages/info/info-search-widget.js

@@ -0,0 +1,8 @@
+
+import InfoSearchWidget from './src/InfoSearchWidget.vue';
+
+InfoSearchWidget.install = function(Vue) {
+    Vue.component(InfoSearchWidget.name, InfoSearchWidget);
+};
+
+export default InfoSearchWidget;

+ 8 - 0
packages/info/search-widget.js

@@ -0,0 +1,8 @@
+
+import SearchWidget from './src/SearchWidget.vue';
+
+SearchWidget.install = function(Vue) {
+    Vue.component(SearchWidget.name, SearchWidget);
+};
+
+export default SearchWidget;

+ 315 - 0
packages/info/src/DocGenerator.vue

@@ -0,0 +1,315 @@
+<template>
+    <div class="grid-container-2">
+        <div class="grid-header-2">
+            <QueryCondition ref="queryCondition"
+                            :infoFilterFields="infoFilterFields"
+                            :showButton="true"
+                            :isSearchWidget="true"
+                            @simpleSearch="simpleSearch()"
+                            @complexSearch="complexSearch()"
+                            @refreshSearch="queryInfoWindowData"
+                            @changeSearch="tabIndex = 1;">
+                <template v-slot:header>
+                    <li role="presentation"
+                        :class="{'active': tabIndex === 2}">
+                        <a @click="$refs.queryCondition.changeSearch(1); tabIndex = 2;">{{$t('lang.DocGenerator.selectedDatas')}}</a>
+                    </li>
+                </template>
+                <template v-slot:body>
+                    <div class="tab-pane"
+                        :class="{'active': tabIndex === 2}">
+                    </div>
+                </template>
+            </QueryCondition>
+        </div>
+
+        <div class="grid-content-2">
+            <DocGeneratorGrid ref="docGeneratorGrid" 
+                
+                v-show="tabIndex === 1"
+                :infoWindowNo="infoWindowNo"
+                :infoGridFields="infoGridFields"
+                :infoWindowData="infoWindowData"
+                :isSelectedById="isSelectedById"
+                @refreshSearch="queryInfoWindowData"
+                @selectChanged="selectChanged">
+            </DocGeneratorGrid>
+
+            <DocGeneratorSelected 
+                ref="docGeneratorSelected"
+                v-show="tabIndex === 2"
+                :infoWindowNo="infoWindowNo"
+                :infoGridFields="infoGridFields"
+                @selectChanged="queryInfoWindowData">
+            </DocGeneratorSelected>
+
+        </div>
+        <Loading ref="loading" />
+    </div>
+</template>
+<script>
+var Common = require("../../common/Common.js");
+var InfoUtil = require("./InfoUtil.js");
+var Loading = require("../../loading/src/Loading.vue").default;
+var QueryCondition = require("./QueryCondition.vue").default;
+var DocGeneratorSelected = require("./DocGeneratorSelected.vue").default;
+var DocGeneratorGrid = require("./DocGeneratorGrid.vue").default;
+
+module.exports = {
+    name: "DocGenerator",
+    
+    props: ["infoWindowNo"],
+
+    data: function () {
+        return {
+            infoWindowDto: {},
+            infoFilterFields: [],   // 过滤字段
+            infoGridFields: [],     // 表格字段
+            infoWindowData: [],
+            tabIndex: 1,
+        }
+    },
+
+    components: {
+        QueryCondition, Loading,  
+        DocGeneratorSelected,
+        DocGeneratorGrid
+    },
+
+    methods: {
+        
+        /**
+         * 加载查询窗口的定义
+         * @author YangZhiJie 20210909
+         */
+        init: function () {
+            var _self = this;
+            if (_self.infoWindowNo === undefined || _self.infoWindowNo === null || _self.infoWindowNo === '') {
+                return;
+            }
+            _self.$refs.loading.show();
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/uniqueByNo'),
+                type: 'GET',
+                dataType: 'json',
+                data: { "infoWindowNo": _self.infoWindowNo },
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    _self.$refs.loading.show();
+                    _self.infoWindowDto = data;
+                    _self.$emit("showTitle", data.name);
+                    _self.$nextTick(function () {
+                        _self.initQueryPage();
+                    });
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.show();
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 根据用户配置更新查询窗口的定义
+         * @author YangZhiJie 20210909
+         */
+        initQueryPage: function () {
+            var _self = this;
+            if (_self.infoWindowDto === undefined || _self.infoWindowDto === null) {
+                return;
+            }
+            
+            _self.infoWindowDto.infoGridFields.forEach(function (item) {
+                _self.$set(item, "width", 150);
+            });
+
+            _self.infoWindowDto.infoFilterFields.forEach(function (item) {
+                item.value = {
+                    "infoFilterFieldId": item.id,
+                    "value1": "",
+                    "value2": ""
+                }
+            });
+
+            
+            InfoUtil.restoreInfoFilterField(_self.infoWindowDto.no, _self.infoWindowDto.infoFilterFields);
+            InfoUtil.restoreInfoGridField(_self.infoWindowDto.no, _self.infoWindowDto.infoGridFields);
+
+            _self.infoFilterFields = _self.infoWindowDto.infoFilterFields;
+            _self.infoGridFields = _self.infoWindowDto.infoGridFields;
+
+
+            _self.$nextTick(function () {
+                _self.queryInfoWindowData();
+            });
+        },
+
+        /**
+         * 重新开始简单查询
+         * @author YangZhiJie 20210909
+         */
+        simpleSearch: function () {
+            this.$refs.docGeneratorGrid.resetPagination();
+            this.queryInfoWindowDataSimple();
+        },
+
+        /**
+         * 从新开始复杂查询
+         * @author YangZhiJie 20210909
+         */
+        complexSearch: function () {
+            this.$refs.docGeneratorGrid.resetPagination();
+            this.queryInfoWindowDataComplex();
+        },
+
+        /**
+         * 查询窗口数据查询
+         * @author YangZhiJie 20210909
+         */
+        queryInfoWindowData: function () {
+            var isSimpleSearch = this.$refs.queryCondition.isSimpleQuery();
+            if (isSimpleSearch) {
+                this.queryInfoWindowDataSimple();
+            } else {
+                this.queryInfoWindowDataComplex();
+            }
+        },
+
+        /**
+         * 简单数据查询
+         * @author YangZhiJie 20210909
+         */
+        queryInfoWindowDataSimple: function () {
+            var _self = this;
+
+            var infoQueryParam = this.$refs.docGeneratorGrid.getQueryParam();
+            infoQueryParam.infoFilterFieldValues = _self.$refs.queryCondition.getQueryCondition();            
+            _self.$refs.loading.show();
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/queryInfoWindowDataSimple'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(infoQueryParam),
+                success: function (data) {
+                    _self.$refs.loading.hide();
+                    console.log(data);
+                    if (data != undefined && data.range != undefined) {
+                        _self.infoWindowData = data.dataList;
+                        _self.$refs.docGeneratorGrid.setPagination(data.totalSize, Math.ceil(data.totalSize / data.range.length));
+                    }
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.hide();
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 复杂数据查询
+         * @author YangZhiJie 20210909
+         */
+        queryInfoWindowDataComplex: function () {
+            var _self = this;
+
+            var infoQueryParam = this.$refs.docGeneratorGrid.getQueryParam();
+            infoQueryParam.infoFilterFieldValues = _self.$refs.queryCondition.getQueryCondition();
+
+            _self.$refs.loading.show();
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/queryInfoWindowDataComplex'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(infoQueryParam),
+                success: function (data) {
+                    _self.$refs.loading.hide();
+                    console.log(data);
+                    if (data != undefined && data.range != undefined) {
+                        _self.infoWindowData = data.dataList;
+                        _self.$refs.docGeneratorGrid.setPagination(data.totalSize, Math.ceil(data.totalSize / data.range.length));
+                    }
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.hide();
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 选择的内容发生了改变
+         * @author YangZhiJie 20210909
+         */
+        selectChanged: function(selectDatas){
+            this.$refs.docGeneratorSelected.parseDataList(selectDatas);
+        },
+
+        /**
+         * 根据id判断数据是否被选中
+         * 供外部接口调用
+         * @author YangZhiJie 20210909
+         */
+        isSelectedById: function(id){
+            return this.$refs.docGeneratorSelected.isSelectedById(id);
+        },
+
+        /**
+         * 获取选中的数据
+         * 供外部接口调用
+         * @author YangZhiJie 20210909
+         */
+        getSelectedData: function(){
+            return this.$refs.docGeneratorSelected.getSelectedData();
+        }
+    },
+
+    mounted: function(){
+        this.init();
+    },
+
+    watch: {
+        infoWindowNo: function(newValue, oldValue){
+            this.init();
+        }
+    }
+}
+</script>
+<style scoped>
+.grid-container-2 {
+    display: grid;
+    grid-template-columns: 100%;
+    grid-template-rows: min-content auto;
+    width: 100%;
+    height: calc(100% - 10px);
+}
+
+.grid-header-2 {
+    grid-row: 1 / 2;
+    grid-column: 1 / 2;
+}
+
+.grid-content-2 {
+    grid-row: 2 / 3;
+    grid-column: 1 / 2;
+    overflow: auto;
+}
+</style>
+
+<style scoped>
+
+/** 修复分页的样式 By YangZhiJie 2021-07-06 11:23 */
+nav >>> ul.pagination {
+    margin: 0 !important;
+}
+
+</style>

+ 479 - 0
packages/info/src/DocGeneratorGrid.vue

@@ -0,0 +1,479 @@
+<template>
+    <div class="flex-container-1">
+        <div class="flex-content-1 table-fix-head">
+            <table class="fixed-table table-striped table-bordered"
+                   :width="tableWidth"
+                   height="40px">
+                <thead>
+                    <tr height="40px">
+                        <th width="50px"
+                            class="fixed-cell">
+                            <input @click="selectAll($event)"
+                                   :checked="allSelected"
+                                   type="checkbox" />
+                        </th>
+
+                        <th v-for="infoGridField in infoGridFields"
+                            v-show="infoGridField.isShow"
+                            :key="infoGridField.fieldName"
+                            :width="infoGridField.width + 'px'"
+                            @dragover="ondragover($event, infoGridField)"
+                            @click="onSort(infoGridField)">
+
+                            <div class="rz-handle"
+                                 draggable='true'
+                                 :id="'infoGridFieldId_' + infoGridField.fieldName"
+                                 @dragstart="ondragstart($event, infoGridField)"
+                                 @drag="ondrag($event, infoGridField)"
+                                 @dragend="ondragend($event, infoGridField)">
+                            </div>
+
+                            <div class="td-max">
+                                {{infoGridField.name}}
+                            </div>
+                        </th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr v-for="rowData in dataList"
+                        :key="rowData.id"
+                        height="40px">
+                        <td class="fixed-cell">
+                            <input v-model="rowData.checked"
+                                   type="checkbox"
+                                   :value="rowData.id" />
+                        </td>
+                        <td v-for="infoGridField in infoGridFields"
+                            :key="infoGridField.fieldName + '_' + rowData.id">
+                            <input v-if="infoGridField.simpleDisplayType == 'TextEditor'"
+                                   type="text"
+                                   class="form-control"
+                                   :value="getDisplayValue(rowData, infoGridField)"
+                                   @keyup="valueChange($event, rowData, infoGridField)" />
+                            <input v-if="infoGridField.simpleDisplayType == 'NumberEditor'"
+                                   type="number"
+                                   class="form-control"
+                                   :value="getDisplayValue(rowData,infoGridField)"
+                                   @keyup="valueChange($event, rowData, infoGridField)" />
+                            <span v-if="infoGridField.simpleDisplayType == null || infoGridField.simpleDisplayType == '' || infoGridField.simpleDisplayType == 'NONE'">
+                                {{ rowData.data[infoGridField.fieldName] == undefined ? "" : rowData.data[infoGridField.fieldName].displayValue[0] }}
+                            </span>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+
+        <div class="flex-footer-1"
+             style="margin-top: 10px;">
+            <div class="pull-left">
+                <span>
+                    第
+                    {{(pagination.current_page-1)*pagination.per_page+1}}
+                    -
+                    {{pagination.current_page*pagination.per_page}}
+                    条,共计
+                    {{pagination.total}}
+                    条,每页
+                </span>
+                <PageSizeSelect v-on:pageSizeChanged="gridSizeSelect">
+
+                </PageSizeSelect>
+
+                <span>条</span>
+            </div>
+            <div class="pull-right">
+                <Pagination :pagination="pagination"
+                            :callback="refreshSearch"></Pagination>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var InfoUtil = require("./InfoUtil.js");
+var Pagination = require("../../vue-bootstrap-pagination/src/vue-bootstrap-pagination.vue").default;
+var PageSizeSelect = require("../../page-size-select/src/PageSizeSelect.vue").default;
+
+module.exports = {
+    props: [
+        "infoWindowNo",     // 查询窗口编号
+        "infoGridFields",   // 表格字段
+        "infoWindowData",         // 渲染的数据
+        "isSelectedById",   // 方法:根据id判断数据是否被选中
+    ],
+
+    data: function () {
+        return {
+            dataList: [],
+            pagination: {
+                total: 0,
+                per_page: Common.pageSize,    // required
+                current_page: 1, // required
+                last_page: 10,    // required
+            },
+            sortStyle: '',
+            sortClause: '',
+        }
+    },
+
+    components: {
+        Pagination,
+        PageSizeSelect,
+    },
+
+    methods: {
+        /**
+         * 重新设置分页
+         * 供外部组件调用
+         * @author YangZhiJie 20210909
+         */
+        resetPagination: function () {
+            this.pagination.current_page = 1;
+            this.pagination.last_page = 10;
+            this.pagination.total = 0;
+        },
+
+        /**
+         * 设置分页
+         * @author YangZhiJie 20210909
+         */
+        setPagination: function (total, lastPage) {
+            this.pagination.last_page = lastPage;
+            this.pagination.total = total;
+        },
+
+        /**
+         * 获取查询参数
+         * 供外部组件调用
+         * @author YangZhiJie 20210909
+         */
+        getQueryParam: function () {
+            return {
+                infoWindowNo: this.infoWindowNo,
+                start: (this.pagination.current_page - 1) * this.pagination.per_page,
+                length: this.pagination.per_page,
+                sortClause: this.sortClause,
+            }
+        },
+
+        /**
+         * 页码数量改变
+         * @author YangZhiJie 20210909
+         */
+        gridSizeSelect: function (newPageSize) {
+            this.pagination.per_page = newPageSize;
+            this.pagination.current_page = 1;
+        },
+
+        /**
+         * 列开始拖动
+         * @author YangZhiJie 20210909
+         */
+        ondragstart: function (event, gridFieldItem) {
+            var _self = this;
+            _self.startX = event.pageX;
+            _self.startWidth = Number(gridFieldItem.width);
+            event.dataTransfer.setDragImage(event.target, 0, 20);
+            event.dataTransfer.effectAllowed = 'move';
+        },
+
+        /**
+         * 列拖动
+         * @author YangZhiJie 20210909
+         */
+        ondrag: function (event, gridFieldItem) {
+            var _self = this;
+            gridFieldItem.width = _self.startWidth + (event.pageX - _self.startX);
+        },
+
+
+        /**
+         * 列结束拖动
+         * @author YangZhiJie 20210909
+         */
+        ondragend: function (event, gridFieldItem, index) {
+            var _self = this;
+
+            let newWidth = _self.startWidth + (event.pageX - _self.startX);
+            if (newWidth < 50) {
+                newWidth = 50;
+            }
+            gridFieldItem.width = newWidth;
+
+            gridFieldItem.width = gridFieldItemClone.width;
+            InfoUtil.saveInfoGridFields(_self.infoWindowDto.no, _self.infoGridFields);
+            
+        },
+
+        ondragover: function (event, gridFieldItem) {
+            event.preventDefault()
+            event.dataTransfer.dropEffect = 'move';
+        },
+
+        /**
+         * 排序
+         * @author YangZhiJie 20210909
+         */
+        onSort: function (infoGridField) {
+            var _self = this;
+            var fieldName = null;
+            if (infoGridField.sortFieldName != undefined && infoGridField.sortFieldName != "") {
+                fieldName = infoGridField.sortFieldName;
+            } else {
+                fieldName = infoGridField.fieldName;
+            }
+            _self.sortClause = fieldName + _self.sortStyle;
+
+            this.$emit("refreshSearch");
+
+            _self.sortStyle = ((_self.sortStyle === " ASC") ? " DESC" : " ASC");
+        },
+
+        /**
+         * 发送查询请求
+         * @author YangZhiJie 20210909
+         */
+        refreshSearch: function(){
+            this.$emit("refreshSearch");
+        },
+
+        /**
+         * 冻结表头
+         * @author YangZhiJie 20210909
+         */
+        fixedTableHeader: function () {
+            let _self = this;
+            _self.$nextTick(function () {
+                var $th = $('.table-fix-head').find('thead');
+                var $fixedCell = $('.table-fix-head').find('.fixed-cell');
+                $('.table-fix-head').on('scroll', function () {
+                    $th.css('transform', 'translateY(' + this.scrollTop + 'px)');
+                    $fixedCell.css('transform', 'translateX(' + this.scrollLeft + 'px)');
+                });
+            });
+        },
+
+
+        /**
+         * 表格中填写的值发生了改变
+         * @author YangZhiJie 20210909
+         */
+        valueChange: function (event, rowData, infoGridField) {
+            var _self = this;
+            var value = event.target.value;
+            const fieldName = infoGridField.fieldName;
+            if (rowData.data[fieldName] == undefined) {
+                var tempFieldValue = {
+                    displayValue: [value]
+                };
+                this.$set(rowData.data, fieldName, tempFieldValue);
+            } else {
+                this.$set(rowData.data[fieldName].displayValue, 0, value);
+            }
+            rowData.checked = true;
+        },
+
+        /**
+         * 获取显示的值
+         * @author YangZhiJie 20210909
+         */
+        getDisplayValue(rowData, infoGridField) {
+            let fieldValue = rowData.data[infoGridField.fieldName];
+            if (fieldValue === undefined || fieldValue === null) {
+                return "";
+            } else {
+                return fieldValue.displayValue[0];
+            }
+        },
+
+
+        /**
+         * 选择所有/取消选择的数据
+         * @author YangZhiJie 20210909
+         */
+        selectAll: function (event) {
+            var isChecked = event.currentTarget.checked;
+
+            if (this.dataList === undefined || this.dataList === null || this.dataList.length == 0) {
+                return;
+            }
+
+            for (let index = 0, length = this.dataList.length; index < length; index++) {
+                this.dataList[index].checked = isChecked;
+            }
+        },
+
+        /**
+         * 设置dataList数据
+         * @author YangZhiJie 20210908
+         */
+        setDataList: function (dataList) {
+            let _self = this;
+            if (dataList === null || dataList === undefined) {
+                this.dataList.splice(0, this.dataList.length);
+                return;
+            }
+            if (!Array.isArray(dataList)) {
+                console.error("参数异常,参数不是数组类型。");
+                console.error(dataList);
+                return;
+            }
+
+            const tempDataList = JSON.parse(JSON.stringify(dataList));
+            for (let index = 0, length = tempDataList.length; index < length; index++) {
+                if(this.isSelectedById != null){
+                    tempDataList[index].checked = this.isSelectedById(tempDataList[index].id);
+                }else{
+                    tempDataList[index].checked = false;
+                }
+            }
+
+            this.dataList = tempDataList;
+
+            this.$nextTick(function(){
+                _self.fixedTableHeader();
+            });
+        }
+    },
+
+    computed: {
+        /**
+         * 自动计算表格的宽度
+         * @author YangZhiJie 20210909
+         */
+        tableWidth: function () {
+            var totalWidth = 50;
+            if (this.infoGridFields !== undefined) {
+                this.infoGridFields.forEach(function (infoGridField) {
+                    if (infoGridField.width !== undefined
+                        && infoGridField.width !== null
+                        && infoGridField.width !== '') {
+                        totalWidth += Number(infoGridField.width);
+                    }
+                });
+            }
+            return totalWidth + "px";
+        },
+
+        /**
+         * 是否选择了全部的数据
+         * @author YangZhiJie 20210909
+         */
+        allSelected: function () {
+            var _self = this;
+            if (this.dataList === undefined || this.dataList === null || this.dataList.length == 0) {
+                return false;
+            }
+
+            for (let index = 0, length = this.dataList.length; index < length; index++) {
+                if (this.dataList[index].checked === false) {
+                    return false;
+                }
+            }
+
+            return true;
+        },
+    },
+
+    watch: {
+
+        infoWindowData: function (newValue, oldValue) {
+            this.setDataList(newValue);
+        },
+
+        dataList: {
+            handler: function(newValue, oldValue){
+                // 清除延迟执行
+                if(this.selectChangedTimeout !== undefined && this.selectChangedTimeout !== null){
+                    window.clearTimeout(this.selectChangedTimeout);
+                }
+    
+                // 设置延迟执行
+                this.selectChangedTimeout = setTimeout(()=>{
+                    console.log('selectChanged');
+                    this.$emit("selectChanged", this.dataList);
+                }, 500);
+            },
+            deep: true
+        }
+    }
+}
+</script>
+
+<style scoped>
+.flex-container-1 {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: calc(100% - 10px);
+}
+
+.flex-content-1 {
+    flex: 1;
+    overflow: auto;
+    width: 100%;
+    margin-top: 5px;
+}
+
+.flex-footer-1 {
+    height: 35px;
+    /*放大缩小比例为0 */
+    flex: 0 0 35px;
+}
+</style>
+
+
+<style scoped>
+.fixed-table {
+    table-layout: fixed;
+}
+
+table.fixed-table th {
+    position: relative;
+    min-width: 10px;
+}
+
+table.fixed-table tr td:first-child,
+table.fixed-table tr th:first-child {
+    text-align: center;
+}
+
+table.fixed-table th,
+table.fixed-table td {
+    text-align: left;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    padding: 0.1em;
+    border-right: 1px solid rgba(0, 0, 0, 0.05);
+    background-color: white;
+}
+
+table.fixed-table th .rz-handle {
+    width: 10px;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    right: 0;
+    background: repeating-linear-gradient(
+        45deg,
+        transparent,
+        transparent 2px,
+        rgba(0, 0, 0, 0.05) 2px,
+        rgba(0, 0, 0, 0.05) 4px
+    );
+    cursor: ew-resize !important;
+}
+
+table.fixed-table th .rz-handle.rz-handle-active {
+    border-right: 2px solid #000;
+    transform: scaleX(100);
+    background: rgba(0, 0, 0, 0.05) 2px;
+}
+
+.rz-handle:hover {
+    background: rgba(0, 0, 0, 0.2) 4px;
+}
+</style>

+ 396 - 0
packages/info/src/DocGeneratorSelected.vue

@@ -0,0 +1,396 @@
+<template>
+    <div class="flex-container-2">
+        <div class="flex-content-2 table-fix-head-2">
+            <table class="fixed-table table-striped table-bordered"
+                   :width="tableWidth"
+                   height="40px">
+                <thead>
+                    <tr height="40px">
+                        <th width="50px"
+                            class="fixed-cell">
+                            序号
+                        </th>
+
+                        <th width="50px"
+                            class="fixed-cell">
+                            操作
+                        </th>
+
+                        <th v-for="infoGridField in infoGridFields"
+                            v-show="infoGridField.isShow"
+                            :key="infoGridField.fieldName"
+                            :width="infoGridField.width + 'px'"
+                            @click="onSort(infoGridField)">
+
+                            <div class="rz-handle"
+                                 draggable='true'
+                                 :id="'infoGridFieldId_' + infoGridField.fieldName"
+                                 @dragstart="ondragstart($event, infoGridField)"
+                                 @drag="ondrag($event, infoGridField)"
+                                 @dragend="ondragend($event, infoGridField)">
+                            </div>
+
+                            <div class="td-max">
+                                {{infoGridField.name}}
+                            </div>
+                        </th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr v-for="(rowData, index) in dataList"
+                        :key="rowData.id"
+                        height="40px">
+                        <td class="fixed-cell">
+                            {{ index + 1 }}
+                        </td>
+                        <td class="fixed-cell">
+                            <span class="label label-danger"
+                                style="cursor: pointer;"
+                                @click="removeRow(rowData)">删除</span>
+                        </td>
+                        <td v-for="infoGridField in infoGridFields"
+                            :key="infoGridField.fieldName + '_' + rowData.id">
+                            <input v-if="infoGridField.simpleDisplayType == 'TextEditor'"
+                                   type="text"
+                                   class="form-control"
+                                   :value="getDisplayValue(rowData, infoGridField)"
+                                   @keyup="valueChange($event, rowData, infoGridField)" />
+                            <input v-if="infoGridField.simpleDisplayType == 'NumberEditor'"
+                                   type="number"
+                                   class="form-control"
+                                   :value="getDisplayValue(rowData,infoGridField)"
+                                   @keyup="valueChange($event, rowData, infoGridField)" />
+                            <span v-if="infoGridField.simpleDisplayType == undefined">{{rowData.data[infoGridField.fieldName]==undefined?"":rowData.data[infoGridField.fieldName].displayValue[0]}}</span>
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+
+        <div class="flex-footer-2"
+             style="margin-top: 10px;">
+            <div class="pull-left">
+                <span>
+                    共计 {{dataList.length}} 条
+                </span>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var Notify = require("../../common/Notify.js");
+var InfoUtil = require("./InfoUtil.js");
+
+module.exports = {
+    props: [
+        "infoWindowNo",     // 查询窗口编号
+        "infoGridFields",   // 表格字段
+    ],
+
+    data: function () {
+        return {
+            dataList: [],
+            sortStyle: '',
+            sortClause: '',
+        }
+    },
+
+    components: {
+    },
+
+    methods: {
+
+        /**
+         * 列开始拖动
+         * @author YangZhiJie 20210909
+         */
+        ondragstart: function (event, gridFieldItem) {
+            var _self = this;
+            _self.startX = event.pageX;
+            _self.startWidth = Number(gridFieldItem.width);
+        },
+
+        /**
+         * 列拖动
+         * @author YangZhiJie 20210909
+         */
+        ondrag: function (event, gridFieldItem) {
+            var _self = this;
+            gridFieldItem.width = _self.startWidth + (event.pageX - _self.startX);
+        },
+
+        /**
+         * 列结束拖动
+         * @author YangZhiJie 20210909
+         */
+        ondragend: function (event, gridFieldItem, index) {
+            var _self = this;
+            var gridFieldItemClone = {
+                "id": gridFieldItem.id,
+                "index": index,
+                "isShow": gridFieldItem.isShow,
+                "width": gridFieldItem.width,
+                "sortNo": gridFieldItem.sortNo,
+                "fieldName": gridFieldItem.fieldName
+            };
+            gridFieldItemClone.width = _self.startWidth + (event.pageX - _self.startX);
+            if (gridFieldItemClone.width < 50) {
+                gridFieldItemClone.width = 50;
+            }
+            gridFieldItem.width = gridFieldItemClone.width;
+            InfoUtil.saveInfoGridField(_self.infoWindowNo, gridFieldItemClone);
+            InfoUtil.restoreInfoGridField(_self.infoWindowNo, _self.infoGridFields);
+        },
+
+
+        /**
+         * 排序
+         * @author YangZhiJie 20210909
+         */
+        onSort: function (infoGridField) {
+            var _self = this;
+            var fieldName = null;
+            if (infoGridField.sortFieldName != undefined && infoGridField.sortFieldName != "") {
+                fieldName = infoGridField.sortFieldName;
+            } else {
+                fieldName = infoGridField.fieldName;
+            }
+            _self.sortClause = fieldName + _self.sortStyle;
+            _self.sortStyle = ((_self.sortStyle === " ASC") ? " DESC" : " ASC");
+            console.warn("暂时不支持排序。");
+        },
+
+
+        /**
+         * 冻结表头
+         * @author YangZhiJie 20210909
+         */
+        fixedTableHeader: function () {
+            let _self = this;
+            _self.$nextTick(function () {
+                var $th = $('.table-fix-head-2').find('thead');
+                var $fixedCell = $('.table-fix-head-2').find('.fixed-cell');
+                $('.table-fix-head-2').on('scroll', function () {
+                    $th.css('transform', 'translateY(' + this.scrollTop + 'px)');
+                    $fixedCell.css('transform', 'translateX(' + this.scrollLeft + 'px)');
+                });
+            });
+        },
+
+
+        /**
+         * 表格中填写的值发生了改变
+         * @author YangZhiJie 20210909
+         */
+        valueChange: function (event, rowData, infoGridField) {
+            var _self = this;
+            var value = event.target.value;
+            const fieldName = infoGridField.fieldName;
+            if (rowData.data[fieldName] == undefined) {
+                var tempFieldValue = {
+                    displayValue: [value]
+                };
+                this.$set(rowData.data, fieldName, tempFieldValue);
+            } else {
+                this.$set(rowData.data[fieldName].displayValue, 0, value);
+            }
+            rowData.checked = true;
+        },
+
+        /**
+         * 获取显示的值
+         * @author YangZhiJie 20210909
+         */
+        getDisplayValue(rowData, infoGridField) {
+            let fieldValue = rowData.data[infoGridField.fieldName];
+            if (fieldValue === undefined || fieldValue === null) {
+                return "";
+            } else {
+                return fieldValue.displayValue[0];
+            }
+        },
+
+
+        /**
+         * 设置dataList数据
+         * @author YangZhiJie 20210908
+         */
+        parseDataList: function (dataList) {
+            let _self = this;
+            if (dataList === null || dataList === undefined) {
+                return;
+            }
+            if (!Array.isArray(dataList)) {
+                console.error("参数异常,参数不是数组类型。");
+                console.error(dataList);
+                return;
+            }
+
+            const tempDataList = JSON.parse(JSON.stringify(dataList));
+            for (let index = 0, length = tempDataList.length; index < length; index++) {
+                let tempData = tempDataList[index];
+                let rowData = this.getRowDataById(tempData.id);
+                if(tempData.checked === true){
+                    if(rowData === null){
+                        this.$set(this.dataList, this.dataList.length, tempData);
+                    } else {
+                        let dataListIndex = this.dataList.indexOf(rowData);
+                        this.$set(this.dataList, dataListIndex, tempData);
+                    }
+                } else if(tempData.checked === false){
+                    if(rowData !== null){
+                        let dataListIndex = this.dataList.indexOf(rowData);
+                        this.dataList.splice(dataListIndex, 1);
+                    }
+                }
+            }
+
+            this.$nextTick(function(){
+                _self.fixedTableHeader();
+            });
+        },
+
+        /**
+         * 删除用户行
+         * @param rowData 行数据
+         * @author YangZhiJie 2021-09-08
+         */
+        removeRow: function(rowData){
+            let index = this.dataList.indexOf(rowData);
+            if(index >= 0){
+                this.dataList.splice(index, 1);
+            }
+            this.$emit("selectChanged");
+        },
+
+        /**
+         * 根据id获取行数据
+         * @author YangZhiJie 20210909
+         */
+        getRowDataById: function(id){
+            for (let index = 0, length = this.dataList.length; index < length; index++) {
+                if(this.dataList[index].id === id){
+                    return this.dataList[index];
+                }
+            }
+            return null;
+        },
+
+        /**
+         * 根据id判断数据是否被选中
+         * 供外部接口调用
+         * @author YangZhiJie 20210909
+         */
+        isSelectedById: function(id){
+            let rowData = this.getRowDataById(id);
+            return rowData === null ? false : true;
+        },
+
+
+        /**
+         * 获取选中的数据
+         * 供外部接口调用
+         * @author YangZhiJie 20210909
+         */
+        getSelectedData: function(){
+            return this.dataList;
+        }
+
+    },
+
+    computed: {
+        /**
+         * 自动计算表格的宽度
+         */
+        tableWidth: function () {
+            var totalWidth = 50;
+            if (this.infoGridFields !== undefined) {
+                this.infoGridFields.forEach(function (infoGridField) {
+                    if (infoGridField.width !== undefined
+                        && infoGridField.width !== null
+                        && infoGridField.width !== '') {
+                        totalWidth += Number(infoGridField.width);
+                    }
+                });
+            }
+            return totalWidth + "px";
+        },
+    },
+}
+</script>
+
+<style scoped>
+.flex-container-2 {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    height: calc(100% - 10px);
+}
+
+.flex-content-2 {
+    flex: 1;
+    overflow: auto;
+    width: 100%;
+    margin-top: 5px;
+}
+
+.flex-footer-2 {
+    height: 35px;
+    /*放大缩小比例为0 */
+    flex: 0 0 35px;
+}
+</style>
+
+
+<style scoped>
+.fixed-table {
+    table-layout: fixed;
+}
+
+.fixed-cell{
+    text-align: center;
+}
+
+table.fixed-table th {
+    position: relative;
+    min-width: 10px;
+}
+
+table.fixed-table tr td:first-child,
+table.fixed-table tr th:first-child {
+    text-align: center;
+}
+
+table.fixed-table th,
+table.fixed-table td {
+    text-align: left;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    padding: 0.1em;
+    border-right: 1px solid rgba(0, 0, 0, 0.05);
+    background-color: white;
+}
+
+table.fixed-table th .rz-handle {
+    width: 10px;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    right: 0;
+    background: repeating-linear-gradient(
+        45deg,
+        transparent,
+        transparent 2px,
+        rgba(0, 0, 0, 0.05) 2px,
+        rgba(0, 0, 0, 0.05) 4px
+    );
+    cursor: ew-resize !important;
+}
+
+table.fixed-table th .rz-handle.rz-handle-active {
+    border-right: 1px dotted #000;
+}
+</style>

+ 50 - 0
packages/info/src/EnumSelectWidgetInfo.vue

@@ -0,0 +1,50 @@
+<template>
+	<div>
+		<label :for="'enum-select-widget-' + field.id" class="m-form-group-label">{{field.name}}</label>
+		<select class="form-control form-control-complex form-input" :id="'enum-select-widget-' + field.id" v-model="selectedValue">
+			<option value=""></option>
+		  	<option v-for="keyValue in field.keyValues" :value="keyValue.keyStr" :key="keyValue.keyStr">{{keyValue.value}}</option>
+		</select>
+	</div>
+</template>
+
+<script>
+	module.exports = {
+		props: ["field", "fieldValue"],
+
+		data: function(){
+			return {
+				selectedValue: ((this.fieldValue == undefined || this.fieldValue.displayValue == undefined) ? "" : this.fieldValue.displayValue[0]),
+			}
+		},
+
+		watch: {
+			selectedValue: function(curVal,oldVal){
+				if(curVal != oldVal){					
+					var newFieldValue = {
+						displayValue: [curVal],
+						fieldType: 'String'
+					}
+					this.$emit("valueChanged", newFieldValue);
+				}
+			}
+		}
+	}
+
+</script>
+<style scoped>
+	@media (min-width: 768px){
+		.m-form-group-label{
+			width: 80px;
+			text-align: right;
+			padding-right: 10px;
+		}
+	}
+	.form-control-complex{
+		width: 200px !important;
+		float: none !important;
+	}
+	.form-input{
+		border-radius: 4px !important;
+	}
+</style>

+ 290 - 0
packages/info/src/InfoHeader.vue

@@ -0,0 +1,290 @@
+<template>
+    <div class="container-fluid">
+        <div class="navbar-header">
+            <a class="navbar-brand">{{headerName}}</a>
+        </div>
+
+        <div class="collapse navbar-collapse"
+             id="bs-example-navbar-collapse-2">
+            <div class="navbar-form navbar-left"
+                 role="search">
+
+                <div class="dropdown float-left">
+                    <button class="btn btn-default dropdown-toggle"
+                            type="button"
+                            id="dropdownMenuQueryFilter"
+                            data-toggle="dropdown"
+                            aria-haspopup="true"
+                            aria-expanded="true">
+                        <span class="glyphicon glyphicon-list-alt"></span>
+                    </button>
+                    <vuedraggable class="dropdown-menu"
+                                  aria-labelledby="dropdownMenuQueryFilter"
+                                  v-model="infoFilterFieldsClone"
+                                  @change="filterFieldSortChaned()"
+                                  @click="stopPropagation($event)"
+                                  v-if="infoFilterFieldsClone != undefined">
+                        <div v-for="(item,index) in infoFilterFieldsClone"
+                             style="margin-left: 15px;margin-right: 15px;" :key="item.key">
+                                <div class="column">
+                                    <input :id="'InfoForm' + '_' + infoWindowNo + '_' + item.fieldName"
+                                           class="isShow-checkbox"
+                                           type="checkbox"
+                                           :disabled="item.mandatory && item.isShow"
+                                           v-model="item.isShow"
+                                           @change="visibleChanged(item)" />
+                                    <label :for="'InfoForm' + '_' + infoWindowNo + '_' + item.fieldName"
+                                           class="column-name"
+                                           :class="{'column-red' : item.mandatory == true}">{{item.name}}</label>
+                                </div>
+                        </div>
+                    </vuedraggable>
+                </div>
+
+                <div class="dropdown float-left dropdown-infoGrid">
+                    <button class="btn btn-default dropdown-toggle"
+                            type="button"
+                            id="dropdownMenuQueryFilter2"
+                            data-toggle="dropdown"
+                            aria-haspopup="true"
+                            aria-expanded="true">
+                        <span class="glyphicon glyphicon-calendar"></span>
+                    </button>
+
+                    <vuedraggable class="dropdown-menu"
+                                  aria-labelledby="dropdownMenuQueryFilter2"
+                                  v-model="infoGridFieldsClone"
+                                  @change="gridFieldSortChaned()"
+                                  @click="stopPropagation($event)"
+                                  v-if="infoGridFieldsClone != undefined"  draggable=".info-grid-column-dragable">
+                        <div v-for="(item) in infoGridFieldsClone"
+                             style="margin-left: 15px;margin-right: 15px;"  :key="item.key" class="info-grid-column-dragable">
+                            <div class="column">
+                                <input :id="'InfoGrid' + '_' + infoWindowNo + '_' + item.fieldName"
+                                        class="isShow-checkbox"
+                                        type="checkbox"
+                                        :disabled="item.mandatory && item.isShow"
+                                        v-model="item.isShow"
+                                        @change="gridFieldvisibleChanged(item)" />
+                                <label :for="'InfoGrid' + '_' + infoWindowNo + '_' + item.fieldName"
+                                        class="column-name"
+                                        :class="{'column-red' : item.mandatory == true}">{{item.name}}</label>
+                            </div>
+                        </div>
+                    </vuedraggable>
+                </div>
+                <button v-if="htmlHelpUrl != undefined && htmlHelpUrl != ''"
+                        @click="openHtmlHelp(htmlHelpUrl)"
+                        class="btn btn-default btn-help">
+                    <span class="glyphicon glyphicon-question-sign"></span>
+                </button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+
+var InfoUtil = require("./InfoUtil.js");
+
+import vuedraggable from 'vuedraggable';
+
+export default {
+    props: ["infoFilterFields", "headerName", "infoGridFields", "htmlHelpUrl", "infoWindowNo"],
+    
+    data: function () {
+        return {
+            sortMap: {},
+            infoFilterFieldsClone: [],
+            infoGridFieldsClone: []
+        }
+    },
+
+
+    components: {
+        vuedraggable
+    },
+
+    methods: {
+        cloneInfoFilterFields: function () {
+            var _self = this;
+            if (_self.infoFilterFields != null) {
+                _self.infoFilterFieldsClone.splice(0, _self.infoFilterFieldsClone.length);
+
+                
+                let tempInfoFilterFields = [];
+
+                for (var i = 0, len1 = _self.infoFilterFields.length; i < len1; i++) {
+                    var fieldClone = {
+                        "key": ('InfoFormField' + '_' + _self.infoWindowNo + '_' + _self.infoFilterFields[i].fieldName),
+                        "fieldName": _self.infoFilterFields[i].fieldName,
+                        "name": _self.infoFilterFields[i].name,
+                        "mandatory": _self.infoFilterFields[i].mandatory,
+                        "isShow": _self.infoFilterFields[i].isShow,
+                        "sortNo": _self.infoFilterFields[i].sortNo,
+                    }
+                    tempInfoFilterFields.push(fieldClone);
+                }
+
+                tempInfoFilterFields.sort(function (item1, item2) {
+                    return item1.sortNo - item2.sortNo;
+                });
+
+                tempInfoFilterFields.forEach(function(tempInfoFilterField){
+                    _self.infoFilterFieldsClone.push(tempInfoFilterField);
+                });
+            }
+        },
+        cloneInfoGridFields: function () {
+
+            var _self = this;
+
+            if (_self.infoGridFields != null) {
+                _self.infoGridFieldsClone.splice(0, _self.infoGridFieldsClone.length);
+
+                let tempInfoGridFields = [];
+
+                for (var i = 0, len1 = _self.infoGridFields.length; i < len1; i++) {
+                    var fieldClone = {
+                        "key": ('InfoGridField' + '_' + _self.infoWindowNo + '_' + _self.infoGridFields[i].fieldName),
+                        "fieldName": _self.infoGridFields[i].fieldName,
+                        "name": _self.infoGridFields[i].name,
+                        "mandatory": _self.infoGridFields[i].mandatory,
+                        "isShow": _self.infoGridFields[i].isShow,
+                        "sortNo": _self.infoGridFields[i].sortNo,
+                        "width": _self.infoGridFields[i].width
+                    }
+                    tempInfoGridFields.push(fieldClone);
+                }
+
+                tempInfoGridFields.sort(function (item1, item2) {
+                    return item1.sortNo - item2.sortNo;
+                });
+
+                tempInfoGridFields.forEach(function(tempInfoGridField){
+                    _self.infoGridFieldsClone.push(tempInfoGridField);
+                });
+            }
+            
+        },
+        /**
+         * 打开帮助页面
+         */
+        openHtmlHelp: function (htmlHelpUrl) {
+            window.open(htmlHelpUrl);
+        },
+
+        //将数据传递给父组件
+        visibleChanged: function (infoFilterFieldItem) {
+            var _self = this;
+            _self.$nextTick(function () {
+                if (infoFilterFieldItem.mandatory) {
+                    infoFilterFieldItem.ishow = true;
+                    return;
+                }
+                _self.$emit("filterFieldPropertyChanged", _self.infoFilterFieldsClone);
+            });
+        },
+
+        gridFieldvisibleChanged: function (item) {
+            var _self = this;
+            _self.$nextTick(function () {
+                if (item.mandatory) {
+                    item.ishow = true;
+                    return;
+                }
+                this.$emit("gridFieldPropertyChanged", _self.infoGridFieldsClone);
+            });
+        },
+
+        // 停止冒泡
+        stopPropagation: function (e) {
+            e.stopPropagation();
+        },
+        
+        filterFieldSortChaned: function () {
+            console.log("filterFieldSortChaned")
+            var _self = this;
+            for (var i = 0, len1 = _self.infoFilterFieldsClone.length; i < len1; i++) {
+                _self.infoFilterFieldsClone[i].sortNo = i * 10;
+            }
+            this.$emit("filterFieldPropertyChanged", _self.infoFilterFieldsClone);
+        },
+
+        gridFieldSortChaned: function () {
+            console.log("gridFieldSortChaned")
+            var _self = this;
+            for (var i = 0, len1 = _self.infoGridFieldsClone.length; i < len1; i++) {
+                _self.infoGridFieldsClone[i].sortNo = i * 10;
+            }
+            this.$emit("gridFieldPropertyChanged", _self.infoGridFieldsClone);
+        }
+
+    },
+    mounted: function () {
+        this.cloneInfoFilterFields();
+        this.cloneInfoGridFields();
+    },
+
+    watch: {
+        "infoFilterFields": function () {
+            this.cloneInfoFilterFields();
+        },
+        "infoGridFields": function () {
+            this.cloneInfoGridFields();
+        },
+    }
+}
+
+</script>
+<style scoped>
+.visible-checkbox {
+    width: 17px;
+    height: 17px;
+    text-align: center;
+    margin-right: 5px;
+    margin-left: 15px;
+}
+
+.column-name {
+    height: 25px;
+    line-height: 25px;
+}
+
+.fa-arrow-up {
+    float: right;
+    height: 25px;
+    line-height: 25px;
+    text-align: center;
+    margin-right: 10px;
+    margin-left: 10px;
+}
+
+.fa-arrow-down {
+    float: right;
+    height: 25px;
+    line-height: 25px;
+    margin-right: 10px;
+    margin-left: 10px;
+}
+
+.column-prop {
+    display: block;
+    width: 260px;
+}
+
+.clear-fix {
+    clear: both;
+}
+
+.float-left {
+    float: left;
+}
+
+.dropdown-infoGrid {
+    margin-left: 10px;
+}
+.btn-help {
+    margin-left: 20px;
+}
+</style>

+ 261 - 0
packages/info/src/InfoMultiSearchWidget.vue

@@ -0,0 +1,261 @@
+<template>
+    <div class="content">
+        <div class="box">
+            <span v-for="item in selectedDatas"
+                  class="selected-tag">
+                {{item.value}}
+                <button @click="deleteSelected(item)"
+                        type="button"
+                        class="close"
+                        aria-label="Remove option">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </span>
+            <span @click="showSearchDialog"
+                  class="glyphicon glyphicon-search search-icon"></span>
+        </div>
+        <Modal ref="modal"
+               @ok="searchDialogOk"
+               @cancel="searchDialogCancel"
+               :full="true">
+            <InfoFilter ref="info"
+                        v-on:dataSelected="dataSelected"
+                        v-on:deleteSelected="deleteSelected"
+                        :fieldValue="fieldValue"
+                        :whereClause="whereClause"
+                        :isSearchWidget="true"></InfoFilter>
+            <div slot="header">{{infoWindowDto.name}}</div>
+        </Modal>
+        <Loading ref="loading"></Loading>
+    </div>
+</template>
+<script>
+var Modal = require("../../modal/src/Modal.vue").default;
+var InfoFilter = require("./InfoWindow.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+
+module.exports = {
+    props: ["infoWindowNo", "fieldValue", "whereClause", "displayName"],
+
+    data: function () {
+        return {
+            labelNames: [],
+            infoWindowDto: {},
+            infoWindowData: {},
+            isShowAuto: false,
+            searchText: "",
+            selectIndex: -1,
+            selectedDatas: []
+        }
+    },
+
+    components: {
+        Modal, InfoFilter, Loading
+    },
+
+    methods: {
+        // 显示搜索对话框
+        showSearchDialog: function () {
+            var _self = this;
+            _self.$refs.modal.showModal();
+            _self.$nextTick(function () {
+                _self.$refs.modal.showModal();
+                if (_self.$refs.info.infoWindowNo != _self.infoWindowNo) {
+                    _self.$refs.info.loadByInfoWindowNo(_self.infoWindowNo);
+                } else {
+                    _self.$refs.info.refresh();
+                }
+            })
+        },
+
+        searchDialogOk: function () {
+
+        },
+
+        searchDialogCancel: function () {
+
+        },
+
+        // 数据已经选择
+        dataSelected: function (data) {
+            var _self = this;
+
+            if (data == undefined) {
+                return;
+            }
+
+            console.log("已经选择了数据:" + data.id);
+
+            var newIds = [];
+            if (_self.fieldValue.ids != undefined) {
+                for (var i = 0; i < _self.fieldValue.ids.length; i++) {
+                    newIds.push(_self.fieldValue.ids[i]);
+                }
+            }
+
+            if (newIds.indexOf(data.id) < 0) {
+                newIds.push(data.id);
+            }
+
+            var displayValue = _self.fieldValue.displayValue;
+            if (_self.displayName != undefined && _self.displayName != "") {
+                var arr = _self.displayName.split(",");
+                var text = "";
+                arr.forEach(function (item) {
+                    console.log("选择的text:" + data.data[item].displayValue[0]);
+                    text += data.data[item].displayValue[0];
+                })
+                displayValue.push(text);
+            }
+
+            var newFieldValue = {
+                displayValue: displayValue,
+                fieldType: 'MultiSearchBoxEditor',
+                ids: newIds,
+            }
+            _self.$emit("valueChanged", newFieldValue);
+        },
+
+        // 值改变
+        valueChanged: function (fieldValue) {
+            this.$emit("valueChanged", fieldValue);
+            this.reComputeSearchText(fieldValue);
+        },
+
+        /**
+         * 重新计算显示值
+         * @package [fieldValue]
+         * @return {[type]}
+         */
+        reComputeSearchText: function (fieldValue) {
+            var arr = [];
+            var ids = fieldValue.ids;
+            var displayValue = fieldValue.displayValue;
+            if (ids != undefined && displayValue != undefined && ids.length == displayValue.length) {
+                for (var i = 0; i < ids.length; i++) {
+                    var obj = {
+                        id: ids[i],
+                        value: displayValue[i]
+                    }
+                    arr.push(obj);
+                }
+            }
+            return this.selectedDatas = arr;
+        },
+
+        // 删除选中数据
+        deleteSelected: function (item) {
+            var id = item.id;
+            var _self = this;
+            var newIds = [];
+            var displayValues = [];
+            if (_self.fieldValue.ids != undefined) {
+                for (var i = 0; i < _self.fieldValue.ids.length; i++) {
+                    newIds.push(_self.fieldValue.ids[i]);
+                }
+            }
+
+            if (_self.fieldValue.displayValue != undefined) {
+                for (var i = 0; i < _self.fieldValue.displayValue.length; i++) {
+                    displayValues.push(_self.fieldValue.displayValue[i]);
+                }
+            }
+
+            var index = newIds.indexOf(id);
+            if (index > -1) {
+                newIds.splice(index, 1);
+                _self.fieldValue.ids.splice(index, 1);
+                if (displayValues.length > index) {
+                    displayValues.splice(index, 1);
+                    _self.fieldValue.displayValue.splice(index, 1);
+                }
+            }
+
+            var newFieldValue = {
+                displayValue: displayValues,
+                fieldType: 'MultiSearchBoxEditor',
+                ids: newIds,
+            }
+
+            this.$emit("valueChanged", newFieldValue);
+            this.reComputeSearchText(newFieldValue);
+        }
+    },
+
+    watch: {
+        fieldValue: function (to) {
+            this.reComputeSearchText(to);
+        }
+
+    },
+
+    computed: {
+
+    },
+
+    mounted: function () {
+        this.reComputeSearchText(this.fieldValue);
+    }
+}
+</script>
+
+<style scoped>
+.required-mark {
+    color: red;
+    margin-right: 10px;
+}
+
+.box {
+    width: 200px;
+    min-height: 34px;
+    border: 1px solid rgba(60, 60, 60, 0.26);
+    border-radius: 4px;
+    white-space: normal;
+    transition: border-radius 0.25s;
+    background-color: #ffffff;
+    position: relative;
+}
+
+.search-icon {
+    position: absolute;
+    top: 9px;
+    right: 7px;
+}
+
+.selected-tag {
+    color: #333;
+    background-color: #f0f0f0;
+    border: 1px solid #ccc;
+    border-radius: 4px;
+    height: 26px;
+    margin: 4px 1px 0px 3px;
+    padding: 3px 0.25em;
+    line-height: 34px;
+}
+
+.close {
+    float: none;
+    margin-right: 0;
+    font-size: 20px;
+    appearance: none;
+    padding: 0;
+    cursor: pointer;
+    background: 0 0;
+    border: 0;
+    font-weight: 700;
+    line-height: 1;
+    color: #000;
+    text-shadow: 0 1px 0 #fff;
+    filter: alpha(opacity=20);
+    opacity: 0.2;
+}
+
+.div-readonly {
+    background-color: #eee !important;
+}
+
+.content {
+    display: inline-block !important;
+    width: 200px;
+}
+</style>

+ 262 - 0
packages/info/src/InfoSearchWidget.vue

@@ -0,0 +1,262 @@
+<template>
+    <div class="input-group">
+        <input aria-describedby="addon"
+               type="text"
+               class="form-control input-search"
+               :class="validInput"
+               @input="textChange($event)"
+               v-on:keyup.up='selectUp'
+               v-on:keyup.down='selectDown'
+               v-on:keyup.enter='selectEnter'
+               v-on:keyup.esc="$refs.autoComplete.hide()"
+               :value="searchText"
+               placeholder="请选择内容" />
+        <span @click="showSearchDialog"
+              class="input-group-addon">
+            <span class="glyphicon glyphicon-search"></span>
+        </span>
+        <Modal ref="modal"
+               @ok="searchDialogOk"
+               @cancel="searchDialogCancel"
+               :full="true">
+            <InfoWindow ref="info"
+                        v-if="showSubInfoWindow"
+                        v-on:dataSelected="dataSelected"
+                        :whereClause="whereClause"
+                        :fieldValue="fieldValue"
+                        :isSearchWidget="true"></InfoWindow>
+            <div slot="header">{{titleName}}</div>
+        </Modal>
+        <SearchAutoCompleteWidget ref="autoComplete"
+                                  :infoWindowNo="infoWindowNo"
+                                  :whereClause="whereClause"
+                                  class="auto-complete"
+                                  v-on:selectData="dataSelected" />
+
+        <Loading ref="loading"></Loading>
+    </div>
+</template>
+<script>
+
+var Modal = require("../../modal/src/Modal.vue").default;
+var SearchAutoCompleteWidget = require("./SearchAutoCompleteWidget.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+
+export default {
+    // infoWindowNo: 查询窗口Id
+    // fieldValue: 显示的值
+    // fieldValue:{
+    //		displayValue: ['Jack'],
+    //		fieldType: 'Key',
+    //		id: 1
+    // }
+    // titleName: 弹出窗口的标题
+    // displayName: 文本框中显示的字段
+    name: 'InfoSearchWidget',
+    props: ["infoWindowNo", "fieldValue", "titleName", "displayName", "whereClause"],
+
+    data: function () {
+        return {
+            labelNames: [],
+            isVisible: [],
+            searchText: '',
+            leftPosition: 0,
+            showSubInfoWindow: false,	// 显示子查询窗口
+        }
+    },
+
+    components: {
+        Modal, SearchAutoCompleteWidget, Loading,
+    },
+
+    methods: {
+        selectDown: function () {
+            this.$refs.autoComplete.selectDown();
+        },
+
+        selectUp: function () {
+            this.$refs.autoComplete.selectUp();
+        },
+
+        selectEnter: function () {
+            var _self = this;
+
+            var data = _self.$refs.autoComplete.getSelectData();
+            if (data != undefined) {
+                _self.dataSelected(data);
+                _self.$refs.autoComplete.hide();
+            } else {
+                if (_self.searchText == undefined || _self.searchText.length == 0) {
+                    if (_self.$refs.autoComplete.isVisible() == true) {
+                        _self.$refs.autoComplete.hide();
+                    } else {
+                        _self.$refs.autoComplete.initSearch("");
+                    }
+                }
+            }
+        },
+
+        // 显示搜索对话框
+        showSearchDialog: function () {
+            var _self = this;
+            console.log(this.$refs.modal);
+            this.$refs.modal.showModal();
+            this.showSubInfoWindow = true;
+
+            _self.$refs.autoComplete.hide();
+
+            this.$nextTick(function () {
+                if (this.$refs.info) {
+                    if (this.$refs.info.infoWindowNo != this.infoWindowNo) {
+                        _self.$refs.info.loadByInfoWindowNo(this.infoWindowNo);
+                    } else {
+                        _self.$refs.info.refresh();
+                    }
+                }
+            });
+        },
+
+        /**
+         * 搜索框【确定】按钮点击事件
+         */
+        searchDialogOk: function () {
+            var _self = this;
+            var selectedModelDatas = _self.$refs.info.getSelectedModelDatas();
+            if (selectedModelDatas != undefined || selectedModelDatas.length > 0) {
+                // 选中了数据,更新数据
+                _self.dataSelected(selectedModelDatas[0]);
+            }
+            this.showSubInfoWindow = false;
+        },
+
+        searchDialogCancel: function () {
+
+            this.showSubInfoWindow = false;
+        },
+
+        // 输入的文本发生改变
+        textChange: function (e) {
+            var text = e.target.value;
+            console.log("textChange: " + text);
+            if (text == undefined || text.length == 0) {
+                var newFieldValue = {
+                    id: null,
+                    displayValue: [],
+                    fieldType: 'Key'
+                }
+                this.$emit("valueChanged", newFieldValue);
+            }
+            this.$refs.autoComplete.initSearch(text);
+        },
+
+        // 数据已经选择
+        dataSelected: function (modelData) {
+            var _self = this;
+            this.$refs.modal.hideModal();
+
+            _self.$refs.autoComplete.hide();
+
+            if (modelData == undefined) {
+                return;
+            }
+
+            console.log("已经选择了数据:" + modelData.id);
+
+            var newFieldValue = {
+                id: modelData.id,
+                displayValue: [modelData.data[_self.displayName].displayValue[0]],
+                fieldType: 'Key'
+            }
+
+            if (newFieldValue.displayValue.length > 0) {
+                _self.searchText = newFieldValue.displayValue[0];
+            }
+            this.$emit("valueChanged", newFieldValue);
+            this.$emit("dataSelected", modelData);
+        },
+
+        initSearchText: function (tempFieldValue) {
+            if (tempFieldValue == undefined || tempFieldValue.displayValue == undefined || tempFieldValue.displayValue.length == 0) {
+                return "";
+            } else {
+                return tempFieldValue.displayValue[0];
+            }
+        },
+
+        mounted: function () {
+            this.searchText = this.initSearchText(this.fieldValue);
+        }
+    },
+
+    computed: {
+        titleNames: function () {
+            if (this.labelNames == undefined || this.labelNames.length == 0) {
+                this.labelNames = this.field.name.split(",");
+            }
+            this.reCalVisible();
+            return this.labelNames;
+        },
+
+
+        /**
+         * 是否是有效的数据
+         */
+        validInput: function () {
+            var isValid = true;
+            if (this.searchText != undefined && this.searchText.length > 0) {
+                if (this.fieldValue != undefined && (this.fieldValue.id == undefined || this.fieldValue.id <= 0)) {
+                    isValid = false;
+                } else if (this.fieldValue != undefined && (this.fieldValue.displayValue != undefined && this.fieldValue.displayValue[0] != this.searchText)) {
+                    isValid = false;
+                }
+            }
+
+            return {
+                "invalid-input": isValid == false,
+            }
+        },
+    },
+
+    watch: {
+        redraw: function () {
+            console.log("searchWidgetRedraw");
+            this.reCalVisible();
+        },
+
+        fieldValue: {
+            handler(currentValue, oldValue) {
+                console.log("currentValue:" + JSON.stringify(currentValue));
+                this.searchText = this.initSearchText(currentValue);
+                console.log("searchText:" + this.searchText);
+            },
+            deep: true
+        },
+
+        // 查询条件变化时,重新查询数据
+        whereClause: function () {
+            this.$refs.info.loadByInfoWindowNo(this.infoWindowNo);
+        }
+    },
+}
+</script>
+
+<style scoped>
+.auto-complete {
+    left: 0px;
+    top: 34px;
+}
+
+.input-search {
+    width: 160px !important;
+}
+
+.input-group {
+    width: 200px !important;
+}
+
+.invalid-input {
+    color: #fff;
+    background-color: #d9534f;
+    border-color: #d43f3a;
+}
+</style>

+ 142 - 0
packages/info/src/InfoUtil.js

@@ -0,0 +1,142 @@
+var UserStorageResource = require("../../common/UserStorageResource.js");
+
+module.exports = {
+
+	cloneField: function (field) {
+		var fieldClone = {
+			"fieldName": field.fieldName,
+			"name": field.name,
+			"nameEng": field.nameEng,
+			"isShow": field.isShow,
+			"mandatory": field.mandatory,
+			"sortNo":field.sortNo,
+			"width":field.width
+		}
+		return fieldClone;
+	},
+	// 获取InfoFilterField的Key
+	getInfoFilterFieldKey: function(infoFilterFieldItem){
+		if(infoFilterFieldItem == undefined){
+			return undefined;
+		}
+		return "#InfoFilterFieldItem_" + infoFilterFieldItem.id + "_" + infoFilterFieldItem.fieldName + "_" + infoFilterFieldItem.rowNumber;
+	},
+
+	saveInfoFilterFields: function (infoWindowNo, filterFields) {
+		var key = "InfoFilterFields_" + infoWindowNo;
+		var userStorageDtos = [
+			{
+				key: key,
+				value: JSON.stringify(filterFields),
+			}
+		]
+		return new Promise(function (resolve, reject) {
+			UserStorageResource.uploadUserStorage(userStorageDtos).then(successData => {
+				resolve();
+			}, errorData => {
+				Common.processException(errorData);
+				reject();
+			})
+		});
+	},
+
+	saveInfoGridFields: function (infoWindowNo, filterGrids) {
+		var key = "InfoGridFields_" + infoWindowNo;
+
+		var userStorageDtos = [
+			{
+				key: key,
+				value: JSON.stringify(filterGrids),
+			}
+		];
+
+		return new Promise(function (resolve, reject) {
+			UserStorageResource.uploadUserStorage(userStorageDtos).then(successData => {
+				resolve();
+			}, errorData => {
+				Common.processException(errorData);
+				reject();
+			})
+		});
+
+	},
+
+	restoreInfoFilterFields: function (infoWindowNo, localInfoFilterFields) {
+		var key = "InfoFilterFields_" + infoWindowNo;
+		if (localInfoFilterFields != null) {
+			UserStorageResource.uniqueByKey(key).then(successData => {
+				var remoteInfoFilterFields = null;
+	            if (successData != null) {
+	                remoteInfoFilterFields = JSON.parse(successData);
+	            }else{
+	            	remoteInfoFilterFields = null;
+	            }
+				if (remoteInfoFilterFields != null) {
+					localInfoFilterFields.forEach(localInfoFilterField => {
+						remoteInfoFilterFields.forEach(remoteInfoFilterField => {
+							if (remoteInfoFilterField != null && localInfoFilterField != null && 
+								localInfoFilterField.fieldName == remoteInfoFilterField.fieldName) {
+								localInfoFilterField.isShow = remoteInfoFilterField.isShow;
+							    localInfoFilterField.sortNo = remoteInfoFilterField.sortNo == null ? 0 : remoteInfoFilterField.sortNo;
+							}
+						});
+					})
+					localInfoFilterFields.sort(function(item1, item2){
+		                return item1.sortNo - item2.sortNo; 
+		            });
+				}else{
+					localInfoFilterFields.forEach(localInfoFilterField => {
+						if(localInfoFilterField.sortNo == undefined){
+							localInfoFilterField.sortNo = SortNoUtil.newSortNo();
+						}
+					});
+					localInfoFilterFields.sort(function(item1, item2){
+		                return item1.sortNo - item2.sortNo; 
+		            });
+				}
+			}, errorData => {
+				Common.processException(errorData);
+			});
+		}
+	},
+
+	restoreInfoGridFields: function (infoWindowNo, localInfoGridFields) {
+		var key = "InfoGridFields_" + infoWindowNo;
+		if (localInfoGridFields != null) {
+			UserStorageResource.uniqueByKey(key).then(successData => {
+				var remoteInfoGridFields = null;
+	            if (successData != null) {
+	                remoteInfoGridFields = JSON.parse(successData);
+	            }else{
+	            	remoteInfoGridFields = null;
+	            }
+				if (remoteInfoGridFields != null && remoteInfoGridFields != undefined) {
+					localInfoGridFields.forEach(localInfoGridField => {
+						remoteInfoGridFields.forEach(remoteInfoGridField => {
+							if (remoteInfoGridField != null && localInfoGridField != null && 
+								localInfoGridField.fieldName == remoteInfoGridField.fieldName) {
+								localInfoGridField.isShow = remoteInfoGridField.isShow;
+							    localInfoGridField.sortNo = remoteInfoGridField.sortNo == null ? 0 : remoteInfoGridField.sortNo;
+								localInfoGridField.width = remoteInfoGridField.width;
+							}
+						});
+					})
+					localInfoGridFields.sort(function(item1, item2){
+		                return item1.sortNo - item2.sortNo; 
+		            });
+				}else{
+					localInfoGridFields.forEach(localInfoGridField => {
+						if(localInfoGridField.sortNo == undefined){
+							localInfoGridField.sortNo = SortNoUtil.newSortNo();
+						}
+					});
+					localInfoGridFields.sort(function(item1, item2){
+		                return item1.sortNo - item2.sortNo; 
+		            });
+				}
+			}, errorData => {
+				Common.processException(errorData);
+			});
+		}
+	},
+}

+ 163 - 0
packages/info/src/InfoWindow.vue

@@ -0,0 +1,163 @@
+<template>
+    <div>
+        <component v-bind:is="componentName"
+                   ref="infoComponent"
+                   :fieldValue="fieldValue"
+                   :infoWindow="infoWindowDto"
+                   :whereClause="whereClause"
+                   :isSearchWidget="isSearchWidget"
+                   :parentModelData="parentModelData"
+                   :modelData="modelData"
+                   v-on:dataSelected="dataSelected"
+                   v-on:deleteSelected="deleteSelected"></component>
+
+        <Loading ref="loading" />
+    </div>
+</template>
+<script>
+var Common = require("../../common/Common.js");
+var QueryPage = require("./QueryPage.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+var ClientOrgnization = require('./customer/ClientOrgnizationInfo.vue').default;
+
+module.exports = {
+    name: "InfoWindow",
+    props: ["whereClause", "isSearchWidget", "parentModelData", "modelData", "fieldValue"],
+    data: function () {
+        return {
+            "infoWindowNo": "",
+            "infoWindowDto": {},
+            // 显示部件名称
+            "componentName": undefined,
+        }
+    },
+
+    components: {
+        QueryPage,
+        Loading,
+        ClientOrgnization
+    },
+
+    methods: {
+        /**
+         * 加载数据
+         */
+        loadData: function () {
+            if (this.infoWindowNo == undefined || this.infoWindowNo.length == 0) {
+                return;
+            }
+
+            var _self = this;
+            _self.$refs.loading.show();
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/uniqueByNo'),
+                type: 'GET',
+                dataType: 'json',
+                data: { "infoWindowNo": _self.infoWindowNo },
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    _self.$refs.loading.hide();
+                    console.log(data);
+                    _self.infoWindowDto = data;
+
+                    // 计算显示的部件
+                    if (_self.infoWindowDto.componentName != undefined && _self.infoWindowDto.componentName != "") {
+                        _self.componentName = _self.infoWindowDto.componentName;
+                    } else if (_self.infoWindowDto.infoWindowType == undefined || _self.infoWindowDto.infoWindowType == 'TableGrid'
+                        || _self.infoWindowDto.infoWindowType == 'NONE') {
+                        _self.componentName = "QueryPage";
+                    }
+
+                    _self.$nextTick(function () {
+                        if (_self.infoWindowDto.componentName == undefined || _self.infoWindowDto.componentName == "") {
+                            if (_self.infoWindowDto.infoWindowType == undefined || _self.infoWindowDto.infoWindowType == 'TableGrid' || _self.infoWindowDto.infoWindowType == 'NONE') {
+                                _self.$refs.infoComponent.initWidget(data);
+                            } else {
+                                _self.$refs.infoComponent.initWidget(data);
+                            }
+                        }
+                    });
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.hide();
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 根据 infoWindowNo 加载数据
+         */
+        loadByInfoWindowNo: function (infoWindowNo) {
+            this.infoWindowNo = infoWindowNo;
+            this.loadData();
+        },
+
+        /**
+         * 数据选择事件
+         */
+        dataSelected: function (data) {
+            this.$emit("dataSelected", data);
+        },
+
+        valueChanged: function (fieldValue) {
+            this.$emit("valueChanged", fieldValue);
+        },
+
+        /**
+         * 数据删除事件
+         */
+        deleteSelected: function (data) {
+            this.$emit("deleteSelected", data);
+        },
+
+        /**
+         * 获取选择的数据
+         */
+        getSelectedModelDatas: function (data) {
+            var _self = this;
+            return _self.$refs.infoComponent.getSelectedModelDatas();
+        },
+
+        refresh: function () {
+            var _self = this;
+            if (_self.$refs.infoComponent != undefined) {
+                if (_self.componentName == 'QueryPage') {
+                    _self.$refs.infoComponent.pageSearch();
+                } else if (_self.componentName == 'ClientOrgnization') {
+                    _self.$refs.infoComponent.refresh();
+                }
+            }
+        },
+
+        /**
+         * 获取查询窗口的编号
+         */
+        getInfoWindowNo: function(){
+            if (this.$route.params != undefined) {
+                var routeInfoWindowNo = this.$route.params.infoWindowNo;
+
+                // 如果是搜索框,那么填充查询窗口编号
+                if(this.isSearchWidget != true){
+                    this.infoWindowNo = routeInfoWindowNo;
+                }
+            }
+        }
+    },
+
+    watch: {
+        "$route": function (to, from) {
+            this.getInfoWindowNo();
+            this.loadData();
+        }
+    },
+
+    mounted: function () {
+        this.getInfoWindowNo();
+        this.loadData();
+    }
+}
+
+</script>

+ 87 - 0
packages/info/src/InfoWindowUtil.js

@@ -0,0 +1,87 @@
+module.exports = {
+
+    /**
+     * 克隆过滤字段集合
+     * @param {*} infoFilterField 过滤字段
+     */
+    cloneInfoFilterFields: function (infoFilterFields) {
+        var cloneInfoFilterFields = [];
+        for (var i = 0, len = infoFilterFields.length; i < len; i++) {
+            var infoFilterField = infoFilterFields[i];
+            if (infoFilterField.constraintEnum == 'Between') {
+                var cloneObject1 = this.cloneInfoFilterField(infoFilterField);
+                cloneObject1.index = 1;
+                cloneInfoFilterFields.push(cloneObject1);
+
+                var cloneObject2 = this.cloneInfoFilterField(infoFilterField);
+                cloneObject2.index = 2;
+                cloneObject2.name = "~";
+                cloneObject2.value = cloneObject1.value;
+                cloneInfoFilterFields.push(cloneObject2);
+            } else {
+                var cloneObject1 = this.cloneInfoFilterField(infoFilterField);
+                cloneObject1.index = 1;
+                cloneInfoFilterFields.push(cloneObject1);
+            }
+        }
+
+        // 排序
+        cloneInfoFilterFields.sort(function (item1, item2) {
+            return item1.sortNo - item2.sortNo;
+        });
+
+        return cloneInfoFilterFields;
+    },
+
+    /**
+     * 克隆过滤字段
+     * @param {*} infoFilterField 过滤字段
+     */
+    cloneInfoFilterField: function (infoFilterField) {
+        var cloneObject = {
+            "fieldName": infoFilterField.fieldName,
+            "name": infoFilterField.name,
+            "nameEng": infoFilterField.nameEng,
+            "help": infoFilterField.help,
+            "helpEng": infoFilterField.helpEng,
+            "displayType": infoFilterField.displayType,
+            "listFieldNames": infoFilterField.listFieldNames,
+            "infoWindowNo": infoFilterField.infoWindowNo,
+            "sortNo": infoFilterField.sortNo,
+            "isShow": infoFilterField.isShow,
+            "constraintEnum": infoFilterField.constraintEnum,
+            "defaultValue1": infoFilterField.defaultValue1,
+            "defaultValue2": infoFilterField.defaultValue2,
+            "whereClause": infoFilterField.whereClause,
+            "enumClass": infoFilterField.enumClass,
+            "rowNumber": infoFilterField.rowNumber,
+            "columnNumber": infoFilterField.columnNumber,
+            "columnSpan": infoFilterField.columnSpan,
+            "value": {
+                "infoFilterFieldId": infoFilterField.id,
+                "value1": infoFilterField.value.value1,
+                "value2": infoFilterField.value.value2
+            },
+            "keyValues":infoFilterField.keyValues
+        }
+
+
+        if (cloneObject.displayType == 'ListBoxEnumEditor' || cloneObject.displayType == 'SearchBoxEditor') {
+            var fieldValue = {
+                id: {},
+                displayValue: [],
+                fieldType: "Key"
+            };
+            cloneObject.fieldValue = fieldValue;
+        } else if (cloneObject.displayType == 'MultiSearchBoxEditor') {
+            var fieldValue = {
+                displayValue: [],
+                fieldType: "MultiSearchBoxEditor",
+                ids: []
+            };
+            cloneObject.fieldValue = fieldValue;
+        }
+
+        return cloneObject;
+    },
+}

+ 215 - 0
packages/info/src/QueryCondition.vue

@@ -0,0 +1,215 @@
+<template>
+    <div>
+        <div>
+            <ul class="nav nav-tabs m-row"
+                role="tablist">
+                <li role="presentation"
+                    :class="{'active': isSimple === true}">
+                    <a @click="changeSearch(true)">{{$t('lang.QueryCondition.simpleQuery')}}</a>
+                </li>
+                <li role="presentation"
+                    :class="{'active': isSimple === false}">
+                    <a @click="changeSearch(false)">{{$t('lang.QueryCondition.advancedQuery')}}</a>
+                </li>
+                <slot name="header" v-bind:isSimple="isSimple"></slot>
+            </ul>
+
+            <div class="tab-content">
+                <div class="tab-pane"
+                    :class="{'active': isSimple === true}">
+                    <QueryConditionSimple ref="queryConditionSimple"
+                                          @simpleSearch="simpleSearch"
+                                          @refreshSearch="refreshSearch"
+                                          @executeProcess="executeProcess($event)"
+                                          @executeExport="executeExport"
+                                          :infoFilterFields="infoFilterFields"
+                                          :infoButtons="infoButtons"
+                                          :isSearchWidget="isSearchWidget"
+                                          :showButton="showButton"></QueryConditionSimple>
+                </div>
+
+                <div class="tab-pane"
+                     :class="{'active': isSimple === false}">
+
+                    <QueryConditionComplex ref="queryConditionComplex"
+                                           @complexSearch="complexSearch"
+                                           @executeProcess="executeProcess($event)"
+                                           @executeExport="executeExport"
+                                           :filterFields="filterFields"
+                                           :infoButtons="infoButtons"
+                                           :isSearchWidget="isSearchWidget"
+                                           :showButton="showButton"></QueryConditionComplex>
+                </div>
+                <slot name="body"></slot>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+var InfoWindowUtil = require("./InfoWindowUtil.js");
+var QueryConditionSimple = require("./QueryConditionSimple.vue").default;
+var QueryConditionComplex = require("./QueryConditionComplex.vue").default;
+
+module.exports = {
+    name: "QueryCondition",
+
+    props: ["infoFilterFields", "infoButtons", "isSearchWidget", "showButton"],
+
+    data: function () {
+        return {
+            simpleConditionValue: '',
+            isSimple: true,
+            filterFields: [],
+            selectedText: [],
+        }
+    },
+
+    components: {
+        QueryConditionSimple, QueryConditionComplex
+    },
+
+    methods: {
+
+        /**
+         * 是否是简单查询
+         * @return {Object} 查询条件
+         */
+        isSimpleQuery: function () {
+            return this.isSimple === true;
+        },
+
+        /**
+         * 获取查询条件供外部调用
+         * @return {Object} 查询条件
+         */
+        getQueryCondition: function () {
+            var _self = this;
+            var values = [];
+            if (_self.isSimple === true) {
+                values = this.$refs.queryConditionSimple.getQueryCondition();
+            } else if (_self.isSimple === false) {
+                values = this.$refs.queryConditionComplex.getQueryCondition();
+            }
+            return values;
+        },
+
+        /**
+         * 触发简单查询
+         * @return {void} 
+         */
+        simpleSearch: function () {
+            this.isSimple = true;
+            this.$emit("simpleSearch");
+        },
+
+
+
+        /**
+         * 获取简单查询条件供外部调用
+         * @return {Array} 查询条件
+         */
+        getSimpleQueryCondition: function () {
+            var _self = this;
+            var values = [];
+            return values;
+        },
+
+
+        /**
+         * 回车键查询
+         * @return {void} 
+         */
+        complexSearch: function () {
+            this.isSimple = false;
+            this.$emit("complexSearch");
+        },
+
+        /**
+         * 刷新搜索
+         */
+        refreshSearch: function () {
+            this.$emit("refreshSearch");
+        },
+
+        /**
+         * 执行流程
+         *
+         */
+        executeProcess: function (infoButton) {
+            this.$emit("executeProcess", infoButton);
+        },
+
+        /**
+         * 执行导出
+         * @return {void} 
+         */
+        executeExport: function () {
+            this.$emit("executeExport");
+        },
+
+        /**
+         * 切换简单查询和高级查询
+         * @param  {Boolean} val 是否是简单查询
+         * @return {void}     
+         */
+        changeSearch: function (val) {
+            var _self = this;
+            if (val === false) {
+                this.simpleConditionValue = "";
+                _self.isSimple = false;
+            }else if(val === true){
+                _self.isSimple = true;
+            }else{
+                _self.isSimple = val;
+            }
+            
+            _self.$emit("changeSearch", val);
+
+            // 当前查询是简单查询,切换到高级查询的时候,清空查询条件
+            if (this.isSimple === true && val === false) {
+                if (_self.filterFields != undefined) {
+                    _self.filterFields.forEach(function (item) {
+                        item.value.value1 = '';
+                        item.value.value2 = '';
+                    });
+                }
+            }
+        },
+    },
+    
+    watch: {
+        $route: function (to, from) {
+            var _self = this;
+            _self.simpleConditionValue = '';
+            _self.filterFields = [];
+        },
+
+        infoFilterFields: function (currentValue, oldValue) {
+            var _self = this;
+
+            _self.filterFields.splice(0, _self.filterFields.length);
+
+            if (currentValue == undefined) {
+                return;
+            }
+
+            var cloneInfoFilterFields = InfoWindowUtil.cloneInfoFilterFields(currentValue);
+
+            for (var i = 0, len = cloneInfoFilterFields.length; i < len; i++) {
+                _self.filterFields.push(cloneInfoFilterFields[i]);
+            }
+        }
+    },
+}
+</script>
+
+<style scoped>
+.m-row {
+    margin-bottom: 5px;
+}
+
+.nav > li > a {
+    padding: 8px 10px;
+}
+</style>

+ 413 - 0
packages/info/src/QueryConditionComplex.vue

@@ -0,0 +1,413 @@
+<!-- 复杂查询过滤 -->
+<template>
+    <div>
+        <div>
+            <div class="form-inline">
+                <div class="form-group m-form-group"
+                     v-for="(item,index) in filterFields"
+                     :key="index"
+                     v-show="item.isShow">
+                    <label v-if="item.displayType != 'ListBoxEnumEditor'"
+                           :for="item.id"
+                           v-tooltip.right="Language.getHelpTrl($i18n.locale, item)"
+                           class="m-form-group-label">
+                        {{Language.getNameTrl($i18n.locale, item)}}
+                    </label>
+                    <div v-if="item.index == 1"
+                         class="form-inline-div"
+                         @keyup.enter="complexSearch()">
+                        <input v-if="item.displayType =='TextEditor'"
+                               type="text"
+                               :id="item.id"
+                               :placeholder="$t('lang.QueryConditionComplex.pleaseInputTheContent')"
+                               class="form-control form-control-complex form-input"
+                               v-model="item.value.value1">
+                        <input v-if="item.displayType =='NumberEditor'"
+                               type="number"
+                               class="form-control form-control-complex form-input"
+                               :id="item.id"
+                               :placeholder="$t('lang.QueryConditionComplex.pleaseEnterANumber')"
+                               v-model="item.value.value1"
+                               onmousewheel="return false;"
+                               @mousewheel="mouseWheelEvent">
+
+                        <select v-if="item.displayType =='CheckBoxEditor'"
+                                v-model="item.value.value1"
+                                class="form-control form-control-complex form-input form-select">
+                            <option value="">{{$t('lang.QueryConditionComplex.all')}}</option>
+                            <option value="true">{{$t('lang.QueryConditionComplex.true')}}</option>
+                            <option value="false">{{$t('lang.QueryConditionComplex.false')}}</option>
+                        </select>
+
+                        <date-time-v2 v-if="item.displayType =='DateTimeBoxEditor'"
+                                        :dateValue="item.value.value1"
+                                        @on-value-change="dateValueChange($event, item, 'value1')"
+                                        class="m-form-input"></date-time-v2>
+
+                        <!--  年份选择器-->
+                        <YearPicker v-if="item.displayType =='YearEditor'"
+                                    :dateValue="item.value.value1"
+                                    @selected="textChanged(item, 'value1', $event)"
+                                    class="m-form-input"></YearPicker>
+
+                        <!--  年月选择器  -->
+                        <VueMonthlyPicker v-if="item.displayType =='YearMonthEditor'"
+                                          id="month-picker"
+                                          v-model="item.value.value1"
+                                          dateFormat="YYYY-MM"
+                                          class="form-inline-div m-form-input"
+                                          @selected="refresh"></VueMonthlyPicker>
+
+                        <!-- 日期选择器  -->
+                        <DateWidget v-if="item.displayType =='DateBoxEditor'"
+                                    :dateValue="item.value.value1"
+                                    @on-value-change="dateValueChange($event, item, 'value1')"
+                                    class="m-form-input" />
+
+                        <EnumSelectWidgetInfo v-if="item.displayType =='ListBoxEnumEditor'"
+                                              :field="item"
+                                              :fieldValue="item.fieldValue"
+                                              v-on:valueChanged="tabValueChanged($event, item, 'value1')">
+                        </EnumSelectWidgetInfo>
+
+                        <InfoSearchWidget v-if="item.displayType =='SearchBoxEditor'"
+                                          :whereClause="item.whereClause"
+                                          :infoWindowNo="item.infoWindowNo"
+                                          :titleName="item.name"
+                                          :fieldValue="item.fieldValue"
+                                          :displayName="item.listFieldNames"
+                                          v-on:valueChanged="tabValueChanged($event, item)"
+                                          class="m-form-input"></InfoSearchWidget>
+
+                        <!-- 多选搜索框 number类型-->
+                        <InfoMultiSearchWidget v-if="item.displayType =='MultiSearchBoxEditor'"
+                                               :whereClause="item.whereClause"
+                                               :infoWindowNo="item.infoWindowNo"
+                                               :fieldValue="item.fieldValue"
+                                               :displayName="item.listFieldNames"
+                                               v-on:valueChanged="multiSearchValueChange($event, item)"
+                                               class="m-form-input"></InfoMultiSearchWidget>
+
+                    </div>
+                    <div v-if="item.index == 2"
+                         class="form-inline-div">
+                        <input v-if="item.displayType =='TextEditor'"
+                               type="text"
+                               :id="item.id"
+                               :placeholder="$t('lang.QueryConditionComplex.pleaseInputTheContent')"
+                               class="form-control form-control-complex form-input"
+                               v-model="item.value.value2"
+                               @keyup.enter="complexSearch()">
+                        <input v-if="item.displayType =='NumberEditor'"
+                               type="number"
+                               class="form-control form-control-complex form-input"
+                               :id="item.id"
+                               :placeholder="$t('lang.QueryConditionComplex.pleaseEnterANumber')"
+                               v-model="item.value.value2"
+                               @keyup.enter="complexSearch()">
+
+                        <select v-if="item.displayType =='CheckBoxEditor'"
+                                v-model="item.value.value2"
+                                class="form-control form-control-complex form-input form-select">
+                            <option value="">{{$t('lang.QueryConditionComplex.all')}}</option>
+                            <option value="true">{{$t('lang.QueryConditionComplex.true')}}</option>
+                            <option value="false">{{$t('lang.QueryConditionComplex.false')}}</option>
+                        </select>
+
+                        <!-- <input v-if="item.displayType =='CheckBoxEditor'" type="checkbox" class="form-control form-control-complex form-input" :id="item.id" v-model="item.value.value2" @keyup.enter="complexSearch()"> -->
+                        <!-- <input v-if="item.displayType =='DateTimeBoxEditor'" type="date" placeholder="yyyy-MM-dd" class="form-control form-control-complex form-input" :id="item.id" v-model="item.value.value2" @keyup.enter="complexSearch()"> -->
+                        <DateTimeV2 v-if="item.displayType =='DateTimeBoxEditor'"
+                                        :dateValue="item.value.value2"
+                                        @on-value-change="dateValueChange($event, item, 'value2')"
+                                        class="m-form-input"></DateTimeV2>
+
+                        <!--  年份选择器-->
+                        <YearPicker v-if="item.displayType =='YearEditor'"
+                                    :dateValue="item.value.value2"
+                                    @selected="textChanged(item, 'value2', $event)"
+                                    class="m-form-input"></YearPicker>
+
+                        <!--  年月选择器  -->
+                        <vue-monthly-picker v-if="item.displayType =='YearMonthEditor'"
+                                          id="month-picker"
+                                          v-model="item.value.value2"
+                                          dateFormat="YYYY-MM"
+                                          class="form-inline-div m-form-input"
+                                          @selected="refresh"></vue-monthly-picker>
+
+                        <!-- 日期选择器  -->
+                        <DateWidget v-if="item.displayType =='DateBoxEditor'"
+                                    :dateValue="item.value.value2"
+                                    @on-value-change="dateValueChange($event, item, 'value2')"
+                                    class="m-form-input" />
+
+                        <EnumSelectWidgetInfo v-if="item.displayType =='ListBoxEnumEditor'"
+                                              :field="item"
+                                              :fieldValue="item.fieldValue"
+                                              v-on:valueChanged="tabValueChanged($event, item)"
+                                              class="m-form-input">
+                        </EnumSelectWidgetInfo>
+
+                        <!--单选number类型-->
+                        <InfoSearchWidget v-if="item.displayType =='SearchBoxEditor'"
+                                          :whereClause="item.whereClause"
+                                          :infoWindowNo="item.infoWindowNo"
+                                          :titleName="item.name"
+                                          :fieldValue="item.fieldValue"
+                                          :displayName="item.listFieldNames"
+                                          v-on:valueChanged="tabValueChanged($event, item)"
+                                          class="m-form-input"></InfoSearchWidget>
+
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <div slot="footer">
+            <div>
+                <div>
+                    <button id="filter"
+                            type="button"
+                            @click="complexSearch()"
+                            class="btn btn-primary m-btn">{{$t('lang.QueryConditionComplex.filter')}}</button>
+
+                    <template v-if="!isSearchWidget">
+                        <button type="button"
+                                @click="executeExport()"
+                                class="btn btn-success m-btn">{{$t('lang.QueryConditionComplex.export')}}</button>
+
+                        <template v-for="infoButton in infoButtons" :key="infoButton.name">
+                            <button @click="executeProcess(infoButton)"
+                                    v-tooltip.right="Language.getHelpTrl($i18n.locale, infoButton)"
+                                    class="btn btn-info btn-process m-btn">{{Language.getNameTrl($i18n.locale, infoButton)}}</button>
+                        </template>
+                    </template>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+
+<script>
+
+var EnumSelectWidgetInfo = require("./EnumSelectWidgetInfo.vue").default;
+var DateTimeV2 = require("../../datetime-v2/src/DateTimeV2.vue").default;
+var InfoSearchWidget = require("./InfoSearchWidget.vue").default;
+var InfoMultiSearchWidget = require("./InfoMultiSearchWidget.vue").default;
+var YearPicker = require("../../year-picker/src/YearPicker.vue").default;
+var DateWidget = require("../../date/src/Date.vue").default;
+var VueMonthlyPicker = require("../../vue-monthly-picker/src/VueMonthlyPicker.vue").default;
+var Language = require("../../common/Language.js");
+
+module.exports = {
+
+    props: ["filterFields", "infoButtons", "isSearchWidget", "showButton"],
+
+    data: function () {
+        this.Language = Language;
+        return {
+            simpleConditionValue: '',
+            selectedText: [],
+        }
+    },
+
+    components: {
+        EnumSelectWidgetInfo,
+        DateTimeV2,
+        InfoSearchWidget,
+        InfoMultiSearchWidget,
+        DateWidget,
+        VueMonthlyPicker,
+        YearPicker
+    },
+
+    methods: {
+        /**
+                 * 日期时间选择框值改变事件
+                 */
+        dateValueChange: function (value, item, fieldName) {
+            item.value[fieldName] = value;
+        },
+        /**
+         * 年月选择器时间改变
+         */
+        refresh: function () {
+
+        },
+        /**
+         * 年份选择器的值改变
+         * @param {Object} item
+         * @param {Object} key
+         * @param {Object} val
+         */
+        textChanged: function (item, key, val) {
+            var value = val + " ";
+            item.value[key] = value.replace(/(^\s*)|(\s*$)/g, "");
+        },
+
+
+        /**
+           * 搜索框值发生改变
+           * @param  {Object} newFieldValue 改变后的值
+           * @param  {Object} item          当前field
+           * @return {void}               
+           */
+        tabValueChanged: function (newFieldValue, item) {
+            item.fieldValue = newFieldValue;
+
+            if (item.index == 1) {
+                if (item.displayType == "ListBoxEnumEditor") {
+                    item.value.value1 = newFieldValue.displayValue[0];
+                } else {
+                    item.value.value1 = newFieldValue.id;
+                }
+            } else if (item.index == 2) {
+                item.value.value2 = newFieldValue.id;
+            }
+
+            item.value.infoFilterFieldId = item.id;
+        },
+
+        // 多选搜索框值发生改变事件
+        multiSearchValueChange: function (newFieldValue, item) {
+            var _self = this;
+            item.fieldValue = newFieldValue;
+            var ids = newFieldValue.ids;
+            var texts = newFieldValue.displayValue;
+            if (item.index == 1) {
+                item.value.value1 = ids.join(',');
+            } else if (item.index == 2) {
+                item.value.value2 = ids.join(',');
+            }
+            item.value.infoFilterFieldId = item.id;
+        },
+
+        /**
+         * 回车键查询
+         * @return {void} 
+         */
+        complexSearch: function () {
+            this.$emit("complexSearch");
+        },
+
+        /**
+         * 执行导出
+         * @return {void} 
+         */
+        executeExport: function () {
+            this.$emit("executeExport");
+        },
+
+        /**
+         * 获取查询条件供外部调用
+         * @return {Object} 查询条件
+         */
+        getQueryCondition: function () {
+            var _self = this;
+            var values = [];
+            _self.filterFields.forEach(function (item) {
+                if (item.index == 1) {
+                    item.value.fieldName = item.fieldName;
+                    values.push(item.value);
+                }
+            });
+            return values;
+        },
+
+        /**
+         * 执行流程
+         *
+         */
+        executeProcess: function (infoButton) {
+            this.$emit("executeProcess", infoButton);
+        },
+
+        /**
+         * 禁止滚轮滚动影响数字
+         */
+        mouseWheelEvent: function (event) {
+            if (event || Window.event) {
+                event.preventDefault();
+            }
+        }
+    }
+}
+</script>
+
+
+<style scoped>
+.form-input {
+    border-radius: 4px !important;
+}
+.form-inline-div {
+    display: inline-block;
+}
+.m-select {
+    display: inline-block !important;
+}
+.m-form-group {
+    margin-left: 5px !important;
+    margin-right: 5px !important;
+    margin-bottom: 5px !important;
+}
+/* .m-form-group-label{
+		width: 100px;
+		text-align: right;
+		padding-right: 10px;
+	} */
+
+@media (max-width: 768px) {
+    .m-form-group-label {
+        text-align: left;
+        padding-right: 10px;
+    }
+}
+
+@media (min-width: 768px) {
+    .m-form-group-label {
+        width: 100px;
+        text-align: center;
+        padding-right: 10px;
+    }
+
+    .input-switches {
+        width: 100%;
+    }
+}
+.btn-process {
+    margin-right: 5px;
+}
+
+.m-row {
+    margin-bottom: 5px;
+    text-align: left;
+}
+
+.form-control-complex {
+    float: none !important;
+    width: auto !important;
+}
+
+.input-simple {
+    width: 100% !important;
+}
+
+.input-switches {
+    width: 200px;
+}
+.form-select {
+    width: 200px !important;
+}
+.m-form-input {
+    border-radius: 4px !important;
+    width: 200px !important;
+}
+.form-control-complex {
+    width: 200px !important;
+}
+.m-btn {
+    float: left;
+    margin-right: 5px;
+}
+</style>

+ 122 - 0
packages/info/src/QueryConditionSimple.vue

@@ -0,0 +1,122 @@
+<!-- 简单查询过滤 -->
+<template>
+    <div>
+        <div>
+            <input type="text"
+                   v-model="simpleConditionValue"
+                   @keyup.enter="simpleSearch"
+                   class="form-control input-simple"
+                   :placeholder="$t('lang.QueryConditionSimple.enterSearchCriteria')">
+        </div>
+        <div v-if="showButton" style="margin-top: 5px">
+            <div>
+                <button id="filter"
+                        type="button"
+                        @click="simpleSearch()"
+                        class="btn btn-primary m-btn">{{$t('lang.QueryConditionSimple.filter')}}</button>
+
+                <button id="filter"
+                        type="button"
+                        @click="refreshSearch()"
+                        class="btn btn-primary m-btn">{{$t('lang.QueryConditionSimple.refresh')}}</button>
+
+                <template v-if="!isSearchWidget">
+                    <button type="button"
+                            @click="executeExport()"
+                            class="btn btn-success m-btn">{{$t('lang.QueryConditionSimple.export')}}</button>
+
+                    <template v-for="infoButton in infoButtons" :key="infoButton.name">
+                        <button @click="executeProcess(infoButton)"
+                                v-tooltip.right="Language.getHelpTrl($i18n.locale, infoButton)"
+                                class="btn btn-info btn-process m-btn">{{Language.getNameTrl($i18n.locale, infoButton)}}</button>
+                    </template>
+                </template>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+
+var Language = require("../../common/Language.js");
+
+module.exports = {
+
+    props: ["infoFilterFields", "infoButtons", "isSearchWidget", "showButton"],
+
+    data: function () {
+        this.Language = Language;
+        return {
+            simpleConditionValue: '',
+            selectedText: [],
+        }
+    },
+
+    components: {
+    },
+
+    methods: {
+
+
+        /**
+         * 触发简单查询
+         * @return {void} 
+         */
+        simpleSearch: function () {
+            this.$emit("simpleSearch");
+        },
+
+        /**
+         * 刷新搜索
+         */
+        refreshSearch: function () {
+            this.$emit("refreshSearch");
+        },
+
+        /**
+         * 执行导出
+         * @return {void} 
+         */
+        executeExport: function () {
+            this.$emit("executeExport");
+        },
+
+        /**
+         * 执行流程
+         *
+         */
+        executeProcess: function (infoButton) {
+            this.$emit("executeProcess", infoButton);
+        },
+
+        /**
+         * 获取查询条件供外部调用
+         * @return {Object} 查询条件
+         */
+        getQueryCondition: function () {
+            var _self = this;
+            var values = [];
+            _self.infoFilterFields.forEach(function (item) {
+                if (item.displayType == "TextEditor" && item.constraintEnum != 'Between') {
+                    item.value.value1 = _self.simpleConditionValue;
+                    item.value.fieldName = item.fieldName;
+                    values.push(item.value);
+                }
+            });
+            return values;
+        }
+    }
+}
+</script>
+
+
+<style scoped>
+.m-row {
+    margin-bottom: 5px;
+    text-align: left;
+}
+
+.input-simple {
+    width: 100% !important;
+}
+</style>

+ 950 - 0
packages/info/src/QueryPage.vue

@@ -0,0 +1,950 @@
+<template>
+    <div class="flex-container"
+         :class="{'flex-container-modal': isSearchWidget == true, 'flex-container' : (isSearchWidget == null || isSearchWidget == false)}">
+        <div class="flex-header">
+            <div class="panel panel-default"
+                 style="margin-bottom: 5px;">
+                <div class="panel-heading"
+                     style="padding: 0px;">
+                    <InfoHeader :htmlHelpUrl="infoWindowDto.htmlHelpUrl"
+                                :infoGridFields="infoGridFields"
+                                :headerName="infoWindowDto.name"
+                                :infoFilterFields="infoFilterFields"
+                                v-on:filterFieldPropertyChanged="filterFieldPropertyChanged($event)"
+                                v-on:gridFieldPropertyChanged="gridFieldPropertyChanged($event)"
+                                :infoWindowNo="infoWindowDto.no"></InfoHeader>
+                </div>
+                <div class="panel-body"
+                     style="padding-bottom: 5px; padding-top: 8px;">
+                    <QueryCondition ref="queryCondition"
+                                    :infoFilterFields="infoFilterFields"
+                                    :infoButtons="infoWindowDto.infoButtons"
+                                    v-on:simpleSearch="simpleSearch"
+                                    v-on:complexSearch="complexSearch"
+                                    v-on:refreshSearch="pageSearch"
+                                    v-on:executeProcess="executeProcess($event)"
+                                    v-on:executeExport="executeExport"
+                                    :isSearchWidget="isSearchWidget"
+                                    :showButton="true"></QueryCondition>
+                </div>
+            </div>
+        </div>
+        <div class="flex-content">
+            <div class="flex-main table-fix-head"
+                 :id="tableOutDivId">
+                <table class="fixed-table table-striped table-bordered"
+                       :width="tableWidth"
+                       height="40px">
+                    <thead>
+                        <tr height="40px">
+                            <th width="50px">{{$t('lang.QueryPage.serialNumber')}}</th>
+                            <th width="30px"
+                                @click.self="changeSelectMode"
+                                class="text-center"
+                                :class="{'mulitiple-select': multipleSelect}">
+                                <input type="checkbox"
+                                       v-model="isSelectAll">
+                            </th>
+                            <th v-for="(infoGridField,index) in infoGridFields"
+                                v-show="infoGridField.isShow"
+                                :key="infoGridField.fieldName"
+                                :width="infoGridField.width + 'px'"
+                                @dragover="ondragover($event, infoGridField)"
+                                @click="onSort(infoGridField)">
+
+                                <div class="rz-handle"
+                                     draggable='true'
+                                     :id="'infoGridFieldId_' + infoGridField.fieldName"
+                                     @dragstart="ondragstart($event, infoGridField)"
+                                     @drag="ondrag($event, infoGridField)"
+                                     @dragend="ondragend($event, infoGridField)">
+                                </div>
+
+                                <div class="td-max"
+                                     v-tooltip.right="Language.getHelpTrl($i18n.locale, infoGridField)">
+                                    {{Language.getNameTrl($i18n.locale, infoGridField)}}
+                                </div>
+                            </th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr v-for="(item1,index) in infoWindowData.dataList"
+                            @dblclick="selectNode(item1)"
+                            :key="'tr' + item1.id"
+                            height="40px"
+                            :class="{'warning':item1.select}">
+
+                            <td style="text-align: center;">{{index+1+(pagination.current_page-1)*pagination.per_page}}</td>
+
+                            <td class="text-center">
+                                <input type="checkbox"
+                                       :checked="item1.select"
+                                       @click.self="selectNodeForSearch(item1)" />
+                            </td>
+                            <td v-for="item2 in infoGridFields"
+                                v-show="item2.isShow"
+                                id="item.id">
+                                <span v-if="item2.simpleDisplayType == 'Image'">
+                                    <QueryPageImage :className="item2.selectClause"
+                                                    :imageNames="item1.data[item2.fieldName].displayValue[0]"
+                                                    v-if="item1.data[item2.fieldName] != undefined">
+                                    </QueryPageImage>
+                                </span>
+                                <span v-else>
+                                    {{item1.data[item2.fieldName] != undefined ? item1.data[item2.fieldName].displayValue[0] : ""}}
+                                </span>
+                            </td>
+                        </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        <div class="flex-footer"
+             style="margin-top: 10px;">
+            <div class="pull-left">
+                <span>{{$t('lang.QueryPage.the')}}{{(pagination.current_page-1)*pagination.per_page+1}}-{{pagination.current_page*pagination.per_page}}{{$t('lang.QueryPage.strip')}},{{$t('lang.QueryPage.total')}}{{pagination.total}}{{$t('lang.QueryPage.strip')}},{{$t('lang.QueryPage.displayOnEachPage')}}</span>
+                <PageSizeSelect v-on:pageSizeChanged="gridSizeSelect"></PageSizeSelect>
+                <span>{{$t('lang.QueryPage.strip')}}</span>
+            </div>
+            <div class="pull-right">
+                <Pagination :pagination="pagination"
+                            :callback="pageSearch"></Pagination>
+            </div>
+        </div>
+        <Loading ref="loading" />
+        <Modal ref="modal">
+            <ProcessReportResult :processReportResult="processReportResult"
+                                 :pdfOnly="pdfOnly"
+                                 :excelOnly="excelOnly"></ProcessReportResult>
+            <div slot="header">{{$t('lang.QueryPage.resultsOfEnforcement')}}</div>
+        </Modal>
+    </div>
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var Modal = require("../../modal/src/Modal.vue").default;
+
+var PageSizeSelect = require("../../page-size-select/src/PageSizeSelect.vue").default;
+var Pagination = require("../../vue-bootstrap-pagination/src/vue-bootstrap-pagination.vue").default;
+
+var QueryCondition = require("./QueryCondition.vue").default;
+var InfoHeader = require("./InfoHeader.vue").default;
+var InfoUtil = require("./InfoUtil.js");
+var Notify = require("../../common/Notify.js").default;
+
+var Loading = require("../../loading/src/Loading.vue").default;
+var ProcessReportResult = require("../../process/src/ProcessReportResult.vue").default;
+var Uuid = require("../../common/Uuid.js");
+var QueryPageImage = require("./QueryPageImage.vue").default;
+
+var DownloadService = require("../../common/DownloadService.js");
+var HtmlWindowResource = require("../../html-window/src/api/HtmlWindowResource.js");
+var CustomerWindowResource = require("../../customer-window/src/api/CustomerWindowResource.js");
+var ProcessReportResource = require("../../process/src/api/ProcessReportResource.js");
+
+var UserStorageResource = require("../../common/UserStorageResource.js");
+
+var Language = require("../../common/Language.js");
+
+module.exports = {
+
+    name: "QueryPage",
+
+    props: ["whereClause", "isSearchWidget", "parentModelData", "modelData", "fieldValue"],
+
+    data: function () {
+        this.Language = Language;
+        return {
+            infoWindowDto: {},
+            queryResult: {},
+            infoWindowData: {},
+            selectedModelDatas: [],	// 当前界面的选择项 (multiple)
+            pagination: {
+                total: 0,
+                per_page: Common.pageSize,    // required
+                current_page: 1, // required
+                last_page: 10,    // required
+            },
+            isSelectAll: false,
+            infoQueryParam: [],
+            processReportResult: [],
+            multipleSelect: false,
+            uuid: Uuid.createUUID(),
+            sortStr: "",// 排序
+            sortStyle: " ASC",
+            currentIsSimpleSearch: true,
+            startX: "",
+            startWidth: "",
+            processReportDto: "",
+            pdfOnly: "",
+            excelOnly: "",
+            infoGridFields: [],
+            infoFilterFields: [],
+            // 表格外面的DIV的id
+            tableOutDivId: Uuid.createUUID(),
+        }
+    },
+
+    components: {
+        Common,
+        Pagination,
+        QueryCondition,
+        Modal,
+        InfoHeader,
+        Notify,
+        Loading,
+        ProcessReportResult,
+        Uuid,
+        InfoUtil,
+        PageSizeSelect,
+        QueryPageImage,
+        DownloadService
+    },
+
+    methods: {
+        // 页码数量改变
+        gridSizeSelect: function (newPageSize) {
+            this.pagination.per_page = newPageSize;
+            this.pagination.current_page = 1;
+        },
+
+        ondragstart: function (event, gridFieldItem) {
+            var _self = this;
+            _self.startX = event.pageX;
+            _self.startWidth = Number(gridFieldItem.width);
+            event.dataTransfer.setDragImage(event.target, 0, 20);
+            event.dataTransfer.effectAllowed = 'move';
+        },
+
+        ondrag: function (event, gridFieldItem) {
+            var _self = this;
+            gridFieldItem.width = _self.startWidth + (event.pageX - _self.startX);
+        },
+
+        ondragend: function (event, gridFieldItem, index) {
+            var _self = this;
+            let newWidth = _self.startWidth + (event.pageX - _self.startX);
+            if (newWidth < 50) {
+                newWidth = 50;
+            }
+            gridFieldItem.width = newWidth;
+
+            InfoUtil.saveInfoGridFields(_self.infoWindowDto.no, _self.infoGridFields);
+
+        },
+
+
+        ondragover: function (event, gridFieldItem) {
+            event.preventDefault()
+            event.dataTransfer.dropEffect = 'move';
+        },
+
+
+        // 初始化数据
+        initWidget: function (data) {
+            var _self = this;
+            if (data == undefined) {
+                return;
+            }
+
+            _self.infoWindowDto = data;
+            _self.infoFilterFields = data.infoFilterFields;
+            _self.infoGridFields = data.infoGridFields;
+            // var sortNo = 10;
+            _self.infoGridFields.forEach(function (item) {
+                _self.$set(item, "width", 150);
+            });
+            _self.infoFilterFields.forEach(function (item) {
+                item.value = {
+                    "infoFilterFieldId": item.id,
+                    "value1": "",
+                    "value2": ""
+                }
+            });
+            InfoUtil.restoreInfoFilterFields(_self.infoWindowDto.no, _self.infoFilterFields);
+            InfoUtil.restoreInfoGridFields(_self.infoWindowDto.no, _self.infoGridFields);
+
+
+            _self.simpleSearch();
+        },
+
+        complexSearch: function () {
+            var _self = this;
+            _self.pagination.current_page = 1;
+            var infoQueryParam = {
+                infoWindowNo: _self.infoWindowDto.no,
+                start: (_self.pagination.current_page - 1) * _self.pagination.per_page,
+                length: _self.pagination.per_page,
+                sortClause: "",
+                infoFilterFieldValues: _self.$refs.queryCondition.getQueryCondition(),
+                whereClause: _self.whereClause,
+                parentModelData: _self.parentModelData,
+                modelData: _self.modelData,
+                isSearchWidget: _self.isSearchWidget
+            };
+            _self.infoQueryParam = infoQueryParam;
+            _self.currentIsSimpleSearch = false;
+            _self.queryInfoWindowDataComplex();
+        },
+
+        pageSearch: function () {
+            var _self = this;
+            _self.infoQueryParam.start = (_self.pagination.current_page - 1) * _self.pagination.per_page;
+            _self.infoQueryParam.length = _self.pagination.per_page;
+            var isSimpleQuery = _self.$refs.queryCondition.isSimpleQuery();
+            if (isSimpleQuery) {
+                _self.queryInfoWindowDataSimple();
+            } else {
+                _self.queryInfoWindowDataComplex();
+            }
+        },
+
+        /**
+       * 简单查询
+       * @return {void} 
+       */
+        simpleSearch: function () {
+            var _self = this;
+            _self.pagination.current_page = 1;
+            var infoQueryParam = {
+                infoWindowNo: _self.infoWindowDto.no,
+                start: (_self.pagination.current_page - 1) * _self.pagination.per_page,
+                length: _self.pagination.per_page,
+                sortClause: "",
+                infoFilterFieldValues: _self.$refs.queryCondition.getQueryCondition(),
+                whereClause: _self.whereClause,
+                parentModelData: _self.parentModelData,
+                modelData: _self.modelData,
+                isSearchWidget: _self.isSearchWidget
+            };
+            _self.infoQueryParam = infoQueryParam;
+            _self.currentIsSimpleSearch = true;
+            _self.queryInfoWindowDataSimple();
+        },
+
+        queryInfoWindowDataComplex: function () {
+            var _self = this;
+            _self.$refs.loading.show();
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/queryInfoWindowDataComplex'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(_self.infoQueryParam),
+                success: function (data) {
+                    _self.$refs.loading.hide();
+                    data.dataList.forEach(function (item) {
+                        item.select = false;
+                    });
+                    _self.infoWindowData = data;
+                    _self.selectedModelDatas.splice(0, _self.selectedModelDatas.length);
+
+                    _self.reSelectedNode(_self.fieldValue);
+                    _self.pagination.total = data.totalSize;
+                    _self.pagination.last_page = Math.ceil(data.totalSize / data.range.length);
+
+                    _self.fixedTableHeader();
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.hide();
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        queryInfoWindowDataSimple: function () {
+            var _self = this;
+            if (_self.$refs.loading) {
+                _self.$refs.loading.show();
+            }
+
+            _self.infoQueryParam.whereClause = _self.whereClause;
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/queryInfoWindowDataSimple'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(_self.infoQueryParam),
+                success: function (data) {
+                    if (_self.$refs.loading) {
+                        _self.$refs.loading.hide();
+                    }
+                    data.dataList.forEach(function (item) {
+                        item.select = false;
+                    });
+                    _self.infoWindowData = data;
+
+                    _self.selectedModelDatas.splice(0, _self.selectedModelDatas.length);
+
+                    _self.reSelectedNode(_self.fieldValue);
+                    _self.pagination.total = data.totalSize;
+                    _self.pagination.last_page = Math.ceil(data.totalSize / data.range.length);
+
+                    _self.fixedTableHeader();
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    if (_self.$refs.loading) {
+                        _self.$refs.loading.hide();
+                    }
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 双击表格行事件
+         */
+        selectNode: function (modelData) {
+            this.$emit("dataSelected", modelData);
+        },
+
+        /**
+         * 选择/取消选择表格行中的复选框事件
+         */
+        selectNodeForSearch: function (modelData) {
+            var _self = this;
+            if ((_self.fieldValue != undefined) && (_self.fieldValue.fieldType == "ManyToManyKey")) {
+                // CRUD-ManyToManySetBoxEditor多选搜索框-多选
+
+            } else if ((_self.fieldValue != undefined) && (_self.fieldValue.displayType == "MultiSearchBoxEditor")) {
+                // 多选
+
+            } else if ((_self.fieldValue != undefined) && (_self.fieldValue.fieldType == "Key")) {
+                // 单选
+                // 清空选择项
+                _self.selectedModelDatas.splice(0, _self.selectedModelDatas.length);
+            }
+            var currentStatus = modelData.select;
+            var index = _self.selectedModelDatas.indexOf(modelData);
+            if (!_self.multipleSelect) {
+                _self.selectedModelDatas = [];
+                _self.infoWindowData.dataList.forEach(function (item) {
+                    item.select = false;
+                });
+            }
+
+            modelData.select = !currentStatus;
+
+            if (index < 0) {
+                _self.selectedModelDatas.push(modelData);
+                _self.$emit("dataSelected", modelData);
+            } else {
+                _self.selectedModelDatas.splice(index, 1);
+                _self.$emit("deleteSelected", modelData);
+            }
+        },
+
+        /**
+         * 获取选择的数据
+         */
+        getSelectedModelDatas: function (data) {
+            var _self = this;
+            return _self.selectedModelDatas;
+        },
+
+        /**
+         * 条件字段属性发生改变
+         */
+        filterFieldPropertyChanged: function (infoFilterFieldsClone) {
+            let _self = this;
+            InfoUtil.saveInfoFilterFields(_self.infoWindowDto.no, infoFilterFieldsClone).then(successData => {
+                InfoUtil.restoreInfoFilterFields(_self.infoWindowDto.no, _self.infoFilterFields);
+            }, errorData => {
+                console.log(errorData);
+            });
+        },
+
+        /**
+         * 表格字段属性发生改变
+         */
+        gridFieldPropertyChanged: function (infoGridFieldsClone) {
+            let _self = this;
+            InfoUtil.saveInfoGridFields(_self.infoWindowDto.no, infoGridFieldsClone).then(successData => {
+                InfoUtil.restoreInfoGridFields(_self.infoWindowDto.no, _self.infoGridFields);
+            }, errorData => {
+                console.log(errorData);
+            });
+        },
+
+        /**
+         * 执行流程
+         * @param  {Object} infoButton 
+         * @return {void}             
+         */
+        executeProcess: function (infoButton) {
+            var _self = this;
+
+            if (infoButton == null) {
+                return;
+            }
+
+            if (infoButton.htmlWindowNo != null && infoButton.htmlWindowNo.length > 0) {
+                HtmlWindowResource.uniqueByNo(infoButton.htmlWindowNo).then(htmlWindowDto => {
+                    if (htmlWindowDto != undefined) {
+                        var htmlWindowUrl = htmlWindowDto.htmlFileName;
+                        var autoCloseInterval = htmlWindowDto.autoCloseInterval;
+                        var regExp = new RegExp("[{].*?[}]", "g");
+                        var result = htmlWindowUrl.match(regExp);
+                        if (htmlWindowUrl != undefined && htmlWindowUrl != "") {
+                            for (var index = 0, len = result.length; index < len; index++) {
+                                var tempResult = result[index];
+                                console.log("{" + tempResult + "}匹配");
+                                if (tempResult == "{URL}") {
+                                    htmlWindowUrl = htmlWindowUrl.replace("{URL}", Common.getRedirectUrl(""));
+                                    console.log("{" + htmlWindowUrl + "}地址");
+                                } else if (tempResult == "{RecordId}") {
+                                    if (_self.selectedModelDatas.length == 0) {
+                                        Notify.error("错误", "未选择可操作的数据", true);
+                                        return;
+                                    }
+                                    htmlWindowUrl = htmlWindowUrl.replace("{RecordId}", _self.getFirstSelectRecordId());
+                                } else if (tempResult == "{RecordIds}") {
+                                    if (_self.selectedModelDatas.length == 0) {
+                                        Notify.error("错误", "未选择可操作的数据", true);
+                                        return;
+                                    }
+                                    htmlWindowUrl = htmlWindowUrl.replace("{RecordIds}", _self.getSelectedRecordIds());
+                                } else if (tempResult == "{Token}") {
+                                    htmlWindowUrl = htmlWindowUrl.replace("{Token}", Common.getToken());
+                                } else if (tempResult == "{infoWindowNo}") {
+                                    htmlWindowUrl = htmlWindowUrl.replace("{infoWindowNo}", _self.infoWindowDto.no);
+                                } else {
+                                    if (_self.selectedModelDatas.length == 0) {
+                                        Notify.error("错误", "未选择可操作的数据", true);
+                                        return;
+                                    } else if (_self.selectedModelDatas.length > 1) {
+                                        Notify.error("错误", "请选择一条数据", true);
+                                        return;
+                                    }
+                                    var tempResult1 = tempResult.replace("{", "").replace("}", "");
+                                    htmlWindowUrl = htmlWindowUrl.replace(tempResult, _self.getFirstSelectModelDataFieldValue(tempResult1));
+                                }
+                            }
+
+                            var openWindow = window.open(htmlWindowUrl);
+
+                            // 自动关闭
+                            if (autoCloseInterval != undefined) {
+                                setTimeout(function () {
+                                    openWindow.close();
+                                    openWindow = undefined;
+                                }, autoCloseInterval * 1000);
+                            }
+                        }
+                    }
+                }, errorData => {
+                    Common.processException(errorData);
+                })
+            } else if (infoButton.customerWindowNo != null && infoButton.customerWindowNo.length > 0) {
+                CustomerWindowResource.uniqueByNo(infoButton.customerWindowNo).then(customerWindowDto => {
+                    var customerWindowRouteUrl = customerWindowDto.routeUrl;
+                    if (customerWindowRouteUrl != undefined && customerWindowRouteUrl != "") {
+                        if (customerWindowRouteUrl == "exportInfoData") {
+                            var downloadUrl = Common.getApiURL('exportResource/exportInfoData') + "?infoWindowNo=" + _self.infoWindowDto.no
+                                + "&recordIds=" + _self.getSelectedRecordIds().join(",")
+                                + "&token=" + Common.getToken();
+                            window.open(downloadUrl);
+                        } else {
+                            // if (_self.selectedModelDatas.length == 0) {
+                            // Notify.error("错误", "未选择可操作的数据", true);
+                            // return;
+                            // }
+                            localStorage.setItem(_self.uuid, JSON.stringify(_self.selectedModelDatas));
+
+                            var routeDate = {
+                                path: customerWindowRouteUrl,
+                                query: {
+                                    "uuid": _self.uuid
+                                }
+                            }
+                            _self.$router.push(routeDate);
+                        }
+                    }
+                }, errorData => {
+                    Common.processException(errorData);
+                });
+            } else if (infoButton.processReportNo != null && infoButton.processReportNo.length > 0) {
+
+                var processReportNo = infoButton.processReportNo;
+                if (processReportNo != undefined && processReportNo != "") {
+                    var _self = this;
+                    var ids = _self.getSelectedRecordIds();
+                    if (ids.length < 1) {
+                        Notify.error("错误", "未选择可操作的数据", true);
+                        return;
+                    }
+
+                    _self.$refs.loading.show();
+                    ProcessReportResource.uniqueByNo(infoButton.processReportNo).then(successData => {
+                        _self.processReportDto = successData;
+                        if (successData) {
+                            _self.pdfOnly = successData.pdfOnly;
+                            _self.excelOnly = successData.excelOnly;
+                        }
+                        ProcessReportResource.runProcessByIds(infoButton.processReportNo, ids).then(successData => {
+                            _self.$refs.modal.show = true;
+                            _self.$refs.loading.hide();
+                            _self.processReportResult = successData;
+                            _self.$emit("processExecuteFinish");
+                        }, errorData => {
+                            _self.$refs.loading.hide();
+                            Common.processException(errorData);
+                        });
+                    }, errorData => {
+                        _self.$refs.loading.hide();
+                        Common.processException(errorData);
+                    });
+                }
+            }
+        },
+
+        /**
+         * 执行导出
+         * @return {void} 
+         */
+        executeExport: function () {
+            var _self = this;
+
+            var infoFilterFieldValues = _self.$refs.queryCondition.getQueryCondition();
+            var infoFilterFieldValueStrs = JSON.stringify(infoFilterFieldValues);
+
+            var isSimpleQuery = _self.$refs.queryCondition.isSimpleQuery();
+
+            var downloadUrl = Common.getApiURL('exportResource/exportInfoData') + "?infoWindowNo=" + _self.infoWindowDto.no
+                + "&recordIds=" + _self.getSelectedRecordIds().join(",")
+                + "&infoFilterFieldValues=" + _self.uuid
+                + "&simpleSearch=" + isSimpleQuery;
+
+
+            var timeStr = moment().format('_YYYYMMDD_hhmmss');
+            var fileName = (_self.infoWindowDto == null ? '导出数据' + timeStr + '.xls' : _self.infoWindowDto.name + timeStr + '.xls');
+            let userStorageDtos = [
+                {
+                    key: _self.uuid,
+                    value: infoFilterFieldValueStrs,
+                }
+            ]
+            // 组装查询条件,然后放到后台数据库当中
+            UserStorageResource.uploadUserStorage(userStorageDtos).then(successData => {
+                DownloadService.downloadFile(downloadUrl, fileName);
+            }, errorData => {
+                Common.processException(errorData);
+            });
+        },
+
+
+        /**
+         * 获取选择数据的Id集合
+         */
+        getSelectedRecordIds: function () {
+            var _self = this;
+            var recordIds = [];
+            _self.selectedModelDatas.forEach(function (selectedModelData) {
+                recordIds.push(selectedModelData.id);
+            });
+            return recordIds;
+        },
+
+        /**
+         * 获取选中的第一个数据的Id
+         */
+        getFirstSelectRecordId: function () {
+            var _self = this;
+            if (_self.selectedModelDatas.length > 0) {
+                return _self.selectedModelDatas[0].id;
+            }
+            return undefined;
+        },
+
+        /**
+         * 获取选中的第一个数据的FieldValue
+         */
+        getFirstSelectModelDataFieldValue: function (name) {
+            var _self = this;
+            if (_self.selectedModelDatas.length > 0) {
+                if (_self.selectedModelDatas[0].data[name] != undefined) {
+                    return _self.selectedModelDatas[0].data[name].displayValue[0];
+                }
+            }
+            return undefined;
+        },
+
+        /**
+         * 切换全选/单选
+         */
+        changeSelectMode: function () {
+            this.multipleSelect = !this.multipleSelect;
+        },
+
+        /**
+         * 排序
+         */
+        onSort: function (infoGridField) {
+            var _self = this;
+            var fieldName = null;
+            if (infoGridField.sortFieldName != undefined && infoGridField.sortFieldName != "") {
+                fieldName = infoGridField.sortFieldName;
+            } else {
+                fieldName = infoGridField.fieldName;
+            }
+            var sortStr = fieldName + _self.sortStyle;
+
+            _self.infoQueryParam.sortClause = sortStr;
+            if (_self.currentIsSimpleSearch) {
+                _self.queryInfoWindowDataSimple();
+            } else {
+                _self.queryInfoWindowDataComplex();
+            }
+
+            _self.sortStyle = _self.sortStyle == " ASC" ? " DESC" : " ASC";
+        },
+
+        /**
+         * 重新加载数据以后,重新计算当前界面的选择项
+         */
+        reSelectedNode: function (fieldValue) {
+            if (!fieldValue) {
+                return;
+            }
+            var _self = this;
+            var ids = [];
+            if (fieldValue.displayType == "MultiSearchBoxEditor") {
+                ids = fieldValue.ids;
+            } else if (fieldValue.fieldType == "ManyToManyKey") {
+                ids = fieldValue.ids;
+            } else if (fieldValue.fieldType == "Key") {
+                ids.push(fieldValue.id);
+            } else {
+                return;
+            }
+
+            _self.selectedModelDatas.splice(0, _self.selectedModelDatas.length);
+            if (this.infoWindowData && this.infoWindowData.dataList) {
+                this.infoWindowData.dataList.forEach(function (node) {
+                    if (ids.indexOf(node.id) > -1) {
+                        _self.selectedModelDatas.push(node);
+                    }
+                });
+            }
+        },
+
+
+
+        /**
+         * 冻结表头
+         */
+        fixedTableHeader: function () {
+            let _self = this;
+            _self.$nextTick(function () {
+                var $th = $('#' + _self.tableOutDivId).find('thead');
+                $('#' + _self.tableOutDivId).on('scroll', function () {
+                    $th.css('transform', 'translateY(' + this.scrollTop + 'px)');
+                });
+            });
+        }
+
+    },
+    computed: {
+        tableWidth: function () {
+            var totalWidth = 50;
+            if (this.infoWindowDto != undefined && this.infoGridFields != undefined) {
+                this.infoGridFields.forEach(function (item) {
+                    if (item.isShow) {
+                        totalWidth += Number(item.width);
+                    }
+                });
+            }
+            return totalWidth + "px";
+        }
+    },
+    watch: {
+
+        /**
+         * 是否选择了全部的数据
+         */
+        isSelectAll: function (val) {
+            var _self = this;
+            if (_self.multipleSelect) {
+                if (_self.isSelectAll) {
+                    _self.selectedModelDatas.splice(0, _self.selectedModelDatas.length);
+                    if (val) {
+                        _self.infoWindowData.dataList.forEach(function (item) {
+                            item.select = true;
+                            _self.selectedModelDatas.push(item);
+                        })
+                    }
+                } else {
+                    _self.infoWindowData.dataList.forEach(function (item) {
+                        item.select = false;
+                    })
+                    _self.selectedModelDatas = [];
+                }
+            } else {
+                _self.isSelectAll = false;
+            }
+        },
+
+        /**
+         * 路由发生改变
+         */
+        $route: function (to, from) {
+            var _self = this;
+            _self.infoWindowData = {};
+        },
+
+        /**
+         * 选择的数据发生改变
+         */
+        // selectedModelDatas: function(to, from){
+        // 	var _self = this;
+        // 	if(!_self.multipleSelect){
+        // 		if(to.length > 1){
+        // 			_self.selectedModelDatas.splice(0, to.length - 1);
+        // 		}
+        // 	}
+        // },
+
+        /**
+         * fieldValue发生改变
+         */
+        fieldValue: function (to, from) {
+            this.reSelectedNode(to);
+        }
+    }
+}
+</script>
+
+<style scoped>
+.sort-button {
+    float: left;
+}
+
+.fixed-table {
+    table-layout: fixed;
+}
+table.fixed-table tr th {
+    text-align: center;
+}
+table.fixed-table td {
+    text-align: center;
+    word-wrap: break-word;
+    word-break: normal;
+}
+.btn-process {
+    float: right;
+    margin-left: 15px;
+}
+.mulitiple-select {
+    background-color: #6699cc !important;
+}
+
+table.fixed-table th {
+    position: relative;
+    min-width: 25px;
+}
+
+table.fixed-table th,
+table.fixed-table td {
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    border-right: 1px solid rgba(0, 0, 0, 0.05);
+}
+
+table.fixed-table th {
+    min-width: 10px;
+    background-color: #f8f8f8;
+}
+
+table.fixed-table th .rz-handle {
+    width: 10px;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    right: 0;
+    background: repeating-linear-gradient(
+        45deg,
+        transparent,
+        transparent 2px,
+        rgba(0, 0, 0, 0.05) 2px,
+        rgba(0, 0, 0, 0.05) 4px
+    );
+    cursor: ew-resize !important;
+}
+
+table.fixed-table th .rz-handle.rz-handle-active {
+    border-right: 2px solid #000;
+    transform: scaleX(100);
+    background: rgba(0, 0, 0, 0.05) 2px;
+}
+
+.rz-handle:hover {
+    background: rgba(0, 0, 0, 0.2) 4px;
+}
+
+.flex-container {
+    display: flex;
+    /* 垂直*/
+    flex-direction: column;
+    width: 100%;
+    /*视口被均分为100单位的vh 占据整个窗口*/
+    height: calc(100vh - 75px);
+}
+
+.flex-container-modal {
+    display: flex;
+    /* 垂直*/
+    flex-direction: column;
+    width: 100%;
+    /*视口被均分为100单位的vh 占据整个窗口*/
+    height: calc(100vh - 215px);
+}
+
+.flex-header {
+    /*放大缩小比例为0 占据垂直方向80px*/
+    height: 400px;
+    flex: 0 0 100px;
+}
+
+.flex-footer {
+    height: 40px;
+    flex: 0 0 40px;
+}
+
+.flex-content {
+    display: flex;
+    flex: 1;
+    height: 0;
+}
+
+.flex-main {
+    overflow: scroll;
+    flex: 1;
+    margin-bottom: 0px;
+}
+</style>
+
+
+<style scoped>
+/** 修复分页的样式 By YangZhiJie 2021-07-06 11:23 */
+nav >>> ul.pagination {
+    margin: 0 !important;
+}
+</style>
+
+<style scoped>
+.warning {
+    background-color: #fcf8e3 !important;
+}
+
+.text-center {
+    text-align: center !important;
+}
+</style>

+ 92 - 0
packages/info/src/QueryPageImage.vue

@@ -0,0 +1,92 @@
+<template>
+    <div>
+        <span>
+            <img :src="getImageSrc(item)"
+                       class="img-thumbnail m-div"
+                       v-for="item,index in images"
+                       v-if="images && images.length > 0"
+                       @click="showImage(item)" />
+        </span>
+        <Modal ref="fullImage"
+               full="true"
+               :title="$t('lang.QueryPageImage.viewPicture')"
+               :showOkButton="false">
+            <img :src="getImageSrc(selectedImage)"
+                       style="height: 100%;width: 100%"
+                       v-for="item,index in images"
+                       v-if="selectedImage && selectedImage.length > 0" />
+
+            <div slot="footer">
+                <button class="btn btn-success"
+                        type="button"
+                        @click="download">{{$t('lang.QueryPageImage.downloadPictures')}}</button>
+            </div>
+        </Modal>
+    </div>
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var Modal = require("../../modal/src/Modal.vue").default;
+var ImagePreview = require("../../image-preview/src/ImagePreview.vue").default;
+
+module.exports = {
+    props: ["className", "imageNames"],
+    data: function () {
+        this.Common = Common;
+        return {
+            images: [],
+            selectedImage: "",
+        }
+    },
+
+    components: {
+        Modal, ImagePreview
+    },
+
+    methods: {
+        showImage: function (item) {
+            this.selectedImage = item;
+            this.$refs.fullImage.show = true;
+        },
+        /**
+       * 获取图片地址
+       * @param  {String} item 图片名称
+       * @return {String}      图片URL地址
+       */
+        getImageSrc: function (item) {
+            var _self = this;
+            if (item != undefined && item != null) {
+                return Common.getImageSrc(_self.selectClause, item);
+            } else {
+                return "";
+            }
+        },
+        download: function () {
+            window.open(this.getImageSrc(this.selectedImage));
+        }
+    },
+    mounted: function () {
+        if (this.imageNames == undefined || this.imageNames.length == 0) {
+            this.images = [];
+        } else {
+            this.images = this.imageNames.split(",");
+        }
+    },
+    watch: {
+        /**
+         * fieldValue发生改变
+         */
+        imageNames: function (value) {
+            if (value == undefined || value.length == 0) {
+                this.images = [];
+            } else {
+                this.images = value.split(",");
+            }
+        }
+    }
+}
+</script>
+
+<style scoped>
+</style>

+ 301 - 0
packages/info/src/SearchAutoCompleteWidget.vue

@@ -0,0 +1,301 @@
+<template>
+    <div class="div-autoComplete"
+         v-show="isShowAuto"
+         v-bind:style="{ left: realLeftComputed }">
+        <div class="table-responsive">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <td align="center"
+                            v-for="item in infoWindowDto.infoGridFields"
+                            width="100px"
+                            height="40px">
+                            {{item.name}}
+                        </td>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr v-for="(item1, index) in infoWindowData.dataList"
+                        @click="selectNode(item1)"
+                        height="40px"
+                        :class="{'select-tr': index == selectIndex}">
+                        <td v-for="item2 in infoWindowDto.infoGridFields"
+                            align="center">
+                            {{(item1.data[item2.fieldName] == undefined) ? "" : item1.data[item2.fieldName].displayValue[0]}}
+                        </td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+</template>
+<script>
+var Common = require("../../common/Common.js");
+var Modal = require("../../modal/src/Modal.vue").default;
+
+module.exports = {
+    props: ["infoWindowNo", "fieldId", "leftPosition", "whereClause", "parentModelData", "modelData"],
+
+    data: function () {
+        return {
+            infoWindowDto: {},
+            infoWindowData: {},
+            isShowAuto: false,
+            selectIndex: -1,
+            realLeft: {},
+            isSearchWidget: true
+        }
+    },
+
+    components: {
+        Modal
+    },
+
+    methods: {
+        /**
+         * 向下
+         * @return {void} 
+         */
+        selectDown: function () {
+            var _self = this;
+            var length = _self.infoWindowData.dataList.length;
+            if (_self.selectIndex < (length - 1)) {
+                _self.selectIndex++;
+            }
+        },
+
+        /**
+         * 向上
+         * @return {void} 
+         */
+        selectUp: function () {
+            var _self = this;
+            var length = _self.infoWindowData.dataList.length;
+            if (_self.selectIndex > 0) {
+                _self.selectIndex--;
+            }
+        },
+
+        /**
+         * 获取当前数据 供外部调用
+         * @return {Object} ModelData
+         */
+        getSelectData: function () {
+            var _self = this;
+
+            if (_self.selectIndex < 0) {
+                return undefined;
+            }
+
+            var data = _self.infoWindowData.dataList[_self.selectIndex];
+            return data;
+        },
+
+        /**
+         * 获取第一行数据
+         */
+        getFirstData: function () {
+            var _self = this;
+            if (_self.infoWindowData != null && _self.infoWindowData.dataList != null && _self.infoWindowData.dataList.length > 0) {
+                return _self.infoWindowData.dataList[0];
+            }
+            return null;
+        },
+
+        /**
+         * 隐藏
+         * @return {[type]} [description]
+         */
+        hide: function () {
+            var _self = this;
+            _self.isShowAuto = false;
+        },
+
+
+        /**
+         * 是否可见
+         * @return {Boolean} [description]
+         */
+        isVisible: function () {
+            return this.isShowAuto;
+        },
+
+        /**
+         * 查询InfoWindowDto
+         * @return {void} 
+         */
+        getInfoWindowDto: function (resolve, reject) {
+            var _self = this;
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/uniqueByNo'),
+                type: 'GET',
+                dataType: 'json',
+                data: { "infoWindowNo": _self.infoWindowNo },
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    _self.infoWindowDto = data;
+                    resolve();
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                    reject();
+                }
+            });
+        },
+
+        /**
+         * 生成查询条件
+         * @param  {String} text 查询条件
+         * @return {Array}      
+         */
+        getSimpleQueryCondition: function (text) {
+            var _self = this;
+            var values = [];
+            if (_self.infoWindowDto.infoFilterFields != undefined) {
+                _self.infoWindowDto.infoFilterFields.forEach(function (item) {
+                    if (item.displayType == "TextEditor") {
+                        var value = {
+                            fieldName: item.fieldName,
+                            value1: text
+                        }
+                        values.push(value);
+                    }
+                });
+            }
+            return values;
+        },
+
+        /**
+         * 根据条件初始化查询
+         * @param  {String} text 查询条件
+         * @return {void}      
+         */
+        initSearch: function (text) {
+            var _self = this;
+            if (text != undefined) {
+                if (_self.infoWindowDto == undefined || _self.infoWindowDto.infoGridFields == undefined) {
+                    new Promise(_self.getInfoWindowDto).then(function (value) {
+                        // success
+                        _self.getInfoWindowData(text);
+                    }, function (error) {
+                        // error
+                    });
+                } else {
+                    _self.getInfoWindowData(text);
+                }
+            } else {
+                _self.isShowAuto = false;
+            }
+        },
+
+        /**
+         * 选择节点
+         * @return {void} 
+         */
+        selectNode: function (data) {
+            this.$emit("selectData", data);
+        },
+        /**
+         * 查询infoWindowData
+         * @return {void} 
+         */
+        getInfoWindowData: function (text) {
+            var _self = this;
+            var infoFilterFieldValues = _self.getSimpleQueryCondition(text);
+
+            var infoQueryParam = {
+                infoWindowNo: _self.infoWindowNo,
+                start: 0,
+                length: 10,
+                sortClause: "",
+                infoFilterFieldValues: infoFilterFieldValues,
+                whereClause: _self.whereClause,
+                modelData: _self.modelData,
+                parentModelData: _self.parentModelData,
+                isSearchWidget: _self.isSearchWidget
+            }
+
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/queryInfoWindowDataSimple'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(infoQueryParam),
+                success: function (data) {
+                    _self.infoWindowData = data;
+                    _self.selectIndex = -1;
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+    },
+
+    mounted: function () {
+
+    },
+
+    computed: {
+        realLeftComputed: function () {
+            return (((this.realLeft > 0) ? this.realLeft : 0) + 'px');
+        }
+    },
+
+    watch: {
+        infoWindowData: function (val) {
+            var _self = this;
+            if (val.dataList != undefined && val.dataList.length > 0) {
+                _self.isShowAuto = true;
+            } else {
+                _self.isShowAuto = false;
+            }
+        },
+
+        /**
+         * 距离左侧的距离
+         * @param  {[type]} currentValue [description]
+         * @param  {[type]} oldValue     [description]
+         * @return {[type]}              [description]
+         */
+        leftPosition: function (currentValue, oldValue) {
+            var _self = this;
+            console.log("initialLeft changed: " + currentValue);
+            _self.realLeft = currentValue;
+            console.log("realLeft2:" + _self.realLeft);
+        }
+    }
+}
+</script>
+
+<style scoped>
+.div-autoComplete {
+    position: absolute;
+    z-index: 999;
+    background-color: #fff;
+    border: 1px #e5e5e5 solid;
+    padding: 15px 15px 0px 15px;
+    box-shadow: 0 0 5px rgba(81, 203, 238, 1);
+    /*width: 300px;*/
+    /*height: 300px;*/
+    /*max-height: 400px;*/
+    max-width: 800px;
+    min-width: 400px;
+    /*overflow: auto;*/
+    top: 32px;
+}
+
+.no-padding-left {
+    padding-left: 0px !important;
+}
+
+.select-tr {
+    background-color: #eee;
+}
+</style>

+ 278 - 0
packages/info/src/SearchWidget.vue

@@ -0,0 +1,278 @@
+<template>
+    <div class="input-group">
+        <input aria-describedby="addon"
+               type="text"
+               class="form-control"
+               :class="validInput"
+               @input="textChange($event)"
+               v-on:keyup.up='selectUp'
+               v-on:keyup.down='selectDown'
+               v-on:keyup.enter='selectEnter'
+               v-on:keyup.esc="$refs.autoComplete.hide()"
+               style="border-top-right-radius: 0; border-bottom-right-radius: 0;"
+               :value="searchText"
+               :readonly="readonly" />
+        <div :class="[classSize != undefined ? 'input-group-btn classSize' : 'input-group-btn']">
+            <button type="button"
+                    :class="[classSize != undefined ? 'btn btn-default classSize' : 'btn btn-default']"
+                    @click="showSearchDialog">
+                <span class="glyphicon glyphicon-search"></span>
+            </button>
+            <slot name="button1"></slot>
+            <slot name="button2"></slot>
+            <slot name="button3"></slot>
+        </div>
+
+        <Modal ref="modal"
+               @ok="searchDialogOk"
+               @cancel="searchDialogCancel"
+               :full="true">
+            <info ref="info"
+                  v-on:dataSelected="dataSelected"
+                  :fieldValue="fieldValue"
+                  :whereClause="whereClause"
+                  :isSearchWidget="true"
+                  :modelData="modelData"></info>
+            <div slot="header">{{titleName}}</div>
+        </Modal>
+
+        <SearchAutoCompleteWidget ref="autoComplete"
+                                  :infoWindowNo="infoWindowNo"
+                                  :whereClause="whereClause"
+                                  class="auto-complete"
+                                  v-on:selectData="dataSelected" />
+
+        <Loading ref="loading"></Loading>
+    </div>
+</template>
+
+
+<script>
+var Modal = require("../../modal/src/Modal.vue").default;
+var Info = require("./InfoWindow.vue").default;
+var SearchAutoCompleteWidget = require("./SearchAutoCompleteWidget.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+
+module.exports = {
+    name: "SearchWidget",
+    // infoWindowNo: 查询窗口编号
+    // fieldValue: 显示的值
+    // fieldValue:{
+    //		displayValue: ['Jack'],
+    //		fieldType: 'Key',
+    //		id: 1
+    // }
+    // titleName: 弹出窗口的标题
+    // displayName: 文本框中显示的字段
+    // whereClause: 约束条件
+
+    props: ["infoWindowNo", "fieldValue", "titleName", "displayName", "whereClause", "readonly", "modelData", "classSize"],
+
+    data: function () {
+        var inputSearchText = this.initSearchText(this.fieldValue);
+
+        return {
+            isVisible: [],
+            searchText: inputSearchText,
+            leftPosition: 0,
+        }
+    },
+
+    components: {
+        Modal, Info, SearchAutoCompleteWidget, Loading
+    },
+
+    methods: {
+        selectDown: function () {
+            this.$refs.autoComplete.selectDown();
+        },
+
+        selectUp: function () {
+            this.$refs.autoComplete.selectUp();
+        },
+
+        selectEnter: function () {
+            var _self = this;
+
+            var data = _self.$refs.autoComplete.getSelectData();
+            if (data != undefined) {
+                _self.dataSelected(data);
+                _self.$refs.autoComplete.hide();
+            } else {
+                if (_self.searchText == undefined || _self.searchText.length == 0) {
+                    if (_self.$refs.autoComplete.isVisible() == true) {
+                        _self.$refs.autoComplete.hide();
+                    } else {
+                        _self.$refs.autoComplete.initSearch("");
+                    }
+                }
+            }
+        },
+
+        // 显示搜索对话框
+        showSearchDialog: function () {
+            var _self = this;
+
+            // 如果是只读,则直接返回。
+            if (_self.readonly != undefined && _self.readonly == true) {
+                return;
+            }
+
+            console.log(this.$refs.modal);
+            this.$refs.modal.showModal();
+
+            _self.$refs.autoComplete.hide();
+
+            if (this.$refs.info.infoWindowNo != _self.infoWindowNo) {
+                this.$refs.info.loadByInfoWindowNo(this.infoWindowNo);
+            } else {
+                _self.$refs.info.refresh();
+            }
+        },
+
+        /**
+         * 搜索框【确定】按钮点击事件
+         */
+        searchDialogOk: function () {
+            var _self = this;
+            var selectedModelDatas = _self.$refs.info.getSelectedModelDatas();
+            if (selectedModelDatas != undefined || selectedModelDatas.length > 0) {
+                // 选中了数据,更新数据
+                _self.dataSelected(selectedModelDatas[0]);
+            }
+        },
+
+        searchDialogCancel: function () {
+
+        },
+
+        // 输入的文本发生改变
+        textChange: function (e) {
+            var text = e.target.value;
+            console.log("textChange: " + text);
+            if (text == undefined || text.length == 0) {
+                var newFieldValue = {
+                    id: undefined,
+                    displayValue: [],
+                    fieldType: 'Key'
+                }
+                this.$emit("valueChanged", newFieldValue);
+            }
+            this.$refs.autoComplete.initSearch(text);
+        },
+
+        // 数据已经选择
+        dataSelected: function (modelData) {
+            var _self = this;
+            this.$refs.modal.hideModal();
+
+            _self.$refs.autoComplete.hide();
+
+            if (modelData == undefined) {
+                return;
+            }
+
+            console.log("已经选择了数据:" + modelData.id);
+            var displayValue = "";
+            if (modelData.data && modelData.data[_self.displayName]) {
+                displayValue = modelData.data[_self.displayName].displayValue[0];
+            } else if (modelData[_self.displayName]) {
+                displayValue = modelData[_self.displayName];
+            }
+
+            var newFieldValue = {
+                id: modelData.id,
+                displayValue: [displayValue],
+                fieldType: 'Key'
+            }
+
+            if (newFieldValue.displayValue.length > 0) {
+                _self.searchText = newFieldValue.displayValue[0];
+            }
+            this.$emit("valueChanged", newFieldValue);
+            this.$emit("dataSelected", modelData);
+        },
+
+        initSearchText: function (tempFieldValue) {
+            if (tempFieldValue == undefined || tempFieldValue.displayValue == undefined || tempFieldValue.displayValue.length == 0) {
+                return "";
+            } else {
+                return tempFieldValue.displayValue[0];
+            }
+        }
+    },
+
+    computed: {
+
+
+        /**
+         * 是否是有效的数据
+         */
+        validInput: function () {
+            var _self = this;
+            var isValid = true;
+            if (this.searchText != undefined && this.searchText.length > 0) {
+                if (this.fieldValue != undefined && (this.fieldValue.id == undefined || this.fieldValue.id <= 0)) {
+                    isValid = false;
+                } else if (this.fieldValue != undefined && (this.fieldValue.displayValue != undefined && this.fieldValue.displayValue[0] != this.searchText)) {
+                    isValid = false;
+                }
+            }
+            var isflag = false;
+            if (_self.classSize != undefined) {
+                isflag = true
+            }
+            var ss = {
+                "invalid-input": isValid == false,
+                "classSize": isflag == true
+            }
+            return ss;
+        },
+    },
+
+    watch: {
+        redraw: function () {
+            console.log("searchWidgetRedraw");
+        },
+
+        fieldValue: {
+            handler(currentValue, oldValue) {
+                console.log("currentValue:" + JSON.stringify(currentValue));
+                this.searchText = this.initSearchText(currentValue);
+                console.log("searchText:" + this.searchText);
+            },
+            deep: true
+        },
+
+        // 查询条件变化时,重新查询数据
+        whereClause: function () {
+            this.$refs.info.loadByInfoWindowNo(this.infoWindowNo);
+        }
+    },
+
+    created: function () {
+    }
+}
+</script>
+
+<style scoped>
+.auto-complete {
+    left: 0px;
+    top: 34px;
+}
+
+.m-input-group-addon {
+    border-top-right-radius: 5px !important;
+    border-bottom-right-radius: 5px !important;
+}
+
+.invalid-input {
+    color: #fff;
+    background-color: #d9534f;
+    border-color: #d43f3a;
+}
+.classSize {
+    font-size: 40px;
+    height: 60px;
+}
+</style>

+ 175 - 0
packages/info/src/customer/ClientOrgnizationInfo.vue

@@ -0,0 +1,175 @@
+<template>
+    <div>
+        <div>
+            <div class="panel panel-default">
+                <div class="panel-body">
+                    <div v-for="clientOrgnization in clientOrgnizations"
+                         :key="clientOrgnization.id">
+                        <TreeViewNode :node="clientOrgnization"
+                                      v-on:nodeExpand="nodeExpand"
+                                      v-on:nodeSelect="nodeSelect"
+                                      :isRoot="true"
+                                      :isShowCheck="clientOrgnization.isShowCheck"></TreeViewNode>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <Loading ref="loading" />
+    </div>
+</template>
+
+<script>
+var Common = require("../../../common/Common.js");
+var Loading = require("../../../loading/src/Loading.vue").default;
+var TreeViewNode = require("../../../tree/src/TreeViewNode.vue").default;
+
+module.exports = {
+    props: ["whereClause", "isSearchWidget", "modelData", "parentModelData", "fieldValue"],
+
+    data: function () {
+        return {
+            "infoWindowDto": {},
+            "datas": [],
+            sendData: {},
+            clientOrgnizations: []
+        }
+    },
+
+    components: {
+        Common, TreeViewNode, Loading
+    },
+
+    methods: {
+        // 节点打开事件
+        nodeExpand: function (node) {
+            console.log(node);
+        },
+
+        // 节点选择事件
+        nodeSelect: function (node) {
+            console.log(node);
+            if (!node.isClient) {
+                node.selected = !node.selected;
+                if (node.selected) {
+                    // 增加
+                    var modelData1 = {
+                        "id": node.id,
+                        "data": {
+                            "name": {
+                                "displayValue": [node.name],
+                            }
+                        },
+                    }
+                    this.$emit("dataSelected", modelData1);
+                } else {
+                    // 减少
+                    var modelData1 = {
+                        "id": node.id,
+                        "data": {
+                            "name": {
+                                "displayValue": [node.name],
+                            }
+                        },
+                    }
+                    this.$emit("deleteSelected", modelData1);
+                }
+                // this.reSelectedNode(newFieldValue);
+            }
+        },
+
+        // 获取单位部门
+        refresh: function () {
+            var _self = this;
+            $.ajax({
+                type: "get",
+                dataType: "json",
+                url: Common.getApiURL("orgnizationResource/getClientOrgnizations"),
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    function setOpen(node) {
+                        node.open = false;
+                        node.selected = false;
+                        if (node.isClient) {
+                            node.isShowCheck = false;
+                        } else {
+                            node.isShowCheck = true;
+                        }
+                        node.text = node.name;
+                        if (node.childrenDatas != undefined && node.childrenDatas.length > 0) {
+                            node.childrenDatas.forEach(function (item) {
+                                setOpen(item);
+                            })
+                        }
+                    }
+                    data.forEach(function (item) {
+                        if (item.childrenDatas == null) {
+                            item.open = true;
+                        }
+                        setOpen(item);
+                    })
+                    // add by jack 20180201
+                    // 展开一级树
+                    // if (data.childrenDatas != undefined && data.childrenDatas.length > 0) {
+                    // 	data.childrenDatas.forEach(function (item) {								
+                    // item.open = true;
+                    //	});
+                    // }
+
+                    _self.clientOrgnizations = data;
+                    _self.reSelectedNode(_self.fieldValue);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        reSelectedNode: function (fieldValue) {
+            if (fieldValue == undefined) {
+                return;
+            }
+            var ids = fieldValue.ids;
+            function setOpen(node) {
+                if (ids.indexOf(node.id) > -1) {
+                    node.selected = true;
+                } else {
+                    node.selected = false;
+                }
+                if (node.childrenDatas != undefined && node.childrenDatas.length > 0) {
+                    node.childrenDatas.forEach(function (item) {
+                        setOpen(item);
+                    })
+                }
+            }
+            if (ids != undefined) {
+                this.clientOrgnizations.forEach(function (item) {
+                    setOpen(item);
+                })
+            }
+        }
+
+    },
+
+    watch: {
+        fieldValue: function (to, from) {
+            this.reSelectedNode(to);
+        }
+    },
+
+    mounted: function () {
+        this.refresh();
+    }
+}
+</script>
+    
+<style scoped>
+.clear-div {
+    clear: both;
+}
+
+.filter-button {
+    float: right;
+}
+</style>

+ 8 - 0
packages/loading/index.js

@@ -0,0 +1,8 @@
+
+import Loading from './src/Loading.vue';
+
+Loading.install = function(Vue) {
+    Vue.component(Loading.name, Loading);
+};
+
+export default Loading;

+ 183 - 0
packages/loading/src/Loading.vue

@@ -0,0 +1,183 @@
+<template>
+    <div class="loading"
+         v-show="visible">
+        <input ref="focusInput"
+               class="focus-input"/>
+        <div :id="id"
+             class="spinner">
+            <div class="circ1"></div>
+            <div class="circ2"></div>
+            <div class="circ3"></div>
+            <div class="circ4"></div>
+            <h4>{{text}}</h4>
+        </div>
+    </div>
+</template>
+
+<script>
+var UUID = require("../../common/Uuid.js");
+
+module.exports = {
+    name: "Loading",
+    props: ["text"],
+    data: function () {
+        return {
+            "id": 'loading_' + UUID.createUUID(),
+            "visible": false,
+            "uuid": UUID.createUUID(),
+        }
+    },
+
+    methods: {
+        // 居中显示
+        centerLoader: function () {
+            var _self = this;
+            var winW = $(window).width();
+            var winH = $(window).height();
+
+            var id = '#' + _self.id;
+            var spinnerW = $(id).outerWidth();
+            var spinnerH = $(id).outerHeight();
+
+            $(id).css({
+                'position': 'absolute',
+                'left': ((winW / 2) - (spinnerW / 2)) + 'px',
+                'top': ((winH / 2) - (spinnerH / 2)) + 'px'
+            });
+        },
+
+        // 显示加载中的界面
+        show: function () {
+            var _self = this;
+            _self.visible = true;
+
+            window[_self.uuid] = document.activeElement;
+
+            _self.$nextTick(function () {
+                _self.centerLoader();
+                $(window).resize(function () {
+                    _self.centerLoader();
+                });
+                $(_self.$refs.focusInput).focus();
+            });
+        },
+
+        // 隐藏加载中的界面
+        hide: function () {
+            var _self = this;
+            if (window[_self.uuid] != undefined) {
+                console.log("loading uuid:" + window[_self.uuid]);
+                window[_self.uuid].focus();
+                window[_self.uuid] = undefined;
+            }
+            _self.visible = false;
+        }
+    },
+
+    mounted: function () {
+        this.hide();
+    }
+}
+
+</script>
+
+<style scoped>
+.loading {
+    display: block;
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    top: 0px;
+    left: 0px;
+    z-index: 99999;
+    background-color: #2ecc71;
+    opacity: 0.5;
+}
+
+.circle1 {
+    top: 0;
+    left: 0;
+}
+.circle2 {
+    top: 0;
+    right: 0;
+}
+.circle3 {
+    right: 0;
+    bottom: 0;
+}
+.circle4 {
+    left: 0;
+    bottom: 0;
+}
+
+.spinner {
+    width: 90px;
+    height: 30px;
+    text-align: center;
+}
+
+.spinner > div {
+    background-color: #fff;
+    height: 15px;
+    width: 15px;
+    margin-left: 3px;
+    border-radius: 50%;
+    display: inline-block;
+
+    -webkit-animation: stretchdelay 0.7s infinite ease-in-out;
+    animation: stretchdelay 0.7s infinite ease-in-out;
+}
+
+.spinner .circ2 {
+    -webkit-animation-delay: -0.6s;
+    animation-delay: -0.6s;
+}
+
+.spinner .circ3 {
+    -webkit-animation-delay: -0.5s;
+    animation-delay: -0.5s;
+}
+
+.spinner .circ4 {
+    -webkit-animation-delay: -0.4s;
+    animation-delay: -0.4s;
+}
+
+.spinner .circ5 {
+    -webkit-animation-delay: -0.3s;
+    animation-delay: -0.3s;
+}
+
+@-webkit-keyframes stretchdelay {
+    0%,
+    40%,
+    100% {
+        -webkit-transform: translateY(-10px);
+    }
+    20% {
+        -webkit-transform: translateY(-20px);
+    }
+}
+
+@keyframes stretchdelay {
+    0%,
+    40%,
+    100% {
+        transform: translateY(-10px);
+        -webkit-transform: translateY(-10px);
+    }
+    20% {
+        transform: translateY(-20px);
+        -webkit-transform: translateY(-20px);
+    }
+}
+
+.focus-input {
+    position: absolute;
+    left: -10px;
+    top: -10px;
+    width: 0px;
+    height: 0px;
+}
+</style>

+ 8 - 0
packages/modal/index.js

@@ -0,0 +1,8 @@
+
+import Modal from './src/Modal.vue';
+
+Modal.install = function(Vue) {
+    Vue.component(Modal.name, Modal);
+};
+
+export default Modal;

+ 197 - 0
packages/modal/src/Modal.vue

@@ -0,0 +1,197 @@
+<template>
+    <div v-show="show" :transition="transition">
+        <div class="modal" @click.self="clickMask">
+            <div class="modal-dialog" :class="modalClass" name="dialog">
+                <div class="modal-content">
+                    <!--Header-->
+                    <div class="modal-header">
+                        <a type="button" class="close" @click="cancel">x</a>
+                        <h4 class="modal-title">
+                            <slot name="header">
+                                {{title}}
+                            </slot>
+                        </h4>
+                    </div>
+                    <!--Container-->
+                    <div class="modal-body">
+                        <slot></slot>
+                    </div>
+                    <!--Footer-->
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-info" v-if="showCanelButton" @click="cancel">{{cancelText}}</button>
+                        <button type="button" class="btn btn-primary" v-if="showOkButton" @click="ok">{{okText}}</button>
+                        <slot name="footer">
+                        </slot>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="modal-backdrop in"></div>
+    </div>
+</template>
+
+<script>
+
+var ModalFix = require("./ModalFix.js");
+
+module.exports = {
+    name: "Modal",
+    props: {
+        title: String,
+        small: [Boolean, String],
+        large: [Boolean, String],
+        full: [Boolean, String],
+        // 为true时无法通过点击遮罩层关闭modal
+        force: Boolean,
+        // 自定义组件transition
+        transition: String,
+    	// 显示确定按钮
+    	showOkButton: {
+			type: Boolean,
+			default: true
+    	},
+    	// 显示取消按钮
+    	showCanelButton: {
+			type: Boolean,
+			default: true
+        }
+    },
+
+    data: function(){
+        return {
+            show: false,
+            okText: "确定",
+            cancelText: "取消",
+            closeText: "关闭",
+            okClass : "",
+            cancelClass : "",
+            closeClass: ""
+        };
+    },
+
+    computed: {
+        modalClass : function() {
+            return {
+                'modal-lg': this.large || this.large == 'true',
+                'modal-sm': this.small || this.small == 'true',
+                'modal-full': this.full || this.full == 'true'
+            }
+        }
+    },
+        
+    created: function() {
+        if (this.show) {
+            if(document.body.className.indexOf('modal-open') < 0){
+                document.body.className += ' modal-open';
+            }
+        }
+    },
+
+    watch: {
+        show : function (value) {
+            console.log("modal watch show = " + value);
+            // 在显示时去掉body滚动条,防止出现双滚动条
+            if (value) {
+                if(document.body.className.indexOf('modal-open') < 0){
+                    document.body.className += ' modal-open';
+                }
+            } else {
+                this.$nextTick(function(){
+                    ModalFix.fix();
+                });
+            }
+        }
+    },
+
+    methods: {
+        ok: function() {
+            this.$emit('ok');
+            this.show = false;
+            this.$nextTick(function(){
+                ModalFix.fix();
+            });           
+        },
+
+        cancel: function () {
+            this.$emit('cancel');
+            this.show = false;
+            this.$nextTick(function(){
+                ModalFix.fix();
+            });
+        },
+
+        close: function() {
+            this.$emit('close');
+            this.show = false;
+            this.$nextTick(function(){
+                ModalFix.fix();
+            });           
+        },
+
+        // 点击遮罩层
+        clickMask: function () {
+            if (!this.force) {
+                this.cancel();
+                this.$nextTick(function(){
+                    ModalFix.fix();
+                });
+            }
+        },
+        
+        /**
+         * 显示
+         */
+        showModal: function(){
+        	this.show = true;
+        },
+        
+        /**
+         * 隐藏
+         */
+        hideModal: function(){
+        	this.show = false;
+        }
+    },
+    
+    mounted: function(){
+    	
+    }
+}
+</script>
+
+
+<style scoped>
+    .modal {
+        display: block;
+    }
+    .modal-transition {
+        transition: all .6s ease;
+    }
+    .modal-leave {
+        /* 样式没什么用,但可以让根标签的transitionEnd生效,以去掉modal-leave */
+        border-radius: 1px !important;
+    }
+    .modal-transition .modal-dialog, .modal-transition .modal-backdrop {
+        transition: all .5s ease;
+    }
+    .modal-enter .modal-dialog, .modal-leave .modal-dialog {
+        opacity: 0;
+        transform: translateY(-30%);
+    }
+
+    .modal-enter .modal-backdrop, .modal-leave .modal-backdrop {
+        opacity: 0;
+    }
+
+    .modal-full{
+        width: 95%;
+    }
+
+    .modal-lg{
+        width: 75%;
+    }
+
+    .modal-sm{
+        width: 50%;
+    }
+</style>

+ 34 - 0
packages/modal/src/ModalFix.js

@@ -0,0 +1,34 @@
+module.exports = {
+    /**
+     * 获取BootstrapDialog插件显示的模态框的数量
+     */
+    getBootstrapDialogCount: function(){
+        return $("div[class*='modal'][class*='bootstrap-dialog']").length;
+    },
+
+    /**
+     * 获取Modal.vue插件显示的数量
+     */
+    getActiveModalCount: function(){
+        return $("div:visible").children("div.modal").length;
+    },
+
+    /**
+     * 获取HTML界面模态框可见的总数
+     */
+    getActiveCount: function(){
+        return this.getBootstrapDialogCount() + this.getActiveModalCount();
+    },
+
+    fix: function(){
+        var activeModalCount = this.getActiveCount();
+        if(activeModalCount == 0){
+            // 界面已经没有活动的模态框
+            document.body.className = document.body.className.replace(/\s?modal-open/, '');
+        }else{
+            if(document.body.className.indexOf('modal-open') < 0){
+                document.body.className += ' modal-open';
+            }
+        }
+    }
+}

+ 8 - 0
packages/navbar/index.js

@@ -0,0 +1,8 @@
+
+import Navbar from './src/Navbar.vue';
+
+Navbar.install = function(Vue) {
+    Vue.component(Navbar.name, Navbar);
+};
+
+export default Navbar;

+ 62 - 0
packages/navbar/src/Navbar.vue

@@ -0,0 +1,62 @@
+<template>
+    <div class="m-page-header">
+        <h3>
+            <span class="glyphicon glyphicon-circle-arrow-left m-page-header-image"
+                @click="goBack"
+                 v-if="isGoBack == true || isGoBack == 'true'"></span>
+            
+            {{title}}
+            
+            <div class="pull-right">
+                <slot></slot>
+            </div>
+        </h3>
+    </div>
+</template>
+
+<script>
+
+module.exports = {
+    name: "Navbar",
+    props: [
+        //是否需要返回按钮,true:需要返回按钮,false:不需要返回按钮
+        "isGoBack",
+        //标题
+        "title"
+    ],
+
+    data: function () {
+        return {
+            data: "",
+        }
+    },
+
+    components: {
+
+    },
+
+    methods: {
+        goBack: function () {
+            history.back();
+        }
+    },
+}
+</script>
+
+<style scoped>
+.m-page-header {
+    margin: 5px;
+    padding: 0px;
+    border-bottom: 1px solid #eee;
+}
+
+.m-page-header h3 {
+    margin: 0px;
+    padding: 5px 0px 5px 0px;
+}
+
+.m-page-header-image {
+    top: 4px;
+    cursor: pointer;
+}
+</style>

+ 10 - 0
packages/page-size-select/index.js

@@ -0,0 +1,10 @@
+
+import PageSizeSelect from './src/PageSizeSelect.vue';
+
+
+PageSizeSelect.install = function(Vue) {
+    Vue.component(PageSizeSelect.name, PageSizeSelect);
+};
+
+
+export default PageSizeSelect;

+ 44 - 0
packages/page-size-select/src/PageSizeSelect.vue

@@ -0,0 +1,44 @@
+
+
+<template>
+	<select class="pagesize-select form-control" id="gridSizeSelect" v-model="pageSize">
+		<option value="10">10</option>
+	  	<option value="20">20</option>
+	  	<option value="50">50</option>
+	  	<option value="100">100</option>
+	  	<option value="200">200</option>
+	  	<option value="500">500</option>
+	</select>
+</template>
+
+<script>
+	// 外部可以注册事件pageSizeChanged
+
+	module.exports = {
+    	name: "PageSizeSelect",
+
+		data: function(){
+			return {
+				pageSize : 20
+			}
+		},
+
+		components : {
+
+		},
+
+		watch:{
+			pageSize: function(newValue, oldValue){
+				this.$emit("pageSizeChanged", newValue);
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.pagesize-select{
+		display: inline !important;
+		width: 70px !important;
+		float: none !important;
+	}
+</style>

+ 131 - 0
packages/page-size-select/src/SizePagination.vue

@@ -0,0 +1,131 @@
+// /**
+//  * 可以修改页面显示总数的分页组件。其中页面总数修改以后,会缓存在localstorage中,再次打开界面会记住,并恢复该数量。
+//  */
+// <template>
+//     <div>
+//         <div class="table-header-left">
+//             <span>第
+//                 {{ (pagination.current_page - 1) * pagination.per_page + 1 }}
+//                 -
+//                 {{pagination.current_page*pagination.per_page}}
+//                 条,共计
+//                 {{pagination.total}}
+//                 条,每页
+//             </span>
+
+//             <select class="pagesize-select form-control"
+//                     id="gridSizeSelect"
+//                     v-model="pageSize">
+//                 <option value="10">10</option>
+//                 <option value="20">20</option>
+//                 <option value="50">50</option>
+//                 <option value="100">100</option>
+//                 <option value="200">200</option>
+//                 <option value="500">500</option>
+//             </select>
+
+//             <span>
+//                 条
+//             </span>
+//         </div>
+
+//         <div class="table-header-right">
+//             <Pagination v-if="pagination.last_page > 0"
+//                         :pagination="pagination"
+//                         :callback="callback"></Pagination>
+//         </div>
+//     </div>
+
+// </template>
+
+// <script>
+
+// // 外部可以注册事件pageSizeChanged
+// var Pagination = require("vue-bootstrap-pagination").default;
+
+// module.exports = {
+//     name: "SizePagination",
+
+//     /**
+//      * @property { 字符串 } localStoragePageName 控件名称,分页参数会以 ${localStoragePageName} 为key存储在 localStorage 中。
+//      * @property { object } value 
+//      * {
+//      *      currentPage : 当前所在的页面(从1开始),正整数
+//      *      perPage : 页面显示的数量
+//      *      total : 总数量
+//      * }
+//      * @property {字符串} size: 分页组件的大小,可选值:large、small、空
+//      */
+//     props: [localStoragePageName, value, size],
+
+//     data: function () {
+//         return {
+//             mPagination: {
+//                 per_page: Common.pageSize, // required
+//                 current_page: 1, // required
+//                 last_page: 0, // required
+//                 total: 0,
+//             },
+//             pageSize: 20
+//         }
+//     },
+
+//     components: {
+//         Pagination
+//     },
+
+//     methods: {
+//         /**
+//          * 当页面数量、
+//          */
+//         callback: function () {
+//             // 每页显示的数量
+//             // 当前页数
+//         }
+//     },
+
+//     mounted: function () {
+//         if (this.value != null) {
+//             this.mPagination = {
+//                 per_page: 
+//             }
+//         }
+
+//     },
+
+//     watch: {
+//         pageSize: function (newValue, oldValue) {
+//             let newPagination = {};
+//             Object.assign(newPagination, this.value, { per_page: newValue });
+//             this.$emit("input", newValue);
+//         }
+//     }
+// }
+// </script>
+
+// <style scoped>
+// .page-box {
+//     display: flex;
+//     flex-direction: row;
+//     flex-wrap: wrap;
+//     justify-content: space-between;
+//     align-items: center;
+// }
+
+// .page-item-left {
+//     order: 1;
+//     flex-basis: auto;
+// }
+
+// .page-item-right {
+//     order: 2;
+//     flex-basis: auto;
+//     flex-shrink: 0;
+// }
+
+// .pagesize-select {
+//     display: inline !important;
+//     width: 70px !important;
+//     float: none !important;
+// }
+// </style>

+ 10 - 0
packages/pagination/index.js

@@ -0,0 +1,10 @@
+
+import Pagination from './src/Pagination.vue';
+
+
+Pagination.install = function(Vue) {
+    Vue.component(Pagination.name, Pagination);
+};
+
+
+export default Pagination;

+ 210 - 0
packages/pagination/src/Pagination.vue

@@ -0,0 +1,210 @@
+<template>
+    <nav>
+        <ul class="pagination m-pagination"
+            v-if="lastPage > 0"
+            :class="sizeClass">
+            <li v-if="showHeadAndTail"
+                @click="changePage(1)">
+                <span>{{$t('lang.vueBootstrapPagination.firstPage')}}</span>
+            </li>
+            <li v-if="showPrevious"
+                :class="{ 'disabled' : pagination.currentPage <= 1 }">
+                <span v-if="pagination.currentPage <= 1">
+                    <span aria-hidden="true">{{ config.previousText }}</span>
+                </span>
+
+                <a href="#"
+                   v-if="pagination.currentPage > 1 "
+                   :aria-label="config.ariaPrevioius"
+                   @click.prevent="changePage(pagination.currentPage - 1)">
+                    <span aria-hidden="true">{{ config.previousText }}</span>
+                </a>
+            </li>
+
+            <li v-for="num in array"
+                :key="num"
+                :class="{ 'active': num === pagination.currentPage }">
+                <a href="#"
+                   style="z-index:0"
+                   @click.prevent="changePage(num)">{{ num }}</a>
+            </li>
+
+            <li v-if="showNext"
+                :class="{ 'disabled' : pagination.currentPage === lastPage || lastPage === 0 }">
+                <span v-if="pagination.currentPage === lastPage || lastPage === 0">
+                    <span aria-hidden="true">{{ config.nextText }}</span>
+                </span>
+
+                <a href="#"
+                   v-if="pagination.currentPage < lastPage"
+                   :aria-label="config.ariaNext"
+                   @click.prevent="changePage(pagination.currentPage + 1)">
+                    <span aria-hidden="true">{{ config.nextText }}</span>
+                </a>
+            </li>
+            <li v-if="showHeadAndTail"
+                @click="changePage(lastPage)">
+                <span>{{$t('lang.vueBootstrapPagination.lastPage')}}</span>
+            </li>
+        </ul>
+    </nav>
+
+</template>
+
+<script>
+module.exports = {
+
+    /**
+     * @property { object } pagination 
+     * {
+     *      currentPage : 当前所在的页面(从1开始),正整数
+     *      perPage : 页面显示的数量
+     *      total : 总数量
+     * }
+     * @property { object } options 分页属性
+     * {
+     *      offset: Number, Default 3, 当前页签左边和右边显示的页码数量
+     *      ariaPrevious: String, Default 'Previous', 【上一页】 按钮的 aria 文字
+     *      ariaNext: String, Default 'Next', 【下一页】 按钮的 aria 文字
+     *      previousText: String, Default '«', 修改【上一页】按钮上的文字。
+     *      nextText: String, Default '»', 修改【下一页】按钮上的文字。
+     *      alwaysShowPrevNext, Boolean, Default false, 始终显示 上一页/下一页 按钮,即使在第一页或者是最后一页。
+     * } 
+     * @property { String } size 分页组件的大小,可选值:large、small、空
+     * @property { boolean } showHeadAndTail 是否显示"首页"、"尾页"
+     */
+    props: ["pagination", "options", "size", "showHeadAndTail"],
+
+    model: {
+        // v-model绑定pagination属性
+        prop: 'pagination',
+        // @input绑定paginationChanged事件
+        event: 'paginationChanged',
+    },
+
+    data: function () {
+        return {
+            lastPage: 1,
+        }
+    },
+
+    computed: {
+
+        /**
+         *  是否显示【上一页】按钮
+         */
+        showPrevious: function () {
+            return this.config.alwaysShowPrevNext || this.pagination.currentPage > 1;
+        },
+
+        /**
+         * 是否显示【下一页】按钮
+         */
+        showNext: function () {
+            return this.config.alwaysShowPrevNext || this.pagination.currentPage < this.lastPage;
+        },
+
+
+        array: function () {
+
+            let from = this.pagination.currentPage - this.config.offset;
+            if (from < 1) {
+                from = 1;
+            }
+
+            let to = from + (this.config.offset * 2);
+            if (to >= this.lastPage) {
+                to = this.lastPage;
+            }
+
+            let arr = [];
+            while (from <= to) {
+                arr.push(from);
+                from++;
+            }
+
+            return arr;
+        },
+        config: function () {
+            var _self = this;
+            return Object.assign({
+                offset: 3,
+                ariaPrevious: _self.$t('lang.vueBootstrapPagination.previous'),
+                ariaNext: _self.$t('lang.vueBootstrapPagination.next'),
+                previousText: '«',
+                nextText: '»',
+                alwaysShowPrevNext: false
+            }, this.options);
+        },
+        sizeClass: function () {
+            if (this.size === 'large') {
+                return 'pagination-lg';
+            } else if (this.size === 'small') {
+                return 'pagination-sm';
+            } else {
+                return '';
+            }
+        }
+    },
+
+    watch: {
+
+
+        'pagination': {
+            handler: function(newValue, oldValue) {
+                if (newValue.total == null || newValue.total == 0) {
+                    this.lastPage = 1;
+                } else {
+                    this.lastPage = Math.ceil(newValue.total / newValue.perPage);
+                }
+
+                if (newValue.perPage !== oldValue.perPage) {
+                    // 当新页面数量与老的分页数量不相同的时候,重新计算currentPage
+                    let oldStart = oldValue.perPage * (oldValue.currentPage - 1);
+                    let newCurrentPage = Math.ceil(oldStart / newValue.perPage);
+                    this.changePage(newCurrentPage, true);
+                } else if (this.pagination.currentPage > this.lastPage) {
+                    this.changePage(this.lastPage, true);
+                    return;
+                }
+            },
+            deep: true,
+        }
+    },
+
+    created: function () {
+        if (this.pagination.total == null || this.pagination.total == 0) {
+            this.lastPage = 1;
+        } else {
+            this.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage);
+        }
+    },
+
+    methods: {
+
+        /**
+         * 修改了页数
+         */
+        changePage: function (page, forceFireCallback) {
+            if (this.pagination.currentPage === page && forceFireCallback !== true) {
+                return;
+            }
+
+            let newPagination = {};
+            Object.assign(newPagination, this.pagination, { "currentPage": page });
+
+            // 由于设置
+            this.$emit('paginationChanged', newPagination);
+
+            this.$emit("callback");
+        }
+    },
+}
+
+</script>
+
+<style scoped>
+.m-pagination {
+    margin: 0px;
+}
+</style>

+ 8 - 0
packages/print/print-epc.js

@@ -0,0 +1,8 @@
+
+import PrintEpc from './src/PrintEpc.vue';
+
+PrintEpc.install = function(Vue) {
+    Vue.component(PrintEpc.name, PrintEpc);
+};
+
+export default PrintEpc;

+ 8 - 0
packages/print/print-widget.js

@@ -0,0 +1,8 @@
+
+import PrintWidget from './src/PrintWidget.vue';
+
+PrintWidget.install = function(Vue) {
+    Vue.component(PrintWidget.name, PrintWidget);
+};
+
+export default PrintWidget;

+ 188 - 0
packages/print/src/PrintEpc.vue

@@ -0,0 +1,188 @@
+/**
+ * EPC 打印界面
+ */
+
+<template>
+    <div>
+        <!-- 打印结果模态框 -->
+        <Modal ref="printResultModal"
+               title="发卡机发卡">
+            <div class="alert alert-info"
+                 role="alert"
+                 v-if="printResult != null && printResult.command == 'read'">{{printResult.success ? '操作成功' : '操作失败'}},读取到{{printResult.tagCount}}个标签,{{printResult.epcs}}</div>
+
+            <div class="alert alert-danger"
+                 role="alert"
+                 v-if="printResult != null && printResult.command == 'write'">{{printResult.success ? '操作成功' : '操作失败'}},{{printResult.message}}待写入EPC:{{lastWriteEpc}}</div>
+
+            <div class="alert alert-danger"
+                 role="alert"
+                 v-if="printResult != null && printResult.command == 'restore'">{{printResult.success ? '操作成功' : '操作失败'}},{{printResult.message}}</div>
+
+            <button type="button"
+                    class="btn btn-info"
+                    @click="printSingleEpc(lastWriteEpc)">再次打印</button>
+
+            <button type="button"
+                    class="btn btn-info"
+                    @click="restoreEpc">恢复标签</button>
+
+            <button type="button"
+                    class="btn btn-info"
+                    @click="readEpc">读取</button>
+
+            <button type="button"
+                    class="btn btn-danger"
+                    @click="$refs.printResultModal.show = false">关闭</button>
+
+        </Modal>
+        <Loading ref="loading"></Loading>
+    </div>
+</template>
+
+<script>
+var PrintEpcUtil = require("./PrintEpcUtil.js");
+var Modal = require("../../modal/src/Modal.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+
+module.exports = {
+    name: "PrintEpc",
+
+    props: ['printerName'],
+
+    data: function () {
+        return {
+            lastWriteEpc: '',   // 最后写入的EPC
+            // 打印结果
+            printResult: {
+                command: '',    // 指令
+                success: true,  // 是否操作成功
+                message: '',    // 操作不成功的消息
+                tagCount: 0,    // 标签读取的数量
+                epcs: '',       // 读取的标签信息
+                epc: '',        // 打印的EPC
+            }
+        }
+    },
+
+    components: {
+        Modal, Loading
+    },
+
+
+    methods: {
+        /**
+         * 打印PrintPages
+         */
+        printPrintPages: function (printPages) {
+            let _self = this;
+
+            if (printPages == null || printPages.length == 0) {
+                this.printResult.command = "write";
+                this.printResult.success = false;
+                this.printResult.message = "无可打印的数据。";
+
+                this.$refs.printResultModal.show = true;
+                return;
+            }
+
+            if (printPages.length > 1) {
+                this.printResult.command = "write";
+                this.printResult.success = false;
+                this.printResult.message = "使用发卡机发卡,每次只能发一个标签,打印程序收到了" + printPages.length + "个标签数据。";
+
+                this.$refs.printResultModal.show = true;
+                return;
+            }
+
+            let printPage = printPages[0];
+            let epc = null;
+            for (let index = 0; index < printPage.printItems.length; index++) {
+                if (printPage.printItems[index].displayType == 'EPC') {
+                    epc = printPage.printItems[index].content;
+                }
+            }
+
+            _self.lastWriteEpc = epc;
+
+            this.printSingleEpc(epc);
+        },
+
+        /**
+         * 打印单张EPC
+         * @param epc 待打印的EPC的内容
+         */
+        printSingleEpc: function (epc) {
+            let _self = this;
+
+            _self.$refs.printResultModal.show = false;
+
+            let promise = PrintEpcUtil.printSingleEpc(this.printerName, epc);
+
+            _self.$refs.loading.show();
+            promise.then(successData => {
+                _self.$refs.loading.hide();
+                if (successData.success == false) {
+                    successData.command = "write";
+                    _self.printResult = successData;
+                    _self.$refs.printResultModal.show = true;
+                }
+            }, errorData => {
+                _self.$refs.loading.hide();
+                if (errorData.success == false) {
+                    errorData.command = "write";
+                    _self.printResult = errorData;
+                    _self.$refs.printResultModal.show = true;
+                }
+            });
+        },
+
+        /**
+         * 恢复EPC
+         */
+        restoreEpc: function () {
+            let _self = this;
+
+            _self.$refs.printResultModal.show = true;
+
+            let promise = PrintEpcUtil.resetTag(this.printerName);
+
+            promise.then(successData => {
+                successData.command = "restore";
+                _self.printResult = successData;
+                _self.$refs.printResultModal.show = true;
+            }, errorData => {
+                successData.command = "restore";
+                _self.printResult = errorData;
+                _self.$refs.printResultModal.show = true;
+            });
+        },
+
+        /**
+         * 读取EPC
+         */
+        readEpc: function () {
+            let _self = this;
+
+            _self.$refs.printResultModal.show = true;
+
+            let promise = PrintEpcUtil.readEpc(this.printerName);
+
+            promise.then(successData => {
+                successData.command = "read";
+                _self.printResult = successData;
+            }, errorData => {
+                errorData.command = "read";
+                _self.printResult = errorData;
+            });
+        },
+
+        /**
+         * 显示模态框
+         */
+        show: function () {
+            this.$refs.printResultModal.show = true;
+        }
+    },
+}
+</script>

+ 138 - 0
packages/print/src/PrintEpcUtil.js

@@ -0,0 +1,138 @@
+
+/**
+ * node-red EPC 发卡程序
+ */
+var Uuid = require("../../common/Uuid.js");
+
+
+module.exports = {
+  webSocket: "",     // websocket对象
+
+  socketUrl: "ws://127.0.0.1:18800/api/ws/print",  //  websocket客户端地址
+
+
+  /**
+   * 打印单张EPC
+   */
+  printSingleEpc: function (printerName, epc) {    
+    if (epc == null || epc.length == 0) {
+      var promise = new Promise((resolve, reject) => {
+        reject({
+          success: false,
+          message: "打印数据中没有EPC的定义。",
+        });
+
+      });
+      return promise;
+    }
+
+    var _self = this;
+    var commandData = {
+      id: Uuid.createUUID(),
+      printerName: printerName,
+      command: 'writeEpc',
+      epc: epc,
+    }
+
+    var commandStr = JSON.stringify(commandData);
+    var promise = new Promise((resolve, reject) => {
+      _self.connectNodeRed(resolve, reject, commandStr);
+    });
+
+    return promise;
+  },
+
+
+  readEpc: function (printerName) {
+    var _self = this;
+    var commandData = {
+      id: Uuid.createUUID(),
+      printerName: printerName,
+      command: 'readEpc'
+    }
+
+    var commandStr = JSON.stringify(commandData);
+    var promise = new Promise((resolve, reject) => {
+      _self.connectNodeRed(resolve, reject, commandStr, null);
+    });
+
+    return promise;
+  },
+
+  /**
+   * 恢复标签,清除密码区域
+   */
+  resetTag: function(printerName){
+    var _self = this;
+    var commandData = {
+      id: Uuid.createUUID(),
+      printerName: printerName,
+      command: 'resetTag'
+    }
+
+    var commandStr = JSON.stringify(commandData);
+    var promise = new Promise((resolve, reject) => {
+      _self.connectNodeRed(resolve, reject, commandStr, null);
+    });
+
+    return promise;
+  },
+
+  // 连接node red的websocket
+  connectNodeRed: function (resolve, reject, commandStr) {
+    var _self = this;
+
+    _self.webSocket = new WebSocket(this.socketUrl);
+
+    _self.webSocket.onopen = function (event) {
+      console.log("打印 Websocket 连接成功。");
+      if (commandStr != null && commandStr.length > 0) {
+        _self.sendCommand(commandStr);
+      } else {
+        _self.close();
+      }
+    };
+
+    _self.webSocket.onclose = function (event) {
+      console.log("打印 Websocket 断开连接。");
+    };
+
+    _self.webSocket.onerror = function (event) {
+      console.log("打印 Websocket 出错。");
+      _self.close();
+      reject({
+        success: false,
+        message: "打印 Websocket 出错。" + event.data,
+      });
+    };
+
+    _self.webSocket.onmessage = function (event) {
+      var data = JSON.parse(event.data);
+      console.log(data);
+      resolve(data);
+      _self.close();
+    };
+  },
+
+
+  /**
+   * WebSocket 发送指令
+   * @param {指令} command 
+   */
+  sendCommand: function (command) {
+    if (this.webSocket) {
+      this.webSocket.send(command);
+    }
+  },
+
+  /**
+   * 关闭Websocket
+   */
+  close: function () {
+    if (this.webSocket) {
+      this.webSocket.close()
+      this.webSocket = null;
+    };
+  },
+}
+

+ 160 - 0
packages/print/src/PrintUtil.js

@@ -0,0 +1,160 @@
+
+/**
+ * node-red打印程序
+ */
+
+var Common = require("../../common/Common.js");
+var Uuid = require("../../common/Uuid.js");
+
+
+module.exports = {
+  webSocket: "",     // websocket对象
+
+  socketUrl: "ws://127.0.0.1:18800/api/ws/print",  //  websocket客户端地址
+
+  /**
+   * 获取所有的打印机
+   */
+  getPrinters: function () {
+    var _self = this;
+    var command = {
+      id: Uuid.createUUID(),
+      command: 'getPrinters',
+      data: null,
+    };
+    var commandStr = JSON.stringify(command);
+    return new Promise((resolve, reject) => {
+      _self.connectNodeRed(resolve, reject, commandStr);
+    });
+  },
+
+  /**
+   * 打印条码程序
+   * @param {调用的打印方法, 使用http请求头,举例:url = "http://127.0.0.1:8080/api/JobOrderResource/print} url 
+   * @param {打印机名称, 取值"TOSHIBA" ,"GK888t","Zebra"} printerName 
+   */
+  print: function (url, printerName) {
+    var _self = this;
+    return new Promise((resolve, reject) => {
+      $.ajax({
+        type: "get",
+        url: url,
+        beforeSend: function (request) {
+          Common.addTokenToRequest(request);
+        },
+        success: function (data) {
+          if (data && data.length > 0) {
+            var printPagesStr = JSON.parse(data);
+            _self.printPrintPages(printPagesStr, printerName);
+          }
+        },
+        error: function (XMLHttpRequest, textStatus, errorThrown) {
+          reject(XMLHttpRequest);
+        }
+      });
+    });
+  },
+
+  /**
+   * 打印机文件
+   * @param {*} printPages 
+   */
+  printPrintPages: function(printPages, printerName){
+    var _self = this;
+    var commandData = {
+      id: Uuid.createUUID(),
+      command: 'print',
+      printerName: printerName,
+      data: printPages,
+    }
+    var commandStr = JSON.stringify(commandData);
+    var promise = new Promise((resolve, reject) => {
+      _self.connectNodeRed(resolve, reject, commandStr);
+    });
+    promise.then(successData => {
+      // resolve();
+    }, errorData => {
+      console.log(errorData);
+      // reject(errorData);
+    });
+  },
+
+
+  /**
+   * 打印机文件预览
+   * @param {*} printPages 
+   */
+  printPreview: function(printPages, printerName){
+    var _self = this;
+    var commandData = {
+      id: Uuid.createUUID(),
+      command: 'printPreview',
+      printerName: printerName,
+      data: printPages,
+    }
+    var commandStr = JSON.stringify(commandData);
+    var promise = new Promise((resolve, reject) => {
+      _self.connectNodeRed(resolve, reject, commandStr);
+    });
+    console.log(promise);
+    return promise;
+  },
+
+
+  // 连接node red的websocket
+  connectNodeRed: function (resolve, reject, commandStr) {
+    var _self = this;
+    let printers = [];
+    _self.webSocket = new WebSocket(this.socketUrl);
+    _self.webSocket.onopen = function (event) {
+      console.log("打印 Websocket 连接成功。");
+      if (commandStr != null && commandStr.length > 0) {
+        _self.sendCommand(commandStr);
+      } else {
+        _self.close();
+      }
+    };
+    _self.webSocket.onclose = function (event) {
+      console.log("打印 Websocket 断开连接。");
+      resolve(printers);
+    };
+    _self.webSocket.onerror = function (event) {
+      console.log("打印 Websocket 出错。");
+      _self.close();
+      reject();
+    };
+    _self.webSocket.onmessage = function (event) {
+      var data = JSON.parse(event.data);
+      if (data.command == 'getPrinters') {
+        printers = data.data;
+      }
+
+      if (data.command == 'printPreview') {
+        printers = data.previewImages;
+      }
+      _self.close();
+    };
+  },
+
+
+  /**
+   * WebSocket 发送指令
+   * @param {指令} command 
+   */
+  sendCommand: function (command) {
+    if (this.webSocket) {
+      this.webSocket.send(command);
+    }
+  },
+
+  /**
+   * 关闭Websocket
+   */
+  close: function () {
+    if (this.webSocket) {
+      this.webSocket.close()
+      this.webSocket = null;
+    };
+  },
+}
+

+ 121 - 0
packages/print/src/PrintWidget.vue

@@ -0,0 +1,121 @@
+<template>
+    <div class="form-group">
+        <label for="select-printer">{{$t('lang.PrintWidget.selectPrinter')}}</label>
+        <select class="form-control"
+                id="select-printer"
+                v-model="selectedPrinter"
+                @change="selectedPrinterChanged"
+                style="width: 15em">
+            <option value=""></option>
+            <option :value="printer.name"
+                    :key="printer.name"
+                    v-for="printer in printers">{{printer.name}}</option>
+        </select>
+        <button class="btn btn-default"
+                v-if="showPrintButton"
+                @click="print()">{{$t('lang.PrintWidget.printing')}}</button>
+    </div>
+</template>
+
+<script>
+
+var PrintUtil = require("./PrintUtil.js");
+
+module.exports = {
+    name: "PrintWidget",
+    
+    props: [
+        "printerLocalstorageId", // 控件唯一的Id,用于localstorage存储打印机名称
+        "showPrintButton", // 是否显示打印按钮
+    ],
+
+    data: function () {
+        return {
+            'printers': [], // 系统上的打印机
+            'selectedPrinter': '',   // 选择的打印机
+        }
+    },
+
+    components: {
+    },
+
+    methods: {
+
+        /**
+         * 选择的打印机发生改变,保存打印机的设置
+         */
+        selectedPrinterChanged: function () {
+            this.$emit("changePrinter");
+            if (this.selectedPrinter != null) {
+                localStorage.setItem(this.key, this.selectedPrinter);
+            } else {
+                localStorage.removeItem(this.key);
+            }
+        },
+
+        /**
+         * 恢复选择的打印机
+         */
+        restoreSelectedPrinter: function () {
+            var selectedPrinterStr = localStorage.getItem(this.key);
+            if (selectedPrinterStr != null && selectedPrinterStr.length > 0) {
+                this.selectedPrinter = selectedPrinterStr;
+            } else {
+                this.selectedPrinter = '';
+            }
+        },
+
+        /**
+         * 打印文件
+         */
+        print: function () {
+            var _self = this;
+            this.$emit("print", _self.selectedPrinter);
+        },
+
+        /**
+         * 获取选择打印机的名称
+         */
+        getSelectedPrinterName: function () {
+            return this.selectedPrinter;
+        },
+
+        /**
+         * 获取选择打印的类型
+         */
+        getSelectedPrinterType: function(){
+            for(let index = 0; index < this.printers.length; index ++){
+                if(this.printers[index].name === this.selectedPrinter){
+                    return this.printers[index].type;
+                }
+            }
+            return null;
+        }
+
+    },
+
+
+
+    mounted: function () {
+        var _self = this;
+        _self.restoreSelectedPrinter();
+        PrintUtil.getPrinters().then(successData => {
+            if (successData != null && successData.length > 0) {
+                successData.forEach(item => {
+                    _self.printers.push(item);
+                });
+            }
+            _self.restoreSelectedPrinter();
+        }, errorData => {
+            console.log(errorData);
+            _self.restoreSelectedPrinter();
+        });
+    },
+}
+</script>
+
+<style scoped>
+.m-row {
+    margin-bottom: 15px;
+}
+</style>

+ 8 - 0
packages/process/index.js

@@ -0,0 +1,8 @@
+
+import ProcessReport from './src/ProcessReport.vue';
+
+ProcessReport.install = function(Vue) {
+    Vue.component(ProcessReport.name, ProcessReport);
+};
+
+export default ProcessReport;

+ 8 - 0
packages/process/process-report-result-preview.js

@@ -0,0 +1,8 @@
+
+import ProcessReportResultPreview from './src/ProcessReportResultPreview.vue';
+
+ProcessReportResultPreview.install = function(Vue) {
+    Vue.component(ProcessReportResultPreview.name, ProcessReportResultPreview);
+};
+
+export default ProcessReportResultPreview;

+ 8 - 0
packages/process/process-report-result.js

@@ -0,0 +1,8 @@
+
+import ProcessReportResult from './src/ProcessReportResult.vue';
+
+ProcessReportResult.install = function(Vue) {
+    Vue.component(ProcessReportResult.name, ProcessReportResult);
+};
+
+export default ProcessReportResult;

+ 45 - 0
packages/process/src/EnumSelectWidget.vue

@@ -0,0 +1,45 @@
+<template>
+    <div class="form-group">
+        <div>
+            <select class="form-control"
+                    v-model="selectedValue">
+                <option v-for="keyValue in keyValues"
+                        :value="keyValue.keyStr"
+                        :key="keyValue.keyStr">{{keyValue.value}}</option>
+            </select>
+        </div>
+    </div>
+</template>
+
+<script>
+module.exports = {
+    props: ["keyValues", "enumValue"],
+
+    data: function () {
+        return {
+            selectedValue: (this.enumValue == undefined ? '' : this.enumValue),
+        }
+    },
+
+    watch: {
+        selectedValue: function (curVal, oldVal) {
+            console.log("Enum Selected Value changed:" + curVal);
+            if (curVal != oldVal) {
+                this.$emit("valueChanged", curVal);
+            }
+        },
+
+        enumValue: function (val) {
+            this.selectedValue = val;
+        }
+    }
+}
+
+</script>
+
+<style scoped>
+.required-mark {
+    color: red;
+    margin-right: 10px;
+}
+</style>

+ 455 - 0
packages/process/src/MultiSearchWidget.vue

@@ -0,0 +1,455 @@
+<template>
+    <div class="content">
+        <div class="form-group">
+            <!-- <label v-show="isVisible[0]" class="col-xs-5 col-sm-4 col-md-3 col-lg-2 control-label">
+				<span class="required-mark" v-if="field.mandatory">*</span>{{labelNames[0]}}
+			</label> -->
+            <div style="position:relative; padding-left: 0px;"
+                 class="col-xs-12 col-sm-12 col-md-12 col-lg-12"
+                 v-show="isVisible[0]">
+                <div class="input-group">
+                    <!-- <input :aria-describedby="'addon' + field.id" type="text" class="form-control" v-model="searchText" @input="textChange($event)" :readonly="readonly"/> -->
+                    <!-- <span @click="showSearchDialog" class="input-group-addon" :id="'addon' + field.id" :disabled="readonly">
+							<span class="glyphicon glyphicon-search"></span>
+						</span> -->
+
+                    <div class="box"
+                         :class="{'div-readonly':readonly}">
+                        <span v-for="item in selectDatas"
+                              class="selected-tag">
+                            {{item.text}}
+                            <button v-if="!readonly"
+                                    @click="deleteRecord(item)"
+                                    type="button"
+                                    class="close"
+                                    aria-label="Remove option">
+                                <span aria-hidden="true">&times;</span>
+                            </button>
+                        </span>
+                        <span @click="showSearchDialog"
+                              class="glyphicon glyphicon-search search-icon"></span>
+                    </div>
+
+                    <Modal ref="modal"
+                           @ok="searchDialogOk"
+                           @cancel="searchDialogCancel"
+                           :full="true">
+                        <info ref="info"
+                              v-on:dataSelected="dataSelected"></info>
+                        <div slot="header">{{field.name}}</div>
+                    </Modal>
+                </div>
+                <div class="div-autoComplete"
+                     v-show="isShowAuto">
+                    <table class="table-bordered table-hover">
+                        <thead>
+                            <tr>
+                                <td align="center"
+                                    v-for="item in infoWindowDto.infoGridFields"
+                                    width="100px">
+                                    {{item.name}}
+                                </td>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr v-for="item1 in infoWindowData.dataList"
+                                @click="selectNode(item1)"
+                                height="40px">
+                                <td v-for="item2 in infoWindowDto.infoGridFields"
+                                    align="center">
+                                    {{item1.data[item2.fieldName].displayValue[0]}}
+                                </td>
+                            </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+        <div v-for="(titleName, index) in titleNames"
+             :class="{'form-group' : index > 0}"
+             v-show="isVisible[index]">
+            <label v-if="index > 0"
+                   class="col-xs-5 col-sm-4 col-md-3 col-lg-2 control-label">
+                <span class="required-mark"
+                      v-if="field.mandatory">*</span>{{titleName}}
+            </label>
+            <div v-if="index > 0"
+                 class="col-xs-7 col-sm-8 col-md-9 col-lg-10">
+                <input type="text"
+                       class="form-control"
+                       :value="getFieldStringValue(index)"
+                       disabled>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+var Modal = require("../../modal/src/Modal.vue").default;
+var Info = require("../../info/src/InfoWindow.vue").default;
+var Common = require("../../common/Common.js");
+
+module.exports = {
+    props: ["infoWindowNo", "field", "fieldValue", "readonly"],
+
+    data: function () {
+        return {
+            labelNames: [],
+            isVisible: [],
+            infoWindowDto: {},
+            infoWindowData: {},
+            isShowAuto: false,
+            searchText: "",
+            selectDatas: []
+        }
+    },
+
+    components: {
+        Modal, Info,
+    },
+
+    methods: {
+        // 显示搜索对话框
+        showSearchDialog: function () {
+            var _self = this;
+            if (this.readonly) {
+                return;
+            }
+            _self.$refs.modal.showModal();
+            _self.$nextTick(function () {
+                _self.$refs.modal.showModal();
+                if (_self.$refs.info.infoWindowNo != _self.infoWindowNo) {
+                    _self.$refs.info.loadByInfoWindowNo(_self.infoWindowNo);
+                } else {
+                    _self.$refs.info.refresh();
+                }
+            })
+        },
+
+        searchDialogOk: function () {
+
+        },
+
+        searchDialogCancel: function () {
+
+        },
+
+        // 输入的文本发生改变
+        textChange: function (e) {
+            var _self = this;
+            var text = _self.searchText;
+
+            // if(text == undefined || text.length == 0){
+            // 	var newFieldValue = {
+            // 		id : {},
+            // 		displayValue: [],
+            // 		fieldType: 'Key'
+            // 	}
+            // 	this.$emit("valueChanged", newFieldValue);
+            // }
+
+            if (text.trim() != "") {
+                _self.getInfoWindowData(text);
+            } else {
+                _self.isShowAuto = false;
+            }
+        },
+
+        /**
+         * 查询InfoWindowDto
+         * @return {void} 
+         */
+        getInfoWindowDto: function () {
+            var _self = this;
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/uniqueByNo'),
+                type: 'GET',
+                dataType: 'json',
+                data: { "infoWindowNo": _self.infoWindowNo },
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    _self.infoWindowDto = data;
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 生成查询条件
+         * @param  {String} text 查询条件
+         * @return {Array}      
+         */
+        getSimpleQueryCondition: function (text) {
+            var _self = this;
+            var values = [];
+            if (_self.infoWindowDto.infoFilterFields != undefined) {
+                _self.infoWindowDto.infoFilterFields.forEach(function (item) {
+                    if (item.displayType == "TextEditor") {
+                        var value = {
+                            infoFilterFieldId: item.id,
+                            value1: text
+                        }
+                        values.push(value);
+                    }
+                });
+            }
+            return values;
+        },
+
+        /**
+         * 查询infoWindowData
+         * @return {void} 
+         */
+        getInfoWindowData: function (text) {
+            var _self = this;
+
+            var infoFilterFieldValues = _self.getSimpleQueryCondition(text);
+
+            var infoQueryParam = {
+                infoWindowNo: _self.infoWindowNo,
+                start: 0,
+                length: 5,
+                sortClause: "",
+                infoFilterFieldValues: infoFilterFieldValues
+            }
+
+            $.ajax({
+                url: Common.getApiURL('InfoWindowResource/queryInfoWindowDataSimple'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(infoQueryParam),
+                success: function (data) {
+                    _self.infoWindowData = data;
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 选择数据(自动提示框)
+         * @return {void} 
+         */
+        selectNode: function (data) {
+            var _self = this;
+            _self.isShowAuto = false;
+            _self.dataSelected(data);
+
+        },
+
+        deleteRecord: function (selectData) {
+            var _self = this;
+            var index = _self.selectDatas.indexOf(selectData);
+            if (index > -1) {
+                _self.selectDatas.splice(index, 1);
+                _self.getSelectDataIds();
+            }
+
+        },
+
+        // 数据已经选择
+        dataSelected: function (data) {
+            var _self = this;
+
+            var index = _self.selectDatas.indexOf(data);
+
+            if (index > -1) {
+                return;
+            }
+
+            if (this.$refs.modal != undefined) {
+                this.$refs.modal.hideModal();
+            }
+            _self.$refs.modal.hideModal();
+
+            var text = [];
+
+            var listDisplayFieldNames = this.field.listDisplayFieldNames;
+            var displayValues = listDisplayFieldNames.split(",");
+            for (var i = 0; i < displayValues.length; i++) {
+                var fieldValue = data.data[displayValues[i]];
+                if (fieldValue != undefined && fieldValue.displayValue != undefined) {
+                    text[i] = fieldValue.displayValue[0];
+                } else {
+                    text[i] = "";
+                }
+            }
+
+            data.text = text[0];
+
+            _self.selectDatas.push(data);
+
+
+            /*var selectDataIds = [];
+            _self.selectDatas.forEach(function(item){
+                selectDataIds.push(item.id);
+            })
+
+            var newFieldValue = {
+                displayValue: selectDataIds,
+                fieldType: 'MultiSearchBoxEditor'
+            }
+            this.$emit("valueChanged", newFieldValue);*/
+
+            _self.getSelectDataIds();
+            this.$emit("getData", data);
+        },
+
+        getSelectDataIds: function () {
+            var _self = this;
+            var selectDataIds = [];
+            _self.selectDatas.forEach(function (item) {
+                selectDataIds.push(item.id);
+            })
+
+            var newFieldValue = {
+                displayValue: selectDataIds,
+                fieldType: 'MultiSearchBoxEditor'
+            }
+            this.$emit("valueChanged", newFieldValue);
+        },
+
+        // 获取String字符串
+        getFieldStringValue: function (index) {
+            if (this.fieldValue == undefined || this.fieldValue.displayValue == undefined || this.fieldValue.displayValue.length < index) {
+                return "";
+            } else {
+                return this.fieldValue.displayValue[index];
+            }
+        },
+    },
+
+    computed: {
+        titleNames: function () {
+            if (this.labelNames == undefined || this.labelNames.length == 0) {
+                this.labelNames = this.field.displayName.split(",");
+            }
+            return this.labelNames;
+        }
+    },
+
+    watch: {
+        infoWindowData: function (val) {
+            var _self = this;
+            if (val.dataList != undefined && val.dataList.length > 0) {
+                _self.isShowAuto = true;
+            } else {
+                _self.isShowAuto = false;
+            }
+        }
+    },
+
+    mounted: function () {
+        this.getInfoWindowDto();
+    }
+}
+</script>
+
+<style scoped>
+.required-mark {
+    color: red;
+    margin-right: 10px;
+}
+.div-autoComplete {
+    position: absolute;
+    z-index: 99;
+    background-color: #fff;
+    border: 1px #e5e5e5 solid;
+    padding: 0px 15px 15px 15px;
+    box-shadow: 0 0 5px rgba(81, 203, 238, 1);
+}
+.box {
+    width: 200px;
+    min-height: 34px;
+    border: 1px solid rgba(60, 60, 60, 0.26);
+    border-radius: 4px;
+    white-space: normal;
+    transition: border-radius 0.25s;
+    background-color: #ffffff;
+    position: relative;
+}
+.div-readonly {
+    background-color: #eee !important;
+}
+.close {
+    float: none;
+    margin-right: 0;
+    font-size: 20px;
+    appearance: none;
+    padding: 0;
+    cursor: pointer;
+    background: 0 0;
+    border: 0;
+    font-weight: 700;
+    line-height: 1;
+    color: #000;
+    text-shadow: 0 1px 0 #fff;
+    filter: alpha(opacity=20);
+    opacity: 0.2;
+}
+.selected-tag {
+    color: #333;
+    background-color: #f0f0f0;
+    border: 1px solid #ccc;
+    border-radius: 4px;
+    height: 26px;
+    margin: 4px 1px 0px 3px;
+    padding: 3px 0.25em;
+    line-height: 34px;
+}
+.search-icon {
+    position: absolute;
+    top: 9px;
+    right: 7px;
+}
+.content {
+    display: inline-block !important;
+}
+/*.img-box {
+		width: 60px;
+		float: left;
+		text-align: center;
+		margin-right: 20px;
+	}
+	.img-box div img{
+		width: 60px;
+		height: 60px;
+	}
+	.add-box {
+		width: 60px;
+		height: 60px;
+		float: left;
+		border-radius: 30px;
+		border: 1px #999 dashed;
+		text-align: center;
+		margin-top: 20px;
+	}
+	.add-box:hover {
+		cursor: pointer;
+		background-color: #ccc;
+	}
+	.add-icon {
+		width: 60px;
+		height: 60px;
+		color: #aaa;
+		font-size: 40px;
+		line-height: 60px;
+		position: relative;
+		top: -1px;
+	}
+	.remove-icon {
+		color: red;
+		position: relative;
+		top: 10px;
+		right: -30px;
+		cursor: pointer;
+	}*/
+</style>

+ 184 - 0
packages/process/src/ProcessReport.vue

@@ -0,0 +1,184 @@
+<template>
+    <div>
+        <ProcessReportDynamic v-if="reportType == 'DYNAMIC'"
+                              :processReportDto="processReportDto"
+                              v-on:valueChanged="valueChanged"></ProcessReportDynamic>
+        <ProcessReportStatic v-if="reportType == 'STATIC'"
+                             :processReportDto="processReportDto"
+                             v-on:valueChanged="valueChanged"></ProcessReportStatic>
+    </div>
+</template>
+
+<script>
+
+var Common = require("../../common/Common.js");
+var ProcessReportDynamic = require("./ProcessReportDynamic.vue").default;
+var ProcessReportStatic = require("./ProcessReportStatic.vue").default;
+
+module.exports = {
+    name: "ProcessReport",
+    
+    data: function () {
+        return {
+            "reportType": '',
+            "processReportDto": {},
+        }
+    },
+
+    components: {
+        ProcessReportDynamic,
+        ProcessReportStatic
+    },
+
+    methods: {
+
+        /**
+         * 根据流程报表ID加载流程报表数据
+         * @return {void} 
+         */
+        loadData: function () {
+            var _self = this;
+            $.ajax({
+                url: Common.getApiURL('ProcessReportResource/uniqueByNo'),
+                type: 'get',
+                dataType: 'json',
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                data: {
+                    no: _self.processReportNo
+                },
+                success: function (data) {
+                    console.log("参数结果" + data.reportType);
+                    _self.reportType = data.reportType;
+
+                    if (data.processReportParameters) {
+                        data.processReportParameters.forEach(function (item) {
+                            if (item.fieldValue == undefined) {
+                                item.fieldValue = {
+                                    id: '',
+                                    displayValue: [],
+                                    fieldType: "String"
+                                };
+                            }
+                        });
+                    }
+
+                    _self.processReportDto = data;
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            })
+        },
+        // 值改变事件
+        valueChanged: function (processReportParameter) {
+            var _self = this;
+            console.log("processReportParameter:" + JSON.stringify(processReportParameter))
+            if (_self.processReportDto && _self.processReportDto.processReportParameters) {
+                for (var i = 0; i < _self.processReportDto.processReportParameters.length; i++) {
+                    if (_self.processReportDto.processReportParameters[i].fieldName == processReportParameter.fieldName) {
+                        _self.processReportDto.processReportParameters[i].fieldValue = processReportParameter.fieldValue;
+                    }
+                }
+            }
+            if (processReportParameter.calloutProcessNo && processReportParameter.calloutProcessNo != "") {
+                _self.callout(processReportParameter.calloutProcessNo);
+            }
+
+
+
+            if (_self.processReportDto != null && _self.processReportDto.processReportParameters != null) {
+                for (var i = 0; i < _self.processReportDto.processReportParameters.length; i++) {
+                    var item = _self.processReportDto.processReportParameters[i];
+                    if (item.fieldName != processReportParameter.fieldName
+                        && item.whereClause != null
+                        && item.whereClause.indexOf(":" + processReportParameter.fieldName) >= 0) {
+                        item.fieldValue.id = '';
+                        item.fieldValue.displayValue.splice(0, item.fieldValue.displayValue.length);
+                        _self.$set(_self.processReportDto.processReportParameters, i, item);
+                    }
+                }
+            }
+
+        },
+        callout: function (calloutProcessNo) {
+            var _self = this;
+            $.ajax({
+                url: Common.getApiURL('ProcessReportResource/runProcessReportCallout/') + calloutProcessNo,
+                type: "POST",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(_self.processReportDto),
+                success: function (data) {
+                    if (data && data.processReportParameterDtos) {
+                        var processReportParameterDtos = data.processReportParameterDtos;
+                        for (var i = 0; i < data.processReportParameterDtos.length; i++) {
+                            var fieldName = data.processReportParameterDtos[i].fieldName;
+                            for (var j = 0; j < _self.processReportDto.processReportParameters.length; j++) {
+                                if (_self.processReportDto.processReportParameters[j].fieldName == fieldName) {
+                                    var parameter = _self.processReportDto.processReportParameters[j];
+                                    var cloneObject = {
+                                        "index": 1,
+                                        "isMandatory": false,
+                                        "id": parameter.id,
+                                        "fieldName": parameter.fieldName,
+                                        "name": parameter.name,
+                                        "help": parameter.help,
+                                        "displayType": parameter.displayType,
+                                        "listDisplayFieldNames": parameter.listDisplayFieldNames,
+                                        "infoWindowId": parameter.infoWindowId,
+                                        "sortNo": parameter.sortNo,
+                                        "isShow": parameter.isShow,
+                                        "constraintEnum": parameter.constraintEnum,
+                                        "whereClause": parameter.whereClause,
+                                        "enumClass": parameter.enumClass,
+                                        "fieldValue": data.processReportParameterDtos[i].fieldValue,
+                                        "calloutProcessNo": parameter.calloutProcessNo,
+                                        "mandatory": parameter.mandatory,
+                                        "listDisplayFieldName": parameter.listDisplayFieldName,
+                                        "displayName": parameter.displayName,
+
+                                    }
+                                    _self.$set(_self.processReportDto.processReportParameters, j, cloneObject);
+                                }
+                            }
+                        }
+
+                    }
+
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+
+        }
+
+    },
+
+    watch: {
+        "$route": function (to, from) {
+            this.processReportNo = this.$route.params.no;
+            this.loadData();
+        }
+    },
+
+    mounted: function () {
+        this.processReportNo = this.$route.params.no;
+        this.loadData();
+    },
+}
+</script>
+<style scoped>
+.control-label {
+    font-size: 15px;
+}
+
+.processResult-textarea {
+    width: 100%;
+    height: 200px;
+}
+</style>

+ 208 - 0
packages/process/src/ProcessReportArchive.vue

@@ -0,0 +1,208 @@
+<template>
+    <div>
+        <div v-show="archiveAuthority">
+            <div>
+                <label>报表归档:</label>
+                <button class="btn btn-success"
+                        @click="startArchive = true"
+                        v-show="startArchive == false">归档</button>
+                <button class="btn btn-success"
+                        @click="searchArchiveRecord"
+                        v-show="startArchive == false">归档查询</button>
+                <button class="btn btn-success"
+                        v-show="startArchive == true"
+                        @click="archive()">保存</button>
+                <button class="btn btn-warning"
+                        @click="startArchive = false"
+                        v-show="startArchive == true">取消</button>
+            </div>
+
+            <div v-show="startArchive == true">
+                <div class="form-group">
+                    <label>标题</label>
+                    <input type="text"
+                           class="form-control"
+                           v-model="title" />
+                </div>
+                <div class="form-group">
+                    <label>业务日期</label>
+                    <DateTimeWidget class="form-control"
+                                    :dateValue="businessDate"
+                                    @on-value-change="dateChange" />
+                </div>
+                <div class="form-group">
+                    <label>备注</label>
+                    <input type="description"
+                           class="form-control"
+                           v-model="description" />
+                </div>
+                <div class="form-group">
+                    <label>归档日期</label>
+                    <input type="text"
+                           class="form-control"
+                           v-model="archiveDate"
+                           :readonly="true" />
+                </div>
+                <div class="form-group">
+                    <label>归档人</label>
+                    <input type="text"
+                           class="form-control"
+                           v-model="userName"
+                           :readonly="true" />
+                </div>
+                <div class="form-group"
+                     v-for="item,index in processReportResult.reportResults">
+                    <label>报表路径{{index + 1 }}</label>
+                    <input type="text"
+                           class="form-control"
+                           v-model="item.htmlPreviewUrl"
+                           :readonly="true" />
+                </div>
+            </div>
+        </div>
+    </div>
+
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var Notify = require("../../common/Notify.js");
+var DateTimeWidget = require("../../datetime/src/DateTime.vue").default;
+
+module.exports = {
+    props: ["processReportResult"],
+
+    data: function () {
+        return {
+            archiveAuthority: "",
+            startArchive: false,
+            title: "",
+            businessDate: "",
+            description: "",
+            userName: "",
+            archiveDate: "",
+            interval: ""
+        }
+    },
+
+    components: {
+        DateTimeWidget
+    },
+
+    methods: {
+        initArchiveAuthority: function () {
+            var _self = this;
+            if (this.processReportResult) {
+                this.title = this.processReportResult.processReportName;
+            }
+
+            var loginInfoJson = localStorage.getItem("json_LoginInfo");
+            var loginInfo = JSON.parse(loginInfoJson);
+            if (loginInfo != null) {
+                _self.userName = loginInfo.userName;
+            }
+            if (_self.processReportResult == null || _self.processReportResult.processReportNo == null) {
+                _self.archiveAuthority = false;
+                return;
+            }
+            $.ajax({
+                url: Common.getApiURL('ProcessReportResource/getArchiveAuthority'),
+                type: 'get',
+                dataType: 'json',
+                data: {
+                    "processReportNo": _self.processReportResult.processReportNo
+                },
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    _self.archiveAuthority = data;
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            })
+        },
+        dateChange: function (value) {
+            this.businessDate = value
+        },
+        archive: function () {
+            var _self = this;
+            var url = "";
+            var reportNames = "";
+            var reportResults = this.processReportResult.reportResults;
+            for (var i = 0; i < reportResults.length; i++) {
+                var reportResult = reportResults[i];
+                var pdfDownLoadUrl = reportResult.pdfDownLoadUrl;
+                if (pdfDownLoadUrl && pdfDownLoadUrl.length > 4) {
+                    url += pdfDownLoadUrl.substring(0, pdfDownLoadUrl.length - 4);
+                    reportNames += reportResult.reportName;
+                }
+                if (i + 1 < reportResults.length) {
+                    url += ",";
+                    reportNames += ",";
+                }
+            }
+            $.ajax({
+                url: Common.getApiURL('ArchiveResource/archive'),
+                type: 'get',
+                dataType: 'json',
+                data: {
+                    "help": _self.title,
+                    "businessDate": _self.businessDate,
+                    "description": _self.description,
+                    "url": url,
+                    "reportNames": reportNames
+                },
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    Notify.success("归档成功", "归档成功,可以在【归档查询】页面查询所有归档信息", 1500);
+                    _self.startArchive = false;
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            })
+        },
+        //格式化日期成 "yyyy-MM-dd HH-mm-SS"格式
+        updateDate: function () {
+            var date = new Date();
+            var seperator1 = "-";
+            var seperator2 = ":";
+            var month = date.getMonth() + 1;
+            var strDate = date.getDate();
+            if (month >= 1 && month <= 9) {
+                month = "0" + month;
+            }
+            if (strDate >= 0 && strDate <= 9) {
+                strDate = "0" + strDate;
+            }
+            var currentdate = date.getFullYear() + seperator1 + month + seperator1 + strDate
+                + " " + date.getHours() + seperator2 + date.getMinutes() + seperator2
+                + (date.getSeconds() < 10 ? "0" : "") + date.getSeconds();
+            this.archiveDate = currentdate;
+        },
+        searchArchiveRecord: function () {
+            this.$router.push("/desktop/archive");
+        }
+    },
+    mounted: function () {
+        var _self = this;
+        this.initArchiveAuthority();
+        this.interval = window.setInterval(function () {
+            _self.updateDate()
+        }, 1000);
+
+    },
+    destroyed: function () {
+        clearInterval(this.interval);
+    },
+    watch: {
+        "processReportResult": function () {
+            this.initArchiveAuthority();
+        }
+    }
+}
+</script>

+ 409 - 0
packages/process/src/ProcessReportDynamic.vue

@@ -0,0 +1,409 @@
+<template>
+    <div>
+        <Navbar :title="processReportDto.name"
+                :isGoBack="processReportDto.goback"></Navbar>
+        <div>
+            <h5>{{processReportDto.description}}</h5>
+        </div>
+        <div>
+            <div class="form-inline">
+                <div class="form-group m-form-group"
+                     v-for="item in parameters">
+                    <div class="form-inline-div">
+                        <label v-if="item.displayType != 'ListBoxEnumEditor'"
+                               :for="'editor_' + item.id">{{item.displayName}}</label>
+
+                        <input v-if="item.displayType =='TextEditor'"
+                               type="text"
+                               v-model="item.fieldValue.displayValue[0]"
+                               class="form-control m-input-group"
+                               :id="'editor_' + item.id"
+                               placeholder="请输入内容"
+                               @change="valueChanged(item)" />
+
+                        <textarea rows="5"
+                                  v-if="item.displayType =='TextAreaEditor'"
+                                  type="text"
+                                  v-model="item.fieldValue.displayValue[0]"
+                                  class="form-control m-input-group"
+                                  :id="'editor_' + item.id"
+                                  placeholder="请输入内容"
+                                  @change="valueChanged(item)" />
+
+                        <input v-if="item.displayType =='NumberEditor'"
+                               type="number"
+                               v-model="item.fieldValue.displayValue[0]"
+                               class="form-control m-input-group"
+                               :id="'editor_' + item.id"
+                               placeholder="请输入数字"
+                               @change="valueChanged(item)" />
+
+                        <!-- <input v-if="item.displayType =='CheckBoxEditor'" type="checkbox" v-model="item.fieldValue.displayValue[0]" class="form-control" :id="'editor_' + item.id"/> -->
+
+                        <Switches v-if="item.displayType =='CheckBoxEditor'"
+                                  v-model="item.fieldValue.displayValue[0]"
+                                  color="green"
+                                  type-bold="true"
+                                  @change="valueChanged(item)"></Switches>
+
+                        <DateTime v-if="item.displayType =='DateTimeBoxEditor'"
+                                  :dateValue="item.fieldValue.displayValue[0]"
+                                  class="form-control m-input-group"
+                                  name="datetime"
+                                  :id="'editor_' + item.id"
+                                  @on-value-change="changeValue(item, $event)" />
+
+                        <EnumSelectWidget v-if="item.displayType =='ListBoxEnumEditor'"
+                                          :field="item"
+                                          :fieldValue="item.fieldValue"
+                                          v-on:valueChanged="valueChanged($event, item)"
+                                          class="m-input-group">
+                        </EnumSelectWidget>
+
+                        <MultiSearchWidget v-if="item.displayType =='MultiSearchBoxEditor'"
+                                                :infoWindowNo="item.infoWindowNo"
+                                                :field="item"
+                                                :fieldValue="item.fieldValue"
+                                                v-on:valueChanged="tabValueChanged($event, item)"
+                                                class="m-input-group"></MultiSearchWidget>
+
+                        <SearchWidget v-if="item.displayType =='SearchBoxEditor'"
+                                      :whereClause="item.whereClause"
+                                      :infoWindowNo="item.infoWindowNo"
+                                      :field="item"
+                                      :fieldValue="item.fieldValue"
+                                      v-on:valueChanged="tabValueChanged($event, item)"
+                                      :displayName="item.listDisplayFieldNames"
+                                      class="m-input-group"
+                                      :parentModelData="parentModelData"></SearchWidget>
+
+                    </div>
+                    <!--<div v-if = "item.constraintEnum == 'Between'" class="form-inline-div">
+						<label v-if="item.displayType != 'ListBoxEnumEditor'" :for="'editor_' + item.id">——</label>
+						<input v-if="item.displayType =='NumberEditor'" type="number" v-model="item.value2" class="form-control m-input-group" :id="'editor_' + item.id" placeholder="请输入数字" @change="valueChanged(item)"/>
+		
+						<DateTime v-if="item.displayType =='DateTimeBoxEditor'" :dateValue="item.value2" class="form-control m-input-group" name="datetime" :id="'editor_' + item.id" @on-value-change="changeValue(item, $event)" />
+					</div>	-->
+                </div>
+            </div>
+            <div class="form-group">
+                <button v-if="processReportDto.htmlHelpUrl != null && processReportDto.htmlHelpUrl != ''"
+                        @click="openHtmlHelp(processReportDto.htmlHelpUrl)"
+                        class="btn btn-default">
+                    <span class="glyphicon glyphicon-question-sign"></span>
+                </button>
+                <input type="button"
+                       @click="runProcessDynamic()"
+                       class="btn btn-primary"
+                       style="margin-top: 15px;"
+                       value="确定" />
+            </div>
+            <ProcessReportResultPreview :processReportResult="processReportResult"
+                                        :pdfOnly="processReportDto.pdfOnly"
+                                        :excelOnly="processReportDto.excelOnly"></ProcessReportResultPreview>
+        </div>
+
+        <Loading ref="loading"></Loading>
+    </div>
+</template>
+
+<script>
+
+var Common = require("../../common/Common.js");
+var Notify = require("../../common/Notify.js");
+
+var Navbar = require("../../navbar/src/Navbar.vue").default;
+var DateTime = require("../../datetime/src/DateTime.vue").default;
+var MultiSearchWidget = require("./MultiSearchWidget.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+var ProcessReportResultPreview = require("./ProcessReportResultPreview.vue").default;
+var SearchWidget = require("../../info/src/SearchWidget.vue").default;
+var Switches = require("../../switches/src/Switches.vue").default;
+
+
+
+module.exports = {
+    props: ["processReportDto"],
+    data: function () {
+        return {
+            "processReportId": "",
+            "processReportDtos": {},
+            "processResultData": "",
+            "processReportResult": {},
+            "parameters": [],
+            "parentModelData": {
+                "data": {
+                }
+            },
+        }
+    },
+
+    components: {
+        DateTime,
+        MultiSearchWidget,
+        Loading,
+        ProcessReportResultPreview,
+        SearchWidget,
+        Switches,
+        Navbar
+    },
+
+    methods: {
+        /**
+         * 打开帮助页面
+         */
+        openHtmlHelp: function (htmlHelpUrl) {
+            window.open(htmlHelpUrl);
+        },
+
+
+        /**
+         * 初始化数据
+         * @param  {Object} data 流程报表数据
+         * @return {void}
+         */
+        initData: function (data) {
+            console.log("获取的参数列表:" + JSON.stringify(data));
+            var _self = this;
+            if (data == undefined) {
+                return;
+            }
+            _self.parameters = [];
+            if (data.processReportParameters) {
+                data.processReportParameters.forEach(function (item) {
+                    if (item.constraintEnum == 'Between') {
+                        var cloneObject1 = _self.cloneParameter(item);
+                        _self.parameters.push(cloneObject1);
+                        var cloneObject = {
+                            "index": 2,
+                            "isMandatory": false,
+                            "id": item.id,
+                            "fieldName": item.fieldName + "2",
+                            "name": item.name,
+                            "help": item.help,
+                            "displayType": item.displayType,
+                            "listDisplayFieldNames": item.listDisplayFieldNames,
+                            "infoWindowId": item.infoWindowId,
+                            "sortNo": item.sortNo,
+                            "isShow": item.isShow,
+                            "constraintEnum": item.constraintEnum,
+                            "whereClause": item.whereClause,
+                            "enumClass": item.enumClass,
+                            "fieldValue": {
+                                "id": "",
+                                "constraintEnum": item.constraintEnum,
+                                "displayValue": [],
+                                "fieldType": "String"
+                            },
+                            "calloutProcessNo": item.calloutProcessNo,
+                            "listDisplayFieldName": item.listDisplayFieldName,
+                            "displayName": "~",
+                            "infoWindowNo": item.infoWindowNo
+
+                        }
+                        _self.parentModelData.data[cloneObject.fieldName] = cloneObject.fieldValue;
+                        console.log(cloneObject);
+                        _self.parameters.push(cloneObject);
+                    } else {
+                        var obj = _self.cloneParameter(item);
+                        _self.parameters.push(obj);
+                    }
+                });
+            }
+            // 排序
+            _self.parameters.sort(function (a, b) {
+                return a.sortNo - b.sortNo;
+            })
+
+            console.log(_self.parameters);
+            //_self.processReportDto = data;
+        },
+
+        /**
+         * 执行流程
+         * @return {void}
+         */
+        runProcessDynamic: function () {
+            var _self = this;
+            _self.processResultData = {
+                "processReportNo": _self.processReportDto.no,
+                "modelData": {
+                    data: {}
+                }
+            }
+            _self.parameters.forEach(function (item) {
+                var fieldName = item.fieldName;
+                _self.processResultData.modelData.data[fieldName] = item.fieldValue;
+            })
+            _self.processReportResult = {};
+
+            _self.$refs.loading.show();
+            $.ajax({
+                url: Common.getApiURL('ProcessReportResource/runProcessDynamic'),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(_self.processResultData),
+                success: function (data) {
+                    _self.$refs.loading.hide();
+                    _self.processReportResult = data;
+
+                    if (_self.processReportResult.reportResults != undefined && _self.processReportResult.reportResults.length > 0) {
+                        _self.processReportResult.reportResults.forEach(function (item, index) {
+                            _self.$set(item, "previewIndex", 1);
+                            if (index == 0) {
+                                _self.$set(item, "showPreview", true);
+                            } else {
+                                _self.$set(item, "showPreview", false);
+                            }
+                        })
+                    }
+                    // _self.processReportDto.excelOnly = false;
+                    // _self.processReportDto.pdfOnly = true;
+
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.hide();
+                    Common.processException(XMLHttpRequest, textStatus, errorThrown);
+                }
+            });
+        },
+
+        /**
+         * 时间选择框数据发生改变
+         * @param  {Object} item     当前field
+         * @param  {String} newValue 改变后的值
+         * @return {void}          
+         */
+        changeValue: function (item, newValue) {
+            this.$set(item.fieldValue.displayValue, 0, newValue);
+            this.valueChanged(item);
+        },
+
+        /**
+         * 搜索框值发生改变
+         * @param  {Object} newFieldValue 改变后的值
+         * @param  {Object} item          当前field
+         * @return {void}               
+         */
+        tabValueChanged: function (newFieldValue, item) {
+            //item.fieldValue = newFieldValue;
+            this.$set(item.fieldValue, "displayValue", newFieldValue.displayValue);
+            this.$set(item.fieldValue, "id", newFieldValue.id);
+            this.$set(item.fieldValue, "fieldType", newFieldValue.fieldType);
+            this.valueChanged(item);
+        },
+
+        /**
+         * 复制参数
+         * @param {Object} 
+         */
+        cloneParameter: function (parameter) {
+            var cloneObject = {
+                "index": 1,
+                "isMandatory": false,
+                "id": parameter.id,
+                "fieldName": parameter.fieldName,
+                "name": parameter.name,
+                "help": parameter.help,
+                "displayType": parameter.displayType,
+                "listDisplayFieldNames": parameter.listDisplayFieldNames,
+                "infoWindowId": parameter.infoWindowId,
+                "sortNo": parameter.sortNo,
+                "isShow": parameter.isShow,
+                "constraintEnum": parameter.constraintEnum,
+                "whereClause": parameter.whereClause,
+                "enumClass": parameter.enumClass,
+                "fieldValue": "",
+                "calloutProcessNo": parameter.calloutProcessNo,
+                "listDisplayFieldName": parameter.listDisplayFieldName,
+                "displayName": parameter.displayName,
+                "infoWindowNo": parameter.infoWindowNo,
+                "value2": ""
+
+            }
+            if (parameter.fieldValue) {
+                cloneObject.fieldValue = parameter.fieldValue;
+            } else {
+                cloneObject.fieldValue = {
+                    "id": parameter.id,
+                    "constraintEnum": parameter.constraintEnum,
+                    "displayValue": [],
+                    "fieldType": "String"
+                }
+            }
+            this.parentModelData.data[cloneObject.fieldName] = cloneObject.fieldValue;
+            return cloneObject;
+
+        },
+        // 值改变事件
+        valueChanged: function (processReportParameter) {
+            // this.fieldValue = newFieldValue;
+            this.$emit("valueChanged", processReportParameter);
+            this.parentModelData.data[processReportParameter.fieldName] = processReportParameter.fieldValue;
+            this.parameters.forEach(function (item) {
+                if (item.fieldName == processReportParameter.fieldName) {
+                    item = processReportParameter;
+                }
+            })
+        },
+
+    },
+
+    //监控processReportDto,当dto变化时重新渲染界面
+    watch: {
+        "processReportDto": function (to, from) {
+            this.parameters = [];
+            this.processReportResult.reportResults = undefined;
+            this.initData(this.processReportDto);
+        },
+        "processReportDto.processReportParameters": function () {
+            this.initData(this.processReportDto);
+        },
+        // 路由切换的时候清空报表结果数据
+        "$route": function () {
+            this.processReportResult = {};
+        },
+
+    },
+
+    mounted: function () {
+        this.initData(this.processReportDto);
+    },
+
+}
+</script>
+<style scoped>
+.m-form-group label {
+    font-size: 15px;
+    width: 80px;
+    text-align: center;
+}
+
+.processResult-textarea {
+    width: 100%;
+    height: 200px;
+}
+
+.form-input {
+    border-radius: 4px !important;
+}
+
+.form-inline-div {
+    display: inline;
+}
+
+.m-form-group {
+    margin-left: 5px !important;
+    margin-right: 5px !important;
+    margin-bottom: 5px !important;
+}
+
+.m-input-group {
+    border-radius: 4px !important;
+    width: 200px !important;
+}
+</style>

+ 109 - 0
packages/process/src/ProcessReportResult.vue

@@ -0,0 +1,109 @@
+<template>
+    <div>
+        <div class="form-group"
+             v-if="processReportResult.processResult != undefined">
+            <textarea v-if="isTextAreaShowProcessResult"
+                      class="processResult-textarea"
+                      rows="10"
+                      style="width:100%">1.{{processReportResult.processResult.result}}</textarea>
+            <div v-else>
+                <h5 class="control-label"
+                    style="white-space: pre-line;">1.{{processReportResult.processResult.result}}</h5>
+            </div>
+        </div>
+        <!--
+          	作者:ChenMu
+          	时间:2020-01-20
+          	描述:jasper显示
+          -->
+        <div class="form-group">
+            <div class="form-group"
+                 v-if="processReportResult.processResult != undefined && pdfOnly != true">
+                <a v-if="processReportResult.processResult.modelData&&processReportResult.processResult.modelData.data"
+                   target="_blank"
+                   role="button"
+                   class="btn btn-info">
+                    Excel报表
+                </a>
+            </div>
+            <div class="form-group"
+                 v-if="processReportResult.reportResults != undefined"
+                 v-for="(reportResult, index) in processReportResult.reportResults"
+                 id="pr.id">
+                <h3 class="control-label">
+                    {{index + 1 + (processReportResult.processResult == undefined ? 0 : 1)}}.{{reportResult.reportName}}
+                </h3>
+                <div>
+                    <button v-if="reportResult.isSuccess && pdfOnly != true"
+                            class="btn btn-success"
+                            @click="reportDownload(reportResult.excelDownLoadUrl, reportResult.reportDefinitionType)">Excel报表</button>
+                    <button v-if="reportResult.isSuccess && excelOnly != true"
+                            class="btn btn-success"
+                            @click="reportDownload(reportResult.pdfDownLoadUrl, reportResult.reportDefinitionType)">PDF报表</button>
+                    <span class="control-label label-danger"
+                          v-else>生成失败</span>
+                </div>
+            </div>
+        </div>
+
+        <!--<ProcessReportArchive :processReportResult="processReportResult"></ProcessReportArchive>-->
+    </div>
+
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var ProcessReportArchive = require("./ProcessReportArchive.vue").default;
+var DownloadService = require("../../common/DownloadService.js");
+
+module.exports = {
+    name: "ProcessReportResult",
+    
+    props: ["processReportResult", "pdfOnly", "excelOnly"],
+
+    data: function () {
+        return {
+
+        }
+    },
+
+    components: {
+        Common,
+        ProcessReportArchive,
+        DownloadService,
+
+    },
+
+    methods: {
+        /**
+         * 下载报表
+         * @param {Object} fileName
+         * @author GuoZhiBo 20200410
+         */
+        reportDownload: function (fileName, reportDefinitionType) {
+            if (reportDefinitionType == "UReport2") {
+                window.open(Common.getRootPath() + fileName);
+            } else {
+                DownloadService.reportDownload(fileName);
+            }
+        },
+    },
+
+    mounted: function () {
+
+    },
+
+    computed: {
+        // 是否文本区域显示流程结果
+        isTextAreaShowProcessResult: function () {
+            var result = this.processReportResult.processResult.result;
+            if (result != undefined && result.length > 100) {
+                return true;
+            } else {
+                return false;
+            }
+        },
+
+    }
+}
+</script>

+ 252 - 0
packages/process/src/ProcessReportResultPreview.vue

@@ -0,0 +1,252 @@
+<template>
+    <div>
+        <div class="form-group"
+             v-if="processReportResult.processResult != undefined">
+            <textarea v-if="isTextAreaShowProcessResult"
+                      class="processResult-textarea"
+                      rows="10"
+                      style="width:100%">1.{{processReportResult.processResult.result}}</textarea>
+            <div v-if="isDivShowProcessResult">
+                <h3 class="control-label">1.{{processReportResult.processResult.result}}</h3>
+            </div>
+        </div>
+        <div class="form-group"
+             v-if="processReportResult.processResult != undefined && pdfOnly != true">
+            <a v-if="processReportResult.processResult.modelData&&processReportResult.processResult.modelData.data"
+               target="_blank"
+               role="button"
+               class="btn btn-info">
+                Excel报表
+            </a>
+        </div>
+        <ProcessReportArchive v-if="processReportResult.reportResults != undefined"
+                              :processReportResult="processReportResult"></ProcessReportArchive>
+        <div class="form-group"
+             v-if="processReportResult.reportResults != undefined"
+             v-for="(reportResult, index) in processReportResult.reportResults"
+             id="pr.id">
+            <div class="panel panel-default">
+                <div class="panel-heading"
+                     style="height: 50px;"
+                     @click.self="reportResult.showPreview = !reportResult.showPreview">
+                    <div class="pull-left">
+                        {{index + 1 + (processReportResult.processResult == undefined ? 0 : 1)}}.{{reportResult.reportName}}
+                    </div>
+                    <div class="pull-left"
+                         style="padding-left: 30px; cursor: pointer;"
+                         @click="iframeBack(reportResult, index)">
+                        后退
+                    </div>
+                    <div class="pull-left"
+                         style="padding-left: 30px; cursor: pointer;"
+                         @click="iframeForward(reportResult, index)">
+                        前进
+                    </div>
+                    <div class="pull-left"
+                         style="padding-left: 30px; cursor: pointer;"
+                         v-if="reportResult.reportDefinitionType == 'UReport2'"
+                         @click="editReport(reportResult, index)">
+                        编辑
+                    </div>
+                    <div class="pull-right"
+                         style="width: 200px;">
+                        <div class="input-group m-input-group">
+                            <select v-model="reportResult.previewIndex"
+                                    class="form-control">
+                                <option value="1"
+                                        v-if="excelOnly != true">PDF</option>
+                                <option value="2"
+                                        v-if="pdfOnly != true">Excel</option>
+                            </select>
+                            <span @click="download(reportResult)"
+                                  class="input-group-addon btn-download">下载</span>
+                        </div>
+                    </div>
+                </div>
+                <div class="panel-body"
+                     v-if="reportResult.showPreview">
+                    <div v-if="reportResult.isSuccess">
+                        <div v-if="reportResult.previewIndex == 1"
+                             class="embed-responsive embed-responsive-4by3">
+                            <embed :id="uuid + 'pdfIframe' + index"
+                                   class="embed-responsive-item"
+                                   type="application/pdf"
+                                   :src="getReport(reportResult.pdfDownLoadUrl, '#' + uuid + 'pdfIframe' + index)" />
+                        </div>
+                        <div v-else
+                             class="embed-responsive embed-responsive-4by3">
+                            <iframe :id="uuid + 'htmlIframe' + index"
+                                    class="embed-responsive-item"
+                                    :src="getReport(reportResult.htmlPreviewUrl, '#' + uuid + 'htmlIframe' + index)"></iframe>
+                        </div>
+                    </div>
+                    <div v-else>
+                        生成失败
+                    </div>
+                </div>
+            </div>
+
+            <!-- <h3 class = "control-label">
+	          {{index + 1 + (processReportResult.processResult == undefined ? 0 : 1)}}.{{reportResult.reportName}}
+	        </h3>
+	        <div>
+	        	<a v-if="reportResult.isSuccess" v-bind:href="reportResult.excelDownLoadUrl" target="_blank" role="button" class="btn btn-info">	
+	        		Excel报表
+	        	</a>
+	        	<a v-if="reportResult.isSuccess" v-bind:href="reportResult.pdfDownLoadUrl" target="_blank" role="button" class="btn btn-info">
+	        		PDF报表
+	        	</a>
+	        	<span class = "control-label label-danger" v-else>生成失败</span>
+	        </div> -->
+        </div>
+    </div>
+
+</template>
+
+<script>
+
+var Common = require("../../common/Common.js");
+var Uuid = require("../../common/Uuid.js");
+
+var DownloadService = require("../../common/DownloadService.js");
+
+var ProcessReportArchive = require("./ProcessReportArchive.vue").default;
+
+module.exports = {
+    name: "ProcessReportResultPreview",
+    
+    props: ["processReportResult", "pdfOnly", "excelOnly"],
+
+    data: function () {
+        return {
+            "uuid": Uuid.createUUID(),
+        }
+    },
+
+    components: {
+        ProcessReportArchive,
+    },
+    
+    methods: {
+        /**
+         * 根据id和url获取文件,请求头增加token
+         * @author GuoZhiBo 20200507
+         * @param {Object} url
+         * @param {Object} id
+         */
+        getReport: function (url, id) {
+            var _self = this;
+            var account = localStorage.getItem("account");
+            var token = localStorage.getItem("token");
+            let absoluteUrl = null;
+            if (url.indexOf('ureport') > 0) {
+                absoluteUrl = Common.getRootPath() + url;
+
+                _self.$nextTick(function () {
+                    var iframe = document.querySelector(id);
+                    iframe.src = absoluteUrl;
+                });
+            } else {
+                absoluteUrl = Common.getFileServerUrl() + "Files/" + account + "/GeneratedReport/" + url;
+
+                _self.$nextTick(function () {
+                    var iframe = document.querySelector(id);
+                    var headers = [['token', token, 'account', account]];
+                    var xhr = new XMLHttpRequest();
+                    xhr.open('GET', absoluteUrl);
+                    xhr.responseType = 'blob';
+                    headers.forEach(function (header) {
+                        xhr.setRequestHeader(header[0], header[1]);
+                        xhr.setRequestHeader(header[2], header[3]);
+                    });
+                    xhr.onload = function () {// 请求完成处理函数
+                        if (this.status === 200) {
+                            var blob = this.response;// 获取返回值
+                            iframe.src = window.URL.createObjectURL(this.response);
+                        }
+                    };
+                    xhr.send();
+                });
+            }
+
+
+        },
+        /**
+         * 下载文件
+         */
+        download: function (item) {
+            if (item.previewIndex == 1) {
+                console.log(item.pdfDownLoadUrl);
+                DownloadService.reportDownload(item.pdfDownLoadUrl);
+            } else {
+                DownloadService.reportDownload(item.excelDownLoadUrl);
+            }
+        },
+
+        /**
+         * 后退
+         */
+        iframeBack: function (reportResult, index) {
+            if (reportResult.previewIndex == 1) {
+                document.getElementById('pdfIframe' + index).contentWindow.history.back();
+            } else {
+                document.getElementById('htmlIframe' + index).contentWindow.history.back();
+            }
+        },
+
+
+        /**
+         * 前进
+         */
+        iframeForward: function (reportResult, index) {
+            if (reportResult.previewIndex == 1) {
+                document.getElementById('pdfIframe' + index).contentWindow.history.forward();
+            } else {
+                document.getElementById('htmlIframe' + index).contentWindow.history.forward();
+            }
+        },
+
+
+        /**
+         * 打开报表编辑界面
+         */
+        editReport: function (reportResult) {
+            var _self = this;
+            if (reportResult.reportDefinitionType != 'UReport2') {
+                Notify.error("错误", "当前的报表不支持用户自定义", false);
+                return;
+            }
+            let reportDesignerUrl = reportResult.htmlPreviewUrl.replace("preview", "designer");
+            window.open(reportDesignerUrl);
+        },
+    },
+
+    computed: {
+        // 是否文本区域显示流程结果
+        isTextAreaShowProcessResult: function () {
+            var result = this.processReportResult.processResult.result;
+            if (result != undefined && result.length > 100) {
+                return true;
+            } else {
+                return false;
+            }
+        },
+
+        // 是否Div显示流程个结果
+        isDivShowProcessResult: function () {
+            var result = this.processReportResult.processResult.result;
+            if (result != undefined && result.length <= 100) {
+                return true;
+            } else {
+                return false;
+            }
+        },
+
+    }
+}
+</script>
+<style scoped>
+.btn-download {
+    cursor: pointer;
+}
+</style>

+ 377 - 0
packages/process/src/ProcessReportStatic.vue

@@ -0,0 +1,377 @@
+<template>
+    <div>
+        <Navbar :title="processReportDto.name"
+                :isGoBack="processReportDto.goback"></Navbar>
+        <div>
+            <h5>{{processReportDto.description}}</h5>
+        </div>
+        <div class="form-inline">
+            <div class="form-group"
+                 v-if="processReportDtos != undefined && processReportDtos.processReportParameters != undefined"
+                 v-for="item in processReportDtos.processReportParameters">
+                <label v-if="item.displayType != 'ListBoxEnumEditor'"
+                       class="m-label"
+                       :for="'editor_' + item.id">{{item.displayName}}</label>
+                <span v-show="!item.mandatory"
+                      style="color: red;">该项为必填项字段</span>
+
+                <div class="input-group">
+                    <input v-if="item.displayType =='TextEditor'"
+                           type="text"
+                           v-model="item.fieldValue.displayValue[0]"
+                           class="form-control"
+                           :id="'editor_' + item.id"
+                           placeholder="请输入内容"
+                           @change="valueChanged(item)" />
+
+                    <textarea rows="5"
+                              v-if="item.displayType =='TextAreaEditor'"
+                              type="text"
+                              v-model="item.fieldValue.displayValue[0]"
+                              class="form-control"
+                              :id="'editor_' + item.id"
+                              placeholder="请输入内容"
+                              @change="valueChanged(item)" />
+
+                    <input v-if="item.displayType =='NumberEditor'"
+                           type="number"
+                           v-model="item.fieldValue.displayValue[0]"
+                           class="form-control"
+                           :id="'editor_' + item.id"
+                           placeholder="请输入数字"
+                           @change="valueChanged(item)" />
+
+                    <Switches v-if="item.displayType =='CheckBoxEditor'"
+                              v-model="item.fieldValue.displayValue[0]"
+                              color="green"
+                              type-bold="true"
+                              @change="valueChanged(item)"></Switches>
+
+                    <DateTime v-if="item.displayType =='DateTimeBoxEditor'"
+                              :dateValue="item.fieldValue.displayValue[0]"
+                              class="form-control"
+                              name="datetime"
+                              :id="'editor_' + item.id"
+                              @on-value-change="changeValue(item, $event)" />
+
+                    <EnumSelectWidget v-if="item.displayType =='ListBoxEnumEditor'"
+                                      :keyValues="item.keyValues"
+                                      :enumValue="item.fieldValue.displayValue[0]"
+                                      v-on:valueChanged="changeValue(item, $event)" />
+
+                    <MultiSearchWidget v-if="item.displayType =='MultiSearchBoxEditor'"
+                                            :infoWindowNo="item.infoWindowNo"
+                                            :field="item"
+                                            :fieldValue="item.fieldValue"
+                                            v-on:valueChanged="tabValueChanged($event, item)"></MultiSearchWidget>
+
+                    <SearchWidget v-if="item.displayType =='SearchBoxEditor'"
+                                  :whereClause="item.whereClause"
+                                  :infoWindowNo="item.infoWindowNo"
+                                  :field="item"
+                                  :fieldValue="item.fieldValue"
+                                  v-on:valueChanged="tabValueChanged($event, item)"
+                                  :displayName="item.listDisplayFieldNames"
+                                  :modelData="modelData"></SearchWidget>
+
+                    <VueMonthlyPicker v-if="item.displayType =='YearMonthEditor'"
+                                      :value="item.fieldValue.displayValue[0]"
+                                      :allowChangeValue="false"
+                                      dateFormat="YYYY-MM"
+                                      class="vue-monthly-picker m-date-fieldEditView"
+                                      @selected="changeValue(item, $event)" />
+                    <YearPicker v-if="item.displayType =='YearEditor'"
+                                :dateValue="item.fieldValue.displayValue[0]"
+                                @selected="changeValue(item, $event)"></YearPicker>
+                </div>
+            </div>
+        </div>
+        <div>
+            <div class="form-group">
+                <button v-if="processReportDtos.htmlHelpUrl != null && processReportDtos.htmlHelpUrl != ''"
+                        @click="openHtmlHelp(processReportDtos.htmlHelpUrl)"
+                        class="btn btn-default">
+                    <span class="glyphicon glyphicon-question-sign"></span>
+                </button>
+                <input type="button"
+                       @click="subDatas()"
+                       class="btn btn-primary"
+                       style="margin-top: 15px;"
+                       value="确定"/>
+            </div>
+            <ProcessReportResultPreview :processReportResult="processReportResult"
+                                        :pdfOnly="processReportDto.pdfOnly"
+                                        :excelOnly="processReportDto.excelOnly"></ProcessReportResultPreview>
+
+        </div>
+        <Loading ref="loading"></Loading>
+    </div>
+</template>
+
+<script>
+var Common = require("../../common/Common.js");
+var DateTime = require("../../datetime/src/DateTime.vue").default;
+var YearPicker = require("../../year-picker/src/YearPicker.vue").default;
+var MultiSearchWidget = require("./MultiSearchWidget.vue").default;
+var Loading = require("../../loading/src/Loading.vue").default;
+var ProcessReportResultPreview = require("./ProcessReportResultPreview.vue").default;
+var SearchWidget = require("../../info/src/SearchWidget.vue").default;
+var Switches = require("../../switches/src/Switches.vue").default;
+var Navbar = require("../../navbar/src/Navbar.vue").default;
+var VueMonthlyPicker = require("../../vue-monthly-picker/src/VueMonthlyPicker.vue").default;
+var EnumSelectWidget = require("./EnumSelectWidget.vue").default;
+
+
+
+module.exports = {
+    props: ["processReportDto"],
+    data: function () {
+        return {
+            processReportId: "",
+            processReportDtos: "",
+            processResultData: "",
+            processReportResult: {},
+            complete: true, //参数是否全部填写
+            modelData: {
+                data: {
+
+                }
+            }
+
+        };
+    },
+
+    components: {
+        DateTime,
+        MultiSearchWidget,
+        Loading,
+        ProcessReportResultPreview,
+        SearchWidget,
+        Switches,
+        Navbar,
+        VueMonthlyPicker,
+        YearPicker,
+        EnumSelectWidget
+    },
+
+    methods: {
+        /**
+         * 打开帮助页面
+         */
+        openHtmlHelp: function (htmlHelpUrl) {
+            window.open(htmlHelpUrl);
+        },
+
+        /**
+         * 初始化数据
+         * @param  {Object} data 流程报表数据
+         * @return {void}
+         */
+        initData: function (data) {
+            var _self = this;
+            if (data == undefined) {
+                return;
+            }
+            _self.processReportDtos = '';
+            if (data.processReportParameters) {
+                data.processReportParameters.forEach(function (item) {
+                    if (item.mandatory == undefined) {
+                        item.mandatory = true;
+                    }
+                    if (item.listDisplayFieldName == undefined) {
+                        item.listDisplayFieldName = item.listDisplayFieldNames;
+                    }
+
+                    if (item.fieldValue == undefined) {
+                        item.fieldValue = {
+                            id: '',
+                            displayValue: [],
+                            fieldType: "String"
+                        };
+                    }
+
+                    if (item.displayType == 'YearMonthEditor') {
+                        item.fieldValue.displayValue[0] = moment().format('YYYY-MM');
+                    }
+                    _self.modelData.data[item.fieldName] = item.fieldValue;
+                });
+            }
+
+            _self.processReportDtos = data;
+        },
+
+        /**
+         * 判断参数是否填写
+         */
+        judgeDataFull: function () {
+            var _self = this;
+
+            _self.complete = true;
+            if (
+                _self.processReportDtos &&
+                _self.processReportDtos.processReportParameters
+            ) {
+                _self.processReportDtos.processReportParameters.forEach(
+                    function (item, index) {
+                        item.mandatory = true;
+                        if (item.fieldValue.displayValue.length <= 0) {
+                            item.mandatory = false;
+                        }
+                        _self.complete = _self.complete && item.mandatory;
+                    }
+                );
+            }
+        },
+
+        /**
+         * 执行流程
+         * @return {void}
+         */
+        subDatas: function () {
+            var _self = this;
+            _self.judgeDataFull();
+
+            _self.processResultData = {
+                processReportNo: _self.processReportDto.no,
+                modelData: {
+                    data: {}
+                }
+            };
+
+            if (
+                _self.processReportDtos &&
+                _self.processReportDtos.processReportParameters
+            ) {
+                _self.processReportDtos.processReportParameters.forEach(
+                    function (item, index) {
+                        var fieldName = item.fieldName;
+                        _self.processResultData.modelData.data[fieldName] =
+                            item.fieldValue;
+                    }
+                );
+            }
+
+            _self.processReportResult = {};
+            if (!_self.complete) {
+                return;
+            }
+            _self.$refs.loading.show();
+
+            $.ajax({
+                url: Common.getApiURL("ProcessReportResource/runProcess"),
+                type: "post",
+                dataType: "json",
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                contentType: "application/json",
+                data: JSON.stringify(_self.processResultData),
+                success: function (data) {
+                    _self.$refs.loading.hide();
+                    _self.processReportResult = data;
+
+                    if (
+                        _self.processReportResult.reportResults != undefined &&
+                        _self.processReportResult.reportResults.length > 0
+                    ) {
+                        _self.processReportResult.reportResults.forEach(
+                            function (item, index) {
+                                _self.$set(item, "previewIndex", 2);
+                                if (index == 0) {
+                                    _self.$set(item, "showPreview", true);
+                                } else {
+                                    _self.$set(item, "showPreview", false);
+                                }
+                            }
+                        );
+                    }
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    _self.$refs.loading.hide();
+                    Common.processException(
+                        XMLHttpRequest,
+                        textStatus,
+                        errorThrown
+                    );
+                }
+            });
+        },
+
+        /**
+         * 时间选择框数据发生改变
+         * @param  {Object} item     当前field
+         * @param  {String} newValue 改变后的值
+         * @return {void}
+         */
+        changeValue: function (item, newValue) {
+            this.$set(item.fieldValue.displayValue, 0, newValue);
+            this.valueChanged(item);
+
+        },
+        /**
+         * 搜索框值发生改变
+         * @param  {Object} newFieldValue 改变后的值
+         * @param  {Object} item          当前field
+         * @return {void}
+         */
+        tabValueChanged: function (newFieldValue, item) {
+            //item.fieldValue = newFieldValue;
+            //this.$set(item, "fieldValue", newFieldValue);
+            this.$set(
+                item.fieldValue,
+                "displayValue",
+                newFieldValue.displayValue
+            );
+            this.$set(item.fieldValue, "id", newFieldValue.id);
+            this.$set(item.fieldValue, "fieldType", newFieldValue.fieldType);
+            this.valueChanged(item);
+        },
+        // 值改变事件
+        valueChanged: function (processReportParameter) {
+            // this.fieldValue = newFieldValue;
+            this.$emit("valueChanged", processReportParameter);
+            this.modelData.data[processReportParameter.fieldName] = processReportParameter.fieldValue;
+        },
+
+    },
+
+    //监控processReportDto,当dto变化时重新渲染界面
+    watch: {
+        "processReportDto": function (to, from) {
+            this.processReportResult.reportResults = undefined;
+            this.initData(this.processReportDto);
+        },
+        "processReportDto.processReportParameters": function () {
+            this.initData(this.processReportDto);
+        },
+        $route: function () {
+            this.processReportResult = {};
+        }
+    },
+
+    mounted: function () {
+        this.initData(this.processReportDto);
+    }
+};
+</script>
+<style scoped>
+.control-label {
+    font-size: 15px;
+}
+
+.processResult-textarea {
+    width: 100%;
+    height: 200px;
+}
+
+.m-label {
+    width: 5em;
+    margin-left: 1em;
+}
+</style>
+
+<style>
+.m-date-fieldEditView > div:first-child {
+    width: 100%;
+}
+</style>

+ 362 - 0
packages/process/src/api/ProcessReportResource.js

@@ -0,0 +1,362 @@
+const Common = require('../../../common/Common.js');
+
+/**
+ * 工具类自动生成的API,请勿做任何修改,请勿做任何修改,请勿做任何修改(重要的事情说3遍)
+ * 工具作者: 杨志杰
+ *
+ */
+module.exports = {
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 自动运行流程和报表
+	 */
+    autoRun: function (processReportExecutorDto) {
+        const requestUrl = 'ProcessReportResource/autoRun';
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'post',
+
+                data: JSON.stringify(processReportExecutorDto),
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 格式化流程和报表
+	 */
+    formatReportResult: function () {
+        const requestUrl = 'ProcessReportResource/formatReportResult';
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'get',
+
+                dataType: 'json',
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 根据流程和报表Id的归档权限 (该接口暂时只和登录的用户角色有关,以后可以扩展为根据processReportId来划分)
+	 */
+    getArchiveAuthority: function (processReportNo) {
+        let requestUrl = 'ProcessReportResource/getArchiveAuthority';
+        let paramCount = 0;
+        if (processReportNo != null) {
+            requestUrl += (paramCount > 0) ? '&' : '?';
+            requestUrl += ('processReportNo=' + processReportNo);
+            paramCount++;
+        }
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'get',
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 *
+	 */
+    getProcessReportKeyValues: function () {
+        const requestUrl = 'ProcessReportResource/getProcessReportKeyValues';
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'get',
+
+                dataType: 'json',
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 运行Callout
+	 */
+    runCallout: function (no, modelData) {
+        let requestUrl = 'ProcessReportResource/runCallout/{no}';
+        if (no != null) {
+            requestUrl = requestUrl.replace('{no}', no);
+        }
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'post',
+                contentType: 'application/json',
+
+                dataType: 'json',
+                data: JSON.stringify(modelData),
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 运行超链接报表
+	 */
+    runHyperLinkReport: function (jasperFileName, token, moduleNo) {
+        let requestUrl = 'ProcessReportResource/runHyperLinkReport';
+        let paramCount = 0;
+        if (jasperFileName != null) {
+            requestUrl += (paramCount > 0) ? '&' : '?';
+            requestUrl += ('jasperFileName=' + jasperFileName);
+            paramCount++;
+        }
+        if (token != null) {
+            requestUrl += (paramCount > 0) ? '&' : '?';
+            requestUrl += ('token=' + token);
+            paramCount++;
+        }
+        if (moduleNo != null) {
+            requestUrl += (paramCount > 0) ? '&' : '?';
+            requestUrl += ('moduleNo=' + moduleNo);
+            paramCount++;
+        }
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'get',
+
+                dataType: 'octet-stream',
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 运行流程
+	 */
+    runProcess: function (processResultData) {
+        const requestUrl = 'ProcessReportResource/runProcess';
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'post',
+                contentType: 'application/json',
+
+                dataType: 'json',
+                data: JSON.stringify(processResultData),
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 运行流程
+	 */
+    runProcessByIds: function (no, recordIds) {
+        let requestUrl = 'ProcessReportResource/runProcessByIds/{no}';
+        if (no != null) {
+            requestUrl = requestUrl.replace('{no}', no);
+        }
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'post',
+                contentType: 'application/json',
+
+                dataType: 'json',
+                data: JSON.stringify(recordIds),
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 运行动态参数报表
+	 */
+    runProcessDynamic: function (processResultData) {
+        const requestUrl = 'ProcessReportResource/runProcessDynamic';
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'post',
+                contentType: 'application/json',
+
+                dataType: 'json',
+                data: JSON.stringify(processResultData),
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 运行Callout
+	 */
+    runProcessReportCallout: function (no, processReportDto) {
+        let requestUrl = 'ProcessReportResource/runProcessReportCallout/{no}';
+        if (no != null) {
+            requestUrl = requestUrl.replace('{no}', no);
+        }
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'post',
+                contentType: 'application/json',
+
+                dataType: 'json',
+                data: JSON.stringify(processReportDto),
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    },
+
+    /**
+	 * 工具类自动生成的方法
+	 * 工具作者: 杨志杰
+	 * 根据流程和报表Id获取流程和报表的定义
+	 */
+    uniqueByNo: function (no) {
+        let requestUrl = 'ProcessReportResource/uniqueByNo';
+        let paramCount = 0;
+        if (no != null) {
+            requestUrl += (paramCount > 0) ? '&' : '?';
+            requestUrl += ('no=' + no);
+            paramCount++;
+        }
+
+        return new Promise((resolve, reject) => {
+            $.ajax({
+                url: Common.getApiURL(requestUrl),
+                type: 'get',
+
+                dataType: 'json',
+
+                beforeSend: function (request) {
+                    Common.addTokenToRequest(request);
+                },
+                success: function (data) {
+                    resolve(data);
+                },
+                error: function (XMLHttpRequest, textStatus, errorThrown) {
+                    reject(XMLHttpRequest);
+                }
+            });
+        });
+    }
+};

+ 8 - 0
packages/scanner/index.js

@@ -0,0 +1,8 @@
+
+import Scanner from './src/Scanner.vue';
+
+Scanner.install = function(Vue) {
+    Vue.component(Scanner.name, Scanner);
+};
+
+export default Scanner;

+ 163 - 0
packages/scanner/src/Scanner.vue

@@ -0,0 +1,163 @@
+<template>
+    <div>
+        <div>
+            <button class="btn btn-default"
+                    v-if="!connected"
+                    @click="init">连接</button>
+            <button class="btn btn-default"
+                    @click="acquire"
+                    v-bind:disabled="!connected">扫描</button>
+            <button class="btn btn-default"
+                    v-if="base64Data != null && base64Data.length > 0"
+                    @click="upload">上传</button>
+        </div>
+        <div>
+            <iframe id="pdf-frame"
+                    ref="pdfFrame"
+                    :src="base64Data"
+                    style="width: 100%; height: calc(100vh - 280px);"></iframe>
+        </div>
+    </div>
+</template>
+
+
+<script>
+
+
+module.exports = {
+    name: "Scanner",
+    data: function () {
+        return {
+            connected: false,
+            base64Data: null,
+        }
+    },
+    components: {
+    },
+
+    methods: {
+
+        addText: function (text) {
+            console.log(text);
+        },
+
+
+        init: function () {
+            let _self = this;
+            if (_self.webSocket != null) {
+                _self.webSocket.close();
+                _self.webSocket = null;
+            }
+
+            // 打开一个 web socket
+            _self.webSocket = new WebSocket("ws://127.0.0.1:9005/scanner");
+
+            _self.webSocket.onopen = function () {
+                _self.addText("连接成功。");
+                _self.connected = true;
+            };
+
+            _self.webSocket.onmessage = function (evt) {
+                console.log(evt.data);
+                var response = JSON.parse(evt.data);
+                if (response.code == 2) {
+                    var data = response.message;
+                    _self.showPdfFile(data);
+                } else if (response.code <= 1) {
+                    _self.addText(response.message);
+                } else {
+                    _self.addText(evt.data);
+                }
+            };
+
+            _self.webSocket.onclose = function () {
+                _self.addText("连接关闭。");
+                _self.connected = false;
+            };
+
+            _self.webSocket.onerror = function (e) {
+                console.log(e);
+                _self.addText("连接异常," + e + "。");
+                _self.connected = false;
+            };
+
+            _self.addText("开始连接。");
+        },
+
+
+        /**
+         * 扫描
+         */
+        acquire: function () {
+            let _self = this;
+            _self.webSocket.send('{"code":1}');
+        },
+
+        /**
+         * 显示pdf
+         */
+        showPdfFile: function (data) {
+            if (data == null || data.length == 0) {
+                this.base64Data = null;
+            } else {
+                this.base64Data = "data:application/pdf;base64," + data;
+            }
+        },
+
+        /**
+         * 上传文件
+         */
+        upload: function () {
+            if (this.base64Data == null) {
+                BootstrapDialog.show({
+                    title: "错误",
+                    message: "请先扫描文件。",
+                    type: BootstrapDialog.TYPE_DANGER,
+                });
+                return;
+            }
+
+            var timeStr = "扫描文件" + moment().format("_YYYYMMDD_hhmmss") + ".pdf";
+            let file = this.dataURLtoFile(this.base64Data, timeStr);
+            let e = {
+                target: {
+                    files: [file]
+                }
+            }
+            this.$emit("upload", e);
+        },
+
+        /**
+         * 将base64转换为文件
+         * @param dataurl base64字符串
+         * @param fileName 文件名
+         */
+        dataURLtoFile: function (dataStr, fileName) {
+            var arr = dataStr.split(','),
+                mime = arr[0].match(/:(.*?);/)[1],
+                bstr = atob(arr[1]),
+                n = bstr.length,
+                u8arr = new Uint8Array(n);
+            while (n--) {
+                u8arr[n] = bstr.charCodeAt(n);
+            }
+            return new File([u8arr], fileName, { type: mime });
+        },
+    },
+
+    mounted: function () {
+        this.init();
+    },
+
+    beforeDestroy: function () {
+        var _self = this;
+        if (_self.webSocket != null) {
+            _self.webSocket.close();
+            _self.webSocket = null;
+        }
+    }
+}
+
+</script>
+<style>
+</style>

+ 8 - 0
packages/switches/index.js

@@ -0,0 +1,8 @@
+
+import Switches from './src/Switches.vue';
+
+Switches.install = function(Vue) {
+    Vue.component(Switches.name, Switches);
+};
+
+export default Switches;

+ 426 - 0
packages/switches/src/Switches.vue

@@ -0,0 +1,426 @@
+<template>
+    <label :class="classObject">
+        <input type="checkbox" :disabled="disabled" v-model="value" @click="clickEvent($event)">
+
+        <div></div>
+
+        <span class="vue-switcher__label" v-if="showText === undefined || showText === null || showText === true">
+            <span v-if="value" v-text="textTrue"></span>
+            <span v-if="!value" v-text="textFalse"></span>
+        </span>
+    </label>
+</template>
+
+<script>
+
+module.exports = {
+    name: 'Switches',
+    props: [
+        "selected", // 选择状态, false
+        "disabled", // 禁用, false
+        "showText",   // 是否显示文字提示
+        "textTrueIn",  // True状态下的文字
+        "textFalseIn", // False状态下的文字
+        "color",    // 显示的颜色default,primary,success,info,warning,danger
+        "type-bold",    // 大图标
+    ],
+
+    data: function(){
+        var tempValue = false;
+        var colors = 'red';
+        if(this.selected != undefined){
+            tempValue = (this.selected == 'true' || this.selected == true);
+            if(this.selected == 'true' || this.selected == true){
+            	colors = 'green';
+            }else{
+            	colors = 'red';
+            }
+        }
+
+        return {
+            "value": tempValue,
+            "textTrue": (this.textTrueIn == undefined) ? "是" : this.textTrueIn,
+            "textFalse": (this.textFalseIn == undefined) ? "否" : this.textFalseIn,
+            "theme": "default",
+            "colorCss": colors
+        }
+    },
+    methods:{
+        clickEvent:function(e){
+            var clientX = e.clientX;
+            var elm = $(e.target);
+            var left = elm.offset().left;
+            if(clientX - left > 50){
+               e.preventDefault(); 
+           }
+        }
+    },
+    watch: {
+        value: function(val, oldVal) {
+            this.$emit('input', val);
+        },
+
+        selected: function(val, oldVal) {
+            if(val != undefined){
+                this.value = (val == 'true' || val == true);
+                if(val == 'true' || val == true){
+               		this.colorCss = 'green';
+	            }else{
+	            	this.colorCss = 'red';
+	            }
+            }
+            
+        }
+    },
+
+    computed: {
+        classObject : function() {
+            var _self = this;
+
+            return {
+                'vue-switcher' : true,
+                ['vue-switcher--unchecked'] : !_self.value,
+                ['vue-switcher--disabled'] : !!_self.disabled,
+                ['vue-switcher--bold']: !!_self.typeBold,
+                ['vue-switcher--bold--unchecked']: !!_self.typeBold && !_self.value,
+                [`vue-switcher-theme--${_self.theme}`] : _self.theme,
+                [`vue-switcher-color--${_self.colorCss}`] : _self.colorCss,
+            };
+        }
+    }
+}
+</script>
+
+<style>
+	.vue-switcher {
+	  position: relative;
+	  display: inline-block;
+	  width: 100px;
+	  height: 26px;
+	  line-height: 26px;
+	  margin-bottom: 0px;
+	  top: 7px;
+	}
+	.vue-switcher__label {
+	  position: absolute;
+	  left: 50px;
+	  top: 0px;
+	  height: 26px;
+	  line-height: 26px;
+	  display: block;
+	  font-size: 10px;
+	  margin-bottom: 0px;
+	}
+	.vue-switcher input {
+	  position: absolute;
+	  left: 0px;
+	  top: 0px;
+	  width: 50px;
+	  height: 26px;
+	  line-height: 26px;
+	  opacity: 0;
+	  z-index: 1;
+	  cursor: pointer;
+	  margin: 0px !important;
+	}
+	.vue-switcher div {
+	  position: absolute;
+	  left: 0px;
+	  top: 8px;
+	  width: 40px;
+	  height: 10px;
+	  border-radius: 30px;
+	  display: -webkit-flex;
+	  display: -ms-flex;
+	  display: flex;
+	  align-items: center;
+	  justify-content: flex-start;
+	  cursor: pointer;
+	}
+	.vue-switcher div:after {
+	  position: absolute;
+	  top: -4;
+	  left: 100%;
+	  content: '';
+	  height: 18px;
+	  width: 18px;
+	  border-radius: 100px;
+	  display: block;
+	  transition: all ease 0.3s;
+	  margin-left: -17px;
+	  cursor: pointer;
+	}
+	.vue-switcher--unchecked div {
+	  justify-content: flex-end;
+	}
+	.vue-switcher--unchecked div:after {
+	  left: 15px;
+	}
+	.vue-switcher--disabled div {
+	  opacity: 0.3;
+	}
+	.vue-switcher--disabled input {
+	  cursor: not-allowed;
+	}
+	.vue-switcher--bold div {
+	  top: 0px;
+	  height: 26px;
+	  width: 50px;
+	}
+	.vue-switcher--bold div:after {
+	  margin-left: -22px;
+	  top: 4px;
+	}
+	.vue-switcher--bold--unchecked div:after {
+	  left: 26px;
+	}
+	.vue-switcher--bold .vue-switcher__label span {
+	  padding-bottom: 7px;
+	  display: inline-block;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--default div {
+	  background-color: #b7b7b7;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--default div:after {
+	  background-color: #9d9d9d;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--default.vue-switcher--unchecked div {
+	  background-color: #aaa;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--default.vue-switcher--unchecked div:after {
+	  background-color: #c4c4c4;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--blue div {
+	  background-color: #77b0c8;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--blue div:after {
+	  background-color: #539bb9;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--blue.vue-switcher--unchecked div {
+	  background-color: #c0dae5;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--blue.vue-switcher--unchecked div:after {
+	  background-color: #77b0c8;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--red div {
+	  background-color: #c87777;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--red div:after {
+	  background-color: #b95353;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--red.vue-switcher--unchecked div {
+	  background-color: #e5c0c0;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--red.vue-switcher--unchecked div:after {
+	  background-color: #c87777;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--yellow div {
+	  background-color: #c9c377;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--yellow div:after {
+	  background-color: #bab353;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--yellow.vue-switcher--unchecked div {
+	  background-color: #e6e3c0;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--yellow.vue-switcher--unchecked div:after {
+	  background-color: #c9c377;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--orange div {
+	  background-color: #c89577;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--orange div:after {
+	  background-color: #b97953;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--orange.vue-switcher--unchecked div {
+	  background-color: #e5cec0;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--orange.vue-switcher--unchecked div:after {
+	  background-color: #c89577;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--green div {
+	  background-color: #77c88d;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--green div:after {
+	  background-color: #53b96e;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--green.vue-switcher--unchecked div {
+	  background-color: #c0e5ca;
+	}
+	.vue-switcher-theme--default.vue-switcher-color--green.vue-switcher--unchecked div:after {
+	  background-color: #77c88d;
+	}
+</style>
+<!--<style lang="scss">
+    /**
+     * Default
+     */
+    $color-default-default: #aaa;
+    $color-default-green: #53b96e;
+    $color-default-blue: #539bb9;
+    $color-default-red: #b95353;
+    $color-default-orange: #b97953;
+    $color-default-yellow: #bab353;
+    $switch-width: 100px;
+    $switch-height: 26px;
+
+    $theme-default-colors: (
+        default : $color-default-default,
+        blue    : $color-default-blue,
+        red     : $color-default-red,
+        yellow  : $color-default-yellow,
+        orange  : $color-default-orange,
+        green   : $color-default-green
+    );
+
+    .vue-switcher {
+        position: relative;
+        display: inline-block;
+        width: $switch-width;
+        height: $switch-height;
+        line-height: $switch-height;
+        margin-bottom: 0px;
+        top: 7px;
+
+        &__label {
+            position: absolute;
+            left: $switch-width * 0.6;
+            top: 0px;
+            height: $switch-height;
+            line-height: $switch-height;
+            display: block;
+            font-size: 10px;
+            margin-bottom: 0px;
+        }
+
+        input {
+            position: absolute;
+            left: 0px;
+            top: 0px;
+            width: $switch-width * 0.6;
+            height: $switch-height;
+            line-height: $switch-height;
+            opacity: 0;
+            z-index: 1;
+            cursor: pointer;
+            margin: 0px !important;
+        }
+
+        div {
+            position: absolute;
+            left: 0px;
+            top: ($switch-height - 10) * 0.5;
+            width: $switch-width * 0.4;
+            height: 10px;
+
+            border-radius: 30px;
+            display: -webkit-flex;
+            display: -ms-flex;
+            display: flex;
+            align-items: center;
+            justify-content: flex-start;
+            cursor: pointer;
+
+            &:after {
+                position: absolute;
+                top: (10 - 18) * 0.5;
+                left: 100%;
+                content: '';
+                height: 18px;
+                width: 18px;
+                border-radius: 100px;
+                display: block;
+                transition: all ease .3s;
+                margin-left: -17px;
+                cursor: pointer;
+            }
+        }
+
+        &--unchecked {
+            div {
+                justify-content: flex-end;
+
+                &:after {
+                    left: 15px;
+                }
+            }
+        }
+
+        &--disabled {
+            div {
+                opacity: .3;
+            }
+
+            input {
+                cursor: not-allowed;
+            }
+        }
+
+        &--bold {
+            div {
+                top: ($switch-height - 26) * 0.5;
+                height: $switch-height;
+                width: 51px;
+
+                &:after {
+                    margin-left: -22px;
+                    top: 4px;
+                }
+            }
+
+            &--unchecked {
+                div {
+                    &:after {
+                        left: 26px;
+                    }
+                }
+            }
+
+            .vue-switcher__label {
+                span {
+                    padding-bottom: 7px;
+                    display: inline-block;
+                }
+            }
+        }
+
+        &-theme--default {
+            @each $colorName, $color in $theme-default-colors {
+                &.vue-switcher-color--#{$colorName} {
+
+                    div {
+                        @if $colorName == 'default' {
+                            background-color: lighten($color, 5%);
+                        } @else {
+                            background-color: lighten($color, 10%);
+                        }
+
+                        &:after {
+                            @if $colorName == 'default' {
+                                background-color: darken($color, 5%);
+                            } @else {
+                                background-color: $color
+                            }
+                        }
+                    }
+
+                    &.vue-switcher--unchecked {
+                        div {
+
+                            @if $colorName == 'default' {
+                                background-color: $color;
+                            } @else {
+                                background-color: lighten($color, 30%);
+                            }
+
+                            &:after {
+                                background-color: lighten($color, 10%);
+                            }
+                        }
+                    }
+
+                }
+            }
+        }
+    }
+</style>-->

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott