Selaa lähdekoodia

v2.0新需求

zhangjie 4 vuotta sitten
vanhempi
commit
f006bd2609

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "msyj-client",
-  "version": "0.1.0",
+  "version": "2.0.0",
   "private": true,
   "description": "scan client",
   "author": "chulinice",

+ 9 - 136
src/assets/styles/common-component.less

@@ -32,7 +32,7 @@
     cursor: pointer;
 
     &:hover {
-      color: @error-color;
+      color: @pink-color;
     }
   }
   &-body {
@@ -46,7 +46,10 @@
   }
   &-imgs {
     position: absolute;
+    top: 0;
+    left: 50%;
     width: 600px;
+    margin-left: -300px;
     // box-shadow: 0px 24px 36px 0px rgba(0, 0, 0, 0.3);
     transition: width, height, transform 0.2s linear;
     z-index: 8;
@@ -96,7 +99,7 @@
     transform: translateX(-50%);
     padding: 10px;
     font-size: 30px;
-    color: #333;
+    color: @fontSub;
     z-index: 99;
     li {
       display: inline-block;
@@ -124,10 +127,10 @@
     position: absolute;
     width: 60px;
     height: 60px;
-    top: 50%;
+    top: 0;
     left: 50%;
-    margin: -30px 0 0 -30px;
-    color: @main-color;
+    margin: 0 0 0 -30px;
+    color: @fontSub;
     font-size: 50px;
     text-align: center;
     line-height: 60px;
@@ -159,7 +162,7 @@
 
   // view-ui
   .ivu-modal-content {
-    background: transparent !important;
+    background: transparent;
   }
   .ivu-modal-header {
     display: none;
@@ -174,133 +177,3 @@
     background: rgba(55, 55, 55, 0.8);
   }
 }
-
-// image-edit-upload
-.image-edit-upload {
-  position: relative;
-  height: 400px;
-  background: #fff;
-  padding-bottom: 30px;
-  box-sizing: content-box;
-}
-.cc-edit-upload {
-  &-main {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    top: 0;
-    left: 0;
-    z-index: 8;
-
-    .el-upload-dragger {
-      height: auto;
-    }
-    .el-upload-input {
-      display: none;
-    }
-  }
-  &-cover {
-    height: 400px;
-    > img {
-      display: block;
-      height: 100%;
-      width: auto;
-      margin: 0 auto;
-    }
-  }
-  &-dcover {
-    padding: 109px 0;
-    > i {
-      font-size: 160px;
-      color: #999;
-    }
-  }
-  &-edit {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    top: 0;
-    left: 0;
-    z-index: 9;
-  }
-
-  &-edit-box {
-    height: 350px;
-    position: relative;
-    .img-progress {
-      position: absolute;
-      height: 10px;
-      width: 100%;
-      bottom: 0;
-      left: 0;
-      z-index: 7;
-    }
-  }
-  &-edit-btns {
-    height: 50px;
-    padding-top: 10px;
-    text-align: center;
-    button {
-      margin: 0 10px;
-    }
-  }
-  &-tips {
-    position: absolute;
-    top: 405px;
-    left: 0;
-    font-size: 14px;
-    z-index: 9;
-  }
-}
-
-// import-file
-.cc-import-file {
-  &-tips {
-    line-height: 20px;
-  }
-  &-body {
-    color: @dark-color-light;
-  }
-  &-footer {
-    padding: 10px 0;
-    font-size: 14px;
-    color: @dark-color-light;
-    a {
-      color: @main-color;
-    }
-    a:hover {
-      color: @info-color;
-    }
-  }
-}
-
-// upload-button
-.cc-upload-button {
-  display: inline-block;
-  vertical-align: middle;
-  .ivu-upload {
-    display: inline-block;
-  }
-  > p[class^="cc-tips"] {
-    margin-top: 10px;
-  }
-}
-
-// rich-editor
-.cc-editor {
-  .ql-editor {
-    max-height: 500px !important;
-    height: 300px;
-    overflow: auto;
-  }
-}
-
-// tips
-.cc-tips {
-  &-success {
-    color: @main-color;
-  }
-  &-error {
-    color: @error-color;
-  }
-}

+ 13 - 4
src/assets/styles/pages.less

@@ -604,10 +604,19 @@
           }
         }
       }
-      &-body img {
-        display: block;
-        width: 100%;
-        height: auto;
+      &-body {
+        overflow: hidden;
+        img {
+          display: block;
+          width: 100%;
+          height: auto;
+          cursor: pointer;
+          transition: transform 0.2s;
+
+          &:hover {
+            transform: scale(1.1, 1.1);
+          }
+        }
       }
     }
   }

+ 223 - 0
src/components/SimpleImagePreview.vue

