|
|
@@ -3,6 +3,10 @@
|
|
|
<div class="calculator-header">
|
|
|
<el-page-header @back="handleBack" content="费用计算配置"/>
|
|
|
<div class="header-actions">
|
|
|
+ <el-button @click="openFieldSelectDialog">
|
|
|
+ <el-icon><Plus/></el-icon>
|
|
|
+ 管理字段
|
|
|
+ </el-button>
|
|
|
<el-button type="primary" @click="saveReport">
|
|
|
保存报表
|
|
|
</el-button>
|
|
|
@@ -13,46 +17,6 @@
|
|
|
id="gc-designer-container"
|
|
|
@designer-initialized="handleDesignerInit"
|
|
|
/>
|
|
|
- <!-- 自定义字段列表面板 - 固定显示 -->
|
|
|
- <div class="field-list-panel" id="field-list-panel">
|
|
|
- <div class="panel-header">
|
|
|
- <span class="header-label">费用字段</span>
|
|
|
- <div class="header-actions">
|
|
|
- <el-icon class="action-icon" @click="openFieldSelectDialog">
|
|
|
- <Plus/>
|
|
|
- </el-icon>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="field-tree">
|
|
|
- <div
|
|
|
- v-for="(field, index) in fieldData"
|
|
|
- :key="field.code"
|
|
|
- class="item-content"
|
|
|
- @mouseenter="currentHoverIndex = index"
|
|
|
- @mouseleave="currentHoverIndex = -1"
|
|
|
- >
|
|
|
- <div
|
|
|
- class="name-label"
|
|
|
- :data-bindingpath="field.code"
|
|
|
- :data-coltype="field.colType"
|
|
|
- :data-valtype="field.valType"
|
|
|
- draggable="true"
|
|
|
- @dragstart="handleDragStart($event, field)"
|
|
|
- >
|
|
|
- <span class="field-name">{{ field.name }}</span>
|
|
|
- <span class="field-code">{{ field.code }}</span>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- class="icons"
|
|
|
- :class="{ 'show': currentHoverIndex === index }"
|
|
|
- >
|
|
|
- <el-icon class="icon-btn" title="移除字段" @click.stop="removeField(index)">
|
|
|
- <Delete/>
|
|
|
- </el-icon>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
<!-- 字段选择对话框 -->
|
|
|
<el-dialog v-model="fieldSelectDialogVisible" title="选择字段" width="700px" :close-on-click-modal="false"
|
|
|
@@ -137,8 +101,6 @@
|
|
|
<el-button type="primary" @click="confirmFieldForm">确定</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
- <!-- 拖拽高亮装饰 -->
|
|
|
- <div ref="decorationRef" class="drag-highlight"></div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -171,10 +133,6 @@ const emit = defineEmits(['back', 'save'])
|
|
|
let designer: any = null
|
|
|
let spread: any = null
|
|
|
|
|
|
-// 拖拽相关状态
|
|
|
-let draggedField: any = null
|
|
|
-const decorationRef = ref<HTMLElement | null>(null)
|
|
|
-
|
|
|
// 当前悬停的字段索引,用于控制图标的显示
|
|
|
const currentHoverIndex = ref(-1)
|
|
|
|
|
|
@@ -311,12 +269,12 @@ const confirmAddFields = async () => {
|
|
|
|
|
|
// 添加到已选字段
|
|
|
fieldData.value.push(...newFields)
|
|
|
-
|
|
|
// 刷新字段列表
|
|
|
await loadAllFields()
|
|
|
|
|
|
fieldSelectDialogVisible.value = false
|
|
|
message.success('添加成功')
|
|
|
+ syncFieldsToDesigner()
|
|
|
} catch (error) {
|
|
|
console.error('添加字段失败:', error)
|
|
|
message.error('添加字段失败')
|
|
|
@@ -351,6 +309,7 @@ const removeField = async (index) => {
|
|
|
// 从列表中移除
|
|
|
fieldData.value.splice(index, 1)
|
|
|
message.success('移除成功')
|
|
|
+ syncFieldsToDesigner()
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('移除字段失败:', error)
|
|
|
@@ -401,7 +360,9 @@ const confirmFieldForm = async () => {
|
|
|
|
|
|
// 刷新字段列表
|
|
|
await loadAllFields()
|
|
|
+ filterSelectedFields()
|
|
|
fieldFormDialogVisible.value = false
|
|
|
+ syncFieldsToDesigner()
|
|
|
} catch (error) {
|
|
|
console.error('操作失败:', error)
|
|
|
message.error('操作失败')
|
|
|
@@ -427,6 +388,7 @@ const deleteField = async (field: DynamicTbFeeColVO) => {
|
|
|
if (index > -1) {
|
|
|
fieldData.value.splice(index, 1)
|
|
|
}
|
|
|
+ syncFieldsToDesigner()
|
|
|
} catch (error) {
|
|
|
console.error('删除失败:', error)
|
|
|
message.error('删除失败')
|
|
|
@@ -518,113 +480,34 @@ const handleBack = () => {
|
|
|
emit('back')
|
|
|
}
|
|
|
|
|
|
-// 拖拽开始
|
|
|
-const handleDragStart = (e: DragEvent, field: any) => {
|
|
|
- draggedField = field
|
|
|
- if (e.target) {
|
|
|
- (e.target as HTMLElement).style.opacity = '0.5'
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 初始化拖拽事件监听
|
|
|
-const initDragEvents = () => {
|
|
|
- const host = document.getElementById('gc-designer-container')
|
|
|
- if (!host) return
|
|
|
-
|
|
|
- // 为字段添加拖拽开始事件
|
|
|
- const nameLabels = document.querySelectorAll('.name-label')
|
|
|
- nameLabels.forEach(label => {
|
|
|
- label.addEventListener('dragstart', (e) => {
|
|
|
- const target = e.target as HTMLElement
|
|
|
- const fieldCode = target.getAttribute('data-bindingpath')
|
|
|
- if (fieldCode) {
|
|
|
- const field = fieldData.value.find(f => f.code === fieldCode)
|
|
|
- if (field) {
|
|
|
- handleDragStart(e, field)
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- })
|
|
|
-
|
|
|
- // 拖拽结束
|
|
|
- host.addEventListener('dragend', (e) => {
|
|
|
- if (draggedField) {
|
|
|
- const labels = document.querySelectorAll('.name-label')
|
|
|
- labels.forEach(label => {
|
|
|
- (label as HTMLElement).style.opacity = '1'
|
|
|
- })
|
|
|
- if (decorationRef.value) {
|
|
|
- decorationRef.value.style.display = 'none'
|
|
|
- }
|
|
|
- draggedField = null
|
|
|
+// 将字段同步到设计器的字段列表
|
|
|
+const syncFieldsToDesigner = async () => {
|
|
|
+ if (!designer) return
|
|
|
+ // 构建 bindingPathSchema
|
|
|
+ const properties = {}
|
|
|
+ fieldData.value.forEach(field => {
|
|
|
+ properties[field.code] = {
|
|
|
+ dataFieldType: 'text',
|
|
|
+ type: field.valType === 'NUMBER' ? 'number' : 'string'
|
|
|
}
|
|
|
})
|
|
|
-
|
|
|
- // 拖拽悬停
|
|
|
- host.addEventListener('dragover', (e) => {
|
|
|
- e.preventDefault()
|
|
|
- if (!draggedField || !spread) return
|
|
|
-
|
|
|
- const rect = host.getBoundingClientRect()
|
|
|
- const x = e.clientX - rect.left
|
|
|
- const y = e.clientY - rect.top
|
|
|
-
|
|
|
- const sheet = spread.getActiveSheet()
|
|
|
- const target = sheet.hitTest(x, y)
|
|
|
-
|
|
|
- if (target.row + 2 !== undefined && target.col !== undefined) {
|
|
|
- highlightCell(target.row + 2, target.col)
|
|
|
- } else {
|
|
|
- if (decorationRef.value) {
|
|
|
- decorationRef.value.style.display = 'none'
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- // 放置
|
|
|
- host.addEventListener('drop', (e) => {
|
|
|
- e.preventDefault()
|
|
|
- if (!draggedField || !spread) return
|
|
|
-
|
|
|
- const rect = host.getBoundingClientRect()
|
|
|
- const x = e.clientX - rect.left
|
|
|
- const y = e.clientY - rect.top
|
|
|
-
|
|
|
- const sheet = spread.getActiveSheet()
|
|
|
- const target = sheet.hitTest(x, y)
|
|
|
-
|
|
|
- if (target.row - 11 !== undefined && target.col !== undefined) {
|
|
|
- sheet.setBindingPath(target.row - 11, target.col, draggedField.code)
|
|
|
- message.success(`已绑定字段: ${draggedField.name}`)
|
|
|
- }
|
|
|
-
|
|
|
- if (decorationRef.value) {
|
|
|
- decorationRef.value.style.display = 'none'
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// 高亮单元格
|
|
|
-const highlightCell = (row: number, col: number) => {
|
|
|
- if (!spread || !decorationRef.value) return
|
|
|
-
|
|
|
- const sheet = spread.getActiveSheet()
|
|
|
- const cellRect = sheet.getCellRect(row, col)
|
|
|
- const host = document.getElementById('gc-designer-container')
|
|
|
- if (!host || !cellRect) return
|
|
|
-
|
|
|
- decorationRef.value.style.display = 'block'
|
|
|
- decorationRef.value.style.position = 'absolute'
|
|
|
- decorationRef.value.style.border = '2px solid #409eff'
|
|
|
- decorationRef.value.style.boxShadow = '0 0 8px rgba(64, 158, 255, 0.5)'
|
|
|
- decorationRef.value.style.width = (cellRect.width - 4) + 'px'
|
|
|
- decorationRef.value.style.height = (cellRect.height - 4) + 'px'
|
|
|
- decorationRef.value.style.left = cellRect.x + 'px'
|
|
|
- decorationRef.value.style.top = cellRect.y + 'px'
|
|
|
- decorationRef.value.style.zIndex = '9999'
|
|
|
- decorationRef.value.style.pointerEvents = 'none'
|
|
|
- decorationRef.value.style.backgroundColor = 'rgba(64, 158, 255, 0.1)'
|
|
|
- decorationRef.value.style.borderRadius = '2px'
|
|
|
+ const schema = {
|
|
|
+ type: 'object',
|
|
|
+ $schema: 'http://json-schema.org/draft-04/schema#',
|
|
|
+ properties
|
|
|
+ }
|
|
|
+ // 构建 bindingPathName
|
|
|
+ const bindingPathName = fieldData.value.map(field => ({
|
|
|
+ dataFieldType: 'text',
|
|
|
+ field: field.code,
|
|
|
+ displayName: field.name,
|
|
|
+ type: field.valType === 'NUMBER' ? 'number' : 'string',
|
|
|
+ isVerify: '0'
|
|
|
+ }))
|
|
|
+ // 使用原生 GC Designer API 设置字段树
|
|
|
+ await designer.setData('treeNodeFromJson', JSON.stringify(schema))
|
|
|
+ await designer.setData('oldTreeNodeFromJson', JSON.stringify(schema))
|
|
|
+ await designer.setData('bindingPathDataJSON', JSON.stringify(bindingPathName))
|
|
|
}
|
|
|
|
|
|
const handleDesignerInit = (instance: any) => {
|
|
|
@@ -639,12 +522,12 @@ const handleDesignerInit = (instance: any) => {
|
|
|
designModeCommand.execute(designer)
|
|
|
|
|
|
spread = instance.getWorkbook()
|
|
|
- initDragEvents()
|
|
|
- console.log(feeFileUrl)
|
|
|
// 加载现有报表
|
|
|
if (feeFileUrl.value) {
|
|
|
loadExistingReport()
|
|
|
}
|
|
|
+ // 初始同步字段
|
|
|
+ syncFieldsToDesigner()
|
|
|
}
|
|
|
|
|
|
const init = async (data?: any) => {
|
|
|
@@ -657,6 +540,8 @@ const init = async (data?: any) => {
|
|
|
await loadAllFields()
|
|
|
// 根据projectId过滤已选择的字段
|
|
|
filterSelectedFields()
|
|
|
+ // 同步字段到设计器
|
|
|
+ syncFieldsToDesigner()
|
|
|
}
|
|
|
|
|
|
defineExpose({init})
|
|
|
@@ -754,7 +639,6 @@ defineExpose({init})
|
|
|
|
|
|
.name-label {
|
|
|
padding: 10px 32px 10px 12px;
|
|
|
- cursor: move;
|
|
|
border-radius: 4px;
|
|
|
font-size: 13px;
|
|
|
color: #606266;
|
|
|
@@ -896,14 +780,6 @@ defineExpose({init})
|
|
|
margin-left: 8px;
|
|
|
}
|
|
|
|
|
|
-.drag-highlight {
|
|
|
- display: none;
|
|
|
- position: absolute;
|
|
|
- z-index: 9999;
|
|
|
- pointer-events: none;
|
|
|
- border-radius: 2px;
|
|
|
-}
|
|
|
-
|
|
|
.field-tree::-webkit-scrollbar,
|
|
|
.field-select-list::-webkit-scrollbar {
|
|
|
width: 6px;
|