Browse Source

Merge branch 'http_test' of git.xiaojukeji.com:jacklijiajia/thoth-frontend into http_test

qinzhipeng_v 5 years ago
parent
commit
1d393df648

+ 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>

+ 88 - 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,103 @@ 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)
+    },
+    async getImages(node) { // 获取图片
+      const reg = /<img.*?(?:>|\/>)/gi
+      const imgArr = node.match(reg) // 获取图片数组
+      if (imgArr && imgArr.length > 0) {
+        for (const value of imgArr) {
+          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')
+            await this.beforeUpload(file)
+          }
+        }
+      }
+    },
+    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 })
+    },
+    async beforeUpload(file) { // 上传前
+      try {
+        const res = await this.updateFile(file)
+        // const res = { data: { url: '//pt-starimg.didistatic.com/static/starimg/node/elczkYKpzb1598698408779.png' }}
+        let url = ''
+        if (res.data) {
+          url = 'http:' + res.data.url
+        }
+        const img = `<img src="${url}" />`
+        console.log(img)
+        this.successUpload(img)
+      } catch (error) {
+        this.successUpload('')
+      }
+      return new Promise((resolve, reject) => { resolve() })
+    },
+    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) { // 图片上传成功
+      const reg = /<img.*?(?:>|\/>)/gi
+      let newStr = ''
+      this.inputValue.replace(reg, function(match, ...rest) {
+        newStr = rest[1].substring(0, rest[0]) + file + rest[1].substring(rest[0] + match.length, rest[1].length)
+        return rest[1]
+      })
+      this.$nextTick(() => {
+        this.inputValue = 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>

+ 0 - 3
src/views/projectManage/projectList/css/index.css

@@ -48,9 +48,6 @@
 .bgborder .rotate {
   transform: rotate(270deg);
 }
-.bgborder .el-tabs__nav-wrap::after {
-  background-color: #FFFFFF !important;
-}
 
 .bgborder .el-row .el-col {
   margin: 10px 0;

+ 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>

+ 14 - 2
src/views/quality/components/developmentCycle.vue

@@ -1,7 +1,7 @@
 <template>
   <section>
     <div class="chart-contain">
-      <normal-echart v-if="echartsOption" :chart-id="id" :option="echartsOption" />
+      <normal-echart v-if="echartsOption" :chart-id="id" :option="echartsOption" @onClick="toLink" />
     </div>
   </section>
 </template>
@@ -20,6 +20,11 @@ export default {
       type: Array,
       default: () => [],
       required: false
+    },
+    type: {
+      type: String,
+      default: '',
+      required: false
     }
   },
   data() {
@@ -45,7 +50,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) {
@@ -86,6 +91,13 @@ export default {
           }
         }]
       }
+    },
+    toLink(params) {
+      if (this.type === 'require') {
+        this.$router.push({ name: '需求详情', query: { id: params.data[2] }})
+      } else if (this.type === 'task') {
+        this.$router.push({ name: '任务详情', query: { id: params.data[2] }})
+      }
     }
   }
 }

+ 2 - 2
src/views/quality/defectStatistics.vue

@@ -1003,9 +1003,9 @@ export default {
 .el-cascader {
   width: auto;
 }
-/deep/ .el-tabs__nav-wrap::after{
+/deep/.el-tabs__nav-wrap::after{
   height: 1px;
-  background-color: #999999;
+  background-color: #E4E7ED !important;
   opacity: 0.2;
 }
 </style>

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

@@ -136,7 +136,7 @@
           <i class="el-icon-warning-outline" />
           <span>仅统计状态已变更“已排期”且排期不为空的需求;横坐标表示需求交付日期,纵坐标代表研发交付周期(研发、联调、上线类型排期的总周期)</span>
         </div>
-        <development-cycle :chart-data="developmentCycleData" />
+        <development-cycle :chart-data="developmentCycleData" type="require" />
       </div>
       <div class="chart-item">
         <h3>需求分布图</h3>
@@ -463,7 +463,7 @@ export default {
   .defect-main {
     padding: 20px 20px 0 20px;
     height:100%;
-    width: calc(100%-60px);
+    width:auto;
     background:#ffffff;
     margin: 10px;
     border-radius: 4px;
@@ -578,7 +578,7 @@ export default {
 }
 .charts-main {
   padding-bottom: 20px;
-  width:calc(100%-60px);
+  width:auto;
   background:#ffffff;
   margin: 10px;
   border-radius: 4px;
@@ -589,14 +589,19 @@ 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 {
   color: #F01A1A !important;
   font-size: 18px !important;
 }
+/deep/.el-tabs__nav-wrap:after {
+    background-color: #E4E7ED !important;
+}
 </style>

+ 12 - 7
src/views/quality/taskStatistics.vue

@@ -76,10 +76,10 @@
               <el-tooltip v-if="index === 0" class="item" effect="dark" content="统计区间内,新建的任务数量" placement="top-start">
                 <i class="el-icon-info" />
               </el-tooltip>
-              <el-tooltip v-if="index === 1" class="item" effect="dark" content="统计区间内,任务执行过更新状态为“已上线”的操作并且当前状态是“已上线”的任务数量" placement="top-start">
+              <el-tooltip v-if="index === 1" class="item" effect="dark" content="任务最近一次更新状态为“已上线”在统计区间内,并且当前状态是“已上线”的任务数量" placement="top-start">
                 <i class="el-icon-info" />
               </el-tooltip>
-              <el-tooltip v-if="index === 2" class="item" effect="dark" content="统计区间内,任务执行过更新状态为“Hold”的操作并且当前状态为“Hold”的任务数量" placement="top-start">
+              <el-tooltip v-if="index === 2" class="item" effect="dark" content="任务最近一次更新状态为“Hold”在统计区间内,并且当前状态是“Hold”的任务数量" placement="top-start">
                 <i class="el-icon-info" />
               </el-tooltip>
             </div>
@@ -128,7 +128,7 @@
           <i class="el-icon-warning-outline" />
           <span>仅统计状态已变更“已排期”且排期不为空的任务;横坐标表示任务交付日期,纵坐标代表研发交付周期(研发、联调、上线类型排期的总周期)</span>
         </div>
-        <development-cycle :chart-data="developmentCycleData" />
+        <development-cycle :chart-data="developmentCycleData" type="task" />
       </div>
       <div class="chart-item">
         <h3>任务分布图</h3>
@@ -487,7 +487,7 @@ export default {
   .defect-main {
     padding: 20px 20px 0 20px;
     height:100%;
-    width: calc(100%-60px);
+    width:auto;
     background:#ffffff;
     margin: 10px;
     border-radius: 4px;
@@ -602,7 +602,7 @@ export default {
 }
 .charts-main {
   padding-bottom: 20px;
-  width:calc(100%-60px);
+  width:auto;
   background:#ffffff;
   margin: 10px;
   border-radius: 4px;
@@ -613,14 +613,19 @@ 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 {
   color: #F01A1A !important;
   font-size: 18px !important;
 }
+/deep/.el-tabs__nav-wrap:after {
+  background-color: #E4E7ED !important;
+}
 </style>