|
|
|
|
@ -352,8 +352,8 @@ func renderResultHTML(raw []byte) template.HTML {
|
|
|
|
|
for i, record := range records {
|
|
|
|
|
b.WriteString(renderVisitorCard(record, i+1, len(records)))
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`<details class="field-details"><summary>详细字段</summary>`)
|
|
|
|
|
b.WriteString(renderAny(result, "访客记录"))
|
|
|
|
|
b.WriteString(`<details class="field-details"><summary>展开表格</summary>`)
|
|
|
|
|
b.WriteString(renderRecordsTable(records))
|
|
|
|
|
b.WriteString(`</details>`)
|
|
|
|
|
} else {
|
|
|
|
|
b.WriteString(renderAny(result, "访客记录"))
|
|
|
|
|
@ -387,34 +387,18 @@ func collectRecords(v any) []map[string]any {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderVisitorCard(obj map[string]any, index, total int) string {
|
|
|
|
|
name := firstField(obj, "姓名", "访客姓名")
|
|
|
|
|
if name == "" {
|
|
|
|
|
name = "访客"
|
|
|
|
|
}
|
|
|
|
|
gender := firstField(obj, "性别", "性别_dict", "性别_dictText")
|
|
|
|
|
title := "访客"
|
|
|
|
|
if gender == "男" {
|
|
|
|
|
title = "先生"
|
|
|
|
|
} else if gender == "女" {
|
|
|
|
|
title = "女士"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
company := firstField(obj, "访客公司", "公司", "单位")
|
|
|
|
|
start := firstField(obj, "开始时间", "来访时间")
|
|
|
|
|
end := firstField(obj, "结束时间", "离开时间")
|
|
|
|
|
reason := firstField(obj, "来访事由_dictText", "来访事由_dict", "来访事由")
|
|
|
|
|
host := firstField(obj, "接待人名称", "接待人", "person")
|
|
|
|
|
qrText := firstField(obj, "备注", "二维码", "通行码", "访客码", "QRCode", "qrCode", "qr")
|
|
|
|
|
qrSrc, qrErr := qrImageDataURL(qrText)
|
|
|
|
|
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
b.WriteString(`<article class="visitor-card">`)
|
|
|
|
|
b.WriteString(`<div class="card-head"><span>访客证</span>`)
|
|
|
|
|
b.WriteString(`<div class="card-head"><span>访客二维码</span>`)
|
|
|
|
|
if total > 1 {
|
|
|
|
|
b.WriteString(`<strong>` + html.EscapeString(fmt.Sprintf("%d / %d", index, total)) + `</strong>`)
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`</div>`)
|
|
|
|
|
b.WriteString(`<div class="welcome"><span>尊敬的</span><b>` + html.EscapeString(name) + `</b><span>` + html.EscapeString(title) + `</span></div>`)
|
|
|
|
|
if qrSrc != "" {
|
|
|
|
|
b.WriteString(`<div class="qr-wrap"><img class="qr-code" src="` + html.EscapeString(qrSrc) + `" alt="访客二维码"></div>`)
|
|
|
|
|
} else {
|
|
|
|
|
@ -424,13 +408,9 @@ func renderVisitorCard(obj map[string]any, index, total int) string {
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`<div class="qr-missing">` + html.EscapeString(msg) + `</div>`)
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`<div class="qr-tip">进园区时,请出示此二维码</div>`)
|
|
|
|
|
b.WriteString(`<div class="company">` + html.EscapeString(firstNonEmpty(company, "-")) + `</div>`)
|
|
|
|
|
b.WriteString(`<div class="card-meta">`)
|
|
|
|
|
b.WriteString(renderMetaRow("开始时间", start))
|
|
|
|
|
b.WriteString(renderMetaRow("结束时间", end))
|
|
|
|
|
b.WriteString(renderMetaRow("来访事由", reason))
|
|
|
|
|
b.WriteString(renderMetaRow("接待人", host))
|
|
|
|
|
b.WriteString(`</div></article>`)
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
@ -442,6 +422,35 @@ func renderMetaRow(label, value string) string {
|
|
|
|
|
return `<div class="meta-row"><span>` + html.EscapeString(label) + `</span><strong>` + html.EscapeString(value) + `</strong></div>`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderRecordsTable(records []map[string]any) string {
|
|
|
|
|
var b strings.Builder
|
|
|
|
|
for i, record := range records {
|
|
|
|
|
keys := make([]string, 0, len(record))
|
|
|
|
|
for k := range record {
|
|
|
|
|
keys = append(keys, k)
|
|
|
|
|
}
|
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
|
|
if len(records) > 1 {
|
|
|
|
|
b.WriteString(`<h3 class="table-title">记录 ` + html.EscapeString(fmt.Sprint(i+1)) + `</h3>`)
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`<div class="table-wrap"><table class="detail-table"><tbody>`)
|
|
|
|
|
for _, key := range keys {
|
|
|
|
|
value := stringify(record[key])
|
|
|
|
|
b.WriteString(`<tr><th>` + html.EscapeString(key) + `</th><td>`)
|
|
|
|
|
if src, ok := imageSrc(key, value); ok {
|
|
|
|
|
b.WriteString(`<img class="result-image" src="` + html.EscapeString(src) + `" alt="` + html.EscapeString(key) + `">`)
|
|
|
|
|
b.WriteString(`<div class="image-text">` + html.EscapeString(value) + `</div>`)
|
|
|
|
|
} else {
|
|
|
|
|
b.WriteString(html.EscapeString(value))
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`</td></tr>`)
|
|
|
|
|
}
|
|
|
|
|
b.WriteString(`</tbody></table></div>`)
|
|
|
|
|
}
|
|
|
|
|
return b.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func renderAny(v any, title string) string {
|
|
|
|
|
switch x := v.(type) {
|
|
|
|
|
case map[string]any:
|
|
|
|
|
@ -721,19 +730,10 @@ var pageTemplate = template.Must(template.New("page").Parse(`<!doctype html>
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
.card-head strong { font-size: 13px; font-weight: 600; }
|
|
|
|
|
.welcome {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
padding: 20px 18px 12px;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
}
|
|
|
|
|
.welcome b { font-size: 22px; }
|
|
|
|
|
.qr-wrap {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
padding: 8px 0 10px;
|
|
|
|
|
padding: 20px 0 10px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
}
|
|
|
|
|
.qr-code {
|
|
|
|
|
@ -752,22 +752,10 @@ var pageTemplate = template.Must(template.New("page").Parse(`<!doctype html>
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
background: #fff7f7;
|
|
|
|
|
}
|
|
|
|
|
.qr-tip {
|
|
|
|
|
padding: 0 18px 14px;
|
|
|
|
|
color: #8f6f34;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
.company {
|
|
|
|
|
padding: 14px 18px;
|
|
|
|
|
.card-meta {
|
|
|
|
|
padding: 12px 18px 18px;
|
|
|
|
|
border-top: 1px solid #efe2c8;
|
|
|
|
|
border-bottom: 1px solid #efe2c8;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
.card-meta { padding: 12px 18px 16px; }
|
|
|
|
|
.meta-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
@ -797,6 +785,39 @@ var pageTemplate = template.Must(template.New("page").Parse(`<!doctype html>
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
|
|
|
|
.table-title {
|
|
|
|
|
margin: 0;
|
|
|
|
|
padding: 12px 14px 0;
|
|
|
|
|
color: var(--text);
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
}
|
|
|
|
|
.table-wrap {
|
|
|
|
|
width: 100%;
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
padding: 12px 14px 14px;
|
|
|
|
|
}
|
|
|
|
|
.detail-table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
min-width: 520px;
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
table-layout: fixed;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
line-height: 1.55;
|
|
|
|
|
}
|
|
|
|
|
.detail-table th, .detail-table td {
|
|
|
|
|
border: 1px solid var(--line);
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
white-space: pre-wrap;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
}
|
|
|
|
|
.detail-table th {
|
|
|
|
|
width: 170px;
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
background: #f8fafc;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
text-align: left;
|
|
|
|
|
}
|
|
|
|
|
.api-message, .empty, .single-value {
|
|
|
|
|
padding: 14px 16px;
|
|
|
|
|
color: var(--muted);
|
|
|
|
|
@ -907,7 +928,7 @@ var pageTemplate = template.Must(template.New("page").Parse(`<!doctype html>
|
|
|
|
|
|
|
|
|
|
{{.ResultHTML}}
|
|
|
|
|
|
|
|
|
|
{{if .RawJSON}}<details class="raw-json"><summary>原始响应</summary><pre>{{.RawJSON}}</pre></details>{{end}}
|
|
|
|
|
{{if .RawJSON}}<details class="raw-json"><summary>原始 JSON</summary><pre>{{.RawJSON}}</pre></details>{{end}}
|
|
|
|
|
</main>
|
|
|
|
|
</body>
|
|
|
|
|
</html>`))
|
|
|
|
|
|