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

首页修改和图片、PDF文件预览功能实现

songxy 1 éve
szülő
commit
9f2aac565d

+ 98 - 0
client/src/components/PreView/ImagePreview.ts

@@ -0,0 +1,98 @@
+export type ImagePreviewOption = {
+  maxZoom: number
+  minZoom: number
+  fullScreen: boolean
+}
+class ImagePreview {
+  private currentX: number = 0
+  private currentY: number = 0
+  private xOffset: number = 0
+  private yOffset: number = 0
+  private initialX: number = 0
+  private initialY: number = 0
+
+  private maxZoom: number = 6
+  private minZoom: number = 0.5
+  private initZoom: number = 1
+
+  private initRotate: number = 0
+
+  private isMove: boolean = false
+
+  private el: HTMLElement
+
+  constructor(option?: ImagePreviewOption) {
+    this.maxZoom = option?.['maxZoom'] ?? 6
+    this.minZoom = option?.['minZoom'] ?? 0.5
+  }
+
+  bindMouseEvent(el: HTMLElement) {
+    this.el = el
+    this.el.addEventListener('mousedown', (event: any) => {
+      this.downHandler(event)
+    })
+    this.el.addEventListener('mousemove', (event: any) => {
+      this.moveHandler(event)
+    })
+    this.el.addEventListener('mouseup', (event: any) => {
+      this.upHandler(event)
+    })
+    this.el.addEventListener('mouseleave', (event: any) => {
+      this.upHandler(event)
+    })
+  }
+  downHandler(event: any) {
+    event.preventDefault()
+    if (event.button === 0) {
+      this.isMove = true
+      this.initialX = event.clientX - this.xOffset
+      this.initialY = event.clientY - this.yOffset
+      this.el.style.cursor = 'move'
+    }
+  }
+  moveHandler(event: any) {
+    event.preventDefault()
+    if (this.isMove) {
+      this.currentX = event.clientX - this.initialX
+      this.currentY = event.clientY - this.initialY
+      this.xOffset = this.currentX
+      this.yOffset = this.currentY
+      this.setTranslate()
+    }
+  }
+  upHandler(event) {
+    if (event.button === 0) {
+      this.isMove = false
+      this.el.style.cursor = 'pointer'
+    }
+  }
+  zoomHandler(num: number) {
+    if (num === 1 && this.initZoom >= this.maxZoom) return
+    if (num === -1 && this.initZoom <= this.minZoom) return
+    this.initZoom = this.initZoom + num * 0.5
+    this.setTranslate()
+  }
+  rotateHandler = (num: number) => {
+    if (num !== 0) {
+      this.initRotate += num * 90
+      this.setTranslate()
+    } else {
+      //重置
+      this.initRotate = 0
+      this.currentX =
+        this.currentY =
+        this.xOffset =
+        this.yOffset =
+        this.initialX =
+        this.initialY =
+          0
+      this.initZoom = 1
+      this.el.style.transform = 'none'
+    }
+  }
+  setTranslate() {
+    this.el.style.transform = `translate3d(${this.currentX}px, ${this.currentY}px, 0) scale(${this.initZoom}) rotate(${this.initRotate}deg)`
+  }
+}
+
+export default ImagePreview

