feat: 优化配置编辑页面UI,添加分步引导和美化样式

Coze-Commit-Type: user
Coze-User-ID: 3722323274763196
Coze-Conversation-ID: 9894087
This commit is contained in:
user9994793890
2026-07-10 14:41:33 +08:00
parent a8dbd881ee
commit d756ecd044

View File

@@ -72,40 +72,88 @@
}
.form-group {
margin-bottom: 15px;
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #555;
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
font-size: 14px;
}
.form-group label .label-icon {
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
background: #eef2f7;
border-radius: 4px;
font-size: 12px;
}
.form-group .form-hint {
font-size: 12px;
color: #999;
margin-top: 4px;
font-weight: 400;
}
.form-control {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
padding: 10px 14px;
border: 1.5px solid #e1e8ed;
border-radius: 8px;
font-size: 14px;
transition: all 0.2s;
background: #fafbfc;
}
.form-control:focus {
outline: none;
border-color: #3498db;
background: white;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
select {
background: white;
background: #fafbfc;
cursor: pointer;
}
select[multiple] {
padding: 6px;
}
select[multiple] option {
padding: 8px 12px;
border-radius: 4px;
margin-bottom: 2px;
cursor: pointer;
transition: background 0.15s;
}
select[multiple] option:hover {
background: #eef2f7;
}
select[multiple] option:checked {
background: #3498db;
color: white;
}
.btn {
padding: 8px 16px;
padding: 8px 20px;
border: none;
border-radius: 4px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary {
@@ -144,6 +192,17 @@
background: #d35400;
}
.btn-secondary {
background: #eef2f7;
color: #555;
border: 1px solid #e1e8ed;
}
.btn-secondary:hover {
background: #dde4ed;
color: #333;
}
.btn-sm {
padding: 4px 8px;
font-size: 12px;
@@ -171,21 +230,69 @@
}
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
max-height: 300px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 6px;
max-height: 240px;
overflow-y: auto;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
padding: 12px;
border: 1.5px solid #e1e8ed;
border-radius: 8px;
background: #fafbfc;
}
.checkbox-group::-webkit-scrollbar {
width: 6px;
}
.checkbox-group::-webkit-scrollbar-track {
background: transparent;
}
.checkbox-group::-webkit-scrollbar-thumb {
background: #ccc;
border-radius: 3px;
}
.checkbox-group::-webkit-scrollbar-thumb:hover {
background: #aaa;
}
.checkbox-item {
display: flex;
align-items: center;
gap: 5px;
min-width: 200px;
gap: 8px;
padding: 6px 10px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
user-select: none;
}
.checkbox-item:hover {
background: #eef2f7;
}
.checkbox-item.checked {
background: #e8f4fd;
border: 1px solid #bee5eb;
}
.checkbox-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: #3498db;
cursor: pointer;
flex-shrink: 0;
}
.checkbox-item label {
font-size: 13px;
color: #444;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.status-badge {
@@ -254,50 +361,172 @@
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
background: rgba(0,0,0,0.4);
z-index: 1000;
backdrop-filter: blur(2px);
}
.modal.active {
display: flex;
align-items: center;
justify-content: center;
animation: fadeIn 0.2s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background: white;
border-radius: 8px;
padding: 30px;
max-width: 800px;
width: 90%;
border-radius: 16px;
padding: 0;
max-width: 860px;
width: 92%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
animation: slideUp 0.25s ease;
}
@keyframes slideUp {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal-content::-webkit-scrollbar {
width: 6px;
}
.modal-content::-webkit-scrollbar-track {
background: transparent;
}
.modal-content::-webkit-scrollbar-thumb {
background: #ddd;
border-radius: 3px;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 20px 28px;
border-bottom: 1px solid #eef2f7;
background: linear-gradient(135deg, #f8fafc 0%, #eef2f7 100%);
border-radius: 16px 16px 0 0;
position: sticky;
top: 0;
z-index: 10;
}
.modal-title {
font-size: 20px;
font-weight: 600;
font-size: 18px;
font-weight: 700;
color: #2c3e50;
display: flex;
align-items: center;
gap: 8px;
}
.modal-title::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background: #3498db;
border-radius: 2px;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
font-size: 22px;
cursor: pointer;
color: #999;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
transition: all 0.2s;
}
.close-btn:hover {
background: #eef2f7;
color: #333;
}
.modal-body {
padding: 24px 28px;
}
.modal-footer {
display: flex;
gap: 10px;
justify-content: flex-end;
padding: 16px 28px;
border-top: 1px solid #eef2f7;
background: #fafbfc;
border-radius: 0 0 16px 16px;
}
.form-section {
margin-bottom: 24px;
padding-bottom: 20px;
border-bottom: 1px dashed #eef2f7;
}
.form-section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.form-section-title {
font-size: 13px;
font-weight: 600;
color: #3498db;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 14px;
display: flex;
align-items: center;
gap: 8px;
}
.form-section-title .section-num {
width: 22px;
height: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
background: #3498db;
color: white;
border-radius: 50%;
font-size: 12px;
font-weight: 700;
}
.entity-select-wrapper {
position: relative;
}
.entity-select-wrapper .selected-count {
position: absolute;
top: -8px;
right: 12px;
background: #3498db;
color: white;
font-size: 11px;
padding: 2px 8px;
border-radius: 10px;
font-weight: 600;
z-index: 1;
}
.pagination {
display: flex;
justify-content: center;
@@ -455,53 +684,70 @@
<button class="close-btn" onclick="closeModal()">&times;</button>
</div>
<div class="form-group">
<label>配置名称 *</label>
<input type="text" id="config-name" class="form-control" placeholder="请输入配置名称">
</div>
<div class="form-group">
<label>拆分方式 *</label>
<select id="split-type" class="form-control" onchange="loadEntities()">
<option value="">请选择拆分方式</option>
<option value="company_id">按企业ID</option>
<option value="user_id">按用户ID</option>
<option value="station_id">按场站ID</option>
</select>
</div>
<div class="form-group">
<label>选择企业/用户/场站(可多选) *</label>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<input type="text" id="entity-search" class="form-control" placeholder="输入名称搜索..." style="flex: 1;">
<button type="button" class="btn btn-secondary" onclick="searchEntities()">搜索</button>
<div class="modal-body">
<!-- 基本信息 -->
<div class="form-section">
<div class="form-section-title"><span class="section-num">1</span> 基本信息</div>
<div class="form-group">
<label>配置名称</label>
<input type="text" id="config-name" class="form-control" placeholder="请输入配置名称108路日报">
</div>
</div>
<select id="split-value" class="form-control" multiple size="8">
<option value="">请先选择拆分方式</option>
</select>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 8px;">
<small class="form-text text-muted">按住 Ctrl 或 Shift 键可多选</small>
<div id="entity-pagination" style="display: flex; gap: 4px; align-items: center;"></div>
<!-- 拆分设置 -->
<div class="form-section">
<div class="form-section-title"><span class="section-num">2</span> 拆分设置</div>
<div class="form-group">
<label>拆分方式</label>
<select id="split-type" class="form-control" onchange="loadEntities()">
<option value="">请选择拆分方式</option>
<option value="company_id">按企业</option>
<option value="user_id">按用户</option>
<option value="station_id">按场站</option>
</select>
</div>
<div class="form-group">
<label>选择拆分对象 <span class="form-hint">(可多选,按住 Ctrl/Shift 批量选择)</span></label>
<div class="entity-select-wrapper">
<span class="selected-count" id="selected-entity-count" style="display:none;">已选 0 项</span>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<input type="text" id="entity-search" class="form-control" placeholder="输入名称搜索..." style="flex: 1;" onkeydown="if(event.key==='Enter')searchEntities()">
<button type="button" class="btn btn-secondary" onclick="searchEntities()">搜索</button>
</div>
<select id="split-value" class="form-control" multiple size="8" onchange="updateSelectedCount()">
<option value="">请先选择拆分方式</option>
</select>
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 8px;">
<small class="form-hint">按住 Ctrl 或 Shift 键可多选</small>
<div id="entity-pagination" style="display: flex; gap: 4px; align-items: center;"></div>
</div>
</div>
</div>
</div>
<!-- 字段设置 -->
<div class="form-section">
<div class="form-section-title"><span class="section-num">3</span> 字段设置</div>
<div class="form-group">
<label>选择报表字段 <span class="form-hint">(勾选需要在报表中显示的字段)</span></label>
<div class="checkbox-group" id="fields-list">
<div class="loading">加载中...</div>
</div>
</div>
<div class="form-group">
<label>求和字段 <span class="form-hint">(仅显示已选字段中的数值类型,可多选)</span></label>
<div class="checkbox-group" id="sum-fields-list">
<div class="loading">加载中...</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label>选择字段 *</label>
<div class="checkbox-group" id="fields-list">
<div class="loading">加载中...</div>
</div>
</div>
<div class="form-group">
<label>求和字段(可选)</label>
<div class="checkbox-group" id="sum-fields-list">
<div class="loading">加载中...</div>
</div>
</div>
<div style="display: flex; gap: 10px; justify-content: flex-end;">
<div class="modal-footer">
<button class="btn" onclick="closeModal()">取消</button>
<button class="btn btn-primary" onclick="saveConfig()">保存</button>
<button class="btn btn-primary" onclick="saveConfig()">保存配置</button>
</div>
</div>
</div>
@@ -634,23 +880,37 @@
const sumFieldsList = document.getElementById('sum-fields-list');
fieldsList.innerHTML = allFields.map(field => `
<label class="checkbox-item">
<div class="checkbox-item ${selectedFields.includes(field.key) ? 'checked' : ''}" onclick="toggleCheckboxItem(this)">
<input type="checkbox" value="${field.key}"
${selectedFields.includes(field.key) ? 'checked' : ''}>
${field.display}
</label>
${selectedFields.includes(field.key) ? 'checked' : ''}
onchange="onFieldCheckboxChange(this)">
<label>${field.display}</label>
</div>
`).join('');
// 为字段列表添加 change 事件监听,动态更新求和字段
fieldsList.addEventListener('change', function() {
const checkedFields = Array.from(fieldsList.querySelectorAll('input:checked')).map(cb => cb.value);
refreshSumFieldsList(checkedFields, []);
});
// 初始化求和字段列表(只显示已选字段中的可求和字段)
refreshSumFieldsList(selectedFields, sumFields);
}
// 点击 checkbox-item 容器时切换选中状态
function toggleCheckboxItem(item) {
const cb = item.querySelector('input[type="checkbox"]');
if (event.target === cb) return; // 如果点击的就是 checkbox 本身,不重复触发
cb.checked = !cb.checked;
item.classList.toggle('checked', cb.checked);
onFieldCheckboxChange(cb);
}
// 字段 checkbox 变化时更新求和字段列表
function onFieldCheckboxChange(cb) {
const item = cb.closest('.checkbox-item');
if (item) item.classList.toggle('checked', cb.checked);
const fieldsList = document.getElementById('fields-list');
const checkedFields = Array.from(fieldsList.querySelectorAll('input:checked')).map(c => c.value);
refreshSumFieldsList(checkedFields, []);
}
// 刷新求和字段列表(仅显示已选字段中的数值类型字段)
function refreshSumFieldsList(selectedFields, currentSumFields) {
const sumFieldsList = document.getElementById('sum-fields-list');
@@ -659,18 +919,31 @@
);
if (availableSumFields.length === 0) {
sumFieldsList.innerHTML = '<small class="text-muted">请先在上方选数值类型的字段</small>';
sumFieldsList.innerHTML = '<small style="color:#999;padding:8px;">请先在上方选数值类型的字段</small>';
} else {
sumFieldsList.innerHTML = availableSumFields.map(field => `
<label class="checkbox-item">
<div class="checkbox-item ${currentSumFields.includes(field.key) ? 'checked' : ''}" onclick="toggleCheckboxItem(this)">
<input type="checkbox" value="${field.key}"
${currentSumFields.includes(field.key) ? 'checked' : ''}>
${field.display}
</label>
<label>${field.display}</label>
</div>
`).join('');
}
}
// 更新已选实体计数
function updateSelectedCount() {
const select = document.getElementById('split-value');
const countEl = document.getElementById('selected-entity-count');
const count = select.selectedOptions.length;
if (count > 0) {
countEl.textContent = '已选 ' + count + ' 项';
countEl.style.display = 'inline';
} else {
countEl.style.display = 'none';
}
}
// 当前实体加载状态
let currentEntityType = '';
let entityPage = 1;