You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

292 lines
7.4 KiB

<template>
<el-dialog
title="数据源选择"
:visible="visible"
:close-on-click-modal="false"
width="880px"
top="5vh"
append-to-body
custom-class="data-source-dialog"
@close="handleClose"
>
<!-- 主体我们放一个专门的可滚动区域 -->
<div class="data-source-body">
<el-form label-position="top" class="data-source-form">
<!-- 可选分组显示 -->
<template v-if="hasViewGroups">
<div
v-for="group in viewGroups"
:key="group.viewSource"
class="view-group"
>
<div class="view-title">
{{ group.viewSource + ' (' + group.fields.length + '个字段)' }}
</div>
<div class="checkbox-grid">
<label
v-for="key in group.fields"
:key="key.fieldName"
class="checkbox-item">
<el-checkbox :label="key.fieldName" v-model="selectedKeys">
<span class="label-inner">
<span class="field-name" :title="getFieldDisplayName(key)">{{ getFieldDisplayName(key) }}</span>
</span>
</el-checkbox>
</label>
</div>
</div>
</template>
<template v-else>
<div class="view-group">
<div class="view-title">可用数据字段</div>
<div class="checkbox-grid">
<label
v-for="key in dataKeys"
:key="key.fieldName"
class="checkbox-item">
<el-checkbox :label="key.fieldName" v-model="selectedKeys">
<span class="label-inner">
<span class="field-name" :title="getFieldDisplayName(key)">{{ getFieldDisplayName(key) }}</span>
</span>
</el-checkbox>
</label>
</div>
</div>
</template>
</el-form>
</div>
<!-- footer 使用 el-dialog 的 footer slot(保持默认位置) -->
<div slot="footer" class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</div>
</el-dialog>
</template>
<script>
export default {
name: 'DataSourceDialog',
props: {
visible: Boolean,
dataKeys: {
type: Array,
default: () => []
},
currentText: {
type: String,
default: ''
},
sourceType: {
type: String,
default: 'text'
}
},
emits: ['update:visible', 'confirm'],
data() {
return {
selectedKeys: []
}
},
computed: {
hasViewGroups() {
return this.dataKeys && this.dataKeys.some(k => k.viewSource)
},
viewGroups() {
if (!this.hasViewGroups) return []
const groups = {}
this.dataKeys.forEach(key => {
const vs = key.viewSource || '未知视图'
if (!groups[vs]) groups[vs] = {viewSource: vs, fields: []}
groups[vs].fields.push(key)
})
return Object.values(groups)
}
},
watch: {
visible(newVal) {
if (newVal) this.initializeSelection()
},
dataKeys() {
// 当字段更新时,重新初始化选中(如果对话框是可见的)
if (this.visible) this.initializeSelection()
}
},
methods: {
getFieldDisplayName(key) {
const name = key.displayName || key.fieldName
return name + (key.fieldDescription ? `(${key.fieldDescription})` : '')
},
initializeSelection() {
if (!this.currentText || !this.dataKeys || !this.dataKeys.length) {
this.selectedKeys = []
return
}
const found = []
this.dataKeys.forEach(k => {
const pat = new RegExp(`#\\{${k.fieldName}\\}`, 'g')
if (pat.test(this.currentText)) found.push(k.fieldName)
})
this.selectedKeys = found
},
handleClose() {
this.$emit('update:visible', false)
},
handleConfirm() {
const processed = this.selectedKeys.map(s => {
const f = this.dataKeys.find(k => k.fieldName === s)
return f && f.originalFieldName ? f.originalFieldName : s
})
this.$emit('confirm', processed, this.sourceType)
this.handleClose()
}
}
}
</script>
<style scoped>
/* ---------- 对话框整体 ---------- */
.data-source-dialog {
/* 确保 dialog 在 body 上并覆盖画布 */
}
.el-dialog__wrapper {
z-index: 3000 !important; /* 提升到高于页面画布的层级 */
}
/* 把 el-dialog 设置成 flex 布局:header/body/footer 三段式 */
.data-source-dialog .el-dialog {
display: flex;
flex-direction: column;
max-height: 90vh;
box-sizing: border-box;
}
/* 中间真正滚动的区域:固定高度(max)并可滚动,避免内容跑出弹窗 */
.data-source-body {
flex: 1 1 auto;
overflow: auto;
padding: 12px 18px;
box-sizing: border-box;
}
/* 表单内容器布局 */
.data-source-form {
margin: 0;
}
/* 分组标题 */
.view-group {
margin-bottom: 12px;
padding: 10px;
background: #fafbfc;
border: 1px solid #e8eef0;
border-radius: 8px;
box-sizing: border-box;
}
.view-title {
color: #17b3a3;
font-weight: 700;
margin-bottom: 8px;
font-size: 14px;
}
/* ---------- 复选框网格更紧凑 ---------- */
.checkbox-grid {
display: flex;
flex-wrap: wrap;
margin: -4px; /* 缩小外边距 */
box-sizing: border-box;
}
.checkbox-item {
padding: 4px;
margin: 4px;
width: calc(20% - 8px); /* 默认 5 列布局,更紧凑 */
min-width: 150px; /* 允许更窄 */
background: #fff;
border: 1px solid #e6eef0;
border-radius: 4px; /* 缩小圆角 */
display: flex;
align-items: flex-start;
cursor: pointer;
transition: background 0.12s;
}
.checkbox-item:hover {
background: #f7fdfb;
border-color: #cfeee8;
}
/* 内部的文字和 badge */
.label-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 4px; /* 缩小间距 */
width: 100%;
}
.field-name {
display: block;
font-size: 12px;
color: #333;
line-height: 1.4;
white-space: nowrap; /* 单行显示 */
overflow: hidden; /* 隐藏溢出 */
text-overflow: ellipsis; /* 超出显示 ... */
max-width: 120px; /* 限制最大宽度 */
}
.duplicate-badge {
padding: 1px 4px;
font-size: 10px; /* 更小的 badge */
border-radius: 3px;
}
/* 缩小复选框 */
.checkbox-item .el-checkbox__inner {
width: 14px !important;
height: 14px !important;
}
/* 响应式:小屏幕保持紧凑 */
@media (max-width: 1100px) {
.checkbox-item { width: calc(25% - 8px); min-width: 140px; } /* 4 列 */
}
@media (max-width: 840px) {
.checkbox-item { width: calc(33.333% - 8px); min-width: 130px; } /* 3 列 */
}
@media (max-width: 520px) {
.checkbox-item { width: calc(50% - 8px); min-width: auto; } /* 2 列 */
}
/* ---------- Footer 固定在对话框底部(但不覆写内容) ---------- */
.dialog-footer {
padding: 10px 18px;
border-top: 1px solid #e8eef0;
display: flex;
justify-content: center;
gap: 14px;
background: #fff;
box-sizing: border-box;
z-index: 40;
}
/* 按钮样式保持 */
.dialog-footer .el-button {
min-width: 88px;
height: 36px;
font-weight: 600;
border-radius: 6px;
}
.dialog-footer .el-button--primary {
background: linear-gradient(135deg, #17b3a3 0%, #1dc5ef 100%);
color: #fff;
border: none;
}
</style>