ソースを参照

增加点击图片预览示例,查询窗口表格图片渲染

liuyanpeng 1 年間 前
コミット
82939cdff6

+ 17 - 8
examples/image-preview/src/ImagePreviewExample.vue

@@ -6,36 +6,45 @@
   <h1>图片预览-自定义标题</h1>
   <button class="btn btn-default" @click="preview1">预览</button>
   <ImagePreview ref="imagePreview1">
-    <template #default>
-      美女,Hello
-    </template>
+    <template #default> 美女,Hello </template>
+  </ImagePreview>
+  <h1>图片预览-点击图片预览</h1>
+  <AuthImage
+    :auth-src="src"
+    style="width: 200px; cursor: pointer"
+    @click="preview2"
+  />
+  <ImagePreview ref="imagePreview2">
+    <template #default> 图片查看器 </template>
   </ImagePreview>
 </template>
 
 <script>
-
 import ImagePreview from '@/image-preview/index.js';
+import AuthImage from '@/image/index.js';
 
 export default {
-
   components: {
     ImagePreview,
+    AuthImage,
   },
   data: function () {
     return {
-      
       // eslint-disable-next-line
       src: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fn.sinaimg.cn%2Fent%2Ftransform%2F20161205%2FY3Kh-fxyipxf7613936.jpg&refer=http%3A%2F%2Fn.sinaimg.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1649490096&t=fcae8a8f3a2b9b036708d353e3c5d073',
     };
   },
 
   methods: {
-    preview: function(){
+    preview: function () {
       this.$refs.imagePreview.previewImage(this.src);
     },
-    preview1: function(){
+    preview1: function () {
       this.$refs.imagePreview1.previewImage(this.src);
     },
+    preview2: function () {
+      this.$refs.imagePreview2.previewImage(this.src);
+    },
   },
 };
 </script>

+ 8 - 0
packages/image/index.js

@@ -0,0 +1,8 @@
+
+import AuthImage from './src/AuthImage.vue';
+
+AuthImage.install = function(Vue) {
+  Vue.component(AuthImage.name, AuthImage);
+};
+
+export default AuthImage;

+ 76 - 0
packages/image/src/AuthImage.vue

@@ -0,0 +1,76 @@
+<template>
+  <img :src="src" @onload="onload" @click="$emit('click')" />
+</template>
+<script>
+export default {
+
+  name: 'AuthImage',
+
+  props: {
+    authSrc: {
+      type: String,
+      default: null,
+    },
+  },
+  emits: ['click'],
+
+  data: function () {
+    return {
+      src: '',
+    };
+  },
+
+  watch: {
+    authSrc: function (newValue, oldValue) {
+      if (newValue != oldValue) {
+        this.loadImage(newValue);
+      }
+    },
+  },
+
+  mounted: function () {
+    this.noImage = '/static/assets/client-base-v4/image/no-image.png';
+    this.errorImage = '/static/assets/client-base-v4/image/load-error.png';
+    this.loadingImage = '/static/assets/client-base-v4/image/loading.gif';
+    this.src = this.loadingImage;
+    this.loadImage(this.authSrc);
+  },
+
+  methods: {
+    loadImage: function (url) {
+      let _self = this;
+
+      if (url == null || url == '') {
+        _self.src = this.noImage;
+        return;
+      }
+      this.src = this.loadingImage;
+
+      fetch(url, {
+        method: 'GET',
+        cache: 'default',
+        headers: {
+          token: localStorage.getItem('#token'),
+          'Cache-Control': 'max-age=2592000',
+        },
+      })
+        .then(res => res.blob())
+        .then(blob => {
+          _self.src = URL.createObjectURL(blob);
+        })
+        .catch(err => {
+          console.error(err);
+          _self.src = _self.errorImage;
+        });
+    },
+
+    /**
+     * 图片加载完毕,调用本方法,释放通过URL.createObjectURL()创建的对象URL
+     */
+    onload: function () {
+      let _self = this;
+      URL.revokeObjectURL(_self.src);
+    },
+  },
+};
+</script>

+ 131 - 90
packages/info/src/QueryPage.vue

@@ -1,6 +1,7 @@
 <template>
   <div
-    class="flex-container" :class="{
+    class="flex-container"
+    :class="{
       'flex-container-modal': isSearchWidget == true,
       'flex-container': isSearchWidget == null || isSearchWidget == false,
     }"
@@ -11,11 +12,10 @@
           <InfoHeader
             :html-help-url="infoWindowDto.htmlHelpUrl"
             :info-grid-fields="infoGridFieldsInstance.infoGridFields"
-            :header-name="Language.getNameTrl($i18n.locale, infoWindowDto)" 
+            :header-name="Language.getNameTrl($i18n.locale, infoWindowDto)"
             :sub-header-name="Language.getHelpTrl($i18n.locale, infoWindowDto)"
-            
             :info-filter-fields="infoFilterFields"
-            :info-window-no="infoWindowDto.no" 
+            :info-window-no="infoWindowDto.no"
             @filter-field-property-changed="filterFieldPropertyChanged($event)"
             @grid-field-property-changed="gridFieldPropertyChanged($event)"
           />
@@ -23,11 +23,14 @@
         <div>
           <a-flex justify="space-between" align="center">
             <QueryCondition
-              ref="queryCondition" :info-filter-fields="infoFilterFields"
-              :info-buttons="infoWindowDto.infoButtons" :is-search-widget="isSearchWidget"
+              ref="queryCondition"
+              :info-filter-fields="infoFilterFields"
+              :info-buttons="infoWindowDto.infoButtons"
+              :is-search-widget="isSearchWidget"
               :info-window-no="infoWindowDto.no"
               :search-condition-instance="searchConditionInstance"
-              @simple-search="simpleSearch" @complex-search="complexSearch" 
+              @simple-search="simpleSearch"
+              @complex-search="complexSearch"
             />
 
             <div>
@@ -45,30 +48,28 @@
           </a-flex>
         </div>
       </div>
-      
-      <a-space warp style="margin-top: 5px;">
+
+      <a-space warp style="margin-top: 5px">
         <a-button type="link" @click="pageSearch">
-          {{ $t('lang.QueryConditionSimple.refresh') }}
+          {{ $t("lang.QueryConditionSimple.refresh") }}
           <template #icon>
             <SyncOutlined />
           </template>
         </a-button>
 
-
         <a-button type="link" @click="executeExport">
-          {{ $t('lang.QueryConditionSimple.export') }}
+          {{ $t("lang.QueryConditionSimple.export") }}
           <template #icon>
             <DownloadOutlined />
           </template>
         </a-button>
       </a-space>
 
-
-
       <div v-if="!isSearchWidget" style="display: inline-block">
         <template v-for="infoButton in infoButtons" :key="infoButton.name">
           <a-button
-            v-tooltip.right="Language.getHelpTrl($i18n.locale, infoButton)" type="link"
+            v-tooltip.right="Language.getHelpTrl($i18n.locale, infoButton)"
+            type="link"
             @click="executeProcess(infoButton)"
           >
             {{ Language.getNameTrl($i18n.locale, infoButton) }}
@@ -76,31 +77,60 @@
         </template>
       </div>
 
-
-      <a-segmented v-model:value="selectedView" :options="views" class="m-segmented" size="small">
+      <a-segmented
+        v-model:value="selectedView"
+        :options="views"
+        class="m-segmented"
+        size="small"
+      >
         <template #label="{ value: val, payload = {} }">
           <div style="padding: 2px 2px">
-            <a-avatar :src="payload.src" :alt="val" size="small" shape="square">
-              {{ payload.content }}
-            </a-avatar>
+            <a-tooltip :title="payload.name" placement="top">
+              <a-avatar
+                :src="payload.src"
+                :style="payload.icon ? payload.style : null"
+                :alt="val"
+                size="small"
+                shape="square"
+              >
+                <template #icon>
+                  <component
+                    :is="payload.icon"
+                    style="font-size: 22px; color: #1677ff; background: #f5f5f5"
+                  />
+                </template>
+                {{ payload.content }}
+              </a-avatar>
+            </a-tooltip>
           </div>
         </template>
       </a-segmented>
     </div>
     <!-- xx -->
 
-    <div 
-      style="padding:0;border-bottom: solid 1px #d1cfcf;margin-top:5px; margin-bottom: 5px;"
-    />
     <div
-      class="flex-content" 
-    >
+      style="
+        padding: 0;
+        border-bottom: solid 1px #d1cfcf;
+        margin-top: 5px;
+        margin-bottom: 5px;
+      "
+    />
+    <div class="flex-content">
       <component
         :is="selectedView"
-        v-if="selectedView != null && selectedView != '' && componentLoadedCount == views.length"
-        :info-grid-fields-instance="infoGridFieldsInstance" :sort-instance="sortInstance"
-        :info-window-data-instance="infoWindowDataInstance" :pagination="pagination" :multiple="multiple"
-        :call-out-js-url="infoWindowDto.callOutJsUrl" @data-selected="$emit('dataSelected', $event)"
+        v-if="
+          selectedView != null &&
+            selectedView != '' &&
+            componentLoadedCount == views.length
+        "
+        :info-grid-fields-instance="infoGridFieldsInstance"
+        :sort-instance="sortInstance"
+        :info-window-data-instance="infoWindowDataInstance"
+        :pagination="pagination"
+        :multiple="multiple"
+        :call-out-js-url="infoWindowDto.callOutJsUrl"
+        @data-selected="$emit('dataSelected', $event)"
         @delete-selected="$emit('deleteSelected', $event)"
       />
     </div>
@@ -128,7 +158,11 @@
     <Loading v-if="loading" />
     <Modal v-model:show="modal">
       <template #default>
-        <ProcessReportResult :process-report-result="processReportResult" :pdf-only="pdfOnly" :excel-only="excelOnly" />
+        <ProcessReportResult
+          :process-report-result="processReportResult"
+          :pdf-only="pdfOnly"
+          :excel-only="excelOnly"
+        />
       </template>
       <template #header>
         <div>{{ $t("lang.QueryPage.resultsOfEnforcement") }}</div>
@@ -167,8 +201,17 @@ import dayjs from 'dayjs';
 import QueryPageTable from './QueryPageTable.vue';
 import QueryPageDashboard from './QueryPageDashboard.vue';
 
-import { useSort, useInfoGridFields, useInfoWindowData, useSearchCondition } from './InfoWindowState.js';
-import { SyncOutlined, DownloadOutlined  } from '@ant-design/icons-vue';
+import {
+  useSort,
+  useInfoGridFields,
+  useInfoWindowData,
+  useSearchCondition,
+} from './InfoWindowState.js';
+import {
+  SyncOutlined,
+  DownloadOutlined,
+  TableOutlined,
+} from '@ant-design/icons-vue';
 
 import CssUtil from '../../common/CssUtil.js';
 
@@ -186,7 +229,7 @@ export default {
     QueryPageTable,
     QueryPageDashboard,
     SyncOutlined,
-    DownloadOutlined ,
+    DownloadOutlined,
   },
 
   props: {
@@ -267,17 +310,18 @@ export default {
       modal: false,
       searchType: 'simple',
       sortInstance: useSort(),
+      infoButtons:[],
       searchConditionInstance: useSearchCondition(),
       infoGridFieldsInstance: useInfoGridFields(),
       infoWindowDataInstance: useInfoWindowData(),
       views: [
         {
-          name: '表格视图',
           value: 'QueryPageTable',
           payload: {
-            src: 'https://joeschmoe.io/api/v1/random',
+            name: '表格视图',
+            icon: TableOutlined,
             style: {
-              backgroundColor: '#f56a00',
+              backgroundColor: '#ffffff',
             },
           },
         },
@@ -302,7 +346,7 @@ export default {
       deep: true,
     },
 
-    selectedView: function(newValue, oldValue){
+    selectedView: function (newValue, oldValue) {
       console.log(newValue);
       console.log(oldValue);
     },
@@ -317,16 +361,12 @@ export default {
     }
   },
 
-  mounted: function(){
-
-  },
-
+  mounted: function () {},
 
   methods: {
-
     /**
      * 页面显示数量改变
-     * @param newPageSize 
+     * @param newPageSize
      */
     gridSizeSelect: function (newPageSize) {
       this.pagination.current_page = 1;
@@ -334,7 +374,6 @@ export default {
       localStorage.setItem(`InfoWindowPageSize${this.windowNo}`, newPageSize);
     },
 
-
     // 初始化数据
     initWidget: function (data) {
       var _self = this;
@@ -345,7 +384,10 @@ export default {
       _self.infoWindowDto = data;
       _self.infoFilterFields = data.infoFilterFields;
 
-      _self.infoGridFieldsInstance.init(_self.infoWindowDto.no, data.infoGridFields);
+      _self.infoGridFieldsInstance.init(
+        _self.infoWindowDto.no,
+        data.infoGridFields,
+      );
 
       _self.infoFilterFields.forEach(function (item) {
         item.value = {
@@ -366,7 +408,7 @@ export default {
 
     /**
      * 高级查询
-     * @param value 
+     * @param value
      */
     complexSearch: function (value) {
       var _self = this;
@@ -453,7 +495,6 @@ export default {
           _self.pagination.last_page = Math.ceil(
             data.totalSize / data.range.length,
           );
-
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
           _self.loading = false;
@@ -512,7 +553,6 @@ export default {
           _self.pagination.last_page = Math.ceil(
             data.totalSize / data.range.length,
           );
-
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
           _self.loading = false;
@@ -521,8 +561,6 @@ export default {
       });
     },
 
-
-
     /**
      * 获取选择的数据
      */
@@ -865,18 +903,18 @@ export default {
       return null;
     },
 
-    dynamicInitViews: function(){
+    dynamicInitViews: function () {
       let _self = this;
       let infoWindowDashbaords = _self.infoWindowDto.infoWindowDashbaords;
-      if(infoWindowDashbaords != null && infoWindowDashbaords.length > 0){
-        for(let i = 0, length = infoWindowDashbaords.length; i < length; i ++){
+      if (infoWindowDashbaords != null && infoWindowDashbaords.length > 0) {
+        for (let i = 0, length = infoWindowDashbaords.length; i < length; i++) {
           let infoWindowDashbaord = infoWindowDashbaords[i];
           console.log(infoWindowDashbaord);
-          _self.dynamicInitView(infoWindowDashbaord);          
+          _self.dynamicInitView(infoWindowDashbaord);
         }
       }
     },
-    
+
     /**
      * 初始化Dashbaord界面
      */
@@ -889,15 +927,14 @@ export default {
       const componentName = infoWindowDashbaord.componentName;
 
       // 如果是默认视图的话,进行加载
-      if(infoWindowDashbaord.isDefault === true){
+      if (infoWindowDashbaord.isDefault === true) {
         _self.selectedView = infoWindowDashbaord.componentName;
       }
 
       _self.views.push({
-        name: infoWindowDashbaord.name,
         value: componentName,
         payload: {
-          // src: 'https://joeschmoe.io/api/v1/random',
+          name: infoWindowDashbaord.name,
           src: _self.iconSrc + infoWindowDashbaord.icon,
           style: {
             backgroundColor: '#f56a00',
@@ -905,45 +942,50 @@ export default {
         },
       });
 
-
-
       const cssUrl = infoWindowDashbaord.cssUrl;
 
-      if(cssUrl != null && cssUrl != undefined){
+      if (cssUrl != null && cssUrl != undefined) {
         CssUtil.dynamicLoadCss(cssUrl, componentName);
       }
 
       const jsUrl = infoWindowDashbaord.jsUrl;
-      if(jsUrl != null && jsUrl != undefined){
-
+      if (jsUrl != null && jsUrl != undefined) {
         let promise = new Promise((resolve, reject) => {
-          import(/* webpackIgnore: true */ jsUrl).then(remoteComponent => {
-            resolve(remoteComponent);
-          }).catch(error => {
-            reject(error);
-          });
+          import(/* webpackIgnore: true */ jsUrl)
+            .then(remoteComponent => {
+              resolve(remoteComponent);
+            })
+            .catch(error => {
+              reject(error);
+            });
         });
 
-
-        promise.then(remoteComponent => {
-          console.log('remoteComponent:' + remoteComponent.default.name);
-          if(componentName !== remoteComponent.default.name){
-            let errorMessage = '数据字典-查询窗口自定义组件部件名称定义的是' + componentName + ',但是程序中name定义的是' + remoteComponent.default.name + ',两者必须相同。';
-            console.error(errorMessage);
-            Notify.error('查询窗口自定义组件定义错误', errorMessage, false);
-          }
-          window.app.component(componentName, remoteComponent.default);
-          _self.componentLoadedCount ++;
-        }, errorData => {
-          console.error(errorData);
-        });
+        promise.then(
+          remoteComponent => {
+            console.log('remoteComponent:' + remoteComponent.default.name);
+            if (componentName !== remoteComponent.default.name) {
+              let errorMessage =
+                '数据字典-查询窗口自定义组件部件名称定义的是' +
+                componentName +
+                ',但是程序中name定义的是' +
+                remoteComponent.default.name +
+                ',两者必须相同。';
+              console.error(errorMessage);
+              Notify.error('查询窗口自定义组件定义错误', errorMessage, false);
+            }
+            window.app.component(componentName, remoteComponent.default);
+            _self.componentLoadedCount++;
+          },
+          errorData => {
+            console.error(errorData);
+          },
+        );
       }
     },
 
-    
     /**
      * 页数改变
-     * @param page 
+     * @param page
      */
     handlePageChange: function (page) {
       this.pagination.current_page = page;
@@ -954,8 +996,8 @@ export default {
 
     /**
      * 每页条数改变
-     * @param current 
-     * @param size 
+     * @param current
+     * @param size
      */
     handleShowSizeChange: function (current, size) {
       setTimeout(() => {
@@ -964,7 +1006,6 @@ export default {
       this.pagination.per_page = size;
       localStorage.setItem(`InfoWindowPageSize${this.windowNo}`, size);
     },
-
   },
 };
 </script>
@@ -983,7 +1024,6 @@ export default {
   background-color: #6699cc !important;
 }
 
-
 .flex-container {
   display: flex;
   /* 垂直*/
@@ -1023,7 +1063,7 @@ export default {
 
 <style scoped>
 /** 修复分页的样式 By YangZhiJie 2021-07-06 11:23 */
-nav>>>ul.pagination {
+nav >>> ul.pagination {
   margin: 0 !important;
 }
 </style>
@@ -1037,11 +1077,12 @@ nav>>>ul.pagination {
   text-align: center !important;
 }
 
-.m-segmented{
+.m-segmented {
   float: right;
+  margin-top: 4px;
 }
 
-.m-segmented >>> .ant-segmented-item{
+.m-segmented >>> .ant-segmented-item {
   margin-bottom: 0px;
 }
 </style>

+ 26 - 53
packages/info/src/QueryPageImage.vue

@@ -1,67 +1,36 @@
 <template>
   <div>
     <span>
-      <template
-        v-for="(item,index) in images"
-        :key="'image-' + index"
-      >
-        <img
-          v-if="images && images.length > 0"
-          :src="getImageSrc(item)"
-          class="img-thumbnail m-div"
-          @click="showImage(item)"
+      <template v-for="(item, index) in images" :key="'image-' + index">
+        <AuthImage
+          :auth-src="getImageSrc(item)"
+          style="width: 40px; cursor: pointer"
+          @click="previewImage(item, index)"
         />
-
+        <ImagePreview ref="imagePreview">
+          <template #default> 图片查看器 </template>
+        </ImagePreview>
       </template>
     </span>
-    <Modal
-      v-model:show="modal"
-      full="true"
-      :title="$t('lang.QueryPageImage.viewPicture')"
-      :show-ok-button="false"
-    >
-      <template #default>
-        <template
-          v-for="(item,index) in images"
-          :key="'image-' + index"
-        >
-          <img
-            v-if="selectedImage && selectedImage.length > 0"
-            :src="getImageSrc(selectedImage)"
-            style="height: 100%;width: 100%"
-          />
-        </template>
-      </template>
-      <template #footer>
-        <div>
-          <button
-            class="btn btn-success"
-            type="button"
-            @click="download"
-          >
-            {{ $t('lang.QueryPageImage.downloadPictures') }}
-          </button>
-        </div>
-      </template>
-    </Modal>
   </div>
 </template>
 
 <script>
 import Common from '../../common/Common.js';
-import Modal from '../../modal/src/Modal.vue';
+import AuthImage from '../../image/src/AuthImage.vue';
+import ImagePreview from '../../image-preview/src/ImagePreview.vue';
 
 export default {
-
   components: {
-    Modal,
+    AuthImage,
+    ImagePreview,
   },
   props: {
-    'className': {
+    className: {
       type: Object,
       default: null,
-    }, 
-    'imageNames':{
+    },
+    imageNames: {
       type: String,
       default: null,
     },
@@ -76,8 +45,8 @@ export default {
   },
   watch: {
     /**
-             * fieldValue发生改变
-             */
+     * fieldValue发生改变
+     */
     imageNames: function (value) {
       if (value == undefined || value.length == 0) {
         this.images = [];
@@ -100,18 +69,22 @@ export default {
       this.modal = true;
     },
     /**
-           * 获取图片地址
-           * @param  {String} item 图片名称
-           * @return {String}      图片URL地址
-           */
+     * 获取图片地址
+     * @param  {String} item 图片名称
+     * @return {String}      图片URL地址
+     */
     getImageSrc: function (item) {
       var _self = this;
       if (item != undefined && item != null) {
-        return Common.getImageSrc(_self.selectClause, item);
+        return Common.getImageSrc(_self.className, item);
       } else {
         return '';
       }
     },
+    previewImage: function (image, index) {
+      const src = this.getImageSrc(image);
+      this.$refs.imagePreview[index].preview(src);
+    },
     download: function () {
       window.open(this.getImageSrc(this.selectedImage));
     },