Files
Web_PanelSelection_Vue_Prod/src/pages/panelselection/index.vue
2025-11-18 08:33:12 +08:00

6085 lines
195 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="container">
<!-- 左侧区域 -->
<div class="left-panel">
<div class="left-top">
<el-select ref="projectSelect"
v-model="selectedProjectId"
placeholder="请选择项目"
filterable
clearable
size="small"
@change="handleProjectChangeApp"
@visible-change="onSelectVisibleChange"
style="width: 100%; margin: 0px;"
:disabled="isProjectLocked">
<el-option v-for="project in projectList"
:key="project.id"
:label="project.projectName"
:value="project.id" />
</el-select>
</div>
<div class="left-bottom">
<div class="left-bottom-top">
<el-tree ref="projectTree" :data="treeData"
node-key="id"
:props="treeProps"
:expand-on-click-node="true"
default-expand-all
@node-click="handleNodeClick"
class="full-height-tree">
<template #default="{ node, data }">
<div class="tree-node-container">
<span class="tree-node-label">{{ node.label }}</span>
<div class="tree-node-actions">
<!-- 项目节点 -->
<template v-if="data.type === 'project'">
<el-button size="small" link type="success" @click.stop="addRoomType(data)" v-if="data.project.id !== 1">新增房型</el-button>
<el-button size="small" link type="primary">&emsp;&emsp;</el-button>
<el-button v-if="data.project.projectCode !== '1'" size="small" link type="danger" @click.stop="removeProjectNode(data)">移除</el-button>
<el-popover placement="bottom-end" :width="1100" trigger="click">
<template #reference>
<el-button size="small" class="detail-btn" link type="info" v-if="data.project.id !== 1">详情</el-button>
</template>
<el-descriptions border column="4" size="small" class="details">
<el-descriptions-item label="项目名称">{{ data.project.projectName }}</el-descriptions-item>
<el-descriptions-item label="详细地址">{{ data.project.addressDetail }}</el-descriptions-item>
<el-descriptions-item label="所在地区">{{ data.project.quyu }} · {{ data.project.shengfen }}</el-descriptions-item>
<el-descriptions-item label="项目编号">{{ data.project.projectCode }}</el-descriptions-item>
<el-descriptions-item label="审批状态">
<el-tag size="small" :type="data.project.shengpiStatus === '3' ? 'success' : 'warning'">
{{ data.project.shengpiStatus === '3' ? '已批准' : '待审批' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="报备信息">{{ data.project.customization }}</el-descriptions-item>
<el-descriptions-item label="房间信息">{{ data.project.roomTypeCount }} 种房型 · {{ data.project.roomTotalCount }} 间客房</el-descriptions-item>
<el-descriptions-item label="最后更新">{{ formatDate(data.project.updatetime) }}</el-descriptions-item>
</el-descriptions>
</el-popover>
</template>
<!-- 房型节点 -->
<template v-else-if="data.type === 'room'">
<el-button size="small" link type="success" @click.stop="addPanel(data)">新增面板组</el-button>
<el-button size="small" link type="primary" @click.stop="editRoomName(data)">修改</el-button>
<el-button size="small" link type="danger" @click.stop="deleteRoom(data)">删除</el-button>
</template>
<!-- 面板组节点 -->
<template v-else-if="data.type === 'panel'">
<el-button size="small" link type="primary" @click.stop="editPanelName(data)">修改</el-button>
<el-button size="small" link type="danger" @click.stop="deletePanel(data)">删除</el-button>
</template>
</div>
</div>
</template>
</el-tree>
</div>
<div class="left-bottom-bottom">
<div class="section-header">
<span>面板单元列表</span>
<el-button size="small" type="info" @click="showAddPanelUnitDialogView" plain :disabled="!selectedProjectId">
添加面板单元
</el-button>
</div>
<div class="table-content">
<el-table :data="unitTableData"
border
height="100%"
style="width: 100%;"
size="small"
@row-click="handleUnitRowClick">
<el-table-column prop="panel_name"
label="面板单元名称"
min-width="180"
show-overflow-tooltip />
<el-table-column label="操作" width="120" align="center">
<template #default="{ row }">
<el-button size="small"
link
:type="currentPanelUnit === row.guid ? 'info' : 'primary'"
@click.stop="editPanelUnit(row)">
{{ currentPanelUnit === row.guid ? '编辑中' : '编辑' }}
</el-button>
<el-button size="small" link type="danger" @click.stop="deletePanelUnit(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
<!-- 右侧区域 -->
<div class="right-panel">
<!-- 面板组合配置 -->
<div class="right-middle">
<div class="panel-unit-config-container">
<!-- 左边预览图区域 -->
<div class="preview-section-top">
<div class="section-header">
<span>面板组预览图{{ currentPanelNode?.panelInfo?.thumbnail_small === '生成中' ? '(生成中)' : '' }}</span>
<div>
<el-tooltip content="手动刷新面板组预览图" placement="top">
<el-button size="small" type="info" @click="refreshPanelGroupPreview"
:disabled="!currentPanelNode">
刷新预览
</el-button>
</el-tooltip>
</div>
</div>
<div class="preview-content">
<div v-if="panelGroupPreview" class="preview-image">
<el-image style="max-width: 100%; max-height: 100%; width: 100%; height: 100%"
:src="panelGroupPreview"
:preview-src-list="[panelGroupPreview]"
show-progress
fit="contain" />
</div>
<div v-else class="preview-placeholder">
<el-icon :size="35"><Picture /></el-icon>
<el-text v-if="currentPanelNode?.panelInfo?.thumbnail_small === '生成中'">生成中请稍候</el-text>
<el-text v-else>暂无面板组预览图</el-text>
</div>
</div>
</div>
<div class="table-section">
<div class="section-header">
<span>面板组合配置</span>
<div>
<el-tooltip :content="!currentPanelNode ? '请先选择面板组' : !isAllPanelsConfigured ? '请先完成所有面板的配置' : (currentPanelNode?.panelInfo?.cdr_filename ? '点击下载CDR文件' : '点击生成CDR文件')" placement="top">
<el-button v-if="currentPanelNode?.panelInfo?.cdr_filename"
size="small"
type="success"
@click="downloadCDRFile"
:disabled="!currentPanelNode || !isAllPanelsConfigured">
下载CDR文件
</el-button>
<el-button v-else
size="small"
type="primary"
@click="showPreview(1)"
:disabled="!currentPanelNode || !isAllPanelsConfigured">
生成CDR文件
</el-button>
</el-tooltip>
<el-tooltip :content="!currentPanelNode ? '请先选择面板组' : !isAllPanelsConfigured ? '请先完成所有面板的配置' : '点击生成预览图'" placement="top">
<el-button size="small" type="primary" @click="showPreview(0)"
:disabled="!currentPanelNode || !isAllPanelsConfigured">
生成预览图
</el-button>
</el-tooltip>
</div>
</div>
<!-- 新增表格和预览图的上下布局容器 -->
<div class="table-preview-container">
<!-- 上半部分表格高度减半 -->
<div class="table-content" style="height: 19.3vh;">
<el-table :data="panelConfigTable" border style="width: 100%; height:100%;" size="small">
<!-- 表格列保持不变 -->
<el-table-column label="面板序号" max-width="80" align="center">
<template #default="{ $index }">
{{ $index + 1 }}
</template>
</el-table-column>
<el-table-column label="状态" max-width="75" align="center">
<template #default="{ row, $index }">
<div v-if="row.panelUnitGuid">
<el-tag type="success">已配置</el-tag>
</div>
<div v-else>
<el-tag type="info">未配置</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="面板" min-width="200" align="center">
<template #default="{ row, $index }">
<el-select v-model="row.panelUnitGuid"
placeholder="请选择面板单元"
style="width: 100%;"
size="small"
@change="handlePanelUnitChange($index, row.panelUnitGuid)">
<el-option v-for="unit in availablePanelUnits"
:key="unit.guid"
:label="unit.panel_name"
:value="unit.guid" />
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template #default="{ row, $index }">
<el-button size="small"
link
:type="currentPanelUnit === row.panelUnitGuid ? 'info' : 'primary'"
@click="editPanelUnitConfig(row)"
:disabled="!row.panelUnitGuid">
{{ currentPanelUnit === row.panelUnitGuid ? '编辑中' : '编辑' }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 下半部分新增的面板组预览图 -->
<div class="preview-below-table">
<div class="preview-content">
<div v-if="panelGroupPreviewThumbnail" class="preview-image">
<el-image style="max-width: 100%; max-height: 100%; width: 100%; height: 100%"
:src="panelGroupPreviewThumbnail"
fit="contain" />
</div>
<div v-else class="preview-placeholder">
<el-icon :size="35"><Picture /></el-icon>
<el-text>暂无面板组预览图</el-text>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 单个面板配置 -->
<div class="right-bottom">
<div class="panel-unit-config-container">
<!-- 左边预览图区域保持不变 -->
<div class="preview-section-down">
<div class="section-header">
<span>单面板预览图</span>
<div>
<el-tooltip content="手动刷新预览图" placement="top">
<el-button size="small" type="info" @click="refreshPanelUnitPreview"
:disabled="!currentPanelUnit">
刷新预览
</el-button>
</el-tooltip>
</div>
</div>
<div class="preview-content">
<div v-if="singlePanelPreview" class="preview-image">
<el-image style="max-width: 97%; max-height: 100%; width: 97%; height: 100% "
:src="singlePanelPreview"
:preview-src-list="[singlePanelPreview]"
show-progress
fit="contain" />
</div>
<div v-else class="preview-placeholder">
<el-icon :size="35"><Picture /></el-icon>
<el-text v-if="currentPanelUnitObj && currentPanelUnitObj.design_status === '生成中'">生成中请稍候</el-text>
<el-text v-else>暂无面板预览图</el-text>
</div>
</div>
</div>
<!-- 右边表单区域调整为左右结构 -->
<div class="form-section">
<div class="section-header">
<span>单个面板配置{{ currentPanelUnitObj && currentPanelUnitObj.design_status === '生成中' ? '(生成中)' : '' }}</span>
<div>
<el-tooltip content="点击生成单个面板预览图" placement="top">
<el-button size="small" type="primary" @click="showSinglePanelPreview"
:disabled="!currentPanelUnit">
生成预览图
</el-button>
</el-tooltip>
</div>
</div>
<!-- 新增左右布局容器 -->
<div class="form-content-container">
<!-- 左侧新增的pattern预览图 -->
<div class="pattern-preview-section">
<div class="preview-content">
<div v-if="singlePanelPatternPreview" class="preview-image">
<el-image style="max-width: 100%; max-height: 100%; width: 100%; height: 100% "
:src="singlePanelPatternPreview"
fit="contain" />
</div>
<div v-else class="preview-placeholder">
<el-icon :size="35"><Picture /></el-icon>
<el-text>暂无面板模型预览图</el-text>
</div>
</div>
</div>
<!-- 右侧表格宽度缩小 -->
<div class="table-content" style="width: 70%; height: 39.8vh;">
<el-table
ref="mergedPanelUnitTable"
:data="mergedPanelUnitForm"
border
style="width: 100%; height:100%"
size="small"
highlight-current-row
@current-change="handleMergedPanelUnitRowChange"
@row-click="handleMergedPanelUnitRowClick"
@change="handleUserEdit">
<!-- 表格列保持不变 -->
<el-table-column label="位置" min-width="60" align="center">
<template #default="{ row }">
<span>{{ row.locationName }}</span>
<span v-if="row.locationName.length <= 2">号键</span>
</template>
</el-table-column>
<el-table-column label="图标" min-width="100" align="center">
<template #default="{ row }">
<div v-if="row.icon && row.icon.content" class="icon-preview-cell">
<el-image style="max-width: 22px; max-height: 22px; width: 22px; height: 22px;"
:src="getIconPreviewUrl(row.icon)"
@click="openIconDialog(row)"
fit="contain"
preview-teleported>
<template #error>
<div class="image-slot">
<el-icon><Picture /></el-icon>
</div>
</template>
</el-image>
<el-button size="small"
link type="primary"
@click="openIconDialog(row)"
style="margin-left: 5px;">
更换
</el-button>
</div>
<el-button v-else size="small" link
@click="openIconDialog(row)"
type="primary">
选择图标
</el-button>
</template>
</el-table-column>
<el-table-column label="中文" min-width="90" align="center">
<template #default="{ row }">
<el-button size="small" link
@click="openChineseDialog(row)"
:type="row.chinese && row.chinese.content ? '' : 'primary'">
{{ row.chinese && row.chinese.content ? row.chinese.content : '编辑中文' }}
</el-button>
</template>
</el-table-column>
<el-table-column label="英文" min-width="90" align="center">
<template #default="{ row }">
<el-button size="small" link
@click="openEnglishDialog(row)"
:type="row.english && row.english.content ? '' : 'primary'">
{{ row.english && row.english.content ? row.english.content : '编辑英文' }}
</el-button>
</template>
</el-table-column>
<el-table-column label="背光" min-width="50" align="center">
<template #default="{ row, $index }">
<div class="backlight-control">
<el-checkbox v-model="row.backlightEnabled"
@change="handleBacklightToggle(row, $index)"
style="margin-right: 8px;">
</el-checkbox>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 新增面板组对话框 -->
<el-dialog v-model="showAddPanelDialog" width="1200px" :close-on-click-modal="false"
:title="editingPanelGuid ? '编辑面板组' : '新增面板组'">
<div class="dialog-content">
<!-- 左侧区域系列选择表格 -->
<div class="dialog-left">
<div class="section-title">请选择系列</div>
<el-table :data="filteredSeriesList"
border
highlight-current-row
@row-click="handleSeriesChange"
style="width: 100%;"
height="500">
<el-table-column label="系列名称 版本" min-width="150">
<template #default="{ row }">
{{ row.Series }}_{{ row.Version }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 中间区域模型选择表格 -->
<div class="dialog-middle">
<div class="section-title">{{ selectedSeries ? '请选择模型' : '请先选择系列' }}</div>
<div class="model-selectors">
<!-- PanelCount选择器 -->
<div class="selector-row">
<span class="selector-label">面板数:</span>
<el-select v-model="selectedPanelCount"
placeholder="请选择面板数"
@change="updateModelList"
:disabled="!selectedSeries">
<el-option v-for="count in panelCountOptions" :key="count" :label="count" :value="count" />
</el-select>
</div>
<!-- Direction选择器 -->
<div class="selector-row">
<span class="selector-label">方向:</span>
<el-select v-model="selectedDirection"
placeholder="请选择方向"
@change="updateModelList"
:disabled="!selectedSeries">
<el-option v-for="dir in directionOptions" :key="dir" :label="dir" :value="dir" />
</el-select>
</div>
<!-- Color选择器 -->
<div class="selector-row">
<span class="selector-label">颜色:</span>
<el-select v-model="selectedColor"
placeholder="请选择颜色"
@change="updateModelList"
:disabled="!selectedSeries">
<el-option v-for="color in colorOptions" :key="color" :label="color" :value="color" />
</el-select>
</div>
<!-- 位置选择器 -->
<div class="selector-row">
<span class="selector-label">Logo位置:</span>
<el-select v-model="selectedLogoPosition"
placeholder="请选择Logo位置"
clearable
@change="handleLogoPositionChange"
:disabled="!selectedModel">
<el-option v-for="position in logoPositionOptions"
:key="position.value"
:label="position.label"
:value="position.value" />
</el-select>
</div>
<!-- Logo图片选择器 -->
<div class="selector-row">
<span class="selector-label">Logo图片:</span>
<el-select v-model="selectedLogo"
placeholder="请选择Logo图片"
@change="handleLogoChange"
clearable
:disabled="!selectedModel">
<el-option v-for="logo in logoList"
:key="logo.ID"
:label="formatNameFront12(logo.KeyName)"
:value="logo">
<div style="display: flex; align-items: center; justify-content: space-between; width: 100%;">
<el-image v-if="logo.PreviewPath"
:src="config.PicAds + logo.PreviewPath"
style="max-width: 60px; max-height: 23px; width: 60px; height: 23px; margin-right: 8px;"
fit="contain" />
<span style="flex: 1; text-align: left;">{{ formatNameFront12(logo.KeyName) }}</span>
</div>
</el-option>
</el-select>
</div>
</div>
</div>
<!-- 右侧区域预览图 -->
<div class="dialog-right">
<div class="section-title">预览</div>
<div class="preview-container">
<div v-if="selectedSeriesPreview" class="preview-image">
<div class="preview-info">
<el-image style="max-width: 100%; max-height: 100%; width: 100%; height: 100%"
:src="selectedSeriesPreview"
fit="contain" />
</div>
</div>
<div v-else class="preview-placeholder">
<el-icon :size="40"><Picture /></el-icon>
<el-text>选择系列以查看预览</el-text>
</div>
</div>
</div>
</div>
<!-- 表单信息移到对话框底部 -->
<div class="dialog-bottom">
<el-form :model="newPanelForm" label-width="100px" inline>
<el-form-item label="面板组名称" required>
<el-input v-model="newPanelForm.panel_list_name" placeholder="请输入面板组名称" style="width: 200px;" />
</el-form-item>
<!-- <el-form-item label="备注">
<el-input v-model="newPanelForm.remarks" placeholder="请输入备注信息" style="width: 400px;" />
</el-form-item>-->
</el-form>
</div>
<template #footer>
<el-button @click="showAddPanelDialog = false">取消</el-button>
<el-button type="primary" @click="confirmAddPanel">确定</el-button>
</template>
</el-dialog>
<!-- 新增面板单元对话框 -->
<el-dialog v-model="showAddPanelUnitDialog" width="1200px"
:close-on-click-modal="false"
:title="isEditPanelUnit ? '编辑面板单元' : '新增面板单元'">
<div class="dialog-content">
<!-- 左侧区域系列选择表格 -->
<div class="dialog-left">
<div class="section-title">请选择系列</div>
<el-table :data="filteredSeriesList"
border
highlight-current-row
@row-click="handleSeriesChangeForUnit"
style="width: 100%;"
height="500">
<el-table-column label="系列名称 版本" min-width="150">
<template #default="{ row }">
{{ row.Series }}_{{ row.Version }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 中间区域模型选择区域 -->
<div class="dialog-middle">
<div class="section-title">{{ selectedSeriesForUnit ? '请选择模型' : '请先选择系列' }}</div>
<div class="model-selectors">
<!-- Direction选择器 -->
<div class="selector-row">
<span class="selector-label">方向:</span>
<el-select v-model="selectedDirectionForUnit"
placeholder="请选择方向"
@change="updateModelListForUnit"
:disabled="!selectedSeriesForUnit || directionOptionsForUnit.length === 0">
<el-option v-for="dir in directionOptionsForUnit" :key="dir" :label="dir" :value="dir" />
</el-select>
</div>
<!-- Color选择器 -->
<div class="selector-row">
<span class="selector-label">颜色:</span>
<el-select v-model="selectedColorForUnit"
placeholder="请选择颜色"
@change="updateModelListForUnit"
:disabled="!selectedSeriesForUnit || colorOptionsForUnit.length === 0">
<el-option v-for="color in colorOptionsForUnit" :key="color" :label="color" :value="color" />
</el-select>
</div>
<!-- 面板选择器 -->
<div class="selector-row">
<span class="selector-label">面板:</span>
<el-select v-model="selectedPatternForUnit"
placeholder="请选择面板"
style="width: 100%;"
:disabled="!selectedSeriesForUnit || patternsListForUnit.length === 0">
<el-option v-for="pattern in patternsListForUnit"
:key="pattern.ID"
:label="formatNameFront12(pattern.PatternName)"
:value="pattern.ID" />
</el-select>
</div>
</div>
</div>
<!-- 右侧区域预览图 -->
<div class="dialog-right">
<div class="section-title">预览</div>
<div class="preview-container">
<div v-if="selectedSeriesPreviewForUnit" class="preview-image">
<div class="preview-info">
<el-image style="max-width: 100%; max-height: 100%; width: 100%; height: 100% "
:src="selectedSeriesPreviewForUnit"
:preview-src-list="[selectedSeriesPreviewForUnit]"
show-progress
fit="contain" />
</div>
</div>
<div v-else class="preview-placeholder">
<el-icon :size="40"><Picture /></el-icon>
<el-text>选择系列以查看预览</el-text>
</div>
</div>
</div>
</div>
<!-- 表单信息 -->
<div class="dialog-bottom">
<el-form :model="newPanelUnitForm" label-width="130px" inline>
<el-form-item label="面板单元名称" required>
<el-input v-model="newPanelUnitForm.panel_name" placeholder="请输入面板单元名称" style="width: 300px;" />
</el-form-item>
<!-- <el-form-item label="备注">
<el-input v-model="newPanelUnitForm.remarks" placeholder="请输入备注信息" style="width: 450px;" />
</el-form-item>-->
</el-form>
</div>
<template #footer>
<el-button @click="showAddPanelUnitDialog = false">取消</el-button>
<el-button type="primary"
@click="isEditPanelUnit ? updatePanelUnit() : confirmAddPanelUnit()"
:disabled="!selectedSeriesForUnit || !selectedPatternForUnit">
{{ isEditPanelUnit ? '更新' : '确定' }}
</el-button>
</template>
</el-dialog>
<!-- 图标选择对话框 -->
<el-dialog v-model="iconDialogVisible" title="选择图标" fullscreen>
<div class="icon-select-dialog">
<!-- 图标库选择器 -->
<div class="icon-library-selector">
<span class="selector-label">搜索图标:</span>
<el-input v-model="iconSearchQuery"
placeholder="输入图标名称搜索"
style="width: 200px;"
size="small"
clearable
@input="handleIconSearch">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<!-- 图标网格 -->
<div class="icon-grid-container">
<!-- 左侧图标库选择表格 -->
<div class="icon-library-table-container">
<el-table :data="iconLibraryList"
border
highlight-current-row
@row-click="handleIconLibraryRowClick"
style="width: 100%;"
height="100%"
size="small">
<el-table-column label="图标库" min-width="150">
<template #default="{ row }">
{{ row.Series }}_{{ row.Version }}
</template>
</el-table-column>
</el-table>
</div>
<!-- 右侧图标网格 -->
<div class="icon-grid-wrapper">
<div class="icon-grid">
<div v-for="icon in currentIconList"
:key="icon.ID"
class="icon-item"
@click="saveIconConfig(icon)">
<div class="icon-preview">
<el-image v-if="icon.PreviewPath"
:src="config.PicAds + icon.PreviewPath"
fit="contain"
style="max-width: 40px; max-height: 40px; width: 40px; height: 40px;" />
<el-icon v-else :size="60"><Picture /></el-icon>
</div>
<div class="icon-name">{{ formatNameFront12(icon.KeyName) }}</div>
<!--<div class="icon-library">{{ getLibraryName(icon.FID) }}</div>-->
</div>
</div>
<!-- 空状态 -->
<div v-if="currentIconList.length === 0" class="empty-icons">
<el-icon :size="48"><Picture /></el-icon>
<p>该图标库暂无图标</p>
</div>
</div>
</div>
</div>
<!-- <template #footer>
<el-button @click="iconDialogVisible = false">取消</el-button>
<el-button type="primary" @click="iconDialogVisible = false">确定</el-button>
</template>-->
</el-dialog>
<!-- 中文编辑对话框 -->
<el-dialog v-model="chineseDialogVisible" title="编辑中文内容" width="500px">
<el-form :model="currentEditingLocation?.chinese" label-width="80px">
<el-form-item label="内容">
<el-input v-model="currentEditingLocation.chinese.content" placeholder="请输入中文内容"
maxlength="5" minlength="1" show-word-limit />
</el-form-item>
<el-form-item label="字体">
<el-select v-model="currentEditingLocation.chinese.font" placeholder="请选择字体">
<el-option v-for="font in fontList" :key="font.ID" :label="font.FontName" :value="font.FontName" />
</el-select>
</el-form-item>
<el-form-item label="字号">
<el-input-number v-model="currentEditingLocation.chinese.size"
:min="6"
:max="14"
:step="0.2"
:precision="1"
controls-position="right" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="chineseDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveChineseConfig">确定</el-button>
</template>
</el-dialog>
<!-- 英文编辑对话框 -->
<el-dialog v-model="englishDialogVisible" title="编辑英文内容" width="500px">
<el-form :model="currentEditingLocation?.english" label-width="80px">
<el-form-item label="内容">
<el-input v-model="currentEditingLocation.english.content" placeholder="请输入英文内容"
maxlength="14" minlength="4" show-word-limit />
</el-form-item>
<el-form-item label="字体">
<el-select v-model="currentEditingLocation.english.font" placeholder="请选择字体">
<el-option v-for="font in fontList" :key="font.ID" :label="font.FontName" :value="font.FontName" />
</el-select>
</el-form-item>
<el-form-item label="字号">
<el-input-number v-model="currentEditingLocation.english.size"
:min="4"
:max="10"
:step="0.2"
:precision="1"
controls-position="right" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="englishDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveEnglishConfig">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick, inject, computed, watch, onUnmounted } from 'vue';
import { createAxiosInstance } from '../../axios.js';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import { Picture } from '@element-plus/icons-vue';
// ==================== 引入外部变量 ====================
const formatDate = inject('formatDate');
const config = inject('config'); // config.PicAds 是图片根路径
// ==================== HTTP 实例 ====================
const cloudHttp = createAxiosInstance(0);
const localHttp = createAxiosInstance(1);
// ==================== 响应式变量 ====================
// 项目相关变量
const projectList = ref([]);
const selectedProjectId = ref(null);
const isProjectLocked = ref(false);
const treeData = ref([]);
// el-tree 组件引用(用于程序化选中节点)
const projectTree = ref(null);
// 树结构相关变量
const treeProps = { label: 'name', children: 'children' };
const unitTableData = ref([]);
const isUserEditingPanelUnit = ref(false);
// 面板组对话框相关变量
const showAddPanelDialog = ref(false);
const currentRoomData = ref(null);
const editingPanelGuid = ref(null);
const seriesList = ref([]);
const selectedSeries = ref(null);
const selectedModel = ref(null);
const allModels = ref([]);
const filteredModels = ref([]);
const selectedPanelCount = ref('');
const selectedDirection = ref('');
const selectedColor = ref('');
const panelCountOptions = ref([]);
const directionOptions = ref([]);
const colorOptions = ref([]);
const panelGroupConfig = ref({
panelUnitGuids: [] // 存储面板单元GUID数组
});
// LOGO配置
const logoPositionOptions = ref([
{ label: '左下', value: 'left' },
{ label: '右下', value: 'right' }
]);
const selectedLogoPosition = ref('');
const selectedLogo = ref(null);
const logoList = ref([]);
const logoConfig = ref({
LogoPosition: '',
LogoFileName: '',
LogoIndexNumber: ''
});
// 新增面板单元对话框相关变量
const showAddPanelUnitDialog = ref(false);
const selectedSeriesForUnit = ref(null);
const selectedDirectionForUnit = ref('');
const selectedColorForUnit = ref('');
const selectedPatternForUnit = ref('');
const patternsListForUnit = ref([]);
const filteredModelsForUnit = ref([]);
const directionOptionsForUnit = ref([]);
const colorOptionsForUnit = ref([]);
const selectedSeriesPreviewForUnit = ref([]);
const isEditPanelUnit = ref(false);
const currentEditPanelUnit = ref(null);
// 面板修改列表变量
const mergedPanelUnitForm = ref([]);
const currentMergedPanelUnitRow = ref(null); // 当前选中的mergedPanelUnitForm表格行
const mergedPanelUnitTable = ref(null); // 表格引用
const iconDialogVisible = ref(false);
const chineseDialogVisible = ref(false);
const englishDialogVisible = ref(false);
const currentEditingLocation = ref(null);
const currentEditingType = ref('');
// 面板单元表单
const newPanelUnitForm = ref({
panel_name: '',
remarks: ''
});
// 面板配置相关变量
const currentPanelNode = ref(null);
const patternsList = ref([]);
const panelConfigTable = ref([]);
// 面板单元配置相关变量
const currentPanelUnit = ref(null);
const panelUnitForm = ref([]);
const fontList = ref([]);
const allModelsForUnit = ref([]);
const selectedModelForUnit = ref(null);
// 图片预览相关变量
const selectedSeriesPreview = ref('');
const singlePanelPreview = ref('');
// 图标相关变量
const iconLibraryList = ref([]); // 图标库文件列表
const selectedIconLibrary = ref(''); // 当前选中的图标库文件ID
const iconOptions = ref({}); // 图标数据以文件ID为键存储图标数组
const iconSearchQuery = ref(''); // 图标搜索查询字符串
// 表单数据
const newPanelForm = ref({
panel_list_name: '',
gang_series: '',
model_type: '',
panel_count: null,
gang_material_id: 0,
remarks: '',
logo_json: "{}"
});
const predefineColors = ref([
'#F19947', // 默认背光颜色
'#FFFFFF', // 白色
'#FF8C00', // 橙色
'#FFD700', // 金色
'#ADFF2F', // 绿色
'#1E90FF', // 蓝色
]);
// 加载遮罩实例
let loadingInstance = null;
// 轮询相关变量
const pollingInterval = ref(null);
const isPollingActive = ref(false);
const pollingItems = ref({
panelLists: new Set(), // 存储需要轮询的面板组GUID
panelUnits: new Set() // 存储需要轮询的面板单元GUID
});
// ==================== 计算属性 ====================
// 检查是否有生成中的元素
const hasGeneratingItems = computed(() => {
const hasPanelGroupGenerating = currentPanelNode.value?.panelInfo?.thumbnail_small === '生成中';
const hasPanelUnitGenerating = currentPanelUnitObj.value && currentPanelUnitObj.value.design_status === '生成中';
return hasPanelGroupGenerating || hasPanelUnitGenerating;
});
/**
* 获取当前选中的面板单元对象
*/
const currentPanelUnitObj = computed(() => {
try {
if (!currentPanelUnit.value || !unitTableData.value || !Array.isArray(unitTableData.value)) {
return null;
}
const unit = unitTableData.value.find(unit => unit.guid === currentPanelUnit.value);
return unit || null;
} catch (error) {
console.error('获取当前面板单元对象失败:', error);
return null;
}
});
const currentIconList = computed(() => {
// 获取当前选中的图标库列表
let icons = selectedIconLibrary.value ? iconOptions.value[selectedIconLibrary.value] || [] : [];
// 如果有搜索查询,进行过滤
if (iconSearchQuery.value && iconSearchQuery.value.trim()) {
const query = iconSearchQuery.value.trim().toLowerCase();
icons = icons.filter(icon => {
// 使用formatNameFront12处理图标名称然后进行模糊匹配
const formattedName = formatNameFront12(icon.KeyName).toLowerCase();
return formattedName.includes(query);
});
}
return icons;
});
const availablePanelUnits = computed(() => {
if (!unitTableData.value || unitTableData.value.length === 0) {
return [];
}
return unitTableData.value.filter(unit => unit.is_valid);
});
/**
* 面板组预览图
*/
const panelGroupPreview = computed(() => {
if (!currentPanelNode.value) return '';
const panelInfo = currentPanelNode.value.panelInfo;
// 显示面板组的大缩略图
if (panelInfo.thumbnail_large) {
return config.PicAds + panelInfo.thumbnail_large;
}
return '';
});
const panelGroupPreviewThumbnail = ref('');
watch(
() => currentPanelNode.value,
async (newNode) => {
if (!newNode?.panelInfo?.model_type) {
panelGroupPreviewThumbnail.value = '';
return;
}
try {
const model = await fetchData('PanelSelection/IdModelQuery', { PID: newNode.panelInfo.gang_material_id });
if (model && model[0] && model[0].PreviewPath) {
panelGroupPreviewThumbnail.value = config.PicAds + model[0].PreviewPath;
} else {
panelGroupPreviewThumbnail.value = '';
}
} catch (error) {
panelGroupPreviewThumbnail.value = '';
}
},
{ immediate: true }
);
/**
* 检查所有面板是否已配置
*/
const isAllPanelsConfigured = computed(() => {
if (!panelConfigTable.value.length) return false;
return panelConfigTable.value.every(item => {
return item.panelUnitGuid && item.panelUnitGuid.length === 32;
});
});
/**
* 模型选择器是否启用
*/
const isModelSelectorEnabled = computed(() => {
return selectedPanelCount.value && selectedDirection.value && selectedColor.value;
});
/**
* 面板选择器去除路径为空的对象
*/
const filteredSeriesList = computed(() => {
return seriesList.value.filter(row => row.PreviewPath != undefined && row.PreviewPath != null && row.PreviewPath !== '');
});
/**
* 将面板单元数据按位置名称合并
*/
const buildMergedPanelUnitForm = () => {
try {
const groupedData = {};
if (!panelUnitForm.value || !Array.isArray(panelUnitForm.value) || panelUnitForm.value.length === 0) {
mergedPanelUnitForm.value = [];
return;
}
panelUnitForm.value.forEach(item => {
if (!item || !item.locationName) return;
const locationParts = item.locationName.split('_');
let groupKey = '';
if (locationParts.length >= 3) {
groupKey = locationParts[2];
} else {
groupKey = item.locationName;
}
if (!groupKey || groupKey.trim() === '') {
groupKey = '未分类';
}
if (!groupedData[groupKey]) {
groupedData[groupKey] = {
locationName: groupKey,
originalLocationName: item.locationName,
icon: {},
chinese: {},
english: {},
locations: [],
backlightColor: '#FFFFFF',
backlightEnabled: false
};
}
// 根据类型分配到对应属性
if (item.type === 1) {
groupedData[groupKey].icon = {
...item,
locationName: item.locationName,
backlightColor: item.backlightColor || '#FFFFFF',
backlightEnabled: item.backlightEnabled || false
};
} else if (item.type === 2) {
groupedData[groupKey].chinese = {
...item,
locationName: item.locationName,
backlightColor: item.backlightColor || '#FFFFFF',
backlightEnabled: item.backlightEnabled || false
};
} else if (item.type === 3) {
groupedData[groupKey].english = {
...item,
locationName: item.locationName,
backlightColor: item.backlightColor || '#FFFFFF',
backlightEnabled: item.backlightEnabled || false
};
}
// 保存原始位置数据
groupedData[groupKey].locations.push(item);
// 设置背光默认值(取第一个非默认值的元素)
if (item.backlightColor && item.backlightColor !== '#FFFFFF') {
groupedData[groupKey].backlightColor = item.backlightColor;
groupedData[groupKey].backlightEnabled = true;
}
});
mergedPanelUnitForm.value = Object.values(groupedData).sort(naturalSortSimple);
} catch (error) {
console.error('构建合并面板单元表单时出错:', error);
mergedPanelUnitForm.value = [];
}
};
const naturalSortSimple = (a, b) => {
return a.locationName.localeCompare(b.locationName, 'zh-CN', { numeric: true, sensitivity: 'base' });
};
/**
* 自然排序比较函数
* 支持中英文混合字符串的数字自然排序
*/
const naturalSort = (a, b) => {
// 如果两者相同直接返回0
if (a === b) return 0;
// 将字符串拆分为数字和非数字部分
const regex = /(\d+)|(\D+)/g;
const aParts = a.match(regex) || [];
const bParts = b.match(regex) || [];
// 比较每个部分
const minLength = Math.min(aParts.length, bParts.length);
for (let i = 0; i < minLength; i++) {
const aPart = aParts[i];
const bPart = bParts[i];
// 如果两个部分都是数字,按数字大小比较
if (/^\d+$/.test(aPart) && /^\d+$/.test(bPart)) {
const aNum = parseInt(aPart, 10);
const bNum = parseInt(bPart, 10);
if (aNum !== bNum) {
return aNum - bNum;
}
}
// 否则按字符串比较
else {
const comparison = aPart.localeCompare(bPart, 'zh-CN');
if (comparison !== 0) {
return comparison;
}
}
}
// 如果前面的部分都相同,长度短的排在前面
return aParts.length - bParts.length;
};
// ==================== 工具方法 ====================
const handleUserEdit = () => {
isUserEditingPanelUnit.value = true;
// 重置标志位,给用户一个编辑窗口期
if (window.userEditResetTimeout) {
clearTimeout(window.userEditResetTimeout);
}
window.userEditResetTimeout = setTimeout(() => {
isUserEditingPanelUnit.value = false;
}, 200); // 5秒后重置标志
};
/**
* 处理mergedPanelUnitForm表格行变化
*/
const handleMergedPanelUnitRowChange = (newRow) => {
currentMergedPanelUnitRow.value = newRow;
};
/**
* 处理mergedPanelUnitForm表格行点击
*/
const handleMergedPanelUnitRowClick = (row) => {
currentMergedPanelUnitRow.value = row;
};
/**
* 综合轮询管理
*/
const managePolling = () => {
// 检查当前是否有生成中的项目
const shouldPoll = hasGeneratingItems.value;
if (shouldPoll && !isPollingActive.value) {
startPolling();
} else if (!shouldPoll && isPollingActive.value) {
stopPolling();
}
};
/**
* 启动轮询检查生成状态
*/
const startPolling = () => {
if (isPollingActive.value) return;
isPollingActive.value = true;
console.log('启动轮询检查生成状态...');
pollingInterval.value = setInterval(async () => {
await checkGeneratingStatus();
await refreshPanelUnitPreview();
}, 3000); // 轮询一次
};
/**
* 停止轮询
*/
const stopPolling = () => {
if (pollingInterval.value) {
clearInterval(pollingInterval.value);
pollingInterval.value = null;
}
isPollingActive.value = false;
pollingItems.value.panelLists.clear();
pollingItems.value.panelUnits.clear();
console.log('停止轮询检查');
};
/**
* 检查生成状态并更新数据
*/
const checkGeneratingStatus = async () => {
try {
let hasUpdates = false;
// 检查面板组生成状态
if (currentPanelNode.value?.panelInfo?.thumbnail_small === '生成中') {
const panelListGuid = currentPanelNode.value.panelGuid;
const updated = await checkPanelListStatus(panelListGuid);
if (updated) hasUpdates = true;
}
// 检查面板单元生成状态
if (currentPanelUnitObj.value && currentPanelUnitObj.value.design_status === '生成中') {
const panelUnitGuid = currentPanelUnit.value;
const updated = await checkPanelUnitStatus(panelUnitGuid);
if (updated) hasUpdates = true;
}
// 如果有更新,强制重新渲染相关组件
if (hasUpdates) {
// 触发响应式更新
currentPanelNode.value = { ...currentPanelNode.value };
currentPanelUnit.value = currentPanelUnit.value;
}
// 如果没有生成中的元素,停止轮询
if (!hasGeneratingItems.value) {
stopPolling();
console.log('所有生成任务已完成,停止轮询');
}
} catch (error) {
console.error('轮询检查生成状态失败:', error);
}
};
/**
* 检查面板组状态
*/
const checkPanelListStatus = async (panelListGuid) => {
try {
const rs = await localHttp.post('PanelSelection/WebPanelListQueryByGuid',
JSON.stringify({ GUID: panelListGuid }));
if (rs.data.isok && rs.data.response) {
const data = JSON.parse(rs.data.response);
if (data.length > 0) {
const panelInfo = data[0];
// 检查是否仍然在生成中
if (panelInfo.thumbnail_small !== '生成中') {
// 更新当前面板组信息
if (currentPanelNode.value) {
currentPanelNode.value.panelInfo = panelInfo;
}
// 更新树节点中的面板组信息
updatePanelNodeInTree(panelListGuid, panelInfo);
ElMessage.success('面板组预览图生成完成!');
return true; // 返回true表示有更新
}
}
}
return false;
} catch (error) {
console.error('检查面板组状态失败:', error);
return false;
}
};
/**
* 检查面板单元状态
*/
const checkPanelUnitStatus = async (panelUnitGuid) => {
try {
const rs = await localHttp.post('PanelSelection/WebPanelUnitQueryByGuid',
JSON.stringify({ GUID: panelUnitGuid }));
if (rs.data.isok && rs.data.response) {
const data = JSON.parse(rs.data.response);
if (data.length > 0) {
const panelUnit = data[0];
// 检查是否仍然在生成中
if (panelUnit.design_status !== '生成中') {
// 更新面板单元列表
const unitIndex = unitTableData.value.findIndex(u => u.guid === panelUnitGuid);
if (unitIndex !== -1) {
unitTableData.value[unitIndex] = panelUnit;
}
// 更新当前面板单元对象
if (currentPanelUnit.value === panelUnitGuid) {
// 触发响应式更新
currentPanelUnit.value = panelUnitGuid;
}
ElMessage.success('单面板预览图生成完成!');
return true; // 返回true表示有更新
}
}
}
return false;
} catch (error) {
console.error('检查面板单元状态失败:', error);
return false;
}
};
/**
* 更新树节点中的面板组信息
*/
const updatePanelNodeInTree = (panelGuid, newPanelInfo) => {
const updateNode = (nodes) => {
for (let node of nodes) {
if (node.type === 'panel' && node.panelGuid === panelGuid) {
node.panelInfo = newPanelInfo;
return true;
}
if (node.children && node.children.length > 0) {
if (updateNode(node.children)) {
return true;
}
}
}
return false;
};
updateNode(treeData.value);
};
/**
* 获取当前用户
*/
const getCurrentUser = () => localStorage.getItem('username') || 'system';
/**
* 显示加载遮罩
*/
const showLoading = (text = '加载中...') => {
loadingInstance = ElLoading.service({
lock: true,
text: text,
background: 'rgba(0, 0, 0, 0.7)',
});
};
/**
* 隐藏加载遮罩
*/
const hideLoading = () => {
if (loadingInstance) {
loadingInstance.close();
loadingInstance = null;
}
};
/**
* 类型标签映射
*/
const getTagType = (type) => {
const typeMap = { 1: 'success', 2: 'primary', 3: 'warning' };
return typeMap[type] || 'info';
};
/**
* 类型文本映射
*/
const getTypeText = (type) => {
const textMap = { 1: '图标', 2: '中文', 3: '英文' };
return textMap[type] || '未知';
};
// ==================== 格式化方法 ====================
const formatNameFront1 = (name) => name ? name.replace(/^[^_]+_/, '') : '';
const formatNameFront12 = (name) => {
if (!name) return '';
const parts = name.split('_');
if (parts.length < 4) return '';
return parts.slice(1, -2).join('_');
};
const formatNameFront22 = (name) => {
if (!name) return '';
const parts = name.split('_');
if (parts.length < 4) return '';
return parts.slice(2, -2).join('_');
};
const formatNameFront3 = (name) => {
if (!name) return '';
const match = name.match(/^[^_]*_[^_]*_([^_]*)/);
return match ? match[1] : null;
};
/**
* 处理图标搜索
*/
const handleIconSearch = () => {
// 这个方法会在currentIconList计算属性中使用
// 实际的过滤逻辑在计算属性中实现
};
// ==================== 数据库操作封装 ====================
/**
* 获取数据
*/
const fetchData = async (endpoint, query = {}) => {
try {
const rs = await localHttp.post(endpoint, JSON.stringify(query));
return rs.data.isok && rs.data.response ? JSON.parse(rs.data.response) : [];
} catch (error) {
console.error(`获取${endpoint}数据失败:`, error);
return [];
}
};
/**
* 更新数据库
*/
const updateDatabase = async (endpoint, data) => {
try {
const rs = await localHttp.post(endpoint, JSON.stringify(data));
if (!rs.data.isok) {
console.error(`API返回错误:`, rs.data);
throw new Error(rs.data.message);
}
return rs.data.response;
} catch (error) {
console.error(`${endpoint}操作失败:`, error);
throw error;
}
};
// ==================== 项目相关方法 ====================
/**
* 获取项目列表
*/
const getProject = async () => {
try {
const rs = await cloudHttp.post('AasProjectInfoes/Get_ProjectByStatusCode');
projectList.value = rs.data.response.sort((a, b) => {
if (a.id === 1) return -1;
if (b.id === 1) return 1;
return b.id - a.id;
});
// 优先使用本地保存的项目选择
const savedProjectId = localStorage.getItem('selectedProjectId');
if (savedProjectId) {
const pid = Number(savedProjectId);
if (projectList.value.find(p => p.id === pid)) {
selectedProjectId.value = pid;
// 延迟调用由调用方决定是否 await
await handleProjectChangeApp(pid);
return;
}
}
// 否则回退到默认项目projectCode==='1'
const defaultProject = projectList.value.find(p => p.projectCode === '1');
if (defaultProject) {
selectedProjectId.value = defaultProject.id;
// 保持原来的加载行为
handleProjectChangeApp(defaultProject.id);
}
} catch (error) {
console.error('获取项目列表失败:', error);
ElMessage.error('获取项目列表失败');
}
};
/**
* 等待辅助函数: 轮询直到 predicate 返回 true 或超时
*/
const waitFor = async (predicate, timeout = 5000, interval = 100) => {
const start = Date.now();
while (true) {
try {
if (predicate()) return true;
} catch (e) {
// ignore
}
if (Date.now() - start > timeout) return false;
await new Promise(r => setTimeout(r, interval));
}
};
const getSavedTreeNodeKey = () => {
return localStorage.getItem('selectedTreeNode') ||
localStorage.getItem('selectedTreeNodeId') ||
localStorage.getItem('selectedPanelNode') ||
null;
};
const getTreeNodeDataByKey = (nodeKey) => {
if (!nodeKey) return null;
if (projectTree.value?.getNode) {
const nodeInstance = projectTree.value.getNode(nodeKey);
if (nodeInstance) {
return nodeInstance.data || nodeInstance;
}
}
const findNodeRecursively = (nodes) => {
if (!nodes || nodes.length === 0) return null;
for (const node of nodes) {
if (node.id === nodeKey) return node;
if (node.children && node.children.length) {
const found = findNodeRecursively(node.children);
if (found) return found;
}
}
return null;
};
return findNodeRecursively(treeData.value || []);
};
const getPreferredTreeKey = () => {
if (projectTree.value?.getCurrentKey) {
const currentKey = projectTree.value.getCurrentKey();
if (currentKey) {
return currentKey;
}
}
return getSavedTreeNodeKey();
};
const triggerCurrentTreeNodeClick = async (preferredKey = null) => {
let nodeData = null;
if (projectTree.value?.getCurrentNode && !preferredKey) {
nodeData = projectTree.value.getCurrentNode();
}
if (!nodeData) {
const key = preferredKey || getPreferredTreeKey();
nodeData = getTreeNodeDataByKey(key);
}
if (nodeData) {
await handleNodeClick(nodeData);
}
};
const reloadTreeData = async () => {
const preservedKey = getPreferredTreeKey();
if (!selectedProjectId.value) {
return preservedKey;
}
try {
await handleProjectChangeApp(selectedProjectId.value);
await waitFor(() => treeData.value && treeData.value.length > 0, 5000);
await nextTick();
if (preservedKey && projectTree.value?.setCurrentKey) {
projectTree.value.setCurrentKey(preservedKey);
}
} catch (error) {
console.error('重载项目树失败:', error);
}
return preservedKey;
};
const reloadTreeAndTriggerClick = async () => {
const key = await reloadTreeData();
await triggerCurrentTreeNodeClick(key);
};
/**
* 从 localStorage 按顺序恢复用户的选择:
* 1) selectedProjectId -> await handleProjectChangeApp
* 2) 恢复 tree 选中节点
* 3) 恢复 currentPanelUnit并加载其配置
*/
const restoreSelectionsFromLocalStorage = async () => {
try {
const savedProjectId = localStorage.getItem('selectedProjectId');
const savedPanelUnit = localStorage.getItem('currentPanelUnit');
const savedTreeNode = getSavedTreeNodeKey();
// 1) 恢复项目并等待数据加载完成
if (savedProjectId) {
const pid = Number(savedProjectId);
if (pid && pid !== selectedProjectId.value) {
selectedProjectId.value = pid;
await handleProjectChangeApp(pid);
}
}
// 等待 treeData 已加载(至少有项目节点)和 unitTableData 已加载(无论长度)
await waitFor(() => treeData.value && treeData.value.length > 0, 5000);
// 2) 恢复树的选中节点(如果有)
if (savedTreeNode && projectTree.value) {
try {
// 优先使用 el-tree 的 setCurrentKey 方法
if (projectTree.value.setCurrentKey) {
projectTree.value.setCurrentKey(savedTreeNode);
}
const treeNode = getTreeNodeDataByKey(savedTreeNode);
if (treeNode) {
await handleNodeClick(treeNode);
}
} catch (e) {
console.warn('恢复树选中失败:', e);
}
}
// 3) 恢复 currentPanelUnit如果存在
if (savedPanelUnit) {
// 等待面板单元列表加载完成
await waitFor(() => Array.isArray(unitTableData.value), 5000);
const unit = unitTableData.value.find(u => u.guid === savedPanelUnit);
if (unit) {
// 使用行点击的加载逻辑,确保右侧面板配置加载完毕
await handleUnitRowClick(unit);
} else {
// 如果找不到,尝试直接设置 GUID容错
currentPanelUnit.value = savedPanelUnit;
}
}
} catch (error) {
console.error('恢复本地选择失败:', error);
}
};
/**
* Select 下拉显示时滚动到顶部
*/
const onSelectVisibleChange = (visible) => {
if (visible) {
nextTick(() => {
const dropdown = document.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
if (dropdown) dropdown.scrollTop = 0;
});
}
};
/**
* 项目选择变化处理
*/
const handleProjectChangeApp = async (projectId) => {
treeData.value = [];
unitTableData.value = []; // 清空面板单元列表
if (!projectId) return;
const project = projectList.value.find(p => p.id === projectId);
if (!project) return;
let webProject = await getWebProjectByHotelName(project.projectName);
if (!webProject) webProject = await createWebProject(project);
if (!webProject) {
ElMessage.error(`项目 ${project.projectName} 数据初始化失败,跳过加载`);
return;
}
const projectNode = await buildProjectTree(project, webProject);
treeData.value = [projectNode];
// 加载该项目对应的面板单元列表
await loadPanelUnitsByProject(webProject.guid);
};
/**
* 根据项目GUID加载面板单元列表
*/
const loadPanelUnitsByProject = async (projectGuid) => {
try {
const units = await localHttp.post('PanelSelection/WebPanelUnitQueryByProject', JSON.stringify({ GUID: projectGuid }));
unitTableData.value = JSON.parse(units.data.response) || [];
} catch (error) {
console.error('加载面板单元列表失败:', error);
unitTableData.value = [];
}
};
/**
* 根据酒店名称查询 Web 项目
*/
const getWebProjectByHotelName = async (hotelName) => {
const projects = await fetchData('PanelSelection/WebProjectQuery');
return projects.find(p => p.hotel_name === hotelName);
};
/**
* 创建 Web 项目
*/
const createWebProject = async (project) => {
try {
// 创建项目
const webProject = {
report_id: `${project.projectCode}`,
hotel_name: project.projectName,
room_type_count: project.roomTypeCount,
room_data_json: '[]',
remarks: project.customization,
creator: getCurrentUser()
};
const projectGuid = await updateDatabase('PanelSelection/WebProjectAdd', webProject);
// 创建房型
const roomGuids = [];
for (let i = 0; i < project.roomTypeCount; i++) {
const roomData = {
room_name: `房型${i + 1}`,
room_description: `自动创建的房型`,
panel_group_count: '[]',
remarks: '',
creator: getCurrentUser()
};
const roomGuid = await updateDatabase('PanelSelection/WebRoomTypeAdd', roomData);
if (roomGuid && roomGuid.length === 32) {
roomGuids.push(roomGuid);
}
}
// 更新项目信息
const updatedProject = {
...webProject,
guid: projectGuid,
room_type_count: roomGuids.length,
room_data_json: JSON.stringify(roomGuids)
};
await updateDatabase('PanelSelection/WebProjectUpdate', updatedProject);
return updatedProject;
} catch (error) {
console.error('创建Web项目失败:', error);
ElMessage.error('创建项目数据失败');
return null;
}
};
// ==================== 树结构相关方法 ====================
/**
* 构建项目树结构
*/
const buildProjectTree = async (project, webProject) => {
const roomGuids = JSON.parse(webProject.room_data_json || '[]');
const roomNodes = [];
for (const roomGuid of roomGuids) {
const roomInfo = await getRoomTypeInfoByGuid(roomGuid);
if (roomInfo) {
const panelNodes = [];
const panelGroups = JSON.parse(roomInfo.panel_group_count || '[]');
for (const panelGuid of panelGroups) {
const panelInfo = await getPanelInfoByGuid(panelGuid);
if (panelInfo) {
panelNodes.push({
id: `panel-${roomGuid}-${panelGuid}`,
name: `${panelInfo.panel_list_name}${panelInfo.gang_series.split('_')[0]} ${formatNameFront12(panelInfo.model_type) || '无模型'}`,
type: 'panel',
roomGuid: roomGuid,
panelGuid: panelGuid,
panelInfo,
projectId: project.id
});
}
}
roomNodes.push({
id: roomGuid,
name: roomInfo.room_name,
type: 'room',
projectId: project.id,
webProjectId: webProject.id,
roomGuid: roomGuid,
children: panelNodes
});
}
}
return {
id: `project-${project.id}`,
name: `${project.projectName} (${roomNodes.length})`,
type: 'project',
project,
webProject,
children: roomNodes
};
};
/**
* 根据GUID查询房型信息
*/
const getRoomTypeInfoByGuid = async (roomGuid) => {
const rooms = await fetchData('PanelSelection/WebRoomTypeQuery');
return rooms.find(r => r.guid === roomGuid);
};
/**
* 根据GUID查询面板组信息
*/
const getPanelInfoByGuid = async (panelGuid) => {
try {
const panels = await fetchData('PanelSelection/WebPanelListQuery');
return panels.find(p => p.guid === panelGuid);
} catch (error) {
console.error('查询面板组失败:', error);
return null;
}
};
/**
* 查找项目节点
*/
const findProjectNode = (projectId) => {
if (!projectId || !treeData.value || treeData.value.length === 0) {
return null;
}
return treeData.value.find(node => node.id === `project-${projectId}`);
};
/**
* 查找房型节点
*/
const findRoomNode = (projectId, roomGuid) => findProjectNode(projectId)?.children.find(r => r.roomGuid === roomGuid);
/**
* 移除项目节点
*/
const removeProjectNode = (data) => {
treeData.value = [];
selectedProjectId.value = '';
};
/**
* 树节点点击事件
*/
const handleNodeClick = async (data) => {
// 持久化树的选中节点到 localStorage便于下次恢复
try {
if (data && data.id) {
localStorage.setItem('selectedTreeNode', data.id);
}
} catch (e) {
// ignore
}
// 清空所有行的编辑状态
panelConfigTable.value.forEach(item => {
item.isEditing = false;
});
// 保存当前的面板单元选择状态,以便在切换面板组后恢复
const savedCurrentPanelUnit = currentPanelUnit.value;
// 检查是否是切换到不同的面板组
const isDifferentPanel = currentPanelNode.value &&
data.type === 'panel' &&
currentPanelNode.value.panelGuid !== data.panelGuid;
// 只有切换到不同的面板组时才清空右侧配置表格,但不清空面板单元数据
if (isDifferentPanel || data.type !== 'panel') {
// 清空右侧配置表格
panelConfigTable.value = [];
patternsList.value = [];
// 不清空currentPanelUnit.value保留用户的选择状态
// currentPanelUnit.value = null; // 注释掉这行,保留选择状态
panelUnitForm.value = [];
}
if (data.type === 'panel') {
currentPanelNode.value = data;
await loadPanelConfiguration(data);
// 如果是切换到不同的面板组,尝试恢复之前选中的面板单元
if (isDifferentPanel && savedCurrentPanelUnit) {
// 检查之前选中的面板单元是否仍然存在于当前项目的面板单元列表中
const unitExists = unitTableData.value.some(unit => unit.guid === savedCurrentPanelUnit);
if (unitExists) {
currentPanelUnit.value = savedCurrentPanelUnit;
}
}
} else {
currentPanelNode.value = null;
}
};
// ==================== 房型相关方法 ====================
/**
* 编辑房型名称
*/
const editRoomName = async (data) => {
try {
const { value: newName } = await ElMessageBox.prompt('请输入新的房型名称', '修改房型名称', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValue: data.name,
inputPattern: /.+/,
inputErrorMessage: '房型名称不能为空'
});
const roomInfo = await getRoomTypeInfoByGuid(data.roomGuid);
if (roomInfo) {
await updateDatabase('PanelSelection/WebRoomTypeUpdate', {
...roomInfo,
room_name: newName
});
data.name = newName;
ElMessage.success('房型名称修改成功');
}
} catch (err) {
if (err !== 'cancel') ElMessage.error('修改房型名称失败');
}
};
/**
* 新增房型
*/
const addRoomType = async (data) => {
try {
const { value: roomName } = await ElMessageBox.prompt('请输入新房型名称', '新增房型', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /.+/,
inputErrorMessage: '房型名称不能为空'
});
const projectNode = findProjectNode(data.project.id);
if (!projectNode) return;
// 创建新房型
const roomData = {
room_name: roomName,
room_description: `手动创建的房型`,
panel_group_count: '[]',
remarks: '手动创建',
creator: getCurrentUser(),
project_id: projectNode.webProject.id
};
const newRoomGuid = await updateDatabase('PanelSelection/WebRoomTypeAdd', roomData);
// 更新项目信息
const roomGuids = JSON.parse(projectNode.webProject.room_data_json || '[]');
roomGuids.push(newRoomGuid);
const updatedProject = {
...projectNode.webProject,
room_type_count: roomGuids.length,
room_data_json: JSON.stringify(roomGuids)
};
await updateDatabase('PanelSelection/WebProjectUpdate', updatedProject);
// 更新本地数据
projectNode.webProject = updatedProject;
projectNode.name = `${data.project.projectName} (${roomGuids.length})`;
// 添加新节点
projectNode.children.push({
id: newRoomGuid,
name: roomName,
type: 'room',
projectId: data.project.id,
webProjectId: projectNode.webProject.id,
roomGuid: newRoomGuid,
children: []
});
ElMessage.success('房型新增成功');
} catch (err) {
if (err !== 'cancel') ElMessage.error('新增房型失败: ' + err.message);
}
};
/**
* 删除房型
*/
const deleteRoom = async (data) => {
try {
await ElMessageBox.confirm('确定要删除这个房型吗?此操作将从数据库中删除,且不可恢复。', '确认删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
const projectNode = findProjectNode(data.projectId);
if (!projectNode) return;
// 删除房型
await updateDatabase('PanelSelection/WebRoomTypeDelete', data.guid);
// 更新项目信息
const roomGuids = JSON.parse(projectNode.webProject.room_data_json || '[]');
const updatedRoomGuids = roomGuids.filter(guid => guid !== data.roomGuid);
const updatedProject = {
...projectNode.webProject,
room_type_count: updatedRoomGuids.length,
room_data_json: JSON.stringify(updatedRoomGuids)
};
await updateDatabase('PanelSelection/WebProjectUpdate', updatedProject);
// 更新本地数据
projectNode.webProject = updatedProject;
projectNode.name = `${data.project.projectName} (${updatedRoomGuids.length})`;
// 删除节点
const roomIndex = projectNode.children.findIndex(r => r.roomGuid === data.roomGuid);
if (roomIndex !== -1) {
projectNode.children.splice(roomIndex, 1);
ElMessage.success('房型删除成功');
}
} catch (err) {
if (err !== 'cancel') ElMessage.error('删除房型失败');
}
};
// ==================== 面板组相关方法 ====================
/**
* 下载CDR文件
*/
const downloadCDRFile = () => {
if (!currentPanelNode.value?.panelInfo?.cdr_filename) {
ElMessage.warning('CDR文件不存在');
return;
}
try {
// 构建文件URL
const fileUrl = config.PicAds + currentPanelNode.value.panelInfo.cdr_filename;
// 创建a标签并模拟点击下载
const link = document.createElement('a');
link.href = fileUrl;
link.target = '_blank'; // 在新页面打开
// 从文件名中提取不带路径的文件名
const fileName = currentPanelNode.value.panelInfo.cdr_filename.split('/').pop();
link.download = fileName || 'panel.cdr';
// 将a标签添加到页面触发点击然后移除
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
ElMessage.success('CDR文件下载已开始');
} catch (error) {
console.error('下载CDR文件失败:', error);
ElMessage.error('下载CDR文件失败: ' + error.message);
}
};
/**
* 处理面板单元选择变化
*/
const handlePanelUnitChange = async (index, panelUnitGuid) => {
const selectedUnit = availablePanelUnits.value.find(unit => unit.guid === panelUnitGuid);
if (selectedUnit) {
panelConfigTable.value[index].panelUnitData = selectedUnit;
panelConfigTable.value[index].isEditing = false;
// 更新单面板预览图
await updateSinglePanelPreviewFromUnit(selectedUnit);
await clearPanelGroupPreview();
// 保存面板配置
await savePanelConfig();
}
};
/**
* 编辑面板单元配置
*/
const editPanelUnitConfig = (row) => {
if (!row.panelUnitGuid) {
//ElMessage.warning('请先选择面板单元');
return;
}
const unit = availablePanelUnits.value.find(u => u.guid === row.panelUnitGuid);
if (unit) {
// 触发面板单元行点击事件,加载配置到右侧
handleUnitRowClick(unit);
// 标记当前正在编辑的面板配置行
panelConfigTable.value.forEach(item => {
item.isEditing = false;
});
// 设置当前编辑的面板单元
currentPanelUnit.value = row.panelUnitGuid;
}
};
/**
* 从面板单元数据更新单面板预览图
*/
const updateSinglePanelPreviewFromUnit = async (panelUnit) => {
if (!panelUnit) {
singlePanelPreview.value = '';
return;
}
if (panelUnit.thumbnail_large) {
singlePanelPreview.value = config.PicAds + panelUnit.thumbnail_large;
} else {
singlePanelPreview.value = '';
}
};
/**
* 添加面板组
*/
const addPanel = async (data) => {
currentRoomData.value = data;
resetPanelDialog();
showAddPanelDialog.value = true;
};
/**
* 编辑面板组名称
*/
const editPanelName = async (data) => {
try {
const panelInfo = await getPanelInfoByGuid(data.panelGuid);
if (panelInfo) {
currentRoomData.value = data;
newPanelForm.value = {
panel_list_name: panelInfo.panel_list_name,
gang_series: panelInfo.gang_series,
model_type: panelInfo.model_type || '',
panel_count: panelInfo.panel_count || null,
gang_material_id: panelInfo.gang_material_id || 0,
remarks: panelInfo.remarks || '',
logo_json: panelInfo.logo_json || '',
};
// 解析系列信息
const [seriesName, version] = panelInfo.gang_series.split('_');
const matchedSeries = seriesList.value.find(item =>
item.Series === seriesName && item.Version === version
);
if (matchedSeries) {
selectedSeries.value = matchedSeries;
await getModels(matchedSeries.ID);
if (panelInfo.model_type) {
let matchedModel = allModels.value.find(model =>
model.ModelName === panelInfo.model_type
);
if (!matchedModel && panelInfo.gang_material_id) {
matchedModel = allModels.value.find(model =>
model.ID === panelInfo.gang_material_id
);
}
if (matchedModel) {
selectedPanelCount.value = matchedModel.PanelCount;
selectedDirection.value = matchedModel.Direction;
selectedColor.value = matchedModel.Color;
updateModelList();
selectedModel.value = matchedModel;
newPanelForm.value.model_type = matchedModel.ModelName;
await restoreLogoConfig(panelInfo);
} else {
newPanelForm.value.model_type = panelInfo.model_type;
// 即使没有匹配模型也尝试恢复LOGO配置
await restoreLogoConfig(panelInfo);
}
} else {
// 如果没有模型类型也尝试恢复LOGO配置
await restoreLogoConfig(panelInfo);
}
}
editingPanelGuid.value = data.panelGuid;
showAddPanelDialog.value = true;
}
} catch (error) {
console.error('获取面板组信息失败:', error);
ElMessage.error('获取面板组信息失败');
}
};
/**
* 删除面板组
*/
const deletePanel = async (data) => {
try {
await ElMessageBox.confirm('确定要删除这个面板组吗?此操作将从数据库中删除,且不可恢复。', '确认删除', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
const roomNode = findRoomNode(data.projectId, data.roomGuid);
if (!roomNode) return;
// 从房型中移除面板组
await removePanelFromRoomGroups(data.roomGuid, data.panelGuid);
// 删除面板组
await updateDatabase('PanelSelection/WebPanelListDelete', data.panelGuid);
// 更新树结构
const panelIndex = roomNode.children.findIndex(p => p.panelGuid === data.panelGuid);
if (panelIndex !== -1) {
roomNode.children.splice(panelIndex, 1);
ElMessage.success('面板组删除成功');
}
} catch (err) {
if (err !== 'cancel') ElMessage.error('删除面板组失败');
}
};
/**
* 从房型中移除面板组
*/
const removePanelFromRoomGroups = async (roomGuid, panelGuid) => {
const roomInfo = await getRoomTypeInfoByGuid(roomGuid);
if (!roomInfo) return;
const panelGroups = JSON.parse(roomInfo.panel_group_count || '[]');
const updatedPanelGroups = panelGroups.filter(guid => guid !== panelGuid);
await updateDatabase('PanelSelection/WebRoomTypeUpdate', {
...roomInfo,
panel_group_count: JSON.stringify(updatedPanelGroups)
});
};
// ==================== 面板组对话框相关方法 ====================
/**
* 获取模型数据
*/
const getModels = async (fileId) => {
try {
const rs = await localHttp.post('PanelSelection/ModelQuery', JSON.stringify({ FID: fileId }));
if (rs.data.isok && rs.data.response) {
allModels.value = JSON.parse(rs.data.response);
panelCountOptions.value = [...new Set(allModels.value.map(model => model.PanelCount))].sort();
directionOptions.value = [...new Set(allModels.value.map(model => model.Direction))];
colorOptions.value = [...new Set(allModels.value.map(model => model.Color))];
} else {
allModels.value = [];
}
} catch (error) {
console.error('获取模型列表失败:', error);
allModels.value = [];
ElMessage.error('获取模型列表失败');
}
};
/**
* 更新模型列表
*/
const updateModelList = () => {
if (!selectedPanelCount.value || !selectedDirection.value || !selectedColor.value) {
filteredModels.value = [];
selectedModel.value = null;
newPanelForm.value.model_type = '';
return;
}
filteredModels.value = allModels.value.filter(model => {
return model.PanelCount == selectedPanelCount.value &&
model.Direction === selectedDirection.value &&
model.Color === selectedColor.value;
});
if (filteredModels.value.length === 1) {
selectedModel.value = filteredModels.value[0];
newPanelForm.value.model_type = selectedModel.value.ModelName;
} else {
selectedModel.value = null;
newPanelForm.value.model_type = '';
}
};
/**
* 系列选择变化处理
*/
const handleSeriesChange = async (currentRow) => {
selectedSeries.value = currentRow;
selectedModel.value = null;
allModels.value = [];
filteredModels.value = [];
selectedPanelCount.value = '';
selectedDirection.value = '';
selectedColor.value = '';
panelCountOptions.value = [];
directionOptions.value = [];
colorOptions.value = [];
if (currentRow) {
newPanelForm.value.gang_series = `${currentRow.Series}_${currentRow.Version}`;
if (currentRow.PreviewPath) {
selectedSeriesPreview.value = config.PicAds + currentRow.PreviewPath;
} else {
selectedSeriesPreview.value = '';
}
await getModels(currentRow.ID);
} else {
logoList.value = [];
newPanelForm.value.gang_series = '';
selectedSeriesPreview.value = '';
logoConfig.value = {
LogoPosition: '',
LogoFileName: '',
LogoIndexNumber: ''
};
}
};
/**
* 模型选择处理
*/
const handleModelChange = (model) => {
selectedModel.value = model;
newPanelForm.value.model_type = model ? model.ModelName : '';
};
/**
* 确认添加/编辑面板组
*/
const confirmAddPanel = async () => {
if (!newPanelForm.value.panel_list_name) {
ElMessage.error('请填写面板组名称');
return;
}
if (!selectedSeries.value) {
ElMessage.error('请选择系列');
return;
}
if (!selectedPanelCount.value || !selectedDirection.value || !selectedColor.value) {
ElMessage.error('请完整选择面板数、方向和颜色');
return;
}
if (!selectedModel.value) {
ElMessage.error('请选择模型');
return;
}
let logoJsonValue = JSON.stringify(logoConfig.value);
try {
if (editingPanelGuid.value) {
// 编辑模式
const panelInfo = await getPanelInfoByGuid(editingPanelGuid.value);
if (panelInfo) {
await updateDatabase('PanelSelection/WebPanelListUpdate', {
...panelInfo,
panel_list_name: newPanelForm.value.panel_list_name,
gang_series: newPanelForm.value.gang_series,
model_type: selectedModel.value.ModelName,
gang_material_id: selectedModel.value.ID,
remarks: newPanelForm.value.remarks,
logo_json: logoJsonValue
});
// 更新树节点
const roomNode = findRoomNode(currentRoomData.value.projectId, currentRoomData.value.roomGuid);
if (roomNode) {
const panelNode = roomNode.children.find(p => p.panelGuid === editingPanelGuid.value);
if (panelNode) {
panelNode.name = `${newPanelForm.value.panel_list_name}${newPanelForm.value.gang_series}`;
panelNode.panelInfo = {
...panelNode.panelInfo,
...newPanelForm.value,
model_type: selectedModel.value.ModelName,
gang_material_id: selectedModel.value.ID
};
}
}
ElMessage.success('面板组修改成功');
}
} else {
// 新增模式
const newPanelGuid = await createPanelInDatabase(newPanelForm.value);
if (newPanelGuid) {
await updateRoomPanelGroups(currentRoomData.value.roomGuid, newPanelGuid);
// 添加新节点
const roomNode = findRoomNode(currentRoomData.value.projectId, currentRoomData.value.roomGuid);
if (roomNode) {
const panelInfo = await getPanelInfoByGuid(newPanelGuid);
if (panelInfo) {
roomNode.children.push({
id: `panel-${currentRoomData.value.roomGuid}-${newPanelGuid}`,
name: `${panelInfo.panel_list_name}${panelInfo.gang_series}`,
type: 'panel',
roomGuid: currentRoomData.value.roomGuid,
panelGuid: newPanelGuid,
panelInfo: {
...panelInfo,
panel_info_json: '{"panels":[]}'
},
projectId: currentRoomData.value.projectId,
gang_material_id: selectedModel.value.ID,
logo_json: logoJsonValue
});
}
}
ElMessage.success('面板组新增成功');
}
}
showAddPanelDialog.value = false;
resetPanelDialog();
} catch (error) {
console.error(editingPanelGuid.value ? '修改面板组失败:' : '新增面板组失败:', error);
ElMessage.error(editingPanelGuid.value ? '修改面板组失败' : '新增面板组失败');
}
};
/**
* 创建面板组
*/
const createPanelInDatabase = async (panelData) => {
if (!selectedSeries.value || !selectedModel.value) {
throw new Error('请先选择系列和模型');
}
const fullPanelData = {
panel_list_name: panelData.panel_list_name,
gang_series: panelData.gang_series,
model_type: selectedModel.value.ModelName,
panel_count: selectedModel.value.PanelCount,
gang_material_id: selectedModel.value.ID,
panel_info_json: '{"panelUnitGuids":[]}', // 初始化为空GUID数组
logo_json: panelData.logo_json || "{}",
cdr_filename: '',
thumbnail_large: '',
thumbnail_small: '',
erp_part_number: '',
remarks: panelData.remarks || '',
room_type_id: 1
};
return await updateDatabase('PanelSelection/WebPanelListAdd', fullPanelData);
};
/**
* 更新房型面板组
*/
const updateRoomPanelGroups = async (roomGuid, panelGuid) => {
const roomInfo = await getRoomTypeInfoByGuid(roomGuid);
if (!roomInfo) {
ElMessage.error('未找到对应的房型信息');
return false;
}
const panelGroups = JSON.parse(roomInfo.panel_group_count || '[]');
if (!panelGroups.includes(panelGuid)) {
panelGroups.push(panelGuid);
await updateDatabase('PanelSelection/WebRoomTypeUpdate', {
...roomInfo,
panel_group_count: JSON.stringify(panelGroups)
});
}
return true;
};
/**
* 重置对话框
*/
const resetPanelDialog = () => {
newPanelForm.value = {
panel_list_name: '',
gang_series: '',
model_type: '',
gang_material_id: 0,
panel_count: null,
remarks: '',
logo_json: "{}"
};
selectedSeries.value = null;
selectedModel.value = null;
allModels.value = [];
filteredModels.value = [];
selectedPanelCount.value = '';
selectedDirection.value = '';
selectedColor.value = '';
panelCountOptions.value = [];
directionOptions.value = [];
colorOptions.value = [];
editingPanelGuid.value = null;
logoConfig.value = {
LogoPosition: '',
LogoFileName: '',
LogoIndexNumber: ''
};
};
// ==================== 面板配置相关方法 ====================
/**
* 编辑面板单元
*/
const editPanelUnit = async (row) => {
try {
showLoading('加载面板单元数据中...');
// 设置编辑模式
isEditPanelUnit.value = true;
currentEditPanelUnit.value = row;
// 填充基本表单数据
newPanelUnitForm.value = {
panel_name: row.panel_name,
remarks: row.remarks || ''
};
// 重置选择器
selectedSeriesForUnit.value = null;
selectedDirectionForUnit.value = '';
selectedColorForUnit.value = '';
selectedPatternForUnit.value = '';
// 从面板单元数据中恢复之前的选择
await restorePanelUnitSelections(row);
// 打开对话框
showAddPanelUnitDialog.value = true;
hideLoading();
} catch (error) {
hideLoading();
console.error('加载面板单元数据失败:', error);
ElMessage.error('加载面板单元数据失败');
}
};
/**
* 从面板单元数据恢复选择信息
*/
const restorePanelUnitSelections = async (panelUnit) => {
try {
// 方法1: 使用新增的字段直接恢复选择
if (panelUnit.series_id) {
const series = seriesList.value.find(s => s.ID === panelUnit.series_id);
if (series) {
selectedSeriesForUnit.value = series;
return; // 后续由watch处理
}
}
// 方法2: 从product_type中尝试匹配最后的手段
if (panelUnit.product_type) {
for (const series of seriesList.value) {
if (panelUnit.product_type.includes(series.Series) ||
panelUnit.product_type.includes(series.FileName)) {
selectedSeriesForUnit.value = series;
return; // 后续由watch处理
}
}
}
} catch (error) {
console.error('恢复面板单元选择信息失败:', error);
}
};
/**
* 加载面板单元的选择信息
*/
const loadPanelUnitSelections = async (panelUnit) => {
try {
// 方法1: 从product_type中解析系列信息
if (panelUnit.product_type) {
// 查找匹配的系列
const matchedSeries = seriesList.value.find(series => {
// 尝试从product_type中匹配系列名称
return panelUnit.product_type.includes(series.Series) ||
panelUnit.product_type.includes(series.FileName);
});
if (matchedSeries) {
selectedSeriesForUnit.value = matchedSeries;
await getModelsForUnit(matchedSeries.ID);
// 查找匹配的面板
const patterns = await fetchData('PanelSelection/PatternQuery', { FID: matchedSeries.ID });
const matchedPattern = patterns.find(pattern =>
panelUnit.product_type.includes(pattern.PatternName)
);
if (matchedPattern) {
selectedPatternForUnit.value = matchedPattern.ID;
}
// 尝试从remarks中解析方向和颜色
if (panelUnit.remarks) {
const remarks = panelUnit.remarks.toLowerCase();
if (remarks.includes('左')) selectedDirectionForUnit.value = '左';
if (remarks.includes('右')) selectedDirectionForUnit.value = '右';
if (remarks.includes('白色')) selectedColorForUnit.value = '白色';
if (remarks.includes('黑色')) selectedColorForUnit.value = '黑色';
// 添加更多颜色匹配...
}
}
}
// 方法2: 如果方法1失败尝试从关联的项目中获取信息
if (!selectedSeriesForUnit.value && panelUnit.project_guid) {
// 获取项目信息,尝试找到相关的系列
const projects = await fetchData('PanelSelection/WebProjectQuery');
const project = projects.find(p => p.guid === panelUnit.project_guid);
if (project) {
// 查找该项目下其他面板单元使用的系列
const units = await fetchData('PanelSelection/WebPanelUnitQueryByProject', {
GUID: panelUnit.project_guid
});
for (const unit of units) {
if (unit.guid !== panelUnit.guid && unit.product_type) {
const matchedSeries = seriesList.value.find(series =>
unit.product_type.includes(series.Series)
);
if (matchedSeries) {
selectedSeriesForUnit.value = matchedSeries;
await getModelsForUnit(matchedSeries.ID);
break;
}
}
}
}
}
} catch (error) {
console.error('加载面板单元选择信息失败:', error);
// 即使失败也不影响主要功能
}
};
/**
* 加载面板配置
*/
const loadPanelConfiguration = async (panelNode) => {
try {
if (panelNode.panelInfo && panelNode.panelInfo.gang_material_id) {
await getPatterns(panelNode.panelInfo.gang_material_id);
}
initPanelConfigTable(panelNode);
// 注释掉清空单个面板配置表单和预览图的代码,以保持选择状态
// currentPanelUnit.value = null;
// panelUnitForm.value = [];
// singlePanelPreview.value = ''; // 新增:清除单面板预览图
ElMessage.success('面板配置加载成功');
} catch (error) {
console.error('加载面板配置失败:', error);
ElMessage.error('加载面板配置失败');
}
};
/**
* 删除面板单元
*/
const deletePanelUnit = async (row) => {
try {
await ElMessageBox.confirm(
`确定要删除面板单元 "${row.panel_name}" 吗?此操作不可恢复。`,
'确认删除',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
// 调用后端删除API实际上是更新is_valid为0
await updateDatabase('PanelSelection/WebPanelUnitDelete', row.guid);
ElMessage.success('面板单元删除成功');
// 刷新列表
const projectNode = findProjectNode(selectedProjectId.value);
if (projectNode?.webProject?.guid) {
await loadPanelUnitsByProject(projectNode.webProject.guid);
}
// 如果删除的是当前正在编辑的面板单元,清空右侧配置
if (currentPanelUnit.value === row.guid) {
currentPanelUnit.value = null;
panelUnitForm.value = [];
singlePanelPreview.value = '';
}
} catch (error) {
if (error !== 'cancel') {
console.error('删除面板单元失败:', error);
ElMessage.error('删除面板单元失败');
}
}
};
/**
* 初始化面板配置表格
*/
const initPanelConfigTable = (panelNode) => {
const panelCount = panelNode.panelInfo.panel_count || 3;
panelConfigTable.value = Array.from({ length: panelCount }, (_, index) => ({
panelIndex: index + 1,
panelUnitGuid: null,
panelUnitData: null,
isEditing: false
}));
if (panelNode.panelInfo && panelNode.panelInfo.panel_info_json) {
try {
const savedConfig = JSON.parse(panelNode.panelInfo.panel_info_json);
// 兼容新旧数据结构
if (savedConfig.panelUnitGuids && Array.isArray(savedConfig.panelUnitGuids)) {
// 新数据结构GUID数组
savedConfig.panelUnitGuids.forEach((guid, index) => {
if (panelConfigTable.value[index] && guid) {
panelConfigTable.value[index].panelUnitGuid = guid;
// 查找对应的面板单元数据
const unit = availablePanelUnits.value.find(u => u.guid === guid);
if (unit) {
panelConfigTable.value[index].panelUnitData = unit;
}
}
});
} else if (savedConfig.panels && Array.isArray(savedConfig.panels)) {
// 旧数据结构:兼容处理
savedConfig.panels.forEach((panelConfig, index) => {
if (panelConfigTable.value[index] && panelConfig.panelUnitGuid) {
panelConfigTable.value[index].panelUnitGuid = panelConfig.panelUnitGuid;
const unit = availablePanelUnits.value.find(u => u.guid === panelConfig.panelUnitGuid);
if (unit) {
panelConfigTable.value[index].panelUnitData = unit;
}
}
});
}
} catch (error) {
console.error('解析保存的配置失败:', error);
}
}
};
/**
* 获取Pattern列表
*/
const getPatterns = async (fileId) => {
try {
patternsList.value = await fetchData('PanelSelection/PatternQuery', { FID: fileId });
} catch (error) {
console.error('获取Pattern列表失败:', error);
patternsList.value = [];
}
};
/**
* Pattern选择变化处理
*/
const handlePatternChange = async (index, patternId) => {
const selectedPattern = patternsList.value.find(p => p.ID === patternId);
if (selectedPattern) {
panelConfigTable.value[index].patternData = selectedPattern;
panelConfigTable.value[index].panelUnitGuid = null;
panelConfigTable.value[index].isEditing = false;
await updateSinglePanelPreview(patternId);
await savePanelConfig();
if (patternId) {
await editPanelConfig(index);
}
}
};
/**
* 编辑面板配置
*/
const editPanelConfig = async (index) => {
try {
const panelConfig = panelConfigTable.value[index];
if (!panelConfig.selectedPattern) {
ElMessage.warning('请先选择面板模式');
return;
}
panelConfigTable.value.forEach((item, i) => {
item.isEditing = i === index;
});
await updateSinglePanelPreview(panelConfig.selectedPattern);
const pattern = patternsList.value.find(p => p.ID === panelConfig.selectedPattern);
if (!pattern) {
ElMessage.error('未找到对应的面板模式信息');
return;
}
const locations = await getLocationls(panelConfig.selectedPattern);
let panelUnitGuid = panelConfig.panelUnitGuid;
if (!panelUnitGuid) {
panelUnitGuid = await createPanelUnitIfNotExists(panelConfig, pattern, locations, index);
}
if (panelUnitGuid) {
panelConfig.panelUnitGuid = panelUnitGuid;
await savePanelConfig();
await loadPanelUnitConfiguration(panelUnitGuid, locations);
ElMessage.success('面板配置编辑已启动');
}
} catch (error) {
console.error('编辑面板配置失败:', error);
ElMessage.error('编辑面板配置失败');
}
};
/**
* 检查并创建面板单元(如果不存在)
*/
const createPanelUnitIfNotExists = async (panelConfig, pattern, locations, index) => {
try {
const existingPanelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const expectedPanelName = `${currentPanelNode.value.panelInfo.panel_list_name}_面板${index + 1}`;
let existingPanelUnit = existingPanelUnits.find(p =>
p.panel_name === expectedPanelName && p.is_valid
);
if (existingPanelUnit) {
return existingPanelUnit.guid;
} else {
// 获取当前项目的GUID
const projectNode = findProjectNode(currentPanelNode.value.projectId);
if (!projectNode || !projectNode.webProject) {
throw new Error('未找到项目信息');
}
const panelUnitData = {
panel_name: expectedPanelName,
product_type: pattern.PatternName,
element_count: locations.length,
elements_json: '[]',
cdr_filename: '',
thumbnail_large: '',
thumbnail_small: '',
remarks: `关联面板组: ${currentPanelNode.value.panelInfo.panel_list_name}`,
erp_part_number: '',
design_status: '',
owner: getCurrentUser(),
share_type: '',
project_guid: projectNode.webProject.guid // 新增项目关联
};
const newGuid = await createPanelUnit(panelUnitData);
return newGuid;
}
} catch (error) {
console.error('检查/创建面板单元失败:', error);
throw error;
}
};
/**
* 保存面板配置
*/
const savePanelConfig = async () => {
if (!currentPanelNode.value) {
return;
}
try {
// 构建GUID数组配置
const configData = {
panelUnitGuids: panelConfigTable.value
.map(item => item.panelUnitGuid)
.filter(guid => guid && guid.length === 32) // 只保留有效的GUID
};
const panelInfo = await getPanelInfoByGuid(currentPanelNode.value.panelGuid);
if (panelInfo) {
await updateDatabase('PanelSelection/WebPanelListUpdate', {
...panelInfo,
panel_info_json: JSON.stringify(configData),
// 取消gang_material_id的绑定
//gang_material_id: 0
});
currentPanelNode.value.panelInfo.panel_info_json = JSON.stringify(configData);
currentPanelNode.value.panelInfo.gang_material_id = 0;
}
} catch (error) {
console.error('保存面板配置失败:', error);
ElMessage.error('保存面板配置失败');
}
};
/**
* 生成预览图(面板组合)
*/
const showPreview = async (ProcessType) => {
if (!currentPanelNode.value) {
//ElMessage.warning('请先选择面板组');
return;
}
if (!isAllPanelsConfigured.value) {
ElMessage.warning('请先完成所有面板的配置');
return;
}
const startTime = performance.now();
showLoading('生成预览图中...');
try {
// 从remark字段读取已保存的结构化数据
const panelInfo = await getPanelInfoByGuid(currentPanelNode.value.panelGuid);
let previewObject = null;
/*if (panelInfo && panelInfo.remarks) {
try {
const savedConfig = JSON.parse(panelInfo.remarks);
if (savedConfig.structureData) {
previewObject = savedConfig.structureData;
// 更新Process类型
previewObject.Process = ProcessType;
}
} catch (e) {
console.warn('从remark字段读取面板组配置失败重新生成:', e);
}
}*/
// 如果没有从remark读取到则重新生成
if (!previewObject) {
previewObject = await generatePanelGroupStructureData(true);
if (previewObject) {
previewObject.Process = ProcessType;
}
}
if (!previewObject) {
ElMessage.error('生成预览对象失败');
hideLoading();
return;
}
console.log('面板组预览对象:', previewObject);
// 发送到CDR生成预览图
const rs = await localHttp.post('PanelSelection/SendToCDR', JSON.stringify(previewObject));
if (rs.data.isok) {
const rsv = rs.data.response;
if (rsv) {
// 更新面板组信息
if (panelInfo) {
const updateData = {
...panelInfo,
thumbnail_small: "生成中"
};
await updateDatabase('PanelSelection/WebPanelListUpdate', updateData);
// 将生成的完整previewObject保存到remark字段
try {
const saveData = {
...panelInfo,
remarks: JSON.stringify({
structureData: previewObject,
savedAt: new Date().toISOString(),
version: '1.0',
lastGenerated: Date.now()
})
};
await updateDatabase('PanelSelection/WebPanelListUpdate', saveData);
} catch (saveError) {
console.error('保存面板组预览对象到remark字段失败:', saveError);
}
}
const MIN_EXECUTION_TIME = 555;
const elapsedTime = performance.now() - startTime;
const remainingTime = MIN_EXECUTION_TIME - elapsedTime;
if (remainingTime > 0) await new Promise(resolve => setTimeout(resolve, remainingTime));
if (!isPollingActive.value) {
startPolling();
}
ElMessage.success('设计图已经加入生成队列,请稍后!');
} else {
ElMessage.error('面板设计图生成失败: ' + rsv.Msg);
}
} else {
ElMessage.error('面板设计图生成失败');
}
} catch (error) {
console.error('生成面板组预览对象失败:', error);
ElMessage.error('生成面板组预览对象失败: ' + error.message);
} finally {
hideLoading();
// 保存当前选中的面板单元,以便在重新加载后恢复
const savedCurrentPanelUnit = currentPanelUnit.value;
await triggerCurrentTreeNodeClick();
// 恢复之前选中的面板单元
if (savedCurrentPanelUnit) {
currentPanelUnit.value = savedCurrentPanelUnit;
}
}
};
/**
* 生成预览图(单个面板)
*/
const showSinglePanelPreview = async () => {
if (!currentPanelUnit.value) {
//ElMessage.warning('请先选择面板单元');
return;
}
const startTime = performance.now();
showLoading('生成单个面板预览图中...');
try {
// 从remark字段读取已保存的previewObject
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === currentPanelUnit.value);
let previewObject = null;
/* if (panelUnit && panelUnit.remarks) {
try {
const savedConfig = JSON.parse(panelUnit.remarks);
if (savedConfig.previewObject) {
previewObject = savedConfig.previewObject;
// 更新Process为生成图片
previewObject.Process = 0;
}
} catch (e) {
console.warn('从remark字段读取单个面板配置失败重新生成:', e);
}
} */
// 如果没有从remark读取到则重新生成
if (!previewObject) {
previewObject = await generateSinglePanelPreviewObject(true);
if (previewObject) {
console.log('重新生成单个面板预览对象成功');
}
}
if (!previewObject) {
ElMessage.error('生成预览对象失败');
hideLoading();
return;
}
console.log('单个面板预览对象:', previewObject);
// 发送到CDR生成预览图
const rs = await localHttp.post('PanelSelection/SendToCDR', JSON.stringify(previewObject));
if (rs.data.isok) {
const rsv = rs.data.response;
if (rsv) {
// 更新面板单元的缩略图
await updateDatabase('PanelSelection/WebPanelUnitUpdate', {
...panelUnit,
design_status: "生成中"
});
// 将生成的完整previewObject保存到remark字段
try {
const saveData = {
...panelUnit,
remarks: JSON.stringify({
previewObject: previewObject,
savedAt: new Date().toISOString(),
version: '1.0',
lastGenerated: Date.now()
})
};
await updateDatabase('PanelSelection/WebPanelUnitUpdate', saveData);
console.log('单个面板预览对象已保存到remark字段');
} catch (saveError) {
console.error('保存单个面板预览对象到remark字段失败:', saveError);
}
// 更新本地数据
const unitIndex = unitTableData.value.findIndex(u => u.guid === currentPanelUnit.value);
if (unitIndex !== -1) {
unitTableData.value[unitIndex].design_status = "生成中";
}
const MIN_EXECUTION_TIME = 555;
const elapsedTime = performance.now() - startTime;
const remainingTime = MIN_EXECUTION_TIME - elapsedTime;
if (remainingTime > 0) await new Promise(resolve => setTimeout(resolve, remainingTime));
ElMessage.success('设计图已经加入生成队列,请稍后!');
if (!isPollingActive.value) {
startPolling();
}
} else {
ElMessage.error('单个面板预览图生成失败: ' + rsv.Msg);
}
} else {
ElMessage.error('单个面板预览图生成失败');
}
} catch (error) {
console.error('生成单个面板预览对象失败:', error);
ElMessage.error('生成单个面板预览对象失败: ' + error.message);
} finally {
hideLoading();
}
};
// ==================== 面板单元配置相关方法 ====================
/**
* 单个面板的pattern预览图
*/
const singlePanelPatternPreview = ref('');
watch(currentPanelUnit, async (newGuid) => {
if (!newGuid) {
singlePanelPatternPreview.value = '';
return;
}
// 从 unitTableData 中查找当前面板单元
const panelUnit = unitTableData.value.find(unit => unit.guid === newGuid);
if (!panelUnit || !panelUnit.pattern_id) {
singlePanelPatternPreview.value = '';
return;
}
try {
// 异步获取 pattern 数据
const pattern = await fetchData('PanelSelection/IdPatternQuery', { FID: panelUnit.pattern_id });
if (pattern && pattern[0] && pattern[0].PreviewPath) {
singlePanelPatternPreview.value = config.PicAds + pattern[0].PreviewPath;
} else {
singlePanelPatternPreview.value = '';
}
} catch (error) {
singlePanelPatternPreview.value = '';
}
}, { immediate: true });
/**
* 创建面板单元
*/
const createPanelUnit = async (panelUnitData) => {
try {
const rs = await localHttp.post('PanelSelection/WebPanelUnitAdd', JSON.stringify(panelUnitData));
if (rs.data.isok && rs.data.response) {
return rs.data.response;
}
throw new Error(rs.data.message || '创建面板单元失败');
} catch (error) {
console.error('创建面板单元失败:', error);
throw error;
}
};
/**
* 查询Location数据
*/
const getLocationls = async (patternId) => {
try {
const rs = await localHttp.post('PanelSelection/LocationQuery', JSON.stringify({ PID: patternId }));
if (rs.data.isok && rs.data.response) {
return JSON.parse(rs.data.response);
}
return [];
} catch (error) {
console.error('获取Location数据失败:', error);
return [];
}
};
/**
* 获取所有图标库文件
*/
const getIconLibraries = async () => {
try {
const libraries = await fetchData('PanelSelection/IconLibraryQuery');
iconLibraryList.value = libraries;
// 并行加载所有图标库的图标
const loadPromises = libraries.map(async (library) => {
try {
const icons = await fetchData('PanelSelection/IconQuery', { FID: library.ID });
iconOptions.value[library.ID] = icons;
} catch (error) {
console.error(`加载图标库 ${library.Series} 失败:`, error);
iconOptions.value[library.ID] = [];
}
});
await Promise.all(loadPromises);
// 设置默认选中的图标库
if (libraries.length > 0) {
selectedIconLibrary.value = libraries[0].ID;
}
return libraries;
} catch (error) {
console.error('获取图标库列表失败:', error);
iconLibraryList.value = [];
return [];
}
};
/**
* 获取单个图标库的图标
*/
const getIconsByLibrary = async (libraryId) => {
try {
const icons = await fetchData('PanelSelection/IconQuery', { FID: libraryId });
iconOptions.value[libraryId] = icons;
return icons;
} catch (error) {
console.error(`获取图标库 ${libraryId} 的图标失败:`, error);
return [];
}
};
/**
* 图标选择变化处理
*/
const handleIconChange = (row) => {
console.log('图标选择变化:', row);
};
/**
* 检查面板单元是否存在
*/
const checkPanelUnitExists = async (panelUnitGuid) => {
if (!panelUnitGuid) return false;
try {
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === panelUnitGuid && p.is_valid);
return !!panelUnit;
} catch (error) {
console.error('检查面板单元存在性失败:', error);
return false;
}
};
/**
* 加载已保存的面板单元配置
*/
const loadSavedPanelUnitConfig = async (panelUnitGuid) => {
try {
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === panelUnitGuid);
if (panelUnit) {
// 检查 elements_json 是否为空或无效
if (!panelUnit.elements_json || panelUnit.elements_json === 'null' || panelUnit.elements_json === '[]' || panelUnit.elements_json === '{}') {
// 使用默认数据构建合并数据
buildMergedPanelUnitForm();
return;
}
try {
const savedConfig = JSON.parse(panelUnit.elements_json);
if (savedConfig && Array.isArray(savedConfig) && savedConfig.length > 0) {
// 清空当前数据
panelUnitForm.value = [];
// 重新构建数据
savedConfig.forEach((savedItem) => {
// 根据保存的数据创建对应的表单项
const formItem = {
id: savedItem.locationId || Date.now() + Math.random(),
locationId: savedItem.locationId || Date.now() + Math.random(),
content: savedItem.content || '',
size: savedItem.size || (savedItem.type === 2 ? 11 : savedItem.type === 3 ? 7 : null),
font: savedItem.font || '',
locationName: savedItem.locationName || `位置${savedItem.locationId || '未知'}`,
type: savedItem.type || 1,
backlightColor: savedItem.backlightColor || '#FFFFFF',
backlightEnabled: savedItem.backlightEnabled || false,
fileId: savedItem.fileId || null,
iconId: savedItem.iconId || null,
indexNum: savedItem.indexNum || 0,
keygroup: savedItem.keygroup || 0,
lineNumber: savedItem.lineNumber || 0
};
panelUnitForm.value.push(formItem);
});
// 立即构建合并数据
buildMergedPanelUnitForm();
} else {
buildMergedPanelUnitForm();
}
} catch (error) {
console.error('解析保存的配置失败:', error);
// 如果解析失败,使用默认数据
buildMergedPanelUnitForm();
}
} else {
buildMergedPanelUnitForm();
}
} catch (error) {
console.error('加载保存的面板单元配置失败:', error);
buildMergedPanelUnitForm();
}
};
/**
* 加载面板单元配置
*/
const loadPanelUnitConfiguration = async (panelUnitGuid, locations = null) => {
try {
const exists = await checkPanelUnitExists(panelUnitGuid);
if (!exists) {
ElMessage.warning('面板单元不存在');
return;
}
currentPanelUnit.value = panelUnitGuid;
// 获取面板单元详细信息
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === panelUnitGuid);
if (!panelUnit) {
ElMessage.error('未找到面板单元信息');
return;
}
const actualLocations = locations || [];
// 初始化表单数据
panelUnitForm.value = actualLocations.map((location, index) => {
if (!location) {
return null;
}
let defaultSize = null;
let content = null;
if (location.Type === 2) {
defaultSize = 11;
content = '空'
} else if (location.Type === 3) {
defaultSize = 7;
content = 'empty'
}
return {
id: location.ID || Date.now() + index,
locationId: location.ID || Date.now() + index,
index: index + 1,
content: content,
size: defaultSize,
font: '',
locationName: location.LocationName || `位置${index + 1}`,
type: location.Type || 1,
sizeH: location.SizeH || 0,
sizeW: location.SizeW || 0,
backlightColor: '#FFFFFF',
indexNum: location.IndexNum || 0,
keygroup: location.Keygroup || 0,
lineNumber: location.lineNumber || 0
};
}).filter(item => item !== null); // 过滤掉空项
await loadSavedPanelUnitConfig(panelUnitGuid);
} catch (error) {
console.error('加载面板单元配置失败:', error);
ElMessage.error('加载面板单元配置失败');
// 即使失败也尝试构建默认数据
buildMergedPanelUnitForm();
}
};
/**
* 更新单面板预览图
*/
const updateSinglePanelPreview = async (patternId) => {
if (!patternId) {
singlePanelPreview.value = '';
return;
}
const pattern = patternsList.value.find(p => p.ID === patternId);
if (pattern && pattern.PreviewPath) {
singlePanelPreview.value = config.PicAds + pattern.PreviewPath;
} else {
singlePanelPreview.value = '';
}
};
/**
* 手动刷新面板组预览图
*/
const refreshPanelGroupPreview = async () => {
if (!currentPanelNode.value) {
//ElMessage.warning('请先选择面板组');
return;
}
try {
showLoading('刷新面板组预览图中...');
// 重新查询面板组数据
const rs = await localHttp.post('PanelSelection/WebPanelListQueryByGuid',
JSON.stringify({ GUID: currentPanelNode.value.panelGuid }));
if (rs.data.isok && rs.data.response) {
const data = JSON.parse(rs.data.response);
if (data.length > 0) {
const panelInfo = data[0];
// 更新当前面板组信息
if (currentPanelNode.value) {
currentPanelNode.value.panelInfo = panelInfo;
}
// 更新树节点中的面板组信息
updatePanelNodeInTree(currentPanelNode.value.panelGuid, panelInfo);
if (panelInfo.thumbnail_large && panelInfo.thumbnail_large !== '生成中') {
panelGroupPreview.value = config.PicAds + panelInfo.thumbnail_large;
ElMessage.success('面板组预览图刷新成功');
} else {
panelGroupPreview.value = '';
//ElMessage.info('面板组预览图仍在生成中');
}
}
}
} catch (error) {
console.error('刷新面板组预览失败:', error);
ElMessage.error('刷新失败');
} finally {
hideLoading();
// 移除 reloadTreeAndTriggerClick 调用,避免清空 mergedPanelUnitForm 数据
}
};
/**
* 手动刷新面板单元预览图
*/
const refreshPanelUnitPreview = async () => {
if (!currentPanelUnit.value) {
//ElMessage.warning('请先选择面板单元');
return;
}
try {
showLoading('刷新面板单元数据中...');
singlePanelPreview.value = '';
// 1. 保存当前选中的面板单元GUID
const currentUnitGuid = currentPanelUnit.value;
// 2. 重新加载左侧面板单元列表数据
const projectNode = findProjectNode(selectedProjectId.value);
if (projectNode?.webProject?.guid) {
await loadPanelUnitsByProject(projectNode.webProject.guid);
// 3. 在列表刷新完成后,自动重新选中用户当前正在查看的面板单元
// 等待列表数据更新完成
await nextTick();
// 查找当前面板单元在刷新后的列表中的位置
const refreshedUnit = unitTableData.value.find(u => u.guid === currentUnitGuid);
if (refreshedUnit) {
// 重新设置当前选中的面板单元
currentPanelUnit.value = currentUnitGuid;
await updateSinglePanelPreviewFromUnit(refreshedUnit);
} else {
// 如果刷新后找不到当前面板单元,清空选择
currentPanelUnit.value = null;
panelUnitForm.value = [];
singlePanelPreview.value = '';
ElMessage.warning('当前面板单元已不存在,已清空选择');
}
}
} catch (error) {
console.error('刷新面板单元数据失败:', error);
ElMessage.error('刷新失败');
} finally {
hideLoading();
}
};
/**
* 保存面板单元配置
*/
const savePanelUnitConfig = async () => {
if (!currentPanelUnit.value) {
//ElMessage.warning('请先选择面板单元');
return;
}
const startTime = Date.now();
try {
if (!mergedPanelUnitForm.value || mergedPanelUnitForm.value.length === 0) {
hideLoading();
ElMessage.warning('没有配置数据需要保存');
return;
}
// 将合并数据转换回原始格式
const flatData = [];
mergedPanelUnitForm.value.forEach(locationGroup => {
if (!locationGroup) return;
// 处理图标数据
if (locationGroup.icon) {
flatData.push({
locationId: locationGroup.icon.locationId,
content: locationGroup.icon.content,
type: 1,
size: locationGroup.icon.size || null,
font: locationGroup.icon.font || '',
locationName: locationGroup.icon.locationName || locationGroup.locationName,
backlightColor: locationGroup.backlightColor || '#FFFFFF',
backlightEnabled: locationGroup.backlightEnabled || false,
fileId: locationGroup.icon.fileId || null,
iconId: locationGroup.icon.iconId || null,
indexNum: locationGroup.icon.indexNum || 0,
keygroup: locationGroup.icon.keygroup || 0,
lineNumber: locationGroup.icon.lineNumber || 0
});
}
// 处理中文数据
if (locationGroup.chinese) {
flatData.push({
locationId: locationGroup.chinese.locationId,
content: locationGroup.chinese.content || '',
type: 2,
size: locationGroup.chinese.size || 11,
font: locationGroup.chinese.font || '',
locationName: locationGroup.chinese.locationName || locationGroup.locationName,
backlightColor: locationGroup.backlightColor || '#FFFFFF',
backlightEnabled: locationGroup.backlightEnabled || false,
indexNum: locationGroup.chinese.indexNum || 0,
keygroup: locationGroup.chinese.keygroup || 0,
lineNumber: locationGroup.chinese.lineNumber || 0
});
}
// 处理英文数据
if (locationGroup.english) {
flatData.push({
locationId: locationGroup.english.locationId,
content: locationGroup.english.content || '',
type: 3,
size: locationGroup.english.size || 7,
font: locationGroup.english.font || '',
locationName: locationGroup.english.locationName || locationGroup.locationName,
backlightColor: locationGroup.backlightColor || '#FFFFFF',
backlightEnabled: locationGroup.backlightEnabled || false,
indexNum: locationGroup.english.indexNum || 0,
keygroup: locationGroup.english.keygroup || 0,
lineNumber: locationGroup.english.lineNumber || 0
});
}
});
// 如果没有任何数据,保存空数组
const elementsToSave = flatData.length > 0 ? flatData : [];
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === currentPanelUnit.value);
if (panelUnit) {
let projectGuid = '';
if (selectedProjectId.value) {
const projectNode = findProjectNode(selectedProjectId.value);
if (projectNode && projectNode.webProject) {
projectGuid = projectNode.webProject.guid;
}
}
if (!projectGuid && panelUnit.project_guid) {
projectGuid = panelUnit.project_guid;
}
const updateData = {
...panelUnit,
elements_json: JSON.stringify(elementsToSave),
project_guid: projectGuid,
element_count: elementsToSave.length,
// 清空预览相关字段
thumbnail_large: '',
thumbnail_small: '',
cdr_filename: '',
design_status: '空闲'
};
await updateDatabase('PanelSelection/WebPanelUnitUpdate', updateData);
// 保存成功后刷新面板单元列表
if (projectGuid) {
await loadPanelUnitsByProject(projectGuid);
}
// 清空当前预览图
singlePanelPreview.value = '';
ElMessage.success('配置保存成功');
} else {
ElMessage.error('未找到对应的面板单元');
}
} catch (error) {
console.error('保存面板单元配置失败:', error);
ElMessage.error('保存面板单元配置失败');
}
};
/**
* 面板单元行点击事件
*/
const handleUnitRowClick = async (row) => {
try {
showLoading('加载面板单元配置中...');
isUserEditingPanelUnit.value = false;
// 设置当前选中的面板单元
currentPanelUnit.value = row.guid;
// 先尝试获取该面板单元对应的位置信息
let locations = [];
if (row.series_id && row.pattern_id) {
// 使用存储的系列和面板ID获取位置信息
const series = seriesList.value.find(s => s.ID === row.series_id);
if (series) {
const patterns = await fetchData('PanelSelection/PatternQuery', { FID: series.ID });
const pattern = patterns.find(p => p.ID === row.pattern_id);
if (pattern) {
locations = await getLocationls(pattern.ID);
}
}
}
// 加载面板单元配置到右侧表单
await loadPanelUnitConfiguration(row.guid, locations);
// 更新单面板预览图
await updateSinglePanelPreviewFromUnit(row);
hideLoading();
ElMessage.success(`已加载面板单元: ${row.panel_name}`);
} catch (error) {
hideLoading();
console.error('加载面板单元配置失败:', error);
ElMessage.error('加载面板单元配置失败');
}
};
/**
* 处理图标库行点击
*/
const handleIconLibraryRowClick = (row) => {
selectedIconLibrary.value = row.ID;
};
/**
* 清除当前编辑状态
*/
const clearEditingStatus = () => {
currentPanelUnit.value = null;
// 清除面板配置表格的编辑状态
panelConfigTable.value.forEach(item => {
item.isEditing = false;
});
};
/**
* 系列选择变化处理(面板单元)
*/
const handleSeriesChangeForUnit = async (currentRow) => {
selectedSeriesForUnit.value = currentRow;
if (currentRow) {
if (currentRow.PreviewPath) {
selectedSeriesPreviewForUnit.value = config.PicAds + currentRow.PreviewPath;
} else {
selectedSeriesPreviewForUnit.value = [];
}
// 注意这里不需要手动调用getModelsForUnit和getPatternsForUnit
// 因为watch监听器会自动处理
} else {
selectedSeriesPreviewForUnit.value = [];
selectedDirectionForUnit.value = '';
selectedColorForUnit.value = '';
selectedPatternForUnit.value = '';
}
};
/**
* 获取模型数据(面板单元)
*/
const getModelsForUnit = async (fileId) => {
try {
const rs = await localHttp.post('PanelSelection/ModelQuery', JSON.stringify({ FID: fileId }));
if (rs.data.isok && rs.data.response) {
const allModelsData = JSON.parse(rs.data.response);
allModelsForUnit.value = allModelsData;
// 过滤出面板数为1的模型
const singlePanelModels = allModelsData.filter(model => model.PanelCount === 1);
// 确保选项是有效的字符串数组
directionOptionsForUnit.value = [...new Set(singlePanelModels
.map(model => model.Direction)
.filter(dir => dir && dir.trim() !== ''))];
colorOptionsForUnit.value = [...new Set(singlePanelModels
.map(model => model.Color)
.filter(color => color && color.trim() !== ''))];
// 保存所有模型供后续使用
filteredModelsForUnit.value = singlePanelModels;
// 自动触发模型更新
updateModelListForUnit();
return singlePanelModels;
} else {
filteredModelsForUnit.value = [];
directionOptionsForUnit.value = [];
colorOptionsForUnit.value = [];
return [];
}
} catch (error) {
console.error('获取模型列表失败:', error);
filteredModelsForUnit.value = [];
directionOptionsForUnit.value = [];
colorOptionsForUnit.value = [];
return [];
}
};
/**
* 获取Pattern列表面板单元
*/
const getPatternsForUnit = async (fileId) => {
try {
const patterns = await fetchData('PanelSelection/PatternQuery', { FID: fileId });
// 过滤有效的面板数据
patternsListForUnit.value = patterns.filter(p => p && p.PatternName && p.PatternName.trim() !== '');
return patterns;
} catch (error) {
console.error('获取Pattern列表失败:', error);
patternsListForUnit.value = [];
return [];
}
};
/**
* 更新模型列表(面板单元)
*/
const updateModelListForUnit = () => {
if (!selectedSeriesForUnit.value) return;
// 过滤出面板数为1的模型
const singlePanelModels = allModelsForUnit.value.filter(model =>
model.PanelCount === 1
);
// 如果有方向或颜色选择,进一步过滤
let filteredModels = singlePanelModels;
if (selectedDirectionForUnit.value) {
filteredModels = filteredModels.filter(model =>
model.Direction === selectedDirectionForUnit.value
);
}
if (selectedColorForUnit.value) {
filteredModels = filteredModels.filter(model =>
model.Color === selectedColorForUnit.value
);
}
// 自动选择第一个匹配的模型
if (filteredModels.length > 0) {
selectedModelForUnit.value = filteredModels[0];
console.log('自动选择模型:', selectedModelForUnit.value);
} else {
selectedModelForUnit.value = null;
// 如果没有匹配的选择面板数为1的第一个模型
if (singlePanelModels.length > 0) {
selectedModelForUnit.value = singlePanelModels[0];
// 更新选择器的值为自动选择的模型值
selectedDirectionForUnit.value = singlePanelModels[0].Direction;
selectedColorForUnit.value = singlePanelModels[0].Color;
}
}
};
// 显示添加面板单元对话框
const showAddPanelUnitDialogView = () => {
resetPanelUnitDialog();
showAddPanelUnitDialog.value = true;
};
/**
* 重置面板单元对话框
*/
const resetPanelUnitDialog = () => {
selectedSeriesForUnit.value = null;
selectedDirectionForUnit.value = '';
selectedColorForUnit.value = '';
selectedPatternForUnit.value = '';
patternsListForUnit.value = [];
filteredModelsForUnit.value = [];
directionOptionsForUnit.value = [];
colorOptionsForUnit.value = [];
newPanelUnitForm.value = {
panel_name: '',
remarks: ''
};
// 重置编辑状态
isEditPanelUnit.value = false;
currentEditPanelUnit.value = null;
};
/**
* 确认添加面板单元
*/
const confirmAddPanelUnit = async () => {
if (!newPanelUnitForm.value.panel_name) {
ElMessage.error('请填写面板单元名称');
return;
}
if (!selectedSeriesForUnit.value) {
ElMessage.error('请选择系列');
return;
}
if (!selectedPatternForUnit.value) {
ElMessage.error('请选择面板');
return;
}
try {
// 从项目选择器中获取项目GUID
let projectGuid = '';
if (selectedProjectId.value) {
const projectNode = findProjectNode(selectedProjectId.value);
if (projectNode && projectNode.webProject) {
projectGuid = projectNode.webProject.guid;
}
}
if (!projectGuid) {
ElMessage.error('未找到当前项目信息,请先选择项目');
return;
}
// 获取选中的Pattern信息
const selectedPattern = patternsListForUnit.value.find(p => p.ID === selectedPatternForUnit.value);
if (!selectedPattern) {
ElMessage.error('未找到选中的面板信息');
return;
}
// 准备保存的数据 - 直接使用字段存储
const panelUnitData = {
panel_name: newPanelUnitForm.value.panel_name,
product_type: selectedPattern.PatternName,
element_count: 0,
elements_json: '[]',
cdr_filename: '',
thumbnail_large: '',
thumbnail_small: '',
remarks: newPanelUnitForm.value.remarks || '',
erp_part_number: '',
design_status: '',
owner: getCurrentUser(),
share_type: '私有',
project_guid: projectGuid,
// 直接存储选择信息到字段
series_id: selectedSeriesForUnit.value.ID,
pattern_id: selectedPatternForUnit.value,
direction: selectedDirectionForUnit.value || '',
color: selectedColorForUnit.value || '',
model_type: selectedModelForUnit.value?.ModelName || '',
model_id: selectedModelForUnit.value?.ID || null
};
// 调用API保存面板单元
const newGuid = await createPanelUnit(panelUnitData);
if (newGuid) {
ElMessage.success('面板单元添加成功');
showAddPanelUnitDialog.value = false;
// 刷新面板单元列表
await loadPanelUnitsByProject(projectGuid);
// 重置对话框
resetPanelUnitDialog();
} else {
ElMessage.error('面板单元添加失败');
}
} catch (error) {
console.error('添加面板单元失败:', error);
ElMessage.error('添加面板单元失败: ' + error.message);
}
};
/**
* 更新面板单元
*/
const updatePanelUnit = async () => {
if (!newPanelUnitForm.value.panel_name) {
ElMessage.error('请填写面板单元名称');
return;
}
if (!currentEditPanelUnit.value) {
ElMessage.error('未找到要编辑的面板单元');
return;
}
if (!selectedSeriesForUnit.value) {
ElMessage.error('请选择系列');
return;
}
if (!selectedPatternForUnit.value) {
ElMessage.error('请选择面板');
return;
}
try {
let projectGuid = '';
if (selectedProjectId.value) {
const projectNode = findProjectNode(selectedProjectId.value);
if (projectNode && projectNode.webProject) {
projectGuid = projectNode.webProject.guid;
}
}
if (!projectGuid && currentEditPanelUnit.value.project_guid) {
projectGuid = currentEditPanelUnit.value.project_guid;
}
const selectedPattern = patternsListForUnit.value.find(p => p.ID === selectedPatternForUnit.value);
if (!selectedPattern) {
ElMessage.error('未找到选中的面板信息');
return;
}
// 更新时清空预览相关字段
const updateData = {
...currentEditPanelUnit.value,
panel_name: newPanelUnitForm.value.panel_name,
product_type: selectedPattern.PatternName,
remarks: newPanelUnitForm.value.remarks || '',
series_id: selectedSeriesForUnit.value.ID,
pattern_id: selectedPatternForUnit.value,
direction: selectedDirectionForUnit.value || '',
color: selectedColorForUnit.value || '',
project_guid: projectGuid,
// 清空预览相关字段
elements_json: '[]',
thumbnail_large: '',
thumbnail_small: '',
cdr_filename: '',
design_status: '空闲'
};
await updateDatabase('PanelSelection/WebPanelUnitUpdate', updateData);
ElMessage.success('面板单元更新成功,预览图已重置');
showAddPanelUnitDialog.value = false;
// 刷新列表
if (projectGuid) {
await loadPanelUnitsByProject(projectGuid);
}
// 重置状态
resetPanelUnitDialog();
} catch (error) {
console.error('更新面板单元失败:', error);
ElMessage.error('更新面板单元失败: ' + error.message);
}
};
/**
* 在系列加载完成后恢复选择
*/
const restoreSelectionsAfterSeriesLoad = async () => {
if (!currentEditPanelUnit.value) return;
const panelUnit = currentEditPanelUnit.value;
// 确保数据已经加载完成
await new Promise(resolve => setTimeout(resolve, 100));
// 恢复方向选择
if (panelUnit.direction) {
if (directionOptionsForUnit.value.includes(panelUnit.direction)) {
selectedDirectionForUnit.value = panelUnit.direction;
}
}
// 恢复颜色选择
if (panelUnit.color) {
if (colorOptionsForUnit.value.includes(panelUnit.color)) {
selectedColorForUnit.value = panelUnit.color;
}
}
// 恢复面板选择
if (panelUnit.pattern_id) {
const pattern = patternsListForUnit.value.find(p => p.ID === panelUnit.pattern_id);
if (pattern) {
selectedPatternForUnit.value = panelUnit.pattern_id;
}
}
};
/**
* 获取图标库名称
*/
const getLibraryName = (libraryId) => {
const library = iconLibraryList.value.find(lib => lib.ID === libraryId);
return library ? `${library.Series}_${library.Version}` : '未知库';
};
/**
* 打开图标选择对话框
*/
const openIconDialog = (location) => {
currentEditingLocation.value = location;
currentEditingType.value = 'icon';
// 自动选择
selectedIconLibrary.value = iconLibraryList.value[0].ID;
iconDialogVisible.value = true;
};
/**
* 打开中文编辑对话框
*/
const openChineseDialog = (location) => {
currentEditingLocation.value = location;
currentEditingType.value = 'chinese';
// 初始化表单数据
if (!location.chinese) {
location.chinese = {
content: '',
font: '',
size: 11 // 中文默认字号
};
}
chineseDialogVisible.value = true;
};
/**
* 打开英文编辑对话框
*/
const openEnglishDialog = (location) => {
currentEditingLocation.value = location;
currentEditingType.value = 'english';
// 初始化表单数据
if (!location.english) {
location.english = {
content: '',
font: '',
size: 7 // 英文默认字号
};
}
englishDialogVisible.value = true;
};
/**
* 保存图标配置
*/
const saveIconConfig = (iconData) => {
if (currentEditingLocation.value) {
// 确保图标对象存在
if (!currentEditingLocation.value.icon) {
currentEditingLocation.value.icon = {
id: currentEditingLocation.value.icon.id || Date.now(),
locationId: currentEditingLocation.value.locations?.[0]?.locationId || Date.now(),
content: '',
type: 1,
locationName: currentEditingLocation.value.icon.locationName || currentEditingLocation.value.locationName,
indexNum: currentEditingLocation.value.icon.indexNum || 0,
keygroup: currentEditingLocation.value.icon.keygroup || 0,
lineNumber: currentEditingLocation.value.icon.lineNumber || 0
};
}
// 更新图标配置
currentEditingLocation.value.icon = {
...currentEditingLocation.value.icon,
content: iconData.ShapeNumber,
type: 1,
iconId: iconData.ID,
fileId: iconData.FID,
libraryName: getLibraryName(iconData.FID),
locationName: currentEditingLocation.value.icon.locationName || currentEditingLocation.value.locationName,
indexNum: currentEditingLocation.value.icon.indexNum || 0,
keygroup: currentEditingLocation.value.icon.keygroup || 0,
lineNumber: currentEditingLocation.value.icon.lineNumber || 0
};
// 新增:自动设置中文和英文内容
// 确保中文对象存在
if (!currentEditingLocation.value.chinese) {
currentEditingLocation.value.chinese = {
id: currentEditingLocation.value.chinese.id || Date.now() + 1, // 确保ID不同
locationId: currentEditingLocation.value.chinese.locationId || Date.now() + 1,
content: '',
type: 2,
font: '',// TODO 默认字体
size: 11, // 中文默认字号
locationName: currentEditingLocation.value.chinese.locationName || currentEditingLocation.value.locationName,
indexNum: currentEditingLocation.value.chinese.indexNum || 0,
keygroup: currentEditingLocation.value.chinese.keygroup || 0,
lineNumber: currentEditingLocation.value.chinese.lineNumber || 0
};
}
// 确保英文对象存在
if (!currentEditingLocation.value.english) {
currentEditingLocation.value.english = {
id: currentEditingLocation.value.english.id || Date.now() + 2, // 确保ID不同
locationId: currentEditingLocation.value.english.locationId || Date.now() + 2,
content: '',
type: 3,
font: '',
size: 7, // 英文默认字号
locationName: currentEditingLocation.value.english.locationName || currentEditingLocation.value.locationName,
indexNum: currentEditingLocation.value.english.indexNum || 0,
keygroup: currentEditingLocation.value.english.keygroup || 0,
lineNumber: currentEditingLocation.value.english.lineNumber || 0
};
}
// 使用图标数据的中英文名称自动填充内容
if (iconData.NameCN) {
currentEditingLocation.value.chinese.content = iconData.NameCN || "空";
// 设置默认中文字体(如果当前没有设置)
if (!currentEditingLocation.value.chinese.font && fontList.value.length > 0) {
currentEditingLocation.value.chinese.font = "思源黑体";
}
}
if (iconData.NameEn) {
currentEditingLocation.value.english.content = iconData.NameEn || "empty";
// 设置默认英文字体(如果当前没有设置)
if (!currentEditingLocation.value.english.font && fontList.value.length > 0) {
currentEditingLocation.value.english.font = "Futura Std Medium";
}
}
// 同步背光设置到中文和英文对象
if (currentEditingLocation.value.backlightEnabled) {
currentEditingLocation.value.chinese.backlightColor = currentEditingLocation.value.backlightColor;
currentEditingLocation.value.chinese.backlightEnabled = currentEditingLocation.value.backlightEnabled;
currentEditingLocation.value.english.backlightColor = currentEditingLocation.value.backlightColor;
currentEditingLocation.value.english.backlightEnabled = currentEditingLocation.value.backlightEnabled;
}
updatePanelUnitFormFromMerged();
savePanelUnitConfig();
iconDialogVisible.value = false;
ElMessage.success('图标选择成功,已自动填充中英文内容');
}
};
/**
* 从合并数据更新 panelUnitForm
*/
const updatePanelUnitFormFromMerged = () => {
// 清空现有数据
panelUnitForm.value = [];
// 从合并数据重建 panelUnitForm
mergedPanelUnitForm.value.forEach(location => {
if (location.icon) {
panelUnitForm.value.push({
...location.icon,
locationName: location.originalLocationName || location.locationName
});
}
if (location.chinese) {
panelUnitForm.value.push({
...location.chinese,
locationName: location.originalLocationName || location.locationName
});
}
if (location.english) {
panelUnitForm.value.push({
...location.english,
locationName: location.originalLocationName || location.locationName
});
}
});
};
/**
* 保存中文配置
*/
const saveChineseConfig = () => {
if (currentEditingLocation.value) {
// 确保中文对象存在
if (!currentEditingLocation.value.chinese) {
currentEditingLocation.value.chinese = {
id: currentEditingLocation.value.locations?.[0]?.id || Date.now(),
locationId: currentEditingLocation.value.locations?.[0]?.locationId || Date.now(),
content: '',
type: 2,
locationName: currentEditingLocation.value.originalLocationName || currentEditingLocation.value.locationName,
};
}
const currentFont = currentEditingLocation.value.chinese.font;
const currentSize = currentEditingLocation.value.chinese.size;
// 统一设置所有为空的中文字体和字号
if (currentFont || currentSize) {
mergedPanelUnitForm.value.forEach(row => {
if (!row.chinese) {
row.chinese = {
content: '',
font: '',
size: 11
};
}
// 如果当前行中文字体为空,且编辑的字体不为空,则统一设置
if (currentFont && !row.chinese.font) {
row.chinese.font = currentFont;
}
// 如果当前行中文字号为空,且编辑的字号不为空,则统一设置
if (currentSize && !row.chinese.size) {
row.chinese.size = currentSize;
}
});
}
chineseDialogVisible.value = false;
updatePanelUnitFormFromMerged();
savePanelUnitConfig();
}
};
/**
* 保存英文配置
*/
const saveEnglishConfig = () => {
if (currentEditingLocation.value) {
// 确保英文对象存在
if (!currentEditingLocation.value.english) {
currentEditingLocation.value.english = {
id: currentEditingLocation.value.locations?.[0]?.id || Date.now(),
locationId: currentEditingLocation.value.locations?.[0]?.locationId || Date.now(),
content: '',
type: 3,
locationName: currentEditingLocation.value.originalLocationName || currentEditingLocation.value.locationName
};
}
const currentFont = currentEditingLocation.value.english.font;
const currentSize = currentEditingLocation.value.english.size;
// 统一设置所有为空的英文字体和字号
if (currentFont || currentSize) {
mergedPanelUnitForm.value.forEach(row => {
if (!row.english) {
row.english = {
content: '',
font: '',
size: 7
};
}
// 如果当前行英文字体为空,且编辑的字体不为空,则统一设置
if (currentFont && !row.english.font) {
row.english.font = currentFont;
}
// 如果当前行英文字号为空,且编辑的字号不为空,则统一设置
if (currentSize && !row.english.size) {
row.english.size = currentSize;
}
});
}
englishDialogVisible.value = false;
updatePanelUnitFormFromMerged();
savePanelUnitConfig();
}
};
/**
* 根据FID获取文件名
*/
const getFileNameByFid = (fid) => {
if (!fid) return '';
const series = seriesList.value.find(s => s.ID === fid);
return series ? series.FileName : '';
};
/**
* 获取图标预览URL
*/
const getIconPreviewUrl = (iconData) => {
if (!iconData || !iconData.fileId || !iconData.content) {
return '';
}
try {
// 查找对应的图标库
const library = iconLibraryList.value.find(lib => lib.ID === iconData.fileId);
if (!library) return '';
// 查找具体的图标数据
const icons = iconOptions.value[iconData.fileId] || [];
const icon = icons.find(i => i.ShapeNumber === iconData.content);
if (icon && icon.PreviewPath) {
return config.PicAds + icon.PreviewPath;
}
return '';
} catch (error) {
console.error('获取图标预览URL失败:', error);
return '';
}
};
/**
* 根据图标ID获取对应的文件名
*/
const getIconFileName = async (iconId, defaultFid = null) => {
if (!iconId) return '';
try {
// 首先尝试从所有图标库中查找该图标
for (const libraryId in iconOptions.value) {
const icons = iconOptions.value[libraryId];
const icon = icons.find(i => i.ID === iconId);
if (icon) {
return getFileNameByFid(icon.FID);
}
}
// 如果找不到使用默认的FID
if (defaultFid) {
return getFileNameByFid(defaultFid);
}
return '';
} catch (error) {
console.error('获取图标文件名失败:', error);
return '';
}
};
/**
* 自动保存单个面板配置到remark字段
*/
const autoSavePanelUnitConfig = async () => {
if (!currentPanelUnit.value) return;
try {
// 生成previewObject结构体
const previewObject = await generateSinglePanelPreviewObject(false); // false表示不生成图
if (!previewObject) {
console.warn('生成单个面板预览对象失败');
return;
}
// 获取当前面板单元信息
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === currentPanelUnit.value);
if (panelUnit) {
// 准备保存的数据
const saveData = {
...panelUnit,
remarks: JSON.stringify({
previewObject: previewObject,
savedAt: new Date().toISOString(),
version: '1.0'
})
};
// 保存到数据库
await updateDatabase('PanelSelection/WebPanelUnitUpdate', saveData);
}
} catch (error) {
console.error('自动保存单个面板配置失败:', error);
}
};
/**
* 自动保存面板组配置到remark字段
*/
const autoSavePanelGroupConfig = async () => {
if (!currentPanelNode.value) return;
try {
// 生成结构化数据
const structureData = await generatePanelGroupStructureData(false); // false表示不生成图
if (!structureData) {
console.warn('生成面板组结构化数据失败');
return;
}
// 获取当前面板组信息
const panelInfo = await getPanelInfoByGuid(currentPanelNode.value.panelGuid);
if (panelInfo) {
// 准备保存的数据
const saveData = {
...panelInfo,
remarks: JSON.stringify({
structureData: structureData,
savedAt: new Date().toISOString(),
version: '1.0'
})
};
// 保存到数据库
await updateDatabase('PanelSelection/WebPanelListUpdate', saveData);
}
} catch (error) {
console.error('自动保存面板组配置失败:', error);
}
};
/**
* 清空面板组预览图任务3
*/
const clearPanelGroupPreview = async () => {
if (!currentPanelNode.value) return;
try {
// 清空本地变量
panelGroupPreview.value = '';
// 清空数据库中的预览图字段
const panelInfo = await getPanelInfoByGuid(currentPanelNode.value.panelGuid);
if (panelInfo) {
const updateData = {
...panelInfo,
thumbnail_large: '' // 清空大缩略图
};
await updateDatabase('PanelSelection/WebPanelListUpdate', updateData);
}
} catch (error) {
console.error('清空面板组预览图失败:', error);
}
};
/**
* 生成单个面板预览对象重构自showSinglePanelPreview
*/
const generateSinglePanelPreviewObject = async (generateImage = true) => {
if (!currentPanelUnit.value) return null;
try {
const currentUser = getCurrentUser();
const projectNode = findProjectNode(selectedProjectId.value);
if (!projectNode || !projectNode.webProject) {
return null;
}
const projectName = projectNode.webProject.hotel_name;
const projectId = projectNode.webProject.report_id;
const timestamp = Date.now().toString();
const pictureNumSuffix = timestamp.slice(-8);
const pictureNum = `Unit_${projectName}_${pictureNumSuffix}`;
// 获取当前面板单元信息
const panelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
const panelUnit = panelUnits.find(p => p.guid === currentPanelUnit.value);
if (!panelUnit) return null;
let materialLibraryName = '';
let templateName = '';
let templateNameIndex = '';
let matchedModel = null;
// 获取系列信息
if (panelUnit.series_id) {
const series = seriesList.value.find(s => s.ID === panelUnit.series_id);
if (series) {
materialLibraryName = series.FileName;
// 参考面板组合的模板名称赋值逻辑
if (panelUnit.model_type) {
// 如果面板单元保存了模型类型,直接使用
templateName = panelUnit.model_type;
} else {
// 否则使用系列名称作为备用
templateName = series.Series;
}
}
}
// 获取模型信息来设置 templateNameIndex
if (panelUnit.series_id) {
const models = await fetchData('PanelSelection/ModelQuery', {
FID: panelUnit.series_id
});
// 优先使用面板单元保存的模型信息
if (panelUnit.model_id) {
matchedModel = models.find(model => model.ID === panelUnit.model_id);
}
// 如果没有保存的模型ID尝试通过其他条件匹配
if (!matchedModel && panelUnit.model_type) {
matchedModel = models.find(model =>
model.ModelName === panelUnit.model_type
);
}
// 如果还没有匹配查找面板数为1的模型
if (!matchedModel) {
matchedModel = models.find(model => model.PanelCount === 1);
}
// 如果仍然没有匹配,选择面板数最小的模型
if (!matchedModel && models.length > 0) {
matchedModel = models.reduce((minModel, currentModel) => {
return (currentModel.PanelCount < (minModel?.PanelCount || Infinity)) ? currentModel : minModel;
});
}
if (matchedModel) {
templateNameIndex = matchedModel.ShapeNumber;
// 确保templateName使用模型的名称
if (!panelUnit.model_type) {
templateName = matchedModel.ModelName;
}
}
}
// 获取Pattern信息
let patternFileName = '';
let patternShapeNumber = '';
if (panelUnit.pattern_id) {
const patterns = await fetchData('PanelSelection/PatternQuery', {
FID: panelUnit.series_id
});
const pattern = patterns.find(p => p.ID === panelUnit.pattern_id);
if (pattern) {
patternShapeNumber = pattern.ShapeNumber || '';
// 获取Pattern对应的文件名
if (pattern.FID) {
const patternSeries = seriesList.value.find(s => s.ID === pattern.FID);
patternFileName = patternSeries ? patternSeries.FileName : '';
}
}
}
const locationList = [];
// 处理元素数据
if (panelUnit.elements_json) {
try {
const elementsData = JSON.parse(panelUnit.elements_json);
const locations = await getLocationls(panelUnit.pattern_id);
for (let j = 0; j < elementsData.length; j++) {
const element = elementsData[j];
const location = locations.find(loc => loc.ID === element.locationId);
let shapeNumber = '';
let fileName = '';
let shapeType = 0;
let shapeColor = element.backlightColor || '#FFFFFF';
let shapeFont = element.font || '';
let shapeFontSize = parseFloat(element.size) || 0.0;
let lineNumber = '';
if (element.content) {
if (element.type === 1) {
// 图标类型
shapeType = 1;
const fileId = element.fileId || panelUnit.series_id;
if (fileId) {
const icons = await fetchData('PanelSelection/IconQuery', { FID: fileId });
const icon = icons.find(icon => icon.ShapeNumber === element.content);
shapeNumber = icon && icon.ShapeNumber != null ? String(icon.ShapeNumber) : String(element.content);
// 获取图标对应的文件名
if (icon && icon.FID) {
const iconSeries = seriesList.value.find(s => s.ID === icon.FID);
fileName = iconSeries ? iconSeries.FileName : '';
}
} else {
shapeNumber = String(element.content);
}
} else {
// 文字类型
shapeType = 0;
shapeNumber = String(element.content);
shapeFont = element.font || '';
shapeFontSize = parseFloat(element.size) || 0.0;
// 解析行号
if (location && location.lineNumber) {
lineNumber = location.lineNumber.toString();
}
}
}
let remark = '';
if (element.type !== 1 && (element.font || element.size)) {
remark = `${element.font || ''}_${element.size || ''}`;
if (lineNumber) {
remark += `_${lineNumber}`;
}
}
locationList.push({
IndexNumber: element.indexNum.toString(),
Remark: remark,
ShapeNumber: shapeNumber,
ShapeType: shapeType,
FileName: fileName,
ShapeColor: shapeColor,
ShapeFont: shapeFont,
ShapeFontSize: shapeFontSize,
LineNumber: element.lineNumber.toString() !== '0' ? element.lineNumber.toString() : ''
});
}
} catch (error) {
console.error('解析面板单元数据失败:', error);
}
}
// 获取Trench数据
const Trench = await fetchData('PanelSelection/TrenchQuery', {
MID: matchedModel ? matchedModel.ID : 0
});
const pindxn = Trench.find((item) => item.TrenchName.split('_')[1] === '1');
const patternList = [{
IndexNumber: pindxn ? pindxn.IndexNum.toString() : '1',
ShapeNumber: patternShapeNumber,
FileName: patternFileName,
LocationList: locationList
}];
// 构建完整的previewObject结构体
const previewObject = {
Guid: panelUnit.guid,
MaterialLibraryName: materialLibraryName || '未找到图库文件',
PictureNum: pictureNum,
Process: generateImage ? 0 : -1, // -1表示只生成结构体
ProjectId: projectId || '未知项目ID',
ProjectName: projectName || '未知酒店',
TemplateName: templateName || '未找到模板',
TemplateNameIndex: templateNameIndex || '未找到模板索引',
User: currentUser || '未知用户',
LogoPosition: '',
LogoFileName: '',
LogoIndexNumber: '',
PatternList: patternList
};
return previewObject;
} catch (error) {
console.error('生成单个面板预览对象失败:', error);
return null;
}
};
/**
* 生成面板组结构化数据重构自showPreview
*/
const generatePanelGroupStructureData = async (generateImage = true) => {
if (!currentPanelNode.value) return null;
try {
// 在使用panelConfigTable.value数据前先调用API接口重新获取最新数据
// 1. 重新获取面板组信息
const latestPanelInfo = await getPanelInfoByGuid(currentPanelNode.value.panelGuid);
if (latestPanelInfo) {
currentPanelNode.value.panelInfo = latestPanelInfo;
}
// 2. 重新获取面板单元数据
const latestPanelUnits = await fetchData('PanelSelection/WebPanelUnitQuery');
// 更新availablePanelUnits
availablePanelUnits.value = latestPanelUnits.filter(unit => unit.is_valid);
// 3. 重新初始化面板配置表格,确保数据是最新的
initPanelConfigTable(currentPanelNode.value);
const currentUser = getCurrentUser();
const projectNode = findProjectNode(currentPanelNode.value.projectId);
if (!projectNode || !projectNode.webProject) {
return null;
}
const projectName = projectNode.webProject.hotel_name;
const projectId = projectNode.webProject.report_id;
const timestamp = Date.now().toString();
const pictureNumSuffix = timestamp.slice(-8);
const pictureNum = `List_${projectName}_${pictureNumSuffix}`;
// 使用面板组的基本信息
let materialLibraryName = '';
let templateName = '';
let templateNameIndex = '';
if (currentPanelNode.value.panelInfo) {
templateName = currentPanelNode.value.panelInfo.model_type || '';
// 如果有系列信息,尝试获取文件名
if (currentPanelNode.value.panelInfo.gang_series) {
const series = seriesList.value.find(s =>
`${s.Series}_${s.Version}` === currentPanelNode.value.panelInfo.gang_series
);
if (series) {
materialLibraryName = series.FileName;
// 获取模型信息来设置 templateNameIndex
const models = await fetchData('PanelSelection/ModelQuery', {
FID: series.ID
});
let matchedModel = null;
// 优先使用面板组保存的模型信息
if (currentPanelNode.value.panelInfo.model_id) {
matchedModel = models.find(model => model.ID === currentPanelNode.value.panelInfo.model_id);
}
// 如果没有保存的模型ID尝试通过其他条件匹配
if (!matchedModel && currentPanelNode.value.panelInfo.model_type) {
matchedModel = models.find(model =>
model.ModelName === currentPanelNode.value.panelInfo.model_type
);
}
// 如果还没有匹配,查找面板数匹配的模型
if (!matchedModel) {
const panelCount = currentPanelNode.value.panelInfo.panel_count;
matchedModel = models.find(model => model.PanelCount === panelCount);
}
// 如果仍然没有匹配,选择面板数最小的模型
if (!matchedModel && models.length > 0) {
matchedModel = models.reduce((minModel, currentModel) => {
return (currentModel.PanelCount < (minModel?.PanelCount || Infinity)) ? currentModel : minModel;
});
}
if (matchedModel) {
templateNameIndex = matchedModel.ShapeNumber;
// 确保templateName使用模型的名称
if (!currentPanelNode.value.panelInfo.model_type) {
templateName = matchedModel.ModelName;
}
}
}
}
}
const patternList = [];
// 获取Trench数据
const Trench = await fetchData('PanelSelection/TrenchQuery', {
MID: currentPanelNode.value.panelInfo.gang_material_id
});
// 遍历面板配置,使用面板单元的数据构建预览对象
for (let i = 0; i < panelConfigTable.value.length; i++) {
const panelConfig = panelConfigTable.value[i];
const locationList = [];
if (panelConfig.panelUnitGuid && panelConfig.panelUnitData) {
const panelUnit = panelConfig.panelUnitData;
if (panelUnit.elements_json) {
try {
const elementsData = JSON.parse(panelUnit.elements_json);
// 获取位置信息
let locations = [];
if (panelUnit.pattern_id) {
locations = await getLocationls(panelUnit.pattern_id);
}
for (let j = 0; j < elementsData.length; j++) {
const element = elementsData[j];
const location = locations.find(loc => loc.ID === element.locationId);
let shapeNumber = '';
let fileName = '';
let shapeType = 0;
let shapeColor = element.backlightColor || '#FFFFFF';
let shapeFont = element.font || '';
let shapeFontSize = parseFloat(element.size) || 0.0;
let lineNumber = '';
if (element.content) {
if (element.type === 1) {
// 图标类型
shapeType = 1;
const fileId = element.fileId || panelUnit.series_id;
if (fileId) {
const icons = await fetchData('PanelSelection/IconQuery', { FID: fileId });
const icon = icons.find(icon => icon.ShapeNumber === element.content);
shapeNumber = icon && icon.ShapeNumber != null ? String(icon.ShapeNumber) : String(element.content);
// 获取图标对应的文件名
if (icon && icon.FID) {
const iconSeries = seriesList.value.find(s => s.ID === icon.FID);
fileName = iconSeries ? iconSeries.FileName : '';
}
} else {
shapeNumber = String(element.content);
}
} else {
// 文字类型
shapeType = 0;
shapeNumber = String(element.content);
shapeFont = element.font || '';
shapeFontSize = parseFloat(element.size) || 0.0;
// 解析行号
if (location && location.lineNumber) {
lineNumber = location.lineNumber.toString();
}
}
}
let remark = '';
if (element.type !== 1 && (element.font || element.size)) {
remark = `${element.font || ''}_${element.size || ''}`;
if (lineNumber) {
remark += `_${lineNumber}`;
}
}
locationList.push({
IndexNumber: element.indexNum.toString(),
Remark: remark,
ShapeNumber: shapeNumber,
ShapeType: shapeType,
FileName: fileName,
ShapeColor: shapeColor,
ShapeFont: shapeFont,
ShapeFontSize: shapeFontSize,
LineNumber: element.lineNumber.toString() !== '0' ? element.lineNumber.toString() : ''
});
}
} catch (error) {
console.error('解析面板单元数据失败:', error);
}
}
}
// 获取Pattern的ShapeNumber从tbl_pattern表
let shapeNumber = '';
let fileName = '';
if (panelConfig.panelUnitData && panelConfig.panelUnitData.pattern_id) {
// 获取Pattern信息
const patterns = await fetchData('PanelSelection/PatternQuery', {
FID: panelConfig.panelUnitData.series_id
});
const pattern = patterns.find(p => p.ID === panelConfig.panelUnitData.pattern_id);
if (pattern) {
shapeNumber = pattern.ShapeNumber || '';
// 获取Pattern对应的文件名
if (pattern.FID) {
const patternSeries = seriesList.value.find(s => s.ID === pattern.FID);
fileName = patternSeries ? patternSeries.FileName : '';
}
}
}
// 如果无法从Pattern获取使用产品类型作为备用
if (!shapeNumber && panelConfig.panelUnitData) {
shapeNumber = panelConfig.panelUnitData.product_type || '';
}
const pindxn = Trench.find((item) => item.TrenchName.split('_')[1] === (i + 1).toString());
patternList.push({
IndexNumber: pindxn ? pindxn.IndexNum.toString() : (i + 1).toString(),
ShapeNumber: shapeNumber,
FileName: fileName,
LocationList: locationList
});
}
// 解析LOGO配置
let logoConfigValue = {};
try {
if (currentPanelNode.value.panelInfo.logo_json) {
logoConfigValue = JSON.parse(currentPanelNode.value.panelInfo.logo_json);
}
} catch (error) {
console.error('解析LOGO配置失败:', error);
logoConfigValue = {};
}
// 构建完整的previewObject结构体
const previewObject = {
Guid: currentPanelNode.value.panelGuid,
MaterialLibraryName: materialLibraryName || '未找到图库文件',
PictureNum: pictureNum,
Process: generateImage ? 0 : -1, // -1表示只生成结构体
ProjectId: projectId || '未知项目ID',
ProjectName: projectName || '未知酒店',
TemplateName: templateName || '未找到模板',
TemplateNameIndex: templateNameIndex || '未找到模板索引',
User: currentUser || '未知用户',
LogoPosition: logoConfigValue.LogoPosition || '',
LogoFileName: logoConfigValue.LogoFileName || '',
LogoIndexNumber: logoConfigValue.LogoIndexNumber || '',
PatternList: patternList
};
return previewObject;
} catch (error) {
console.error('生成面板组结构化数据失败:', error);
return null;
}
};
// ==================== LOGO相关方法 ====================
/**
* 获取Logo列表
*/
const getLogoList = async () => {
try {
const logos = await fetchData('PanelSelection/LogoEnQuery');
logoList.value = logos;
} catch (error) {
console.error('获取Logo列表失败:', error);
logoList.value = [];
}
};
/**
* Logo位置选择变化处理
*/
const handleLogoPositionChange = (position) => {
if (!selectedModel.value) {
ElMessage.warning('请先选择模型');
return;
}
// 根据选择的位置获取对应的LOGO索引号
if (position === 'left') {
logoConfig.value.LogoIndexNumber = selectedModel.value.LOGO_L?.toString() || '';
} else if (position === 'right') {
logoConfig.value.LogoIndexNumber = selectedModel.value.LOGO_R?.toString() || '';
} else {
logoConfig.value.LogoIndexNumber = '';
}
selectedLogoPosition.value = position;
// 如果已经选择了LOGO需要更新配置
if (selectedLogo.value) {
handleLogoChange(selectedLogo.value);
} else {
updateLogoConfig();
}
};
/**
* Logo选择变化处理
*/
const handleLogoChange = async (logo) => {
if (!logo) {
// 清空选择
selectedLogo.value = null;
logoConfig.value.LogoFileName = '';
logoConfig.value.LogoPosition = '';
updateLogoConfig();
return;
}
try {
selectedLogo.value = logo;
logoConfig.value.LogoIndexNumber = logo.ShapeNumber || '';
const logoFileName = await getLogoFileNameByFid(logo.FID);
logoConfig.value.LogoFileName = logoFileName;
if (!logoConfig.value.LogoPosition && selectedLogoPosition.value) {
if (selectedLogoPosition.value === 'left') {
logoConfig.value.LogoPosition = selectedModel.value?.LOGO_L?.toString() || '';
} else if (selectedLogoPosition.value === 'right') {
logoConfig.value.LogoPosition = selectedModel.value?.LOGO_R?.toString() || '';
}
}
updateLogoConfig();
} catch (error) {
console.error('处理LOGO选择失败:', error);
ElMessage.error('LOGO选择失败');
}
};
/**
* 根据FID从tbl_cdr_file获取FileName
*/
const getLogoFileNameByFid = async (fid) => {
try {
if (!fid) return '';
// 方法1: 从本地缓存的seriesList中查找
const series = seriesList.value.find(s => s.ID === fid);
if (series && series.FileName) {
return series.FileName;
}
// 方法2: 如果本地没有,从数据库查询
const cdrFiles = await fetchData('PanelSelection/SeriesQuery');
const targetFile = cdrFiles.find(file => file.ID === fid);
return targetFile ? targetFile.FileName : '';
} catch (error) {
console.error('根据FID获取FileName失败:', error);
return '';
}
};
/**
* 更新LOGO配置到表单
*/
const updateLogoConfig = () => {
try {
// 验证配置完整性
const configToSave = { ...logoConfig.value };
// 确保所有字段都有值
configToSave.LogoPosition = configToSave.LogoPosition || '';
configToSave.LogoFileName = configToSave.LogoFileName || '';
configToSave.LogoIndexNumber = configToSave.LogoIndexNumber || '';
// 序列化前验证
if (typeof configToSave !== 'object') {
throw new Error('LOGO配置必须是对象');
}
newPanelForm.value.logo_json = JSON.stringify(configToSave);
} catch (error) {
console.error('更新LOGO配置失败:', error);
// 失败时保存空对象
newPanelForm.value.logo_json = "{}";
ElMessage.error('LOGO配置更新失败');
}
};
/**
* 从保存的数据中恢复LOGO配置
*/
const restoreLogoConfig = async (panelInfo) => {
if (!panelInfo || !panelInfo.logo_json) {
logoConfig.value = {
LogoPosition: '',
LogoFileName: '',
LogoIndexNumber: ''
};
selectedLogoPosition.value = '';
selectedLogo.value = null;
return;
}
try {
const savedConfig = JSON.parse(panelInfo.logo_json);
// 恢复基本配置
logoConfig.value = { ...savedConfig };
// 恢复位置选择器 - 增强容错逻辑
if (savedConfig.LogoPosition) {
// 方法1: 通过LogoPosition直接设置
if (savedConfig.LogoPosition === 'left' || savedConfig.LogoPosition === 'right') {
selectedLogoPosition.value = savedConfig.LogoPosition;
}
// 方法2: 通过模型推断位置
else if (selectedModel.value) {
if (savedConfig.LogoPosition === selectedModel.value.LOGO_L?.toString()) {
selectedLogoPosition.value = 'left';
} else if (savedConfig.LogoPosition === selectedModel.value.LOGO_R?.toString()) {
selectedLogoPosition.value = 'right';
} else {
// 无法推断,清空位置选择
selectedLogoPosition.value = '';
}
} else {
// 没有模型信息,尝试直接使用保存的值
selectedLogoPosition.value = savedConfig.LogoPosition;
}
} else {
selectedLogoPosition.value = '';
}
// 恢复LOGO选择 - 增强容错逻辑
if (savedConfig.LogoFileName && savedConfig.LogoPosition) {
// 确保LOGO列表已加载
if (logoList.value.length === 0) {
await getLogoList();
}
// 查找匹配的LOGO - 改进匹配逻辑
let matchedLogo = null;
// 方法1: 通过ShapeNumber匹配
if (savedConfig.LogoIndexNumber) {
matchedLogo = logoList.value.find(logo =>
logo.ShapeNumber === savedConfig.LogoIndexNumber
);
}
// 方法2: 通过位置信息匹配
if (!matchedLogo && savedConfig.LogoPosition) {
matchedLogo = logoList.value.find(logo =>
logo.ShapeNumber === savedConfig.LogoPosition
);
}
if (matchedLogo) {
selectedLogo.value = matchedLogo;
} else {
console.warn('未找到匹配的LOGO保存的配置:', savedConfig);
selectedLogo.value = null;
// 如果找不到匹配的LOGO但配置中有文件名尝试通过文件名查找
if (savedConfig.LogoFileName) {
const series = seriesList.value.find(s =>
s.FileName === savedConfig.LogoFileName
);
if (series) {
}
}
}
} else {
selectedLogo.value = null;
}
// 更新LOGO配置到表单
updateLogoConfig();
} catch (error) {
console.error('恢复LOGO配置失败:', error);
// 失败时使用默认配置
logoConfig.value = {
LogoPosition: '',
LogoFileName: '',
LogoIndexNumber: ''
};
selectedLogoPosition.value = '';
selectedLogo.value = null;
ElMessage.warning('LOGO配置记录获取失败请手动设置');
}
};
// ==================== 背光相关方法 ====================
/**
* 处理背光开关切换
*/
const handleBacklightToggle = (row, index) => {
if (row.backlightEnabled) {
// 启用背光,设置默认颜色
row.backlightColor = '#F19947';
} else {
// 禁用背光,设置为白色
row.backlightColor = '#FFFFFF';
}
// 更新该行所有相关元素的背光颜色
updateRowBacklight(row);
savePanelUnitConfig(); // 自动保存
};
/**
* 处理背光颜色变化
*/
const handleBacklightColorChange = (row, index) => {
// 更新该行所有相关元素的背光颜色
updateRowBacklight(row);
savePanelUnitConfig(); // 自动保存
};
/**
* 更新整行的背光颜色
*/
const updateRowBacklight = (row) => {
// 更新合并表单中的背光设置
if (row.icon) {
row.icon.backlightColor = row.backlightColor;
row.icon.backlightEnabled = row.backlightEnabled;
}
if (row.chinese) {
row.chinese.backlightColor = row.backlightColor;
row.chinese.backlightEnabled = row.backlightEnabled;
}
if (row.english) {
row.english.backlightColor = row.backlightColor;
row.english.backlightEnabled = row.backlightEnabled;
}
// 同步更新 panelUnitForm 中的数据
syncBacklightToPanelUnitForm(row);
};
/**
* 将背光设置同步到 panelUnitForm
*/
const syncBacklightToPanelUnitForm = (mergedRow) => {
if (!mergedRow.locations || !Array.isArray(mergedRow.locations)) return;
mergedRow.locations.forEach(location => {
const targetItem = panelUnitForm.value.find(item =>
item.locationId === location.locationId || item.id === location.id
);
if (targetItem) {
targetItem.backlightColor = mergedRow.backlightColor;
targetItem.backlightEnabled = mergedRow.backlightEnabled;
}
});
};
/**
* 从保存的数据中恢复背光设置
*/
const restoreBacklightFromSavedData = (savedConfig) => {
if (!savedConfig || !Array.isArray(savedConfig)) return;
// 按位置名称分组,恢复背光设置
const backlightMap = {};
savedConfig.forEach(item => {
if (item.locationName) {
const locationParts = item.locationName.split('_');
let groupKey = '';
if (locationParts.length >= 3) {
groupKey = locationParts[2];
} else {
groupKey = item.locationName;
}
if (!backlightMap[groupKey]) {
backlightMap[groupKey] = {
backlightColor: item.backlightColor || '#FFFFFF',
backlightEnabled: item.backlightEnabled || false
};
}
}
});
// 应用到合并表单
mergedPanelUnitForm.value.forEach(row => {
const backlightSetting = backlightMap[row.locationName];
if (backlightSetting) {
row.backlightColor = backlightSetting.backlightColor;
row.backlightEnabled = backlightSetting.backlightEnabled;
} else {
// 初始化默认值
row.backlightColor = '#FFFFFF';
row.backlightEnabled = false;
}
});
};
// ==================== 字体相关方法 ====================
/**
* 获取字体列表
*/
const getFont = async () => {
try {
const fonts = await fetchData('PanelSelection/FontQuery', {});
fontList.value = fonts;
return fonts;
} catch (error) {
console.error('获取字体列表失败:', error);
return [];
}
};
// ==================== watch监听器 ====================
/**
* 监听单个面板配置变化自动保存到remark字段
*/
watch(() => mergedPanelUnitForm.value, async (newForm, oldForm) => {
if (!currentPanelUnit.value || !newForm || newForm.length === 0) return;
try {
// 防抖处理,避免频繁保存
if (window.panelUnitSaveTimeout) {
clearTimeout(window.panelUnitSaveTimeout);
}
window.panelUnitSaveTimeout = setTimeout(async () => {
// 只有在用户编辑时才保存配置
if (isUserEditingPanelUnit.value) {
await autoSavePanelUnitConfig();
}
// 无论是否在编辑状态,都刷新预览
await refreshPanelGroupPreview();
await refreshPanelUnitPreview();
// 刷新预览后,恢复之前选中的行
nextTick(() => {
if (currentMergedPanelUnitRow.value && mergedPanelUnitTable.value) {
// 尝试通过locationName找到对应的行
const targetRow = newForm.find(row => row.locationName === currentMergedPanelUnitRow.value.locationName);
if (targetRow) {
// 使用表格的setCurrentRow方法设置当前行
mergedPanelUnitTable.value.setCurrentRow(targetRow);
currentMergedPanelUnitRow.value = targetRow;
}
}
});
}, 50); // 防抖
} catch (error) {
console.error('监听面板单元配置变化失败:', error);
}
}, { deep: true, immediate: false });
/**
* 监听面板组配置变化自动保存到remark字段并清空预览
*/
watch(() => panelConfigTable.value, async (newConfig, oldConfig) => {
if (!currentPanelNode.value || !newConfig || newConfig.length === 0) return;
try {
// 防抖处理
if (window.panelGroupSaveTimeout) {
clearTimeout(window.panelGroupSaveTimeout);
}
window.panelGroupSaveTimeout = setTimeout(async () => {
await autoSavePanelGroupConfig();
}, 100); // 防抖
} catch (error) {
console.error('监听面板组配置变化失败:', error);
}
}, { deep: true, immediate: false });
watch([currentPanelNode, currentPanelUnitObj], () => {
managePolling();
}, { deep: true });
watch(selectedProjectId, () => {
stopPolling();
});
// 持久化 selectedProjectId 到 localStorage
watch(selectedProjectId, (val) => {
try {
if (val === null || val === undefined || val === '') {
localStorage.removeItem('selectedProjectId');
} else {
localStorage.setItem('selectedProjectId', String(val));
}
} catch (e) {
// ignore
}
});
// 持久化 currentPanelUnit
watch(currentPanelUnit, (val) => {
try {
if (!val) {
localStorage.removeItem('currentPanelUnit');
} else {
localStorage.setItem('currentPanelUnit', String(val));
}
} catch (e) {
// ignore
}
});
// 监听 panelUnitForm 变化,重新构建合并数据
watch(panelUnitForm, () => {
buildMergedPanelUnitForm();
}, { deep: true });
watch(selectedSeriesForUnit, async (newSeries, oldSeries) => {
if (newSeries) {
try {
// 先重置选择器
selectedDirectionForUnit.value = '';
selectedColorForUnit.value = '';
selectedPatternForUnit.value = '';
selectedModelForUnit.value = null;
// 加载数据
await getModelsForUnit(newSeries.ID);
await getPatternsForUnit(newSeries.ID);
// 如果是编辑模式,恢复之前的选择
if (isEditPanelUnit.value && currentEditPanelUnit.value) {
await restoreSelectionsAfterSeriesLoad();
}
} catch (error) {
console.error('加载系列数据失败:', error);
}
} else {
// 重置选择器
selectedDirectionForUnit.value = '';
selectedColorForUnit.value = '';
selectedPatternForUnit.value = '';
selectedModelForUnit.value = null;
directionOptionsForUnit.value = [];
colorOptionsForUnit.value = [];
patternsListForUnit.value = [];
}
});
// ==================== 初始化 ====================
/**
* 页面加载完成后的初始化
*/
onMounted(async () => {
const startTime = Date.now();
showLoading('页面加载中...');
try {
await getProject();
const data = await fetchData('PanelSelection/SeriesQuery');
seriesList.value = data;
await getFont();
await getIconLibraries();
await getLogoList();
// 尝试从 localStorage 恢复用户上次的选择(按顺序恢复 project -> tree -> panelUnit
await restoreSelectionsFromLocalStorage();
} catch (error) {
console.error('初始化失败:', error);
ElMessage.error('页面加载失败');
} finally {
const elapsed = Date.now() - startTime;
const delay = Math.max(0, 1000 - elapsed);
setTimeout(hideLoading, delay);
}
});
onUnmounted(() => {
stopPolling();
// 清理防抖定时器
if (window.panelUnitSaveTimeout) {
clearTimeout(window.panelUnitSaveTimeout);
}
if (window.panelGroupSaveTimeout) {
clearTimeout(window.panelGroupSaveTimeout);
}
});</script>
<style scoped>
/* 容器布局 */
.container {
display: flex;
height: 100%;
padding: 5px;
gap: 8px;
background-color: #f5f7fa;
}
/* 左侧面板 */
.left-panel {
flex: 3;
display: flex;
flex-direction: column;
gap: 5px;
}
/* 右侧面板 */
.right-panel {
flex: 9;
display: flex;
flex-direction: column;
gap: 5px;
}
/* 面板区域高度分配 */
.left-top {
height: 3%;
}
.left-bottom {
height: 97%;
display: flex;
flex-direction: column;
gap: 5px;
}
.right-middle, .right-bottom {
height: 50%;
}
/* 树组件样式 */
.full-height-tree {
height: 100%;
overflow: auto;
}
.left-bottom-bottom {
height: 52%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.left-bottom-bottom .section-header {
padding: 5px;
border-bottom: 1px solid #e0e0e0;
background: #f8f9fa;
margin: 0;
}
.left-bottom-bottom .table-content {
flex: 1;
overflow: auto;
}
.left-bottom-top {
height: 48%;
overflow: hidden;
}
/* 树节点样式 */
.tree-node-container {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
.tree-node-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tree-node-actions {
display: flex;
gap: 5px;
}
/* 详情弹窗样式 */
.details {
margin-top: 4px;
border-radius: 6px;
overflow: hidden;
font-size: 0.85rem;
}
/* 对话框样式 */
.dialog-content {
display: flex;
gap: 15px;
height: 500px;
margin-bottom: 10px;
}
.dialog-left, .dialog-middle, .dialog-right {
display: flex;
flex-direction: column;
}
.dialog-left {
flex: 1;
}
.dialog-middle {
flex: 2;
}
.dialog-right {
flex: 3;
}
.dialog-bottom {
border-top: 1px solid #e0e0e0;
padding-top: 10px;
}
/* 模型选择器样式 */
.model-selectors {
display: flex;
flex-direction: column;
gap: 15px;
padding: 5px;
background: #f8f9fa;
border-radius: 6px;
height: 100%;
}
.selector-row {
display: flex;
align-items: center;
gap: 10px;
}
.selector-row .el-select:disabled {
opacity: 0.6;
}
.selector-row .el-select:not(:disabled) {
border-color: #409EFF;
}
.selector-label {
width: 100px;
font-weight: bold;
color: #606266;
}
/* 区域标题样式 */
.section-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
color: #303133;
text-align: center;
}
/* 预览容器样式 */
.preview-container {
flex: 1;
display: flex;
flex-direction: column;
background: #f8f9fa;
border-radius: 6px;
padding: 5px;
height: 100%;
overflow: hidden;
}
.preview-container .preview-image {
flex: 1;
min-height: 0;
}
.preview-info {
margin-top: 5px;
text-align: left;
background: white;
padding: 5px;
border-radius: 4px;
}
.preview-info p {
margin: 5px 0;
font-size: 14px;
}
/* 面板配置容器 */
.panel-unit-config-container {
display: flex;
height: 100%;
gap: 0; /* 移除内部间隙 */
border: 1px solid #e0e0e0; /* 添加外部边框 */
border-radius: 6px;
overflow: hidden;
background: white;
}
/* 通用区域样式 */
.preview-section-top, .preview-section-down, .table-section, .form-section {
border: none;
border-radius: 0;
box-shadow: none;
border-right: 1px solid #e0e0e0;
}
.table-section, .form-section {
flex: 8;
}
.preview-section-top {
flex: 10;
}
.preview-section-down {
flex: 3;
}
/* 区域头部样式 */
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 5px;
border-bottom: 1px solid #e0e0e0;
background: #f8f9fa;
margin: 0;
}
.section-header span {
font-size: 16px;
font-weight: bold;
color: #303133;
}
/* 内容区域样式 */
.preview-content, .table-content, .form-content {
flex: 1;
padding: 5px;
overflow: hidden;
}
.left-bottom-bottom .table-content {
padding: 0px;
}
.preview-content {
height: 39.8vh; /*calc(100vh - 570px);*/
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
padding: 5px;
box-sizing: border-box;
}
/* 预览图片样式 */
.preview-image {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
.preview-image > * {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.preview-image img {
max-width: 100%;
max-height: 220px;
object-fit: contain;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.preview-image .el-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
/* 占位符样式 */
.preview-placeholder, .empty-prompt {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
color: #909399;
background: #f8f9fa;
border-radius: 4px;
border: 1px dashed #dcdfe6;
}
.empty-prompt {
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.empty-prompt p {
margin-top: 10px;
font-size: 16px;
}
/* 图标选择对话框样式 */
.icon-select-dialog {
height: 100%;
overflow-y: auto;
}
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 10px;
}
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
border: 1px solid #e0e0e0;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.icon-item:hover {
border-color: #409EFF;
background-color: #f5f7fa;
}
.icon-preview {
margin-bottom: 5px;
}
.icon-name {
font-size: 12px;
text-align: center;
word-break: break-all;
}
/* 表单内容区域调整 */
.form-content {
flex: 1;
padding: 5px;
overflow: auto;
}
/* 图标库选择器样式 */
.icon-library-selector {
margin-bottom: 20px;
padding: 10px;
background: #f8f9fa;
border-radius: 4px;
display: flex;
align-items: center;
gap: 10px;
}
.icon-library-table-container {
display: inline-block;
vertical-align: middle;
}
.selector-label {
font-weight: bold;
color: #606266;
}
/* 图标搜索框样式 */
.icon-search-input {
flex: 1;
max-width: 300px;
}
/* 图标网格容器 */
.icon-grid-container {
max-height: calc(100vh - 110px);
overflow-y: auto;
display: flex;
gap: 15px;
}
/* 图标库表格容器 */
.icon-library-table-container {
height: calc(100vh - 150px);
width: 150px;
flex-shrink: 0;
display: flex;
flex-direction: column;
border: 1px solid #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.icon-library-title {
padding: 8px 12px;
background: #f8f9fa;
border-bottom: 1px solid #e0e0e0;
font-weight: bold;
color: #606266;
text-align: center;
}
/* 图标网格包装器 */
.icon-grid-wrapper {
flex: 1;
display: flex;
flex-direction: column;
}
/* 图标项样式调整 */
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 15px;
border: 1px solid #e0e0e0;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
background: white;
}
.icon-item:hover {
border-color: #409EFF;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.icon-library {
font-size: 11px;
color: #909399;
margin-top: 5px;
text-align: center;
}
/* 空状态样式 */
.empty-icons {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200px;
color: #909399;
}
.empty-icons p {
margin-top: 10px;
}
/* 图标网格布局优化 */
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 15px;
padding: 10px;
}
/* 背光控制样式 */
.backlight-control {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
/* 颜色选择器样式调整 */
.backlight-control .el-color-picker {
vertical-align: middle;
}
.logo-config-section {
margin-top: 15px;
padding: 10px;
background: #f8f9fa;
border-radius: 6px;
border: 1px solid #e0e0e0;
}
.logo-config-section .section-title {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
color: #606266;
text-align: left;
}
/* 图标预览单元格样式 */
.icon-preview-cell {
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
}
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #909399;
}
/* 新增:表格和预览图的上下布局 */
.table-preview-container {
display: flex;
flex-direction: column;
height: calc(100% - 40px);
}
.preview-below-table {
height: 24vh;
border-top: 1px solid #e0e0e0;
display: flex;
flex-direction: column;
}
.preview-below-table .preview-content {
height: calc(100% - 0px); /* 减去header高度 */
flex: 1;
}
/* 新增:表单内容的左右布局 */
.form-content-container {
display: flex;
height: 39.8vh;
gap: 0px;
}
.pattern-preview-section {
width: 30%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.pattern-preview-section .section-header {
padding: 5px;
border-bottom: 1px solid #e0e0e0;
background: #f8f9fa;
margin: 0;
text-align: center;
}
.pattern-preview-section .preview-content {
flex: 1;
padding: 5px;
display: flex;
justify-content: center;
align-items: center;
}
/* 调整表格区域宽度 */
.form-section .table-content {
width: 70%;
}
/* 确保预览图容器正确显示 */
.preview-below-table .preview-image,
.pattern-preview-section .preview-image {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.preview-below-table .preview-image .el-image,
.pattern-preview-section .preview-image .el-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
</style>