Ver Fonte

iframe组件添加定时切换和切换动画效果

lma há 4 meses atrás
pai
commit
d897972748

+ 8 - 1
report-ui/src/views/bigscreenDesigner/designer/components/dynamicForm.vue

@@ -192,6 +192,11 @@
             :chart-type="item.chartType"
             @change="changed($event, item.name)"
           />
+          <multiIframeManager
+            v-if="item.type == 'multiIframeManager'"
+            v-model="formData[item.name]"
+            @change="(val) => changed(val, item.name)"
+          />
         </div>
         <div v-else-if="isShowForm(item, '[object Array]')" :key="'a-' + index">
           <el-collapse accordion>
@@ -347,6 +352,7 @@ import dynamicAddRadar from "./dynamicAddRadar";
 import MonacoEditor from "@/components/MonacoEditor/index";
 import componentLinkage from './componentLinkage';
 import imageSelect from './imageSelect';
+import multiIframeManager from './multiIframeManager.vue';
 export default {
   name: "DynamicForm",
   components: {
@@ -360,7 +366,8 @@ export default {
     customUpload,
     dynamicAddRadar,
     MonacoEditor,
-    componentLinkage
+    componentLinkage,
+    multiIframeManager
   },
   model: {
     prop: "value",

+ 189 - 0
report-ui/src/views/bigscreenDesigner/designer/components/multiIframeManager.vue

@@ -0,0 +1,189 @@
+<template>
+  <div class="multi-iframe-manager">
+    <el-button
+      type="primary"
+      size="mini"
+      icon="el-icon-plus"
+      plain
+      @click="handleAddClick"
+    >
+      新增地址
+    </el-button>
+    <el-table :data="formData" style="width: 100%">
+      <el-table-column prop="name" label="名称" width="80">
+        <template slot-scope="scope">
+          <span>{{ scope.row.name || `地址${scope.$index + 1}` }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="url" label="地址" show-overflow-tooltip>
+        <template slot-scope="scope">
+          <el-tooltip :content="scope.row.url" placement="top">
+            <span class="url-text">{{ scope.row.url }}</span>
+          </el-tooltip>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="150" align="center">
+        <template slot-scope="scope">
+          <span
+            class="editor"
+            @click="handleEditorClick(scope.$index, scope.row)"
+          >
+            <i class="el-icon-edit" /> 编辑
+          </span>
+          <span
+            class="delete"
+            @click="handleDeleteClick(scope.$index, scope.row)"
+          >
+            <i class="el-icon-delete" /> 删除
+          </span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-dialog
+      :title="isAddFlag ? '新增iframe地址' : '修改iframe地址'"
+      :visible.sync="dialogVisible"
+      width="30%"
+      :before-close="handleClose"
+    >
+      <el-form ref="iframeForm" :model="iframeForm" label-width="80px">
+        <el-form-item label="名称">
+          <el-input v-model="iframeForm.name" size="mini" placeholder="给iframe地址一个名称"></el-input>
+        </el-form-item>
+        <el-form-item label="地址" required>
+          <el-input v-model="iframeForm.url" size="mini" placeholder="请输入iframe地址"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button size="mini" @click="handleClose">取 消</el-button>
+        <el-button size="mini" type="primary" @click="handleSaveClick">确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MultiIframeManager',
+  model: {
+    prop: 'formData',
+    event: 'input'
+  },
+  props: {
+    formData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      isAddFlag: true, // true 新增, false 编辑
+      indexEditor: -1, // 编辑第几个数据
+      iframeForm: {
+        name: '',
+        url: ''
+      },
+      dialogVisible: false // 显示弹窗
+    }
+  },
+  methods: {
+    // 弹出框关闭
+    handleClose() {
+      this.dialogVisible = false
+      this.resetForm()
+    },
+    // 重置表单
+    resetForm() {
+      this.iframeForm = {
+        name: '',
+        url: ''
+      }
+    },
+    // 新增按钮
+    handleAddClick() {
+      this.resetForm()
+      this.isAddFlag = true
+      this.dialogVisible = true
+    },
+    // 修改按钮
+    handleEditorClick(index, row) {
+      this.isAddFlag = false
+      this.iframeForm = JSON.parse(JSON.stringify(row))
+      this.dialogVisible = true
+      this.indexEditor = index
+    },
+    // 删除
+    handleDeleteClick(index) {
+      this.$confirm('确定要删除该地址吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.formData.splice(index, 1)
+        this.$emit('input', this.formData)
+        this.$emit('change', this.formData)
+        this.$message({
+          type: 'success',
+          message: '删除成功!'
+        })
+      }).catch(() => {})
+    },
+    // 确定
+    handleSaveClick() {
+      if (!this.iframeForm.url) {
+        this.$message.error('请输入iframe地址')
+        return
+      }
+      
+      const obj = JSON.parse(JSON.stringify(this.iframeForm))
+      
+      // 如果没有设置名称,则使用默认名称
+      if (!obj.name) {
+        obj.name = this.isAddFlag 
+          ? `地址${this.formData.length + 1}` 
+          : `地址${this.indexEditor + 1}`
+      }
+      
+      if (this.isAddFlag) {
+        // 新增
+        this.formData.push(obj)
+        this.dialogVisible = false
+      } else {
+        // 编辑
+        this.formData[this.indexEditor] = obj
+        this.dialogVisible = false
+      }
+      
+      this.$emit('input', this.formData)
+      this.$emit('change', this.formData)
+      this.resetForm()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.multi-iframe-manager {
+  margin-bottom: 20px;
+  
+  .editor,
+  .delete {
+    margin: 0 5px;
+    font-size: 12px;
+    color: #409EFF;
+    cursor: pointer;
+  }
+  
+  .delete {
+    color: #F56C6C;
+  }
+  
+  .url-text {
+    display: inline-block;
+    max-width: 150px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+</style> 

+ 29 - 5
report-ui/src/views/bigscreenDesigner/designer/tools/configure/texts/widget-iframe.js

@@ -24,12 +24,36 @@ export const widgetIframe = {
         value: 'iframe',
       },
       {
-        type: 'el-input-text',
-        label: '地址',
-        name: 'iframeAdress',
+        type: 'el-select',
+        label: '切换动画',
+        name: 'transitionEffect',
         required: false,
-        placeholder: '',
-        value: 'https://ajreport.beliefteam.cn/index.html',
+        placeholder: '请选择切换动画效果',
+        selectOptions: [
+          {name: '无动画', code: 'none'},
+          {name: '淡入淡出', code: 'fade'},
+          {name: '滑动', code: 'slide'},
+          {name: '缩放', code: 'zoom'}
+        ],
+        value: 'none',
+      },
+      {
+        type: 'el-input-number',
+        label: '切换间隔(秒)',
+        name: 'autoSwitchInterval',
+        required: false,
+        placeholder: '0表示不自动切换',
+        value: 0,
+      },
+      {
+        type: 'multiIframeManager',
+        label: 'iframe地址管理',
+        name: 'iframeUrls',
+        required: false,
+        value: [{
+          name: '默认地址',
+          url: 'https://ajreport.beliefteam.cn/index.html'
+        }],
       },
     ],
     // 数据

+ 198 - 22
report-ui/src/views/bigscreenDesigner/designer/widget/texts/widgetIframe.vue

@@ -1,10 +1,29 @@
 <template>
-  <iframe
-    :style="styleColor"
-    :src="this.toGetUrl(styleColor.iframeAdress)"
-    width="100%"
-    height="100%"
-  />
+  <div class="iframe-container" :style="containerStyle">
+    <transition :name="transitionName">
+      <iframe
+        v-if="hasIframes"
+        :ref="`iframe-${currentIframeIndex}`"
+        :key="`iframe-${currentIframeIndex}`"
+        :style="iframeStyle"
+        :src="processedUrl"
+        width="100%"
+        height="100%"
+        class="iframe"
+      />
+    </transition>
+    
+    <!-- 切换按钮,只在有多个iframe时显示 -->
+    <div v-if="hasMultipleUrls" class="iframe-switcher">
+      <div 
+        v-for="(iframe, index) in iframeUrls" 
+        :key="index"
+        :class="['switcher-dot', currentIframeIndex === index ? 'active' : '']"
+        @click="switchToIframe(index)"
+        :title="iframe.name || `地址${index+1}`">
+      </div>
+    </div>
+  </div>
 </template>
 
 <script>
@@ -17,61 +36,218 @@ export default {
   },
   data() {
     return {
-      options: {}
+      options: {},
+      currentIframeIndex: 0,
+      switchTimer: null,
+      processedUrl: ''
     };
   },
   computed: {
     transStyle() {
       return this.objToOne(this.options);
     },
-    styleColor() {
+    // 容器样式 - 分离样式属性
+    containerStyle() {
       return {
         position: this.ispreview ? "absolute" : "static",
         width: this.transStyle.width + "px",
         height: this.transStyle.height + "px",
         left: this.transStyle.left + "px",
         top: this.transStyle.top + "px",
-        right: this.transStyle.right + "px",
-        iframeAdress: this.transStyle.iframeAdress
+        right: this.transStyle.right + "px"
       };
+    },
+    // iframe样式
+    iframeStyle() {
+      return {
+        width: "100%",
+        height: "100%",
+        border: "none"
+      };
+    },
+    // 获取iframe地址数组
+    iframeUrls() {
+      return this.transStyle.iframeUrls || [];
+    },
+    // 判断是否有iframe地址配置
+    hasIframes() {
+      return this.iframeUrls.length > 0;
+    },
+    // 判断是否有多个iframe地址
+    hasMultipleUrls() {
+      return this.iframeUrls.length > 1;
+    },
+    // 当前显示的iframe地址
+    currentIframeUrl() {
+      if (!this.hasIframes || this.currentIframeIndex >= this.iframeUrls.length) {
+        return '';
+      }
+      return this.iframeUrls[this.currentIframeIndex].url;
+    },
+    // 根据选择的动画效果返回transition名称
+    transitionName() {
+      const effect = this.transStyle.transitionEffect;
+      if (!effect || effect === 'none') return '';
+      return effect;
     }
   },
   watch: {
     value: {
       handler(val) {
         this.options = val;
+        // 如果配置变化,重新设置自动切换
+        this.setupAutoSwitch();
+        // 更新处理后的URL
+        this.updateUrl();
+        
+        // 当iframe列表变更时,确保当前索引在有效范围内
+        if (this.hasIframes && this.currentIframeIndex >= this.iframeUrls.length) {
+          this.currentIframeIndex = 0;
+        }
       },
       deep: true
+    },
+    currentIframeUrl: {
+      handler() {
+        this.updateUrl();
+      }
     }
   },
   mounted() {
     this.options = this.value;
+    this.setupAutoSwitch();
+    this.updateUrl();
+  },
+  beforeDestroy() {
+    // 组件销毁前清除定时器
+    this.clearSwitchTimer();
   },
   methods: {
+    // 更新处理后的URL
+    updateUrl() {
+      if (this.currentIframeUrl) {
+        this.processedUrl = this.toGetUrl(this.currentIframeUrl);
+      } else {
+        this.processedUrl = '';
+      }
+    },
+    
     toGetUrl(url) {
-      if (url.indexOf('{') < 0 && url.indexOf('}' < 0)) {
-        return url
+      if (!url || (url.indexOf('{') < 0 && url.indexOf('}') < 0)) {
+        return url;
       }
-      const reg = /{[a-zA-Z0-9]*\}/g
-      const list = url.match(reg)
-      console.log(list)
-      let result = url
-      const query = this.$route.query
+      const reg = /{[a-zA-Z0-9]*\}/g;
+      const list = url.match(reg);
+      if (!list) return url;
+      
+      let result = url;
+      const query = this.$route.query;
       for (let i = 0; i < list.length; i++) {
-        const sub = list[i]
-        const key = sub.replace('{', '').replace('}', '')
-        result = result.replace(sub, query[key])
+        const sub = list[i];
+        const key = sub.replace('{', '').replace('}', '');
+        result = result.replace(sub, query[key] || '');
+      }
+      return result;
+    },
+    // 切换到指定的iframe
+    switchToIframe(index) {
+      if (index === this.currentIframeIndex || index >= this.iframeUrls.length) return;
+      
+      this.currentIframeIndex = index;
+    },
+    // 设置自动切换
+    setupAutoSwitch() {
+      this.clearSwitchTimer();
+      
+      const interval = this.transStyle.autoSwitchInterval;
+      if (interval && interval > 0 && this.iframeUrls.length > 1) {
+        this.switchTimer = setInterval(() => {
+          this.currentIframeIndex = (this.currentIframeIndex + 1) % this.iframeUrls.length;
+        }, interval * 1000);
+      }
+    },
+    // 清除自动切换定时器
+    clearSwitchTimer() {
+      if (this.switchTimer) {
+        clearInterval(this.switchTimer);
+        this.switchTimer = null;
       }
-      return result
     }
   }
 };
 </script>
 
 <style scoped lang="scss">
-iframe {
+.iframe-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+}
+
+.iframe {
   width: 100%;
   height: 100%;
   border: none;
+  position: absolute;
+  top: 0;
+  left: 0;
+}
+
+/* 切换指示器样式 */
+.iframe-switcher {
+  position: absolute;
+  bottom: 10px;
+  left: 50%;
+  transform: translateX(-50%);
+  display: flex;
+  gap: 8px;
+  z-index: 10;
+}
+
+.switcher-dot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: rgba(255, 255, 255, 0.5);
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.switcher-dot.active {
+  background-color: #fff;
+  box-shadow: 0 0 5px rgba(255, 255, 255, 0.8);
+}
+
+/* 淡入淡出动画 */
+.fade-enter-active, .fade-leave-active {
+  transition: opacity 0.5s;
+}
+.fade-enter, .fade-leave-to {
+  opacity: 0;
+}
+
+/* 滑动动画 */
+.slide-enter-active, .slide-leave-active {
+  transition: transform 0.5s;
+}
+.slide-enter {
+  transform: translateX(100%);
+}
+.slide-leave-to {
+  transform: translateX(-100%);
+}
+
+/* 缩放动画 */
+.zoom-enter-active, .zoom-leave-active {
+  transition: transform 0.5s, opacity 0.5s;
+}
+.zoom-enter {
+  transform: scale(0.8);
+  opacity: 0;
+}
+.zoom-leave-to {
+  transform: scale(1.2);
+  opacity: 0;
 }
 </style>