@@ -0,0 +1,223 @@
+<template>
+  <Modal
+    :class="prefixCls"
+    v-model="modalIsShow"
+    title="图片预览"
+    fullscreen
+    footer-hide
+    :mask-closable="false"
+  >
+    <div slot="header"></div>
+    <div :class="[`${prefixCls}-close`]" @click="cancel">
+      <i class="el-icon-circle-close"></i>
+      <Icon type="ios-close" />
+    </div>
+
+    <div :class="[`${prefixCls}-body`]" ref="ReviewBody">
+      <div
+        :class="[`${prefixCls}-guide`, `${prefixCls}-guide-prev`]"
+        @click.stop="showPrev"
+      >
+        <Icon type="ios-arrow-back" />
+      </div>
+      <div
+        :class="[`${prefixCls}-guide`, `${prefixCls}-guide-next`]"
+        @click.stop="showNext"
+      >
+        <Icon type="ios-arrow-forward" />
+      </div>
+      <div
+        :class="[
+          `${prefixCls}-imgs`,
+          { [`${prefixCls}-imgs-nosition`]: nosition }
+        ]"
+        :style="styles"
+        v-if="modalIsShow"
+        @click="cancel"
+      >
+        <img
+          :key="curImage.id"
+          :src="curImage.url"
+          :alt="curImage.studentName"
+          ref="PreviewImgDetail"
+          @load="reizeImage"
+        />
+      </div>
+      <div :class="[`${prefixCls}-none`]" v-if="!curImage.url">
+        <Icon type="md-image" />
+        <p>暂无数据</p>
+      </div>
+
+      <div :class="[`${prefixCls}-loading`]" v-show="loading">
+        <Icon class="ivu-load-loop" type="ios-loading" />
+      </div>
+    </div>
+
+    <div :class="[`${prefixCls}-footer`]">
+      <ul>
+        <li title="旋转" @click.stop="toRotate">
+          <Icon type="ios-refresh-circle" />
+        </li>
+      </ul>
+    </div>
+  </Modal>
+</template>
+
+<script>
+const prefixCls = "cc-image-preview";
+
+export default {
+  name: "simple-image-preview",
+  props: {
+    curImage: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      prefixCls,
+      modalIsShow: false,
+      styles: { width: "", height: "", top: "", left: "", transform: "" },
+      initWidth: 500,
+      transform: {
+        scale: 1,
+        rotate: 0
+      },
+      loading: false,
+      loadingSetT: null,
+      nosition: false
+    };
+  },
+  methods: {
+    reizeImage() {
+      if (this.loadingSetT) clearTimeout(this.loadingSetT);
+
+      const imgDom = this.$refs.PreviewImgDetail;
+      const { naturalWidth, naturalHeight } = imgDom;
+      const imageSize = this.getImageSizePos({
+        win: {
+          width: this.$refs.ReviewBody.clientWidth,
+          height: this.$refs.ReviewBody.clientHeight
+        },
+        img: {
+          width: naturalWidth,
+          height: naturalHeight
+        },
+        rotate: 0
+      });
+
+      this.styles = Object.assign(this.styles, {
+        width: imageSize.width + "px",
+        height: imageSize.height + "px",
+        top: imageSize.top + "px",
+        left: imageSize.left + "px",
+        marginLeft: "auto",
+        transform: "none"
+      });
+      this.transform = {
+        scale: 1,
+        rotate: 0
+      };
+      this.loading = false;
+      setTimeout(() => {
+        this.nosition = false;
+      }, 100);
+    },
+    getImageSizePos({ win, img, rotate }) {
+      const imageSize = {
+        width: 0,
+        height: 0,
+        top: 0,
+        left: 0
+      };
+      const isHorizontal = !!(rotate % 180);
+
+      const rateWin = isHorizontal
+        ? win.height / win.width
+        : win.width / win.height;
+      const hwin = isHorizontal
+        ? {
+            width: win.height,
+            height: win.width
+          }
+        : win;
+
+      const rateImg = img.width / img.height;
+
+      if (rateImg <= rateWin) {
+        imageSize.height = Math.min(hwin.height, img.height);
+        imageSize.width = Math.floor(
+          (imageSize.height * img.width) / img.height
+        );
+      } else {
+        imageSize.width = Math.min(hwin.width, img.width);
+        imageSize.height = Math.floor(
+          (imageSize.width * img.height) / img.width
+        );
+      }
+      imageSize.left = (win.width - imageSize.width) / 2;
+      imageSize.top = (win.height - imageSize.height) / 2;
+      return imageSize;
+    },
+    cancel() {
+      this.modalIsShow = false;
+      this.$emit("on-close");
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    showPrev() {
+      this.$emit("on-prev");
+      // this.initData();
+    },
+    showNext() {
+      this.$emit("on-next");
+      // this.initData();
+    },
+    // dome-move
+    setStyleTransform() {
+      const { scale, rotate } = this.transform;
+      this.styles.transform = `scale(${scale}, ${scale}) rotate(${rotate}deg)`;
+    },
+    toRotate() {
+      this.transform.rotate = this.transform.rotate + 90;
+      this.setStyleTransform();
+      // 调整图片尺寸
+      const { naturalWidth, naturalHeight } = this.$refs.PreviewImgDetail;
+      const imageSize = this.getImageSizePos({
+        win: {
+          width: this.$refs.ReviewBody.clientWidth,
+          height: this.$refs.ReviewBody.clientHeight
+        },
+        img: {
+          width: naturalWidth,
+          height: naturalHeight
+        },
+        rotate: this.transform.rotate
+      });
+
+      this.styles = Object.assign(this.styles, {
+        width: imageSize.width + "px",
+        height: imageSize.height + "px",
+        top: imageSize.top + "px",
+        left: imageSize.left + "px"
+      });
+      // 360度无缝切换到0度
+      if (this.transform.rotate >= 360) {
+        setTimeout(() => {
+          this.nosition = true;
+          this.transform.rotate = 0;
+          this.setStyleTransform();
+          setTimeout(() => {
+            this.nosition = false;
+          }, 100);
+        }, 200);
+        // 200ms当次旋转动画持续时间
+      }
+    }
+  }
+};
+</script>

