You can use a column-gutter to introduce the small gap between the lines below “Bar” and “Baz”. For the regular lines you would then have to use table.hline which ignores the gutter. This does not feel like a particularly elegant solution but it works for your specific use case. If you want to make the stroke of the horizontal lines adjustable, just turn table-lines into a function.
#let tabledata = (
([123], [234], [345], [456], [567], [678]),
([123], [234], [345], [456], [567], [678]),
([123], [234], [345], [456], [567], [678]),
([123], [234], [345], [456], [567], [678]),
)
#let table-lines = (
table.hline(y: 0),
table.hline(y: 2),
..range(1, tabledata.len()).map(i => table.hline(y: 2 + i, stroke: 0.5pt)),
table.hline(y: 2 + tabledata.len()),
)
#let framing(stroke) = (x, y) => (
bottom: if (y == 0) { stroke }
)
#align(center)[
#table(
columns: 6,
column-gutter: 0.5em,
stroke: framing(0.5pt),
table.header(
table.cell(rowspan: 2, [Foo]),
table.cell(colspan: 2, [Bar]),
table.cell(colspan: 2, [Foo]),
table.cell(rowspan: 2, [Qux]),
[abc], [xyz], [abc], [xyz]
),
..tabledata.flatten(),
..table-lines
)
]