feat: 优化配置编辑页面UI,添加分步引导和美化样式
Coze-Commit-Type: user Coze-User-ID: 3722323274763196 Coze-Conversation-ID: 9894087
This commit is contained in:
@@ -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()">×</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;
|
||||
|
||||
Reference in New Issue
Block a user