+ 0 - 307
src/components/common/ImagePreview/ImagePreview.vue

@@ -1,307 +0,0 @@
-<template>
-  <Modal
-    :class="prefixCls"
-    v-model="modalIsShow"
-    title="图片预览"
-    :mask-closable="false"
-    fullscreen
-    footer-hide
-    @on-visible-change="visibleChange"
-  >
-    <div slot="header"></div>
-    <div :class="[`${prefixCls}-close`]" @click="modalIsShow = false">
-      <i class="el-icon-circle-close"></i>
-      <Icon type="ios-close" />
-    </div>
-    <div :class="[`${prefixCls}-header`]" v-if="!headerHide">
-      <h3>{{ curFile.name }}</h3>
-      <div :class="[`${prefixCls}-index`]">
-        {{ this.curIndex + 1 }} / {{ this.lastIndex }}
-      </div>
-    </div>
-    <div :class="[`${prefixCls}-body`]" ref="ReviewBody">
-      <div
-        :class="[`${prefixCls}-guide`, `${prefixCls}-guide-prev`]"
-        @click="showPrev"
-      >
-        <Icon type="ios-arrow-back" />
-      </div>
-      <div
-        :class="[`${prefixCls}-guide`, `${prefixCls}-guide-next`]"
-        @click="showNext"
-      >
-        <Icon type="ios-arrow-forward" />
-      </div>
-      <div
-        :class="[
-          `${prefixCls}-imgs`,
-          { [`${prefixCls}-imgs-nosition`]: nosition }
-        ]"
-        :style="styles"
-        v-move-ele.prevent="{ mouseMove }"
-      >
-        <img :src="curFile.url" :alt="curFile.name" ref="PreviewImgDetail" />
-      </div>
-    </div>
-    <div :class="[`${prefixCls}-footer`]">
-      <ul>
-        <li title="合适大小" @click="toOrigin">
-          <Icon type="md-expand" />
-        </li>
-        <li
-          title="放大"
-          @click="toMagnify"
-          :class="{
-            'li-disabled': transform.scale === maxScale
-          }"
-        >
-          <Icon type="md-add-circle" />
-        </li>
-        <li
-          title="缩小"
-          @click="toShrink"
-          :class="{
-            'li-disabled': transform.scale === minScale
-          }"
-        >
-          <Icon type="md-remove-circle" />
-        </li>
-        <li title="旋转" @click="toRotate">
-          <Icon type="ios-refresh-circle" />
-        </li>
-      </ul>
-    </div>
-  </Modal>
-</template>
-
-<script>
-import MoveEle from "./move-ele";
-
-const prefixCls = "cc-image-preview";
-
-export default {
-  name: "image-preview",
-  props: {
-    imageList: {
-      type: Array,
-      default() {
-        return [];
-      }
-    },
-    initIndex: {
-      type: Number,
-      default: 0
-    },
-    headerHide: {
-      type: Boolean,
-      default: false
-    }
-  },
-  directives: {
-    MoveEle
-  },
-  data() {
-    return {
-      prefixCls,
-      modalIsShow: false,
-      curFile: { name: "", url: null },
-      curIndex: 0,
-      minScale: 0.2,
-      maxScale: 3,
-      styles: { width: "", height: "", top: "", left: "", transform: "" },
-      initWidth: 500,
-      transform: {
-        scale: 1,
-        rotate: 0
-      },
-      nosition: false
-    };
-  },
-  computed: {
-    isFirst() {
-      return this.curIndex === 0;
-    },
-    isLast() {
-      return this.lastIndex - 1 === this.curIndex;
-    },
-    lastIndex() {
-      return this.imageList.length;
-    }
-  },
-  // watch: {
-  //   curIndex: {
-  //     immediate: true,
-  //     handler(val, oldVal) {
-  //       this.curFileChange(val);
-  //     }
-  //   }
-  // },
-  mounted() {
-    this.registfileLoad();
-  },
-  methods: {
-    visibleChange(visible) {
-      if (!visible) return;
-      if (!this.imageList.length) return;
-      this.curIndex = this.initIndex;
-      this.curFileChange(this.curIndex);
-    },
-    curFileChange(val) {
-      const index = val || 0;
-      this.nosition = true;
-      this.curFile = this.imageList[index];
-    },
-    registfileLoad() {
-      const imgDom = this.$refs.PreviewImgDetail;
-
-      imgDom.onload = () => {
-        const { naturalWidth, naturalHeight } = imgDom;
-        const imageSize = this.getImageSizePos({
-          win: {
-            width: this.$refs.ReviewBody.clientWidth,
-            height: this.$refs.ReviewBody.clientHeight
-          },
-          img: {
-            width: naturalWidth,
-            height: naturalHeight
-          },
-          rotate: 0
-        });
-
-        this.styles = Object.assign(this.styles, {
-          width: imageSize.width + "px",
-          height: imageSize.height + "px",
-          top: imageSize.top + "px",
-          left: imageSize.left + "px",
-          transform: ""
-        });
-        this.transform = {
-          scale: 1,
-          rotate: 0
-        };
-        setTimeout(() => {
-          this.nosition = false;
-        }, 100);
-      };
-    },
-    getImageSizePos({ win, img, rotate }) {
-      const imageSize = {
-        width: 0,
-        height: 0,
-        top: 0,
-        left: 0
-      };
-      const isHorizontal = !!(rotate % 180);
-
-      const rateWin = isHorizontal
-        ? win.height / win.width
-        : win.width / win.height;
-      const hwin = isHorizontal
-        ? {
-            width: win.height,
-            height: win.width
-          }
-        : win;
-
-      const rateImg = img.width / img.height;
-
-      if (rateImg <= rateWin) {
-        imageSize.height = Math.min(hwin.height, img.height);
-        imageSize.width = Math.floor(
-          (imageSize.height * img.width) / img.height
-        );
-      } else {
-        imageSize.width = Math.min(hwin.width, img.width);
-        imageSize.height = Math.floor(
-          (imageSize.width * img.height) / img.width
-        );
-      }
-      imageSize.left = (win.width - imageSize.width) / 2;
-      imageSize.top = (win.height - imageSize.height) / 2;
-      return imageSize;
-    },
-    cancel() {
-      this.modalIsShow = false;
-    },
-    open() {
-      this.modalIsShow = true;
-    },
-    showPrev() {
-      if (this.isFirst) {
-        this.curIndex = this.lastIndex - 1;
-      } else {
-        this.curIndex -= 1;
-      }
-      this.$emit("on-prev", this.curIndex);
-      this.curFileChange(this.curIndex);
-    },
-    showNext() {
-      if (this.isLast) {
-        this.curIndex = 0;
-      } else {
-        this.curIndex += 1;
-      }
-      this.$emit("on-next", this.curIndex);
-      this.curFileChange(this.curIndex);
-    },
-    // dome-move
-    mouseMove({ left, top }) {
-      this.styles.left = left + "px";
-      this.styles.top = top + "px";
-    },
-    setStyleTransform() {
-      const { scale, rotate } = this.transform;
-      this.styles.transform = `scale(${scale}, ${scale}) rotate(${rotate}deg)`;
-    },
-    toOrigin() {
-      this.transform.scale = 1;
-      this.setStyleTransform();
-    },
-    toMagnify() {
-      const scale = (this.transform.scale * 1.2).toFixed(2);
-      this.transform.scale = scale >= this.maxScale ? this.maxScale : scale;
-      this.setStyleTransform();
-    },
-    toShrink() {
-      const scale = (this.transform.scale * 0.75).toFixed(2);
-      this.transform.scale = scale <= this.minScale ? this.minScale : scale;
-      this.setStyleTransform();
-    },
-    toRotate() {
-      this.transform.rotate = this.transform.rotate + 90;
-      this.setStyleTransform();
-      // 调整图片尺寸
-      const { naturalWidth, naturalHeight } = this.$refs.PreviewImgDetail;
-      const imageSize = this.getImageSizePos({
-        win: {
-          width: this.$refs.ReviewBody.clientWidth,
-          height: this.$refs.ReviewBody.clientHeight
-        },
-        img: {
-          width: naturalWidth,
-          height: naturalHeight
-        },
-        rotate: this.transform.rotate
-      });
-
-      this.styles = Object.assign(this.styles, {
-        width: imageSize.width + "px",
-        height: imageSize.height + "px",
-        top: imageSize.top + "px",
-        left: imageSize.left + "px"
-      });
-      // 360度无缝切换到0度
-      setTimeout(() => {
-        if (this.transform.rotate >= 360) {
-          this.nosition = true;
-          this.transform.rotate = 0;
-          this.setStyleTransform();
-          setTimeout(() => {
-            this.nosition = false;
-          }, 100);
-        }
-      }, 200);
-    }
-  }
-};
-</script>

