mirror of
https://gitee.com/anji-plus/report.git
synced 2026-03-20 09:38:35 +08:00
!211 iframe组件添加定时切换和切换动画效果
Merge pull request !211 from lma/cherry-pick-111
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
@@ -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'
|
||||
}],
|
||||
},
|
||||
],
|
||||
// 数据
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user