wangziqian 5 år sedan
förälder
incheckning
b8f7cbd517

+ 182 - 0
src/components/handleFile/copyPaste.vue

@@ -0,0 +1,182 @@
+<template>
+  <div>
+    <el-button id="pasteUpload" type="primary" style="display: none" @click.stop="pasteUpload">upload</el-button>
+    <normal-dialog
+      :show-dialog="showCopyFile"
+      :title="'上传截图'"
+      :width="'40%'"
+      :submit-button="'上传'"
+      :top="'5vh'"
+      @confirm="confirmUpload()"
+      @cancel="showCopyFile=false"
+    >
+      <div class="file-dialog">
+        <el-form ref="imageForm" label-width="20%" :rules="imageRules" :model="imageName">
+          <el-form-item label="图片命名" prop="name">
+            <el-col style="width: 75%">
+              <el-input v-model="imageName.name" placeholder="请输入图片名称" />
+            </el-col>
+            <el-col style="width: 10%">.png</el-col>
+          </el-form-item>
+        </el-form>
+        <div class="image">
+          <div class="image-center">
+            <img :src="imageUrl" class="image-url">
+          </div>
+        </div>
+      </div>
+    </normal-dialog>
+  </div>
+</template>
+<script>
+import normalDialog from '@/components/dialog/normalDialog'
+import axios from 'axios'
+export default {
+  components: {
+    normalDialog
+  },
+  props: {
+    id: {
+      type: String,
+      default: null,
+      required: false
+    }
+  },
+  data() {
+    return {
+      imageRules: {// 文件上传规则
+        name: [
+          { required: true, message: '请输入图片名称', trigger: 'blur' },
+          { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
+        ]
+      },
+      showCopyFile: false, // 复制文件对话框
+      imageName: { name: null }, // 复制文件对象
+      imageUrl: null // 图片地址
+    }
+  },
+  created() {
+
+  },
+  mounted() {
+    this.lisenPaste()
+  },
+  methods: {
+    lisenPaste() { // 监听粘贴事件
+      let contain
+      console.log(this.id)
+      if (this.id) {
+        contain = document.getElementById(this.id)
+      } else {
+        contain = document.body
+      }
+      contain.onpaste = function(event) {
+        event.stopImmediatePropagation()
+        const data = (event.clipboardData || window.clipboardData)
+        const items = data.items
+        const fileList = [] // 存储文件数据
+        if (items && items.length) { // 检索剪切板items
+          for (let i = 0; i < items.length; i++) { // items[i].getAsFile() 想要的文件
+            fileList.push(items[i].getAsFile())
+            window.uploadFiles = fileList
+          }
+          document.getElementById('pasteUpload').click()
+        }
+      }
+    },
+    pasteUpload() { //
+      if (!window.uploadFiles[0]) return
+      const reader = new FileReader()
+      reader.readAsDataURL(window.uploadFiles[0])
+      reader.onload = () => {
+        const reg = new RegExp(/image\/png/)
+        this.imageUrl = reader.result
+        if (this.imageUrl.match(reg)) { // 判断是否是图片
+          this.showCopyFile = true
+          this.imageName.name = this.generateMixed(10)
+        }
+      }
+    },
+    generateMixed(len) { // 图片随机名字生成
+      const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+      let res = ''
+      for (let i = 0; i < len; i++) {
+        const id = Math.ceil(Math.random() * 35)
+        res += chars[id]
+      }
+      return res
+    },
+    async confirmUpload() {
+      if (this.imageName.name === null || this.imageName.name.replace(/\s+/g, '') === '') {
+        return false
+      }
+      this.showCopyFile = false
+      const res = await this.updateFile(window.uploadFiles[0])
+      const data = res.data
+      const item = {
+        name: `${this.imageName.name}.png` || `${this.generateMixed(10)}.png`,
+        status: 'success',
+        url: 'http:' + data.url
+      }
+      this.$emit('uploadFile', item)
+      this.$message({
+        showClose: true,
+        message: '文件上传成功',
+        type: 'success'
+      })
+      this.imageName.name = null
+      this.imageUrl = null
+      window.uploadFiles = null
+    },
+    updateFile(file) {
+      const param = new FormData() // 创建form对象
+      param.append('file', file)// 通过append向form对象添加数据
+      const config = {
+        headers: {
+          'Content-Type': 'multipart/form-data'
+        },
+        withCredentials: false
+      } // 添加请求头
+      return new Promise((resolve, reject) => {
+        axios.post('http://star.xiaojukeji.com/upload/img.node', param, config)
+          .then(response => {
+            resolve(response)
+          }).catch(err => {
+            reject(err)
+          })
+      })
+    }
+  }
+}
+</script>
+<style scoped lang="scss">
+.file-dialog {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  .el-form {
+    width: 100%;
+  }
+  .image {
+    position: relative;
+    width: 61%;
+    padding-top: 60%;
+    border:1px solid #409EFF;
+    border-radius: 4px;
+    .image-center {
+      padding: 1%;
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      overflow-x: hidden;
+      display: flex;
+      justify-content: center;
+    }
+    .image-url {
+      height: 100%;
+    }
+  }
+}
+</style>

+ 75 - 3
src/components/input/normalArea.vue

@@ -1,7 +1,7 @@
 <template>
   <div>
     <article :id="id">
-      <editor v-model="inputValue" :class="'tinymce_'+id" :init="init" @input="changeText" />
+      <editor :id="'tinymce_'+id" ref="editor" v-model="inputValue" :init="init" @input="changeText" />
     </article>
   </div>
 </template>
@@ -10,6 +10,7 @@ import tinymce from 'tinymce/tinymce'
 import Editor from '@tinymce/tinymce-vue'
 import 'tinymce/themes/silver/theme'
 import 'tinymce/icons/default/icons'
+import axios from 'axios'
 
 export default {
   components: {
@@ -34,6 +35,7 @@ export default {
   },
   data() {
     return {
+      fileUpload: false,
       inputValue: '',
       edit: false,
       init: {
@@ -59,20 +61,90 @@ export default {
   watch: {
     value: {
       handler(newV, oldV) {
-        console.log(newV)
+        // console.log(newV)
+        this.getImages(newV)
         this.inputValue = newV
       },
       immediate: true
     }
   },
   mounted() {
-    tinymce.init({ selector: `.tinymce_${this.id}` })
+    tinymce.init({ selector: `#tinymce_${this.id}` })
+  },
+  beforeDestroy() {
+    // 销毁编辑器
+    this.editor.remove()
   },
   methods: {
     changeText(e) { // 富文本内容改变
       this.inputValue = e
       this.$emit('update:value', this.inputValue)
       this.$emit('change', this.inputValue)
+    },
+    getImages(node) {
+      const reg = /<img.*?(?:>|\/>)/gi
+      const imgArr = node.match(reg) // 获取图片数组
+      if (imgArr && imgArr.length > 0) {
+        imgArr.forEach((value, key) => {
+          const regSrc = /<img.*?src="(.*?)".*?\/?>/i
+          const src = value.match(regSrc)
+          if (src[1].indexOf('base64') > 0) {
+            const file = this.dataURLtoFile(src[1], this.generateMixed(15) + '.png', 'image/png')
+            this.successUpload(`<img src="" />`, key)
+          }
+        })
+      }
+    },
+    generateMixed(len) { // 图片随机名字生成
+      const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
+      let res = ''
+      for (let i = 0; i < len; i++) {
+        const id = Math.ceil(Math.random() * 35)
+        res += chars[id]
+      }
+      return res
+    },
+    dataURLtoFile(base64, filename, contentType) { // base64 -> file
+      var arr = base64.split(',') // 去掉base64格式图片的头部
+      var bstr = atob(arr[1]) // atob()方法将数据解码
+      var leng = bstr.length
+      var u8arr = new Uint8Array(leng)
+      while (leng--) {
+        u8arr[leng] = bstr.charCodeAt(leng) // 返回指定位置的字符的 Unicode 编码
+      }
+      return new File([u8arr], filename, { type: contentType })
+    },
+    updateFile(file) { // 上传图片
+      const param = new FormData() // 创建form对象
+      param.append('file', file)// 通过append向form对象添加数据
+      const config = {
+        headers: {
+          'Content-Type': 'multipart/form-data'
+        },
+        withCredentials: false
+      } // 添加请求头
+      return new Promise((resolve, reject) => {
+        axios.post('http://star.xiaojukeji.com/upload/img.node', param, config)
+          .then(response => {
+            resolve(response)
+          }).catch(err => {
+            reject(err)
+          })
+      })
+    },
+    async successUpload(file, index) { // 图片上传成功
+      const reg = /<img.*?(?:>|\/>)/gi
+      let newStr = ''
+      this.inputValue.replace(reg, function(match, ...rest) {
+        // console.log(match, rest)
+        // console.log(rest[1].substring(0, rest[0]))
+        // console.log(rest[1].substring(rest[0] + match.length, rest[1].length))
+        // debugger
+        newStr = rest[1].substring(0, rest[0]) + rest[1].substring(rest[0] + match.length, rest[1].length)
+        return rest[1].substring(0, rest[0]) + rest[1].substring(rest[0] + match.length, rest[1].length)
+      })
+      // this.inputValue = newStr
+      tinymce.activeEditor.setContent(newStr)
     }
   }
 }

+ 11 - 6
src/views/projectManage/bugList/file/createdBug.vue

@@ -16,13 +16,13 @@
               <div style="width:100%; margin: 0 4%;">
                 <el-form-item label="所属任务" prop="taskId">
                   <el-select v-model="formInline.taskId" filterable placeholder="请选择" style="width:100%;" @click.native="bugListSelect">
-                    <el-option v-for="item in taskEnumList" :key="item.id" :label="item.name" :value="item.id">
+                    <el-option v-for="item in taskEnumList" :key="item.id" :label="item.name + item.taskId" :value="item.id">
                       <div class="belong-task">
+                        <div class="task-id">{{ item.taskId }}</div>
                         <div class="modules-name">
-                          <span v-if="item.moduleInfoName" class="modules">{{ item.moduleInfoName | limit(15) }}</span>
                           <span class="name">{{ item.name }}</span>
+                          <span v-if="item.moduleInfoName" class="modules">{{ item.moduleInfoName }}</span>
                         </div>
-                        <div class="task-id">{{ item.taskId }}</div>
                       </div>
                     </el-option>
                   </el-select>
@@ -703,18 +703,23 @@ export default {
 .belong-task {
   max-width: 500px;
   display: flex;
-  justify-content: space-between;
   .modules-name {
-    width: 80%;
+    width: calc(100% - 100px);
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
   }
-  .modules,.task-id{
+  .modules {
+    color: #999999;
+  }
+  .task-id {
     color: #999999;
+    width: 80px;
+    margin-right: 20px;
   }
   .name {
     color: #333333;
+    margin-right: 20px;
   }
 }
 </style>

+ 3 - 1
src/views/quality/components/changeRequireChart.vue

@@ -17,17 +17,19 @@
         <el-table-column
           prop="name"
           label="任务名称"
-          width="180"
           align="center"
+          min-width="200"
         />
         <el-table-column
           prop="count"
           label="排期解锁次数"
           align="center"
+          width="120"
         />
         <el-table-column
           label="解锁原因"
           align="center"
+          min-width="200"
         >
           <template slot-scope="scope">
             <div v-for="(item,index) in scope.row.remarkList" :key="'item-require'+index" class="reason">{{ item }}</div>

+ 1 - 1
src/views/quality/components/developmentCycle.vue

@@ -45,7 +45,7 @@ export default {
   methods: {
     setChart() {
       this.echartsOption = {
-        grid: { left: '0%', right: '5%', bottom: '0%', top: '10%', containLabel: true },
+        grid: { left: '0%', right: '5%', bottom: '0%', top: '3%', containLabel: true },
         tooltip: {
           showDelay: 0,
           formatter: function(params) {

+ 5 - 3
src/views/quality/requireStatistics.vue

@@ -463,7 +463,7 @@ export default {
   .defect-main {
     padding: 20px 20px 0 20px;
     height:100%;
-    width: calc(100%-60px);
+    width: 100%;
     background:#ffffff;
     margin: 10px;
     border-radius: 4px;
@@ -589,10 +589,12 @@ export default {
     overflow: hidden;
   }
   .chart-item-tip {
-    margin-top: 12px;
-    margin-bottom: 10px;
+    padding: 6px 10px;
+    margin-top: 5px;
     font-size: 12px;
     color: #E6A23C;
+    background: rgba(230, 162, 61, 0.1);
+    border: 2px;
   }
 }
 .strong-font {

+ 5 - 3
src/views/quality/taskStatistics.vue

@@ -487,7 +487,7 @@ export default {
   .defect-main {
     padding: 20px 20px 0 20px;
     height:100%;
-    width: calc(100%-60px);
+    width: 100%;
     background:#ffffff;
     margin: 10px;
     border-radius: 4px;
@@ -613,10 +613,12 @@ export default {
     overflow: hidden;
   }
   .chart-item-tip {
-    margin-top: 12px;
-    margin-bottom: 10px;
+    padding: 6px 10px;
+    margin-top: 5px;
     font-size: 12px;
     color: #E6A23C;
+    background: rgba(230, 162, 61, 0.1);
+    border: 2px;
   }
 }
 .strong-font {