+ 0 - 2
src/components/common/ImagePreview/index.js

@@ -1,2 +0,0 @@
-import ImagePreview from "./ImagePreview.vue";
-export default ImagePreview;

+ 0 - 51
src/components/common/ImagePreview/move-ele.js

@@ -1,51 +0,0 @@
-export default {
-  inserted(el, { value, modifiers }, vnode) {
-    let [_x, _y] = [0, 0];
-    // 当前拖动事务开始前元素的left,top
-    let [oleft, otop] = [0, 0];
-    // 元素移动后的left,top
-    let [left, top] = [0, 0];
-
-    let moveHandle = function(e) {
-      if (modifiers.prevent) {
-        e.preventDefault();
-      }
-      left = oleft + e.pageX - _x;
-      top = otop + e.pageY - _y;
-
-      if (value && value.mouseMove) {
-        value.mouseMove({ left, top });
-      } else {
-        el.style.left = left + "px";
-        el.style.top = top + "px";
-      }
-    };
-
-    let upHandle = function(e) {
-      if (modifiers.prevent) {
-        e.preventDefault();
-      }
-      oleft = left;
-      otop = top;
-
-      if (value && value.mouseUp) value.mouseUp({ oleft, otop });
-
-      document.removeEventListener("mousemove", moveHandle);
-      document.removeEventListener("mouseup", upHandle);
-    };
-
-    el.addEventListener("mousedown", function(e) {
-      if (modifiers.prevent) {
-        e.preventDefault();
-      }
-      _x = e.pageX;
-      _y = e.pageY;
-      oleft = el.offsetLeft;
-      otop = el.offsetTop;
-      if (value && value.mouseDown) value.mouseDown({ oleft, otop });
-
-      document.addEventListener("mousemove", moveHandle);
-      document.addEventListener("mouseup", upHandle);
-    });
-  }
-};

