Add hierarchical region data to candidate and center records
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { formatRegionAddress } from './region-select.mjs';
|
||||
|
||||
export function createAdminViews(context) {
|
||||
const {
|
||||
state,
|
||||
@@ -101,7 +103,10 @@ export function createAdminViews(context) {
|
||||
|
||||
function adminCenters(data) {
|
||||
const roomTypeNames = { standard: '标准考场', computer: '机考考场', accessible: '无障碍考场', spare: '备用考场' };
|
||||
const detailAddresses = new Map(data.centers.map(center => [center.id, center.address]));
|
||||
data.centers.forEach(center => { center.address = formatRegionAddress(center); });
|
||||
const cards = data.centers.map(center => `<article class="panel center-dossier"><header><div><span>${h(center.schoolName)} · <b class="mono">${h(center.code)}</b></span><h2>${h(center.name)}</h2></div><div>${center.pendingChange ? '<span class="pending-mark">变更审批中</span>' : ''}${badge(center.status === 'active' ? 'approved' : 'closed')}<button class="row-action" data-action="edit-center" data-id="${h(center.id)}" ${center.pendingChange ? 'disabled title="已有待审批变更"' : ''}>提交变更</button></div></header><div class="center-metrics"><div><small>结构化考场</small><strong>${center.rooms.length}</strong><span>个</span></div><div><small>启用席位</small><strong>${center.totalCapacity}</strong><span>席</span></div><div><small>开放时间</small><strong>${h(center.gateOpenTime || '未设')}</strong></div></div><dl class="center-profile"><div><dt>详细地址</dt><dd>${h(center.address)}</dd></div><div><dt>考点负责人</dt><dd>${h(center.managerName || '未填写')} · ${h(center.managerPhone || center.contact || '未填写')}</dd></div><div><dt>应急电话</dt><dd>${h(center.emergencyPhone || '未填写')}</dd></div><div><dt>交通提示</dt><dd>${h(center.transport || '未填写')}</dd></div></dl><div class="room-table-wrap"><table class="room-table"><thead><tr><th>考场</th><th>位置</th><th>类型</th><th>容量</th><th>座位编排</th><th>状态</th></tr></thead><tbody>${center.rooms.map(room => `<tr><td><strong>${h(room.name)}</strong><small class="mono">${h(room.code)}</small></td><td>${h(room.building)} · ${h(room.floor || '楼层未填')}</td><td>${h(roomTypeNames[room.roomType] || room.roomType)}</td><td>${h(room.capacity)} 席</td><td>${h(room.seatPlan || '按现场座次表编排')}</td><td>${badge(room.status === 'active' ? 'approved' : 'closed')}</td></tr>`).join('')}</tbody></table></div><footer><span>${h(center.notes || '无补充说明')}</span><time>更新于 ${formatDate(center.updatedAt, true)}</time></footer></article>`).join('');
|
||||
data.centers.forEach(center => { center.address = detailAddresses.get(center.id); });
|
||||
const requests = data.changeRequests || [];
|
||||
return `${excelToolbar('centers', { label: '考点考场档案' })}<section class="center-summary"><div><span>正式考点</span><strong>${data.centers.length}</strong></div><div><span>结构化考场</span><strong>${data.centers.reduce((sum, item) => sum + item.rooms.length, 0)}</strong></div><div><span>待审批变更</span><strong>${requests.filter(item => item.status === 'pending').length}</strong></div></section><div class="center-dossier-grid">${cards || emptyState('还没有正式考点', '提交考点和考场档案,经流程审批后会显示在这里。')}</div><section class="panel center-change-ledger"><div class="panel-title"><div><h2>考点变更台账</h2><p>新增和修改均保留申请快照,审批通过后才更新正式档案。</p></div><button class="row-action" data-route="admin/flows">进入流程中心</button></div><div class="table-scroll"><table><thead><tr><th>申请类型</th><th>考点</th><th>学校</th><th>考场数</th><th>提交时间</th><th>当前状态</th><th>责任人</th></tr></thead><tbody>${requests.map(item => `<tr><td>${item.requestType === 'create' ? '新增考点' : '修改档案'}</td><td><strong>${h(item.name)}</strong><small class="mono">${h(item.code)}</small></td><td>${h(item.schoolName)}</td><td>${item.rooms.length} 个</td><td>${formatDate(item.createdAt, true)}</td><td>${badge(item.status)}</td><td>${h(item.workflow?.assignee?.displayName || '流程已结束')}</td></tr>`).join('') || '<tr><td colspan="7" class="empty-state">暂无考点变更申请</td></tr>'}</tbody></table></div></section>`;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { mountRegionSelects } from './region-select.mjs';
|
||||
|
||||
export function createCandidateViews(context) {
|
||||
const {
|
||||
state,
|
||||
@@ -70,6 +72,7 @@ export function createCandidateViews(context) {
|
||||
const data = await api('/api/candidate/profile');
|
||||
state.pageData = data; state.profile = data.profile;
|
||||
app.innerHTML = onboardingShell('profile', candidateProfile(data, true));
|
||||
mountRegionSelects(app, data.profile, { className: 'region-selects wide-field' });
|
||||
} catch (error) { renderError(error); }
|
||||
return;
|
||||
}
|
||||
@@ -95,6 +98,7 @@ export function createCandidateViews(context) {
|
||||
results: () => candidateResults(data), notices: () => candidateNotices(data.notices)
|
||||
}[page]();
|
||||
app.innerHTML = portalShell('candidate', page, content, ...meta[page]);
|
||||
if (page === 'profile') mountRegionSelects(app, data.profile, { className: 'region-selects wide-field' });
|
||||
} catch (error) { renderError(error); }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { chinaRegions } from '../data/china-regions.mjs';
|
||||
|
||||
const option = (value, label, selected = false) => `<option value="${value}" ${selected ? 'selected' : ''}>${label}</option>`;
|
||||
|
||||
export function regionSelects(region = {}, { required = true, className = 'region-selects' } = {}) {
|
||||
const province = chinaRegions.find(item => item.code === region.provinceCode);
|
||||
const city = province?.cities.find(item => item.code === region.cityCode);
|
||||
const requiredText = required ? 'required' : '';
|
||||
return `<div class="${className}" data-region-group>
|
||||
<label><span>省 / 自治区 / 直辖市${required ? ' *' : ''}</span><select name="provinceCode" data-region-level="province" ${requiredText}><option value="">请选择省份</option>${chinaRegions.map(item => option(item.code, item.name, item.code === region.provinceCode)).join('')}</select></label>
|
||||
<label><span>市 / 州${required ? ' *' : ''}</span><select name="cityCode" data-region-level="city" ${requiredText}><option value="">请先选择省份</option>${(province?.cities || []).map(item => option(item.code, item.name, item.code === region.cityCode)).join('')}</select></label>
|
||||
<label><span>区 / 县${required ? ' *' : ''}</span><select name="districtCode" data-region-level="district" ${requiredText}><option value="">请先选择城市</option>${(city?.districts || []).map(item => option(item.code, item.name, item.code === region.districtCode)).join('')}</select></label>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
export function updateRegionSelects(select) {
|
||||
const group = select.closest('[data-region-group]');
|
||||
if (!group) return;
|
||||
const provinceSelect = group.querySelector('[name="provinceCode"]');
|
||||
const citySelect = group.querySelector('[name="cityCode"]');
|
||||
const districtSelect = group.querySelector('[name="districtCode"]');
|
||||
const province = chinaRegions.find(item => item.code === provinceSelect?.value);
|
||||
if (select.dataset.regionLevel === 'province') {
|
||||
citySelect.innerHTML = `<option value="">请选择城市</option>${(province?.cities || []).map(item => option(item.code, item.name)).join('')}`;
|
||||
districtSelect.innerHTML = '<option value="">请先选择城市</option>';
|
||||
} else if (select.dataset.regionLevel === 'city') {
|
||||
const city = province?.cities.find(item => item.code === citySelect?.value);
|
||||
districtSelect.innerHTML = `<option value="">请选择区县</option>${(city?.districts || []).map(item => option(item.code, item.name)).join('')}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function mountRegionSelects(root, region = {}, options = {}) {
|
||||
const address = root?.querySelector('[name="address"]');
|
||||
if (!address || root.querySelector('[data-region-group]')) return;
|
||||
address.closest('label')?.insertAdjacentHTML('beforebegin', regionSelects(region, options));
|
||||
}
|
||||
|
||||
export function formatRegionAddress(region = {}) {
|
||||
const parts = [region.provinceName, region.cityName, region.districtName]
|
||||
.filter((value, index, values) => value && value !== values[index - 1]);
|
||||
return [...parts, region.address].filter(Boolean).join('');
|
||||
}
|
||||
Reference in New Issue
Block a user