Browse Source

Merge branch 'master' of http://112.74.164.79:3000/chenying/X-web

liqianyi 3 weeks ago
parent
commit
b49e20dafb

+ 47 - 0
src/views/modules/common/image-preview.vue

@@ -0,0 +1,47 @@
+<template>
+  <!-- 该组件仅负责通过 viewer 打开预览,无需可视 DOM -->
+  <div class="image-preview-hook" style="display:none"></div>
+</template>
+
+<script>
+import { downloadUrl } from '@/api/file'
+
+export default {
+  name: 'image-preview',
+  methods: {
+    /**
+     * 打开图片预览
+     * @param {string[]} images 图片地址数组,可为相对路径(将自动拼接 downloadUrl)或绝对路径
+     * @param {number} startIndex 起始预览下标
+     */
+    open(images = [], startIndex = 0) {
+      if (!images || images.length === 0) {
+        this.$message && this.$message.error('没有可预览的图片!')
+        return
+      }
+      // 兼容相对路径,统一转为可访问的完整地址
+      const urls = images.map(u => {
+        if (!u) return ''
+        const isAbs = /^(https?:)?\/\//i.test(u)
+        return isAbs ? u : (downloadUrl + u)
+      }).filter(Boolean)
+
+      if (!urls.length) {
+        this.$message && this.$message.error('没有可预览的图片!')
+        return
+      }
+
+      // 使用全局注册的 $viewerApi 打开预览(支持缩放、旋转、左右切换等)
+      this.$viewerApi({
+        images: urls,
+        options: {
+          toolbar: true,
+          initialViewIndex: startIndex || 0
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style scoped></style>

+ 2 - 2
src/views/modules/common/upload-component.vue

@@ -75,7 +75,7 @@ export default {
       this.$emit('uploadSuccess', newVal)
     },
     fileObjList(newVal) {
-      console.log(newVal)
+      // console.log(newVal)
       this.fileList = newVal
     }
   },
@@ -173,7 +173,7 @@ export default {
     },
     // 改变上传内容
     handleChange(file, fileList) {
-      console.log('fileList:', fileList)
+      // console.log('fileList:', fileList)
       this.fileList = fileList
     },
     // 超限

+ 61 - 4
src/views/modules/home/workboard.vue

@@ -3,7 +3,11 @@
     <div>
       <div>
         <el-row style="margin-bottom: 20px;">
-          <el-col :span="12"><el-button @click="handleUser">选择用户</el-button></el-col>
+          <el-col :span="12">
+            <el-button @click="handleUser">选择用户</el-button>
+            <el-button type="primary" :loading="isRefreshingList" @click="refreshNow"
+              style="margin-left: 10px;">立即刷新</el-button>
+          </el-col>
           <el-col :span="12">
             <!-- <span>当前时间:</span><span>2025-05-12</span> -->
           </el-col>
@@ -72,7 +76,12 @@ export default {
       current: 1,
       size: 50,
       total: 0,
-      loading: false // 防止重复请求
+      loading: false, // 防止重复请求(用户列表加载)
+      // 自动刷新相关
+      refreshMs: 30000, // 列表自动刷新间隔(毫秒)
+      refreshTimer: null, // 定时器句柄
+      isRefreshingList: false, // 防止 getList 并发触发
+      _visibilityHandler: null // 绑定后的可见性事件处理器,便于移除
     }
   },
   created() {
@@ -81,17 +90,56 @@ export default {
   mounted() {
     this.calcHeight()
     window.addEventListener('resize', this.calcHeight)
+    // 启动自动刷新,并在页面可见性变化时进行控制
+    this.startAutoRefresh()
+    this._visibilityHandler = this.handleVisibilityChange.bind(this)
+    document.addEventListener('visibilitychange', this._visibilityHandler)
 
     this.startScroll()
   },
   beforeDestroy() {
     window.removeEventListener('resize', this.calcHeight) // 避免内存泄漏
+    this.stopAutoRefresh()
+    if (this._visibilityHandler) {
+      document.removeEventListener('visibilitychange', this._visibilityHandler)
+      this._visibilityHandler = null
+    }
   },
   methods: {
+    // 立即刷新
+    refreshNow() {
+      this.getList()
+    },
+    // 自动刷新控制
+    startAutoRefresh() {
+      this.stopAutoRefresh()
+      this.refreshTimer = setInterval(() => {
+        // 页面不可见时不刷新
+        if (!document.hidden) {
+          this.getList()
+        }
+      }, this.refreshMs)
+    },
+    stopAutoRefresh() {
+      if (this.refreshTimer) {
+        clearInterval(this.refreshTimer)
+        this.refreshTimer = null
+      }
+    },
+    handleVisibilityChange() {
+      if (document.hidden) {
+        this.stopAutoRefresh()
+      } else {
+        // 页面重新可见时,立即刷新一次并重启自动刷新
+        this.getList()
+        this.startAutoRefresh()
+      }
+    },
     calcHeight() {
       this.$nextTick(() => {
         setTimeout(() => {
           const container = this.$refs.tableContainer
+          if (!container) return
           const top = container.getBoundingClientRect().top
           this.tableHeight = window.innerHeight - top - 50 // 50px 为预留边距
         }, 300) // 预留资源加载时间
@@ -169,6 +217,8 @@ export default {
       })
     },
     getList() {
+      if (this.isRefreshingList) return
+      this.isRefreshingList = true
       this.$http({
         url: this.$http.adornUrl(`/biz-service/workBoard/list`),
         method: 'get',
@@ -180,16 +230,23 @@ export default {
         if (data && data.code === '200') {
           this.list = data.data.records
         }
+      }).catch(() => {
+        // 静默失败,避免大屏闪烁;可按需添加消息提示
+      }).then(() => {
+        this.isRefreshingList = false
       })
     },
     getSelectedUsers() {
       this.$http({
         url: this.$http.adornUrl(`/biz-service/workBoard/selected/user/list`),
         method: 'get',
-        data: {}
+        // GET 请求无需 data,保留空对象会被忽略
       }).then(({ data }) => {
         if (data && data.code === '200') {
-          this.selectedUsers = this.oldSelectedUsers = data.data
+          // 避免两个变量指向同一引用,便于后续差异对比
+          const arr = Array.isArray(data.data) ? data.data : []
+          this.selectedUsers = arr.slice()
+          this.oldSelectedUsers = arr.slice()
         }
       })
     },

+ 47 - 5
src/views/modules/production/plan-submit.vue

@@ -39,11 +39,13 @@
         <el-table-column prop="versionNumber" header-align="center" align="center" min-width="140"
           :show-tooltip-when-overflow="true" label="版本号">
         </el-table-column>
-        <el-table-column header-align="center" align="center" min-width="140" :show-tooltip-when-overflow="true"
+        <el-table-column header-align="center" align="center" min-width="100" :show-tooltip-when-overflow="true"
           label="简图">
           <template slot-scope="scope">
-            <el-button :disabled="!scope.row.attachList2 || scope.row.attachList2.length === 0" type="text" size="small"
-              @click="attachDetail(scope.row.attachList2)">查看</el-button>
+            <div class="thumb-list" v-if="getImageList(scope.row.attachList2).length">
+              <img v-for="(img, idx) in getImageList(scope.row.attachList2)" :key="(img.url || '') + idx" class="thumb"
+                :src="downloadUrl + img.url" :alt="img.fileName" @click="previewFile(img.fileName, img.url)" />
+            </div>
           </template>
         </el-table-column>
         <el-table-column prop="unit" header-align="center" align="center" min-width="140"
@@ -121,14 +123,18 @@
     </span>
 
     <attach-detail-dialog ref="attachDetail" />
+    <!-- 图片预览组件(使用 v-viewer 打开大图) -->
+    <preview-component v-if="previewVisible" ref="preview" />
   </div>
 </template>
 
 <script>
 import AttachDetailDialog from '../common/attach-detail-dialog'
+import PreviewComponent from '../common/preview-component'
+import { downloadUrl } from '@/api/file'
 export default {
   name: 'plan-submit',
-  components: { AttachDetailDialog },
+  components: { AttachDetailDialog, PreviewComponent },
   computed: {},
   data() {
     return {
@@ -145,7 +151,9 @@ export default {
       dataList: [],
       dataListLoading: false,
       expandedRowKeys: [],
-      treeMap: new Map()
+      treeMap: new Map(),
+      previewVisible: false,
+      downloadUrl: downloadUrl
     }
   },
   created() { },
@@ -188,6 +196,23 @@ export default {
         this.$refs.attachDetail.init(attachList)
       })
     },
+    // 过滤图片附件
+    getImageList(attachList) {
+      if (!attachList || !Array.isArray(attachList)) return []
+      return attachList.filter(item => this.isImage(item && item.fileName))
+    },
+    isImage(fileName) {
+      if (!fileName || typeof fileName !== 'string') return false
+      const ext = fileName.split('.').pop().toLowerCase()
+      return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)
+    },
+    // 预览大图(或其他文件走新窗口)
+    previewFile(fileName, url) {
+      this.previewVisible = true
+      this.$nextTick(() => {
+        this.$refs.preview && this.$refs.preview.init(fileName, url)
+      })
+    },
     // 表单提交
     dataFormSubmit() {
       this.$refs['dataForm'].validate((valid) => {
@@ -268,4 +293,21 @@ export default {
   cursor: pointer;
   color: #3e8ef7;
 }
+
+/* 缩略图样式 */
+.thumb-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+  justify-content: center;
+}
+
+.thumb {
+  width: 48px;
+  height: 48px;
+  object-fit: cover;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  cursor: pointer;
+}
 </style>

+ 42 - 8
src/views/modules/tech/draw-management.vue

@@ -40,14 +40,16 @@
         <el-table-column prop="version" header-align="center" align="center" min-width="120"
           :show-tooltip-when-overflow="true" label="版本">
         </el-table-column>
-        <el-table-column header-align="center" align="center" min-width="140" :show-overflow-tooltip="true"
-          label="图纸文件">
+        <el-table-column header-align="center" align="center" min-width="50" label="图纸文件">
           <template slot-scope="scope">
-            <div v-for="(item, index) in scope.row.attachList" style="display: inline">
-              <span v-if="index > 0">,</span>
-              <a :key="item.fileName + index" type="primary" href="#" @click="previewFile(item.fileName, item.url)">{{
-                item.fileName }}</a>
+            <div v-if="scope.row.attachList && scope.row.attachList.length">
+              <template v-if="isImage(scope.row.attachList[0])">
+                <img :src="getThumbUrl(scope.row.attachList[0])" :alt="scope.row.attachList[0].fileName"
+                  style="width:48px;height:48px;object-fit:cover;border-radius:4px;cursor:pointer;border:1px solid #e5e6eb;"
+                  @click="openImagePreview(scope.row.attachList)" />
+              </template>
             </div>
+            <span v-else style="color:#c0c4cc;">无</span>
           </template>
         </el-table-column>
         <!-- <el-table-column
@@ -89,6 +91,7 @@
       @onChose="onChose"></add-or-update>
     <detail v-if="detailVisible" ref="detail" @onChose="onChose" />
     <preview-component v-if="previewVisible" ref="preview" @onChose="onChose"></preview-component>
+    <image-preview ref="imagePreview" />
   </div>
 </template>
 
@@ -98,13 +101,17 @@ import Detail from './draw-detail'
 import { getDrawList } from '@/api/product'
 import ProductComponent from '@/views/modules/common/product-component'
 import PreviewComponent from '@/views/modules/common/preview-component.vue'
+import ImagePreview from '@/views/modules/common/image-preview.vue'
+import { downloadUrl } from '@/api/file'
+import { getFileExt } from '@/api/util'
 export default {
   name: 'draw-management',
   components: {
     ProductComponent,
     AddOrUpdate,
     Detail,
-    PreviewComponent
+    PreviewComponent,
+    ImagePreview
   },
   data() {
     return {
@@ -211,12 +218,39 @@ export default {
       }).catch(() => { })
     },
     // 预览
-    previewFile (fileName, url) {
+    previewFile(fileName, url) {
       this.previewVisible = true
       this.$nextTick(() => {
         this.$refs.preview.init(fileName, url)
       })
     },
+    // 缩略图地址(使用第一张)
+    getThumbUrl(firstAttach) {
+      if (!firstAttach) return ''
+      const url = firstAttach.url || ''
+      const isAbs = /^(https?:)?\/\//i.test(url)
+      return isAbs ? url : (downloadUrl + url)
+    },
+    // 新的图片预览:可左右切换
+    openImagePreview(attachList = []) {
+      if (!attachList || !attachList.length) return
+      const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
+      const imgs = attachList
+        .filter(a => a && a.fileName && imageExts.includes(getFileExt(a.fileName)))
+        .map(a => a.url)
+        .filter(Boolean)
+      if (!imgs.length) {
+        this.$message && this.$message.warning('没有可预览的图片文件')
+        return
+      }
+      this.$refs.imagePreview && this.$refs.imagePreview.open(imgs, 0)
+    },
+    // 判断是否为图片
+    isImage(attach) {
+      if (!attach || !attach.fileName) return false
+      const ext = getFileExt(attach.fileName)
+      return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)
+    },
     // 详情
     detailHandle(id) {
       this.detailVisible = true

+ 45 - 4
src/views/modules/tech/project-product-detail.vue

@@ -52,11 +52,13 @@
         <el-table-column prop="versionNumber" header-align="center" align="center" min-width="140"
           :show-tooltip-when-overflow="true" label="版本号">
         </el-table-column>
-        <el-table-column prop="attachList2" header-align="center" align="center" min-width="140"
+        <el-table-column prop="attachList2" header-align="center" align="center" min-width="100"
           :show-tooltip-when-overflow="true" label="简图">
           <template slot-scope="scope">
-            <el-button :disabled="!scope.row.attachList2 || scope.row.attachList2.length === 0" type="text" size="small"
-              @click="attachDetail(scope.row.attachList2)">查看</el-button>
+            <div class="thumb-list" v-if="getImageList(scope.row.attachList2).length">
+              <img v-for="(img, idx) in getImageList(scope.row.attachList2)" :key="(img.url || '') + idx" class="thumb"
+                :src="downloadUrl + img.url" :alt="img.fileName" @click="previewFile(img.fileName, img.url)" />
+            </div>
           </template>
         </el-table-column>
         <el-table-column prop="productSpec" header-align="center" align="center" min-width="140"
@@ -105,15 +107,18 @@
     </span>
 
     <attach-detail-dialog ref="attachDetail" />
+    <preview-component v-if="previewVisible" ref="preview" />
   </div>
 </template>
 
 <script>
 import UserComponent from '../common/user-component'
 import AttachDetailDialog from '../common/attach-detail-dialog'
+import PreviewComponent from '../common/preview-component'
+import { downloadUrl } from '@/api/file'
 export default {
   name: 'project-product-detail',
-  components: { UserComponent, AttachDetailDialog },
+  components: { UserComponent, AttachDetailDialog, PreviewComponent },
   computed: {},
   data() {
     return {
@@ -125,6 +130,8 @@ export default {
       dataRule: {},
       dataList: [],
       dataListLoading: false,
+      previewVisible: false,
+      downloadUrl: downloadUrl
 
     }
   },
@@ -162,6 +169,23 @@ export default {
         this.$refs.attachDetail.init(attachList)
       })
     },
+    // 过滤图片附件
+    getImageList(attachList) {
+      if (!attachList || !Array.isArray(attachList)) return []
+      return attachList.filter(item => this.isImage(item && item.fileName))
+    },
+    isImage(fileName) {
+      if (!fileName || typeof fileName !== 'string') return false
+      const ext = fileName.split('.').pop().toLowerCase()
+      return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)
+    },
+    // 预览大图
+    previewFile(fileName, url) {
+      this.previewVisible = true
+      this.$nextTick(() => {
+        this.$refs.preview && this.$refs.preview.init(fileName, url)
+      })
+    },
     // 表单提交
     dataFormSubmit() {
       this.$refs['dataForm'].validate((valid) => {
@@ -221,4 +245,21 @@ export default {
   cursor: pointer;
   color: #3e8ef7;
 }
+
+/* 缩略图样式 */
+.thumb-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+  justify-content: center;
+}
+
+.thumb {
+  width: 48px;
+  height: 48px;
+  object-fit: cover;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  cursor: pointer;
+}
 </style>

+ 46 - 5
src/views/modules/tech/project-tech-submit.vue

@@ -39,11 +39,13 @@
         <el-table-column prop="versionNumber" header-align="center" align="center" min-width="140"
           :show-tooltip-when-overflow="true" label="版本号">
         </el-table-column>
-        <el-table-column prop="attachList2" header-align="center" align="center" min-width="140"
+        <el-table-column prop="attachList2" header-align="center" align="center" min-width="100"
           :show-tooltip-when-overflow="true" label="简图">
           <template slot-scope="scope">
-            <el-button :disabled="!scope.row.attachList2 || scope.row.attachList2.length === 0" type="text" size="small"
-              @click="attachDetail(scope.row.attachList2)">查看</el-button>
+            <div class="thumb-list" v-if="getImageList(scope.row.attachList2).length">
+              <img v-for="(img, idx) in getImageList(scope.row.attachList2)" :key="(img.url || '') + idx" class="thumb"
+                :src="downloadUrl + img.url" :alt="img.fileName" @click="previewFile(img.fileName, img.url)" />
+            </div>
           </template>
         </el-table-column>
         <el-table-column prop="unit" header-align="center" align="center" min-width="140"
@@ -100,15 +102,18 @@
     </span>
 
     <attach-detail-dialog ref="attachDetail" />
+    <preview-component v-if="previewVisible" ref="preview" />
 
   </div>
 </template>
 
 <script>
 import AttachDetailDialog from '../common/attach-detail-dialog'
+import PreviewComponent from '../common/preview-component'
+import { downloadUrl } from '@/api/file'
 export default {
   name: 'plan-submit',
-  components: { AttachDetailDialog },
+  components: { AttachDetailDialog, PreviewComponent },
   computed: {},
   data() {
     return {
@@ -124,7 +129,9 @@ export default {
       },
       dataList: [],
       dataListLoading: false,
-      craftsAddOrDetailVisible: false
+      craftsAddOrDetailVisible: false,
+      previewVisible: false,
+      downloadUrl: downloadUrl
     }
   },
   created() { },
@@ -166,6 +173,23 @@ export default {
         this.$refs.attachDetail.init(attachList)
       })
     },
+    // 过滤图片附件
+    getImageList(attachList) {
+      if (!attachList || !Array.isArray(attachList)) return []
+      return attachList.filter(item => this.isImage(item && item.fileName))
+    },
+    isImage(fileName) {
+      if (!fileName || typeof fileName !== 'string') return false
+      const ext = fileName.split('.').pop().toLowerCase()
+      return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)
+    },
+    // 预览大图
+    previewFile(fileName, url) {
+      this.previewVisible = true
+      this.$nextTick(() => {
+        this.$refs.preview && this.$refs.preview.init(fileName, url)
+      })
+    },
     // 表单提交
     dataFormSubmit() {
       this.$refs['dataForm'].validate((valid) => {
@@ -223,4 +247,21 @@ export default {
   cursor: pointer;
   color: #3e8ef7;
 }
+
+/* 缩略图样式 */
+.thumb-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+  justify-content: center;
+}
+
+.thumb {
+  width: 48px;
+  height: 48px;
+  object-fit: cover;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+  cursor: pointer;
+}
 </style>