+ 193 - 0
client/src/components/PreView/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="preView">
+    <div
+      class="preview_box"
+      :style="{
+        width: (isFullScreen ? 100 : width) + '%',
+        height: (isFullScreen ? 100 : height) + '%'
+      }"
+    >
+      <div class="header">
+        <span class="title" v-if="title">{{ title }}</span>
+        <div class="icon_close">
+          <el-icon @click="isFullScreen = !isFullScreen" style="margin-right: 15px"
+            ><FullScreen
+          /></el-icon>
+          <el-icon @click="closeHandler"><Close /></el-icon>
+        </div>
+      </div>
+      <div class="content">
+        <template v-if="fileType === 'image'">
+          <img ref="imgRef" :src="props.src" />
+          <ul class="img_tool">
+            <li @click="zoomHandler(1)">
+              <el-icon><ZoomIn /></el-icon>
+            </li>
+            <li @click="zoomHandler(-1)">
+              <el-icon><ZoomOut /></el-icon>
+            </li>
+            <li @click="rotateHandler(-1)">
+              <el-icon><RefreshLeft /></el-icon>
+            </li>
+            <li @click="rotateHandler(1)">
+              <el-icon><RefreshRight /></el-icon>
+            </li>
+            <li @click="rotateHandler(0)">
+              <el-icon><Refresh /></el-icon>
+            </li>
+          </ul>
+        </template>
+        <template v-else>
+          <vue-office-pdf :src="props.src" />
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import VueOfficePdf from '@vue-office/pdf'
+import ImagePreview from './ImagePreview.ts'
+defineOptions({
+  name: 'PreView'
+})
+const props = withDefaults(
+  defineProps<{
+    src: string
+    visiable: boolean
+    fullScreen: boolean
+    width: number
+    height: number
+    title: string
+  }>(),
+  {
+    visiable: true,
+    fullScreen: false,
+    width: 60,
+    height: 90,
+    title: '资源预览'
+  }
+)
+const emit = defineEmits<{
+  (e: 'close')
+}>()
+const isFullScreen = ref<boolean>(false)
+const fileType = computed(() => {
+  if (!props.src) return ''
+  if (isPdf(props.src)) {
+    return 'pdf'
+  } else if (isPicture(props.src)) {
+    return 'image'
+  }
+})
+
+const isPicture = (src: string): boolean => {
+  if (!src) return false
+  const extensions: string[] = ['jpg', 'jpeg', 'png', 'gif']
+  const extension: string = src?.split('.')?.pop()?.toLowerCase() as string
+  return extensions.indexOf(extension) !== -1 ? true : false
+}
+const isPdf = (src: string): boolean => {
+  if (!src) return false
+  const extensions: string[] = ['pdf']
+  const extension: string = src?.split('.')?.pop()?.toLowerCase() as string
+  return extensions.indexOf(extension) !== -1 ? true : false
+}
+
+const imgRef = ref<HTMLElement>()
+let imagePreview: ImagePreview
+const zoomHandler = (num: number) => {
+  imagePreview.zoomHandler(num)
+}
+const rotateHandler = (num: number) => {
+  imagePreview.rotateHandler(num)
+}
+const closeHandler = () => {
+  emit('close')
+}
+onMounted(() => {
+  if (fileType.value === 'image') {
+    imagePreview = new ImagePreview()
+    imagePreview.bindMouseEvent(imgRef.value as HTMLElement)
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.preView {
+  z-index: 9999999;
+  position: fixed;
+  top: 0px;
+  bottom: 0px;
+  left: 0px;
+  right: 0px;
+  background: rgba(0, 0, 0, 0.5);
+  > .preview_box {
+    background: white;
+    position: absolute;
+    left: 0px;
+    right: 0px;
+    top: 0px;
+    bottom: 0px;
+    margin: auto;
+    padding: 20px;
+    padding-top: 10px;
+    > .header {
+      height: 50px;
+      display: flex;
+      padding: 0px 20px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      border-bottom: 1px solid #eee;
+      > .title {
+        font-size: 20px;
+        font-weight: bold;
+      }
+      > .icon_close {
+        font-size: 20px;
+        right: 20px;
+        cursor: pointer;
+      }
+    }
+    > .content {
+      height: calc(100% - 50px);
+      padding: 0px 20px;
+      position: relative;
+      text-align: center;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      overflow: hidden;
+      > img {
+        display: block;
+        cursor: pointer;
+        user-drag: none;
+        margin-top: 8%;
+        user-select: none;
+        -moz-user-select: none;
+        -webkit-user-drag: none;
+        -webkit-user-select: none;
+        &.scale {
+          transition-duration: 0.2s;
+        }
+      }
+      > .img_tool {
+        display: flex;
+        background: rgba(0, 0, 0, 0.8);
+        border-radius: 5px;
+        padding: 15px 25px;
+        position: absolute;
+        bottom: 10px;
+        > li {
+          color: #fff;
+          font-size: 26px;
+          line-height: 26px;
+          padding: 0px 10px;
+          cursor: pointer;
+        }
+      }
+    }
+  }
+}
+</style>

+ 14 - 9
client/src/directives/Money.ts

@@ -7,18 +7,23 @@ export function Money(app: App<Element>) {
       el.innerHTML = `0${arg === 'unit' ? '元' : ''}`
       return
     }
-    if (value < 10000) {
-      if (value.toString().indexOf('.') != -1) {
-        el.innerHTML = `${value.toFixed(6)}${arg === 'unit' ? '元' : ''}`
+    function valIndexOf(val: number, unit: string = '元') {
+      const str = val.toString()
+      const arr = str.split('.')
+      if (arr.length === 1) {
+        return `${val}${arg === 'unit' ? unit : ''}`
       } else {
-        el.innerHTML = `${value}${arg === 'unit' ? '元' : ''}`
+        if (arr[arr.length - 1].length > 6) {
+          return `${val.toFixed(6)}${arg === 'unit' ? unit : ''}`
+        } else {
+          return `${val}${arg === 'unit' ? unit : ''}`
+        }
       }
+    }
+    if (value < 10000) {
+      el.innerHTML = valIndexOf(value)
     } else {
-      if ((value / 10000).toString().indexOf('.') != -1) {
-        el.innerHTML = `${(value / 10000).toFixed(6)}${arg === 'unit' ? '万元' : ''}`
-      } else {
-        el.innerHTML = `${value / 10000}${arg === 'unit' ? '万元' : ''}`
-      }
+      el.innerHTML = valIndexOf(value / 10000, '万元')
     }
   })
 }

+ 21 - 1
client/src/views/OaSystem/components/DeptTree/index.vue

@@ -9,7 +9,12 @@
     :clearable="clearable"
     @node-click="nodeClickHandler"
     filterable
-  />
+  >
+    <template #default="{ data: { label } }">
+      <span class="item_icon"> </span>
+      {{ label }}
+    </template>
+  </el-tree-select>
 </template>
 
 <script setup lang="ts">
@@ -39,3 +44,18 @@ const nodeClickHandler = (data, node) => {
   emit('nodeClick', node)
 }
 </script>
+
+<style scoped lang="scss">
+.item_icon {
+  display: inline-block;
+  margin-right: 3px;
+  width: 16px;
+  height: 16px;
+  background-image: url('@/assets/imgs/xzq_icon.png');
+  background-repeat: no-repeat;
+  background-size: 100% 100%;
+  &.user {
+    background-image: url('@/assets/imgs/user_icon.png');
+  }
+}
+</style>

+ 10 - 2
client/src/views/OaSystem/home/components/CardItemTwo.vue

@@ -41,13 +41,21 @@ const functions = ref<
 const $emit = defineEmits<{
   (e: 'click', val: any): void
 }>()
+const router = useRouter()
 const clickHandle = (item): void => {
   if (item['title'] === '添加功能') {
     showFuncModal.value = true
   }
   createProcessById(item['key']).then((result) => {
-    console.log('result-------------------')
-    console.log(result)
+    router.push({
+      path: '/processContainer',
+      query: {
+        iframe: '1',
+        iFrameId: item['key'],
+        url: result,
+        title: item['title']
+      }
+    })
   })
 }
 

+ 1 - 1
client/src/views/OaSystem/home/index.vue

@@ -41,7 +41,7 @@ const roles = userStore.getRoles
 if (roles) {
   if (roles.indexOf('test_leader') != -1 || roles.indexOf('test_dept_manager') != -1) {
     //板块领导||部门经理
-    currentIndex.value = '2'
+    currentIndex.value = '1'
   } else {
     //普通员工
     currentIndex.value = '2'

+ 13 - 1
client/src/views/OaSystem/officeCenter/noticeAndLearn/detail.vue

@@ -21,12 +21,14 @@
         </div>
       </div>
       <div v-html="detailForm.content"></div>
+      <PreView :src="currentUrl" v-if="preVisiable" @close="closeHandler" />
     </div>
   </div>
 </template>
 <script setup lang="ts">
 import { useRoute } from 'vue-router'
 import request from '@/config/axios'
+import PreView from '@/components/PreView/index.vue'
 import { useFiles } from './mixins.ts'
 
 const detailForm = ref<{
@@ -40,7 +42,17 @@ const detailForm = ref<{
   readNum: 0,
   content: ''
 })
-const { fileUrls, queryFiles, downloadFile } = useFiles(request)
+const preVisiable = ref<boolean>(false)
+const currentUrl = ref<string>()
+const downloadFile = (url) => {
+  preVisiable.value = true
+  // currentUrl.value = 'http://localhost:3000/test.pdf'
+  currentUrl.value = url
+}
+const closeHandler = () => {
+  preVisiable.value = false
+}
+const { fileUrls, queryFiles } = useFiles(request)
 async function queryDetailById(idStr: any): Promise<void> {
   const urlApi = `/adm/noticeAndLearn/query/detailById`
   const params = {

+ 0 - 4
client/src/views/OaSystem/officeCenter/noticeAndLearn/mixins.ts

@@ -14,9 +14,6 @@ export const useFiles = (request: any) => {
       fileUrls.value = result
     }
   }
-  async function downloadFile(fileUrl: string): Promise<void> {
-    window.open(fileUrl)
-  }
   async function uploadFile(file: any): Promise<void> {
     const url = `${import.meta.env.VITE_FILE_BASE_URI}/upload-and-get-id`
     const sendFData = new FormData()
@@ -53,7 +50,6 @@ export const useFiles = (request: any) => {
   return {
     fileUrls,
     queryFiles,
-    downloadFile,
     uploadFile,
     deleteFile
   }