+ 3 - 0
src/mixins/initStoreMixin.js

@@ -2,6 +2,9 @@ import db from "../plugins/db";
 
 export default {
   methods: {
+    // initDb(name) {
+    //   db.init(name);
+    // },
     getLastLoginUser() {
       return db.getDict("lastLoginUser", "");
     },

+ 15 - 4
src/modules/client/components/ScanAreaSteps.vue

@@ -40,7 +40,8 @@
 import CodeArea from "./steps/CodeArea";
 import CoverArea from "./steps/CoverArea";
 import ImageOrientation from "./steps/ImageOrientation";
-import TailorArea from "./steps/TailorArea";
+import OriginTailorArea from "./steps/OriginTailorArea";
+import TailorTailorArea from "./steps/TailorTailorArea";
 import { deepCopy } from "../../../plugins/utils";
 
 const STEPS_LIST = [
@@ -53,8 +54,12 @@ const STEPS_LIST = [
     title: "信息覆盖区域设置"
   },
   {
-    name: "tailor-area",
-    title: "切图设置"
+    name: "origin-tailor-area",
+    title: "原图切图设置"
+  },
+  {
+    name: "tailor-tailor-area",
+    title: "裁切图切图设置"
   },
   {
     name: "image-orientation",
@@ -64,7 +69,13 @@ const STEPS_LIST = [
 
 export default {
   name: "scan-area-steps",
-  components: { CodeArea, CoverArea, ImageOrientation, TailorArea },
+  components: {
+    CodeArea,
+    CoverArea,
+    ImageOrientation,
+    OriginTailorArea,
+    TailorTailorArea
+  },
   props: {
     imageUrl: {
       type: String,

+ 4 - 4
src/modules/client/components/steps/TailorArea.vue → src/modules/client/components/steps/OriginTailorArea.vue

@@ -35,7 +35,7 @@ export default {
     initCropper() {
       const _this = this;
       const defTailorArea =
-        (this.curSetting && this.curSetting.tailorArea) || {};
+        (this.curSetting && this.curSetting.originTailorArea) || {};
 
       this.cropper = new Cropper(this.$refs.editImage, {
         viewMode: 1,
@@ -50,13 +50,13 @@ export default {
       });
     },
     checkValid() {
-      const tailorArea = {
+      const originTailorArea = {
         ...this.cropper.getData()
       };
-      this.$emit("on-next", { tailorArea });
+      this.$emit("on-next", { originTailorArea });
     },
     pass() {
-      this.$emit("on-next", { tailorArea: {} });
+      this.$emit("on-next", { originTailorArea: {} });
     }
   },
   beforeDestroy() {

+ 72 - 0
src/modules/client/components/steps/TailorTailorArea.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="tailor-area">
+    <div class="code-area-cont">
+      <img :src="imageUrl" ref="editImage" />
+    </div>
+  </div>
+</template>
+
+<script>
+import Cropper from "cropperjs";
+
+export default {
+  name: "tailor-area",
+  props: {
+    imageUrl: {
+      type: String,
+      require: true
+    },
+    curSetting: {
+      type: Object,
+      default() {
+        return {};
+      }
+    }
+  },
+  data() {
+    return {
+      cropper: ""
+    };
+  },
+  mounted() {
+    this.initCropper();
+  },
+  methods: {
+    initCropper() {
+      const _this = this;
+      const defTailorArea =
+        (this.curSetting &&
+          this.curSetting.tailorTailorArea &&
+          this.curSetting.originTailorArea) ||
+        {};
+
+      this.cropper = new Cropper(this.$refs.editImage, {
+        viewMode: 1,
+        checkCrossOrigin: false,
+        zoomable: false,
+        minCropBoxWidth: 10,
+        minCropBoxHeight: 10,
+        ready() {
+          _this.cropper.setData(defTailorArea);
+          _this.$emit("on-ready");
+        }
+      });
+    },
+    checkValid() {
+      const tailorTailorArea = {
+        ...this.cropper.getData()
+      };
+      this.$emit("on-next", { tailorTailorArea });
+    },
+    pass() {
+      this.$emit("on-next", { tailorTailorArea: {} });
+    }
+  },
+  beforeDestroy() {
+    if (this.cropper) {
+      this.cropper.destroy();
+      this.cropper = false;
+    }
+  }
+};
+</script>

+ 32 - 13
src/modules/client/views/GroupScan.vue

@@ -68,7 +68,7 @@
         v-for="(task, tindex) in historyList"
         :key="tindex"
       >
-        <div class="history-item-body">
+        <div class="history-item-body" @click="toViewPaper(task)">
           <img :src="task.formalImgPath" :alt="task.name" />
         </div>
         <p class="history-item-title">
@@ -94,6 +94,13 @@
       @confirm="examNumberValid"
       ref="ScanExceptionDialog"
     ></scan-exception-dialog>
+    <!-- image-preview -->
+    <simple-image-preview
+      :cur-image="curPaper"
+      @on-prev="toPrevPaper"
+      @on-next="toNextPaper"
+      ref="SimpleImagePreview"
+    ></simple-image-preview>
   </div>
 </template>
 
@@ -107,6 +114,7 @@ import {
 import { deepCopy } from "../../../plugins/utils";
 import ScanAreaDialog from "../components/ScanAreaDialog";
 import ScanExceptionDialog from "../components/ScanExceptionDialog";
+import SimpleImagePreview from "@/components/SimpleImagePreview.vue";
 import { mapState, mapActions } from "vuex";
 import log4js from "@/plugins/logger";
 const logger = log4js.getLogger("scan");
@@ -116,7 +124,7 @@ const { ipcRenderer } = require("electron");
 
 export default {
   name: "group-scan",
-  components: { ScanAreaDialog, ScanExceptionDialog },
+  components: { ScanAreaDialog, ScanExceptionDialog, SimpleImagePreview },
   data() {
     return {
       isWaiting: true,
@@ -136,6 +144,7 @@ export default {
         url: "",
         name: ""
       },
+      curPaper: {},
       // encrypt-process
       canScan: false,
       exceptionIsConfirm: false,
@@ -169,9 +178,6 @@ export default {
     taskCount() {
       return this.students.length;
     },
-    isFinished() {
-      return this.taskCount && this.curTaskScanCount === this.taskCount;
-    },
     curSubjectStartCountTime() {
       return this.startCountTime[this.curSubject.id];
     },
@@ -221,7 +227,7 @@ export default {
           scaleX: 1,
           scaleY: 1
         },
-        tailorArea: {
+        originTailorArea: {
           x: 518.4,
           y: 345.6,
           width: 4147.2,
@@ -243,12 +249,6 @@ export default {
       console.log(examNumber);
     },
     getInitFile() {
-      if (this.isFinished) {
-        this.isWaiting = true;
-        if (this.setT) clearTimeout(this.setT);
-        return;
-      }
-
       if (!this.canScan) return;
       this.curImage = getEarliestFile(this.GLOBAL.input);
 
@@ -490,7 +490,7 @@ export default {
       } else {
         this.historyList.splice(historyIndex, 1);
       }
-      this.historyList.unshift(student);
+      this.historyList.unshift({ ...student, url: student.formalImgPath });
     },
     // scan-exception
     resetConfig() {
@@ -514,6 +514,25 @@ export default {
         }
       });
     },
+    // image-preview
+    toViewPaper(paper) {
+      this.curPaper = paper;
+      this.$refs.SimpleImagePreview.open();
+    },
+    toPrevPaper() {
+      const curPaperIndex = this.historyList.findIndex(
+        item => item.id === this.curPaper.id
+      );
+      if (curPaperIndex <= 0) return;
+      this.curPaper = this.historyList[curPaperIndex - 1];
+    },
+    toNextPaper() {
+      const curPaperIndex = this.historyList.findIndex(
+        item => item.id === this.curPaper.id
+      );
+      if (curPaperIndex >= this.historyList.length - 1) return;
+      this.curPaper = this.historyList[curPaperIndex + 1];
+    },
     // encrypt-process
     encryptSuccess() {
       if (!this.exceptionIsConfirm) return;

+ 37 - 6
src/modules/client/views/LineScan.vue

@@ -36,7 +36,7 @@
     </div>
     <div class="scan-history">
       <div class="history-item" v-for="task in historyList" :key="task.id">
-        <div class="history-item-body">
+        <div class="history-item-body" @click="toViewPaper(task)">
           <img :src="task.formalImgPath" :alt="task.name" />
         </div>
         <p class="history-item-title">
@@ -67,6 +67,13 @@
       @confirm="examNumberValid"
       ref="ScanExceptionDialog"
     ></scan-exception-dialog>
+    <!-- image-preview -->
+    <simple-image-preview
+      :cur-image="curPaper"
+      @on-prev="toPrevPaper"
+      @on-next="toNextPaper"
+      ref="SimpleImagePreview"
+    ></simple-image-preview>
   </div>
 </template>
 
@@ -81,6 +88,7 @@ import {
 import { deepCopy } from "../../../plugins/utils";
 import ScanAreaDialog from "../components/ScanAreaDialog";
 import ScanExceptionDialog from "../components/ScanExceptionDialog";
+import SimpleImagePreview from "@/components/SimpleImagePreview.vue";
 import { mapState, mapMutations, mapActions } from "vuex";
 import log4js from "@/plugins/logger";
 const logger = log4js.getLogger("scan");
@@ -90,7 +98,7 @@ const { ipcRenderer } = require("electron");
 
 export default {
   name: "line-scan",
-  components: { ScanAreaDialog, ScanExceptionDialog },
+  components: { ScanAreaDialog, ScanExceptionDialog, SimpleImagePreview },
   data() {
     return {
       isWaiting: true,
@@ -109,6 +117,7 @@ export default {
         url: "",
         name: ""
       },
+      curPaper: {},
       // encrypt-process
       canScan: false,
       exceptionIsConfirm: false, // 异常是否被确认
@@ -176,7 +185,7 @@ export default {
           scaleX: 1,
           scaleY: 1
         },
-        tailorArea: {
+        originTailorArea: {
           x: 518.4,
           y: 345.6,
           width: 4147.2,
@@ -202,7 +211,10 @@ export default {
         this.curSubjectStartCountTime,
         this.curSubject.id
       );
-      this.historyList = dataList;
+      this.historyList = dataList.map(item => {
+        item.url = item.formalImgPath;
+        return item;
+      });
     },
     getInitFile() {
       if (!this.canScan) return;
@@ -309,7 +321,7 @@ export default {
         this.getCurCollectConfig()
       ).catch(error => {
         const content = `${this.curStudent.name}的试卷保存失败,请重新扫描!`;
-        logger.errro(`04保存试卷失败:${this.curStudent.name}`);
+        logger.error(`04保存试卷失败:${this.curStudent.name}`);
         this.$Notice.error({ title: "错误提示", desc: content, duration: 0 });
       });
 
@@ -388,7 +400,7 @@ export default {
       } else {
         this.historyList.splice(historyIndex, 1);
       }
-      this.historyList.unshift(student);
+      this.historyList.unshift({ ...student, url: student.formalImgPath });
     },
     // scan-exception
     resetConfig() {
@@ -412,6 +424,25 @@ export default {
         }
       });
     },
+    // image-preview
+    toViewPaper(paper) {
+      this.curPaper = paper;
+      this.$refs.SimpleImagePreview.open();
+    },
+    toPrevPaper() {
+      const curPaperIndex = this.historyList.findIndex(
+        item => item.id === this.curPaper.id
+      );
+      if (curPaperIndex <= 0) return;
+      this.curPaper = this.historyList[curPaperIndex - 1];
+    },
+    toNextPaper() {
+      const curPaperIndex = this.historyList.findIndex(
+        item => item.id === this.curPaper.id
+      );
+      if (curPaperIndex >= this.historyList.length - 1) return;
+      this.curPaper = this.historyList[curPaperIndex + 1];
+    },
     // encrypt-process
     encryptSuccess() {
       // 只要异常没有被手动确认,则不会自动激活采集。

+ 25 - 1
src/modules/manage/views/PaperExport.vue

@@ -92,6 +92,28 @@
             clearable
           ></InputNumber>
         </FormItem>
+        <FormItem label="分数:">
+          <Input
+            v-model="scoreFilter.scores"
+            style="width: 600px;"
+            placeholder="多个分数请用英文逗号隔开"
+            :disabled="taskRunning"
+            clearable
+          ></Input>
+        </FormItem>
+        <FormItem label="每个分数:">
+          <InputNumber
+            v-model="scoreFilter.countPerScore"
+            :min="1"
+            :max="1000000"
+            :precision="0"
+            :active-change="false"
+            style="width: 120px;margin-right: 5px;"
+            :disabled="taskRunning"
+            clearable
+          ></InputNumber>
+          张
+        </FormItem>
         <FormItem label="本地保存路径:">
           <Input
             v-model="scoreFilter.outputDir"
@@ -227,7 +249,9 @@ export default {
         startScore: null,
         endScore: null,
         outputDir: "",
-        savePathRule: ""
+        savePathRule: "",
+        scores: "",
+        countPerScore: 1
       },
       exams: [],
       IMAGE_TYPE,

+ 2 - 2
src/plugins/db.js

@@ -8,11 +8,11 @@ let db = undefined;
 
 init();
 
-function init() {
+function init(sqlName = "client") {
   if (db) return;
   const databasePath = getDatabaseDir();
   var orgDb = path.join(databasePath, "org.rdb");
-  var clientDb = path.join(databasePath, "client.rdb");
+  var clientDb = path.join(databasePath, sqlName + ".rdb");
 
   if (!fs.existsSync(clientDb)) {
     fs.copyFileSync(orgDb, clientDb);

+ 33 - 11
src/plugins/imageOcr.js

@@ -76,9 +76,13 @@ function decodeImageCode(imgPath, codeArea) {
  */
 async function saveOutputImage(imgPath, paperInfo, collectConfig) {
   const outputOriginPath = saveOriginImage(imgPath, paperInfo);
-  const outputFormalPath = await saveFormalImage(imgPath, paperInfo);
+  const outputFormalPath = await saveFormalImage(
+    imgPath,
+    paperInfo,
+    collectConfig
+  );
   const outputSlicelPath = await saveSliceImage(
-    outputFormalPath,
+    imgPath,
     paperInfo,
     collectConfig
   ).catch(() => {});
@@ -96,16 +100,23 @@ function saveOriginImage(imgPath, paperInfo) {
   return outputOriginPath;
 }
 
-function saveFormalImage(imgPath, paperInfo) {
+function saveFormalImage(imgPath, paperInfo, collectConfig) {
   const outputFormalPath = getOutputImagePath(paperInfo, "formal");
 
-  if (paperInfo.compressRate === 100) {
-    fs.copyFileSync(imgPath, outputFormalPath);
-    return Promise.resolve(outputFormalPath);
-  }
+  const { originTailorArea } = collectConfig;
 
   let imgObj = gm(imgPath);
-  imgObj.setFormat("jpeg").quality(paperInfo.compressRate);
+  // 边缘裁切
+  imgObj.crop(
+    originTailorArea.width,
+    originTailorArea.height,
+    originTailorArea.x,
+    originTailorArea.y
+  );
+  // 压缩
+  if (paperInfo.compressRate !== 100) {
+    imgObj.setFormat("jpeg").quality(paperInfo.compressRate);
+  }
 
   return new Promise((resolve, reject) => {
     imgObj.write(outputFormalPath, function(err) {
@@ -119,7 +130,7 @@ function saveFormalImage(imgPath, paperInfo) {
 
 function saveSliceImage(imgPath, paperInfo, collectConfig) {
   const outputSlicelPath = getOutputImagePath(paperInfo, "slice");
-  const { codeArea, coverArea, tailorArea, imageRotate } = collectConfig;
+  const { codeArea, coverArea, tailorTailorArea, imageRotate } = collectConfig;
 
   // slice图:完整处理流程
   let imgObj = gm(imgPath);
@@ -144,10 +155,21 @@ function saveSliceImage(imgPath, paperInfo, collectConfig) {
     );
 
   // 边缘裁切
-  imgObj.crop(tailorArea.width, tailorArea.height, tailorArea.x, tailorArea.y);
+  imgObj.crop(
+    tailorTailorArea.width,
+    tailorTailorArea.height,
+    tailorTailorArea.x,
+    tailorTailorArea.y
+  );
 
   // 旋转
   if (imageRotate) imgObj.rotate("#FFFFFF", imageRotate);
+
+  // 压缩
+  if (paperInfo.compressRate !== 100) {
+    imgObj.setFormat("jpeg").quality(paperInfo.compressRate);
+  }
+
   return new Promise((resolve, reject) => {
     imgObj.write(outputSlicelPath, function(err) {
       if (err) {
@@ -195,7 +217,7 @@ function getOutputImagePath(paperInfo, type) {
   );
 
   if (!fs.existsSync(outputDir)) makeDirSync(outputDir);
-  return path.join(outputDir, paperInfo.examNumber + ".jpg");
+  return path.join(outputDir, `${paperInfo.examNumber}-${randomCode()}.jpg`);
 }
 
 /**