|
|
|
@ -3,30 +3,65 @@ |
|
|
|
title="数据源选择" |
|
|
|
:visible="visible" |
|
|
|
:close-on-click-modal="false" |
|
|
|
width="800px" |
|
|
|
width="880px" |
|
|
|
top="5vh" |
|
|
|
append-to-body |
|
|
|
custom-class="data-source-dialog" |
|
|
|
@close="handleClose" |
|
|
|
> |
|
|
|
<div class="data-source-content"> |
|
|
|
<el-form label-position="top"> |
|
|
|
<el-form-item label="可用数据字段"> |
|
|
|
<el-checkbox-group v-model="selectedKeys" class="checkbox-grid"> |
|
|
|
<el-checkbox |
|
|
|
v-for="key in dataKeys" |
|
|
|
:key="key.fieldName" |
|
|
|
:label="key.fieldName" |
|
|
|
class="checkbox-item" |
|
|
|
> |
|
|
|
{{ key.fieldName+(key.fieldDescription?('('+key.fieldDescription +')'):'')}} |
|
|
|
</el-checkbox> |
|
|
|
</el-checkbox-group> |
|
|
|
</el-form-item> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 主体:我们放一个专门的可滚动区域 --> |
|
|
|
<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" |
|
|
|
:class="{ 'duplicate-field': key.isDuplicate }" |
|
|
|
> |
|
|
|
<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" |
|
|
|
:class="{ 'duplicate-field': key.isDuplicate }" |
|
|
|
> |
|
|
|
<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> |
|
|
|
@ -43,14 +78,13 @@ export default { |
|
|
|
type: Array, |
|
|
|
default: () => [] |
|
|
|
}, |
|
|
|
// 当前文本内容,用于分析应该勾选哪些数据字段 |
|
|
|
currentText: { |
|
|
|
type: String, |
|
|
|
default: '' |
|
|
|
}, |
|
|
|
sourceType: { |
|
|
|
type: String, |
|
|
|
default: 'text' // 默认是文本类型 |
|
|
|
default: 'text' |
|
|
|
} |
|
|
|
}, |
|
|
|
emits: ['update:visible', 'confirm'], |
|
|
|
@ -59,41 +93,56 @@ export default { |
|
|
|
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() |
|
|
|
} |
|
|
|
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.length) { |
|
|
|
if (!this.currentText || !this.dataKeys || !this.dataKeys.length) { |
|
|
|
this.selectedKeys = [] |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 分析文本内容,找出其中包含的数据字段 |
|
|
|
const foundKeys = [] |
|
|
|
this.dataKeys.forEach(key => { |
|
|
|
// 检查文本中是否包含 #{key} 格式的数据字段 |
|
|
|
const pattern = new RegExp(`#\\{${key.fieldName}\\}`, 'g') |
|
|
|
|
|
|
|
if (pattern.test(this.currentText)) { |
|
|
|
foundKeys.push(key.fieldName) |
|
|
|
} |
|
|
|
const found = [] |
|
|
|
this.dataKeys.forEach(k => { |
|
|
|
const pat = new RegExp(`#\\{${k.fieldName}\\}`, 'g') |
|
|
|
if (pat.test(this.currentText)) found.push(k.fieldName) |
|
|
|
}) |
|
|
|
this.selectedKeys = foundKeys |
|
|
|
this.selectedKeys = found |
|
|
|
}, |
|
|
|
|
|
|
|
handleClose() { |
|
|
|
this.$emit('update:visible', false) |
|
|
|
}, |
|
|
|
|
|
|
|
handleConfirm() { |
|
|
|
this.$emit('confirm', this.selectedKeys, this.sourceType) |
|
|
|
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() |
|
|
|
} |
|
|
|
} |
|
|
|
@ -101,214 +150,147 @@ export default { |
|
|
|
</script> |
|
|
|
|
|
|
|
<style scoped> |
|
|
|
/* ---------- 对话框整体 ---------- */ |
|
|
|
.data-source-dialog { |
|
|
|
max-width: 98vw !important; |
|
|
|
max-height: 92vh !important; |
|
|
|
border-radius: 12px !important; |
|
|
|
box-shadow: 0 12px 32px rgba(0,0,0,0.15) !important; |
|
|
|
background: #fff !important; |
|
|
|
/* 确保 dialog 在 body 上并覆盖画布 */ |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__header { |
|
|
|
padding: 16px 20px 8px !important; |
|
|
|
border-bottom: 1px solid #e4e7ed !important; |
|
|
|
background: #fff !important; |
|
|
|
.el-dialog__wrapper { |
|
|
|
z-index: 3000 !important; /* 提升到高于页面画布的层级 */ |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__title { |
|
|
|
font-size: 20px !important; |
|
|
|
font-weight: 700 !important; |
|
|
|
color: #222 !important; |
|
|
|
/* 把 el-dialog 设置成 flex 布局:header/body/footer 三段式 */ |
|
|
|
.data-source-dialog .el-dialog { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
max-height: 90vh; |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__body { |
|
|
|
padding: 10px 20px 0 20px !important; |
|
|
|
max-height: 70vh !important; |
|
|
|
min-height: 80px; |
|
|
|
overflow-y: auto !important; |
|
|
|
background: #fff !important; |
|
|
|
/* 中间真正滚动的区域:固定高度(max)并可滚动,避免内容跑出弹窗 */ |
|
|
|
.data-source-body { |
|
|
|
flex: 1 1 auto; |
|
|
|
overflow: auto; |
|
|
|
padding: 12px 18px; |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-content { |
|
|
|
width: 100%; |
|
|
|
min-height: 60px; |
|
|
|
/* 表单内容器布局 */ |
|
|
|
.data-source-form { |
|
|
|
margin: 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* 表单项样式优化 */ |
|
|
|
.el-form-item { |
|
|
|
/* 分组标题 */ |
|
|
|
.view-group { |
|
|
|
margin-bottom: 12px; |
|
|
|
padding: 10px; |
|
|
|
background: #fafbfc; |
|
|
|
border: 1px solid #e8eef0; |
|
|
|
border-radius: 8px; |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
|
|
|
|
.el-form-item:last-child { |
|
|
|
margin-bottom: 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.el-form-item__label { |
|
|
|
font-size: 14px !important; |
|
|
|
font-weight: 600 !important; |
|
|
|
color: #303133 !important; |
|
|
|
padding-bottom: 8px !important; |
|
|
|
line-height: 1.4 !important; |
|
|
|
.view-title { |
|
|
|
color: #17b3a3; |
|
|
|
font-weight: 700; |
|
|
|
margin-bottom: 8px; |
|
|
|
font-size: 14px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 复选框网格布局 */ |
|
|
|
/* ---------- 复选框网格更紧凑 ---------- */ |
|
|
|
.checkbox-grid { |
|
|
|
display: grid; |
|
|
|
grid-template-columns: repeat(4, 1fr); /* 固定一行4个 */ |
|
|
|
gap: 8px 10px; |
|
|
|
margin-top: 6px; |
|
|
|
padding: 0; |
|
|
|
background: none; |
|
|
|
border: none; |
|
|
|
border-radius: 0; |
|
|
|
/* 让复选框区域高度自适应内容 */ |
|
|
|
max-height: none; |
|
|
|
overflow-y: visible; |
|
|
|
min-height: 80px; |
|
|
|
display: flex; |
|
|
|
flex-wrap: wrap; |
|
|
|
margin: -4px; /* 缩小外边距 */ |
|
|
|
box-sizing: border-box; |
|
|
|
} |
|
|
|
|
|
|
|
/* 复选框项样式 */ |
|
|
|
.checkbox-item { |
|
|
|
margin: 0 !important; |
|
|
|
display: flex !important; |
|
|
|
align-items: center !important; |
|
|
|
height: 28px; |
|
|
|
padding: 2px 6px; |
|
|
|
padding: 4px; |
|
|
|
margin: 4px; |
|
|
|
width: calc(20% - 8px); /* 默认 5 列布局,更紧凑 */ |
|
|
|
min-width: 150px; /* 允许更窄 */ |
|
|
|
background: #fff; |
|
|
|
border: 1px solid #e4e7ed; |
|
|
|
border-radius: 4px; |
|
|
|
transition: all 0.15s cubic-bezier(.4,0,.2,1); |
|
|
|
border: 1px solid #e6eef0; |
|
|
|
border-radius: 4px; /* 缩小圆角 */ |
|
|
|
display: flex; |
|
|
|
align-items: flex-start; |
|
|
|
cursor: pointer; |
|
|
|
box-shadow: none; |
|
|
|
transition: background 0.12s; |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item:hover { |
|
|
|
border-color: #17b3a3; |
|
|
|
background: #f0fdfa; |
|
|
|
transform: translateY(-1px) scale(1.01); |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item.is-checked { |
|
|
|
border-color: #17b3a3; |
|
|
|
background: #e6f7f4; |
|
|
|
background: #f7fdfb; |
|
|
|
border-color: #cfeee8; |
|
|
|
} |
|
|
|
|
|
|
|
/* 复选框内部元素样式 */ |
|
|
|
.checkbox-item .el-checkbox__input { |
|
|
|
margin-right: 8px; |
|
|
|
/* 内部的文字和 badge */ |
|
|
|
.label-inner { |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: space-between; |
|
|
|
gap: 4px; /* 缩小间距 */ |
|
|
|
width: 100%; |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item .el-checkbox__inner { |
|
|
|
width: 16px !important; |
|
|
|
height: 16px !important; |
|
|
|
border: 2px solid #dcdfe6 !important; |
|
|
|
border-radius: 4px !important; |
|
|
|
transition: all 0.2s ease !important; |
|
|
|
.field-name { |
|
|
|
display: block; |
|
|
|
font-size: 12px; |
|
|
|
color: #333; |
|
|
|
line-height: 1.4; |
|
|
|
white-space: nowrap; /* 单行显示 */ |
|
|
|
overflow: hidden; /* 隐藏溢出 */ |
|
|
|
text-overflow: ellipsis; /* 超出显示 ... */ |
|
|
|
max-width: 120px; /* 限制最大宽度 */ |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item .el-checkbox__inner:hover { |
|
|
|
border-color: #409eff !important; |
|
|
|
.duplicate-badge { |
|
|
|
padding: 1px 4px; |
|
|
|
font-size: 10px; /* 更小的 badge */ |
|
|
|
border-radius: 3px; |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item .el-checkbox__input.is-checked .el-checkbox__inner { |
|
|
|
background-color: #409eff !important; |
|
|
|
border-color: #409eff !important; |
|
|
|
/* 缩小复选框 */ |
|
|
|
.checkbox-item .el-checkbox__inner { |
|
|
|
width: 14px !important; |
|
|
|
height: 14px !important; |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item .el-checkbox__inner::after { |
|
|
|
height: 7px !important; |
|
|
|
left: 4px !important; |
|
|
|
top: 1px !important; |
|
|
|
width: 3px !important; |
|
|
|
border: 2px solid #fff !important; |
|
|
|
border-left: 0 !important; |
|
|
|
border-top: 0 !important; |
|
|
|
/* 响应式:小屏幕保持紧凑 */ |
|
|
|
@media (max-width: 1100px) { |
|
|
|
.checkbox-item { width: calc(25% - 8px); min-width: 140px; } /* 4 列 */ |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item .el-checkbox__label { |
|
|
|
font-size: 13px !important; |
|
|
|
font-weight: 500 !important; |
|
|
|
color: #333 !important; |
|
|
|
padding-left: 0 !important; |
|
|
|
@media (max-width: 840px) { |
|
|
|
.checkbox-item { width: calc(33.333% - 8px); min-width: 130px; } /* 3 列 */ |
|
|
|
} |
|
|
|
|
|
|
|
.checkbox-item.is-checked .el-checkbox__label { |
|
|
|
color: #17b3a3 !important; |
|
|
|
@media (max-width: 520px) { |
|
|
|
.checkbox-item { width: calc(50% - 8px); min-width: auto; } /* 2 列 */ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 对话框底部按钮 */ |
|
|
|
/* ---------- Footer 固定在对话框底部(但不覆写内容) ---------- */ |
|
|
|
.dialog-footer { |
|
|
|
text-align: center; |
|
|
|
padding-top: 12px; |
|
|
|
border-top: 1px solid #e4e7ed; |
|
|
|
margin-top: 350px; |
|
|
|
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: 80px; |
|
|
|
min-width: 88px; |
|
|
|
height: 36px; |
|
|
|
font-size: 14px; |
|
|
|
border-radius: 6px; |
|
|
|
transition: all 0.2s; |
|
|
|
font-weight: 600; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-footer .el-button + .el-button { |
|
|
|
margin-left: 14px; |
|
|
|
border-radius: 6px; |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-footer .el-button--primary { |
|
|
|
background: linear-gradient(135deg, #17b3a3 0%, #1dc5ef 100%); |
|
|
|
color: #fff; |
|
|
|
border: none; |
|
|
|
box-shadow: 0 2px 8px rgba(23,179,163,0.12); |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-footer .el-button--primary:hover { |
|
|
|
background: linear-gradient(135deg, #1dc5ef 0%, #17b3a3 100%); |
|
|
|
transform: translateY(-1px) scale(1.03); |
|
|
|
box-shadow: 0 4px 12px rgba(23,179,163,0.18); |
|
|
|
} |
|
|
|
|
|
|
|
.dialog-footer .el-button--primary:active { |
|
|
|
transform: translateY(0); |
|
|
|
box-shadow: 0 2px 6px rgba(64, 158, 255, 0.3); |
|
|
|
} |
|
|
|
|
|
|
|
/* 对话框整体样式优化 */ |
|
|
|
.data-source-dialog { |
|
|
|
border-radius: 12px !important; |
|
|
|
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15) !important; |
|
|
|
max-height: 85vh !important; |
|
|
|
overflow: visible !important; |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__header { |
|
|
|
padding: 20px 24px 16px !important; |
|
|
|
border-bottom: 1px solid #e4e7ed !important; |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__title { |
|
|
|
font-size: 18px !important; |
|
|
|
font-weight: 600 !important; |
|
|
|
color: #303133 !important; |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__body { |
|
|
|
padding: 20px 24px 0 24px !important; |
|
|
|
max-height: 65vh !important; |
|
|
|
overflow-y: auto !important; |
|
|
|
} |
|
|
|
|
|
|
|
.data-source-dialog .el-dialog__footer { |
|
|
|
padding: 16px 24px 20px !important; |
|
|
|
border-top: 1px solid #e4e7ed !important; |
|
|
|
background: #fff !important; |
|
|
|
position: sticky !important; |
|
|
|
bottom: 0 !important; |
|
|
|
} |
|
|
|
</style> |