feat(routemap): pill markers — job-type icon + sequence number (best practice)
Route-stop markers changed from a plain number disc to a rounded- rectangle "pill" carrying BOTH the job-type icon (material-icons) and the sequence number, in the tech's route color with a white border — the standard for markers that show two facts (logistics/route tools). Number stays bold (primary scan key), icon is context. - markerIcon(skill): map-safe resolver returning ONLY material-icons ligatures (skillSym's custom SVGs don't render in a plain <span>); install→construction, repair→build, tv→live_tv, phone→call, etc. - stops in dayRoutes + suggestRoutes now carry icon; RouteMap renders <span.material-icons> + number in .rm-pill. Hover fan-out, hover-to- front, and cluster logic unchanged (operate on the pill element). Verified: 43 pills render with glyph+number in tech colors; 8-pin cluster still fans out on hover. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2177212a67
commit
7b7980a12b
|
|
@ -80,8 +80,11 @@ function renderMarkers (routes) {
|
|||
homeMk.push({ mk, el: wrap, rid: r.id })
|
||||
}
|
||||
for (const s of (r.stops || [])) {
|
||||
// Pastille = ROUND-RECT (pill) : icône du type de job + numéro d'ordre (best practice quand 2 infos ; cf. outils de tournée).
|
||||
const wrap = document.createElement('div'); wrap.className = 'rm-mk'; wrap.dataset.rid = String(r.id)
|
||||
const disc = document.createElement('div'); disc.className = 'rm-disc'; disc.style.background = r.color; disc.textContent = String(s.seq)
|
||||
const disc = document.createElement('div'); disc.className = 'rm-pill'; disc.style.background = r.color
|
||||
const ic = /^[a-z0-9_]+$/.test(String(s.icon || '')) ? s.icon : 'place'
|
||||
disc.innerHTML = '<span class="material-icons rm-ic">' + ic + '</span><span class="rm-num">' + s.seq + '</span>'
|
||||
wrap.appendChild(disc)
|
||||
const mk = new mapboxgl.Marker({ element: wrap, anchor: 'center' }).setLngLat([+s.lon, +s.lat]).addTo(map)
|
||||
const rec = { mk, el: wrap, disc, rid: r.id, lngLat: [+s.lon, +s.lat], seq: s.seq, tech: r.name, subject: s.subject || '', stop: s, fan: null, members: null }
|
||||
|
|
@ -211,17 +214,18 @@ watch(() => props.routes, () => { draw() }, { deep: true })
|
|||
<!-- NON scoped : les marqueurs sont créés en JS et injectés dans le conteneur Mapbox (hors de l'arbre scoped). Préfixe rm- = pas de collision. -->
|
||||
<style>
|
||||
.rm-mk { cursor: pointer; will-change: transform; }
|
||||
.rm-mk .rm-disc {
|
||||
width: 24px; height: 24px; border-radius: 50%; box-sizing: border-box;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #fff; font-size: 11px; font-weight: 800; line-height: 1;
|
||||
border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0,0,0,.45);
|
||||
.rm-mk .rm-pill {
|
||||
display: inline-flex; align-items: center; gap: 3px; box-sizing: border-box;
|
||||
height: 22px; padding: 0 7px 0 5px; border-radius: 11px; white-space: nowrap;
|
||||
color: #fff; border: 2px solid #fff; box-shadow: 0 1px 3px rgba(0,0,0,.45);
|
||||
transition: transform .18s cubic-bezier(.2,.8,.3,1), box-shadow .15s;
|
||||
}
|
||||
.rm-mk .rm-pill .rm-ic { font-size: 14px; line-height: 1; opacity: .95; }
|
||||
.rm-mk .rm-pill .rm-num { font-size: 12px; font-weight: 800; line-height: 1; }
|
||||
.rm-mk .rm-home { width: 15px; height: 15px; border-radius: 50%; box-sizing: border-box; background: #fff; border: 3px solid #888; box-shadow: 0 1px 3px rgba(0,0,0,.4); }
|
||||
.rm-mk.hot { z-index: 7; }
|
||||
.rm-mk.hot .rm-disc { transform: scale(1.2); box-shadow: 0 3px 9px rgba(0,0,0,.55); }
|
||||
.rm-mk.hot .rm-pill { transform: scale(1.15); box-shadow: 0 3px 9px rgba(0,0,0,.55); }
|
||||
.rm-mk.route-hot { z-index: 5; }
|
||||
.rm-mk.fanned { z-index: 6; }
|
||||
.rm-mk.fanned .rm-disc { box-shadow: 0 3px 10px rgba(0,0,0,.55); }
|
||||
.rm-mk.fanned .rm-pill { box-shadow: 0 3px 10px rgba(0,0,0,.55); }
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2362,6 +2362,16 @@ function skillIcon (sk) {
|
|||
if (/factur|paiement|compta/.test(s)) return 'receipt_long'
|
||||
return 'bolt'
|
||||
}
|
||||
// Icône de compétence pour un MARQUEUR carte (DOM, police material-icons) : UNIQUEMENT des ligatures material (pas les SVG
|
||||
// custom de skillSym) — sinon le glyphe ne s'affiche pas dans un <span class="material-icons"> hors q-icon.
|
||||
function markerIcon (skill) {
|
||||
const s = String(skill || '').toLowerCase()
|
||||
if (/t[ée]l[ée]vis|\btv\b|iptv/.test(s)) return 'live_tv'
|
||||
if (/install/.test(s)) return 'construction'
|
||||
if (/monteur|poteau|hauteur|nacelle|a[ée]rien|grimp/.test(s)) return 'local_shipping'
|
||||
const ic = skillIcon(skill)
|
||||
return (typeof ic === 'string' && /^[a-z0-9_]+$/.test(ic)) ? ic : 'build'
|
||||
}
|
||||
function onTagsChange (t, items) {
|
||||
const newLabels = (items || []).map(x => typeof x === 'string' ? x : x.tag).filter(Boolean)
|
||||
const removed = (t.skills || []).filter(s => !newLabels.includes(s)) // compétences retirées → vérifier l'impact sur les jobs assignés
|
||||
|
|
@ -4476,7 +4486,7 @@ const suggestRoutes = computed(() => {
|
|||
if (e.lat == null || e.lon == null || !isFinite(+e.lat) || !isFinite(+e.lon) || Math.abs(+e.lat) < 0.01) continue // 0,0 / invalides exclus (sinon la carte dézoome jusqu'au golfe de Guinée)
|
||||
const k = (+e.lat).toFixed(4) + ',' + (+e.lon).toFixed(4)
|
||||
const ex = seen.get(k)
|
||||
if (ex) { ex.n++; ex.subject = ex.n + ' jobs — ' + ex.first } else { const s = { lat: +e.lat, lon: +e.lon, seq: stops.length + 1, subject: e.subject, first: e.subject, n: 1, name: e.jobName, entry: e }; seen.set(k, s); stops.push(s) }
|
||||
if (ex) { ex.n++; ex.subject = ex.n + ' jobs — ' + ex.first } else { const s = { lat: +e.lat, lon: +e.lon, seq: stops.length + 1, subject: e.subject, first: e.subject, n: 1, name: e.jobName, entry: e, icon: markerIcon(e.skill) }; seen.set(k, s); stops.push(s) }
|
||||
}
|
||||
if (!stops.length) continue
|
||||
const home = techOrigin(g.techId) // domicile, sinon bureau TARGO
|
||||
|
|
@ -4502,7 +4512,7 @@ const allDayRoutes = computed(() => { // TOUTES les tournées du jour (couleurs
|
|||
if (!jobs.length) continue
|
||||
jobs.sort((a, b) => (a.route_order || 9999) - (b.route_order || 9999) || ((a.start_h ?? 99) - (b.start_h ?? 99))) // ordre de tournée réel
|
||||
const h = techOrigin(t.id) // domicile, sinon bureau TARGO
|
||||
out.push({ id: t.id, name: t.name, color: routeColor(ci++), home: h ? { lat: h.lat, lon: h.lon } : null, stops: jobs.map((j, i) => ({ lat: +j.lat, lon: +j.lon, seq: i + 1, subject: j.subject, name: j.name, job: j })) })
|
||||
out.push({ id: t.id, name: t.name, color: routeColor(ci++), home: h ? { lat: h.lat, lon: h.lon } : null, stops: jobs.map((j, i) => ({ lat: +j.lat, lon: +j.lon, seq: i + 1, subject: j.subject, name: j.name, job: j, icon: markerIcon(j.skill) })) })
|
||||
}
|
||||
return out
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user