This commit is contained in:
728
2023-02-15 16:53:02 +08:00
parent e406303c9f
commit 71db941e6d
10 changed files with 272 additions and 237 deletions

View File

@@ -22,6 +22,17 @@ export function not_nam(
return !is_nam(nam)
}
export function is_nbr(
nbr?: null | string
): nbr is string {
return typeof nbr === "string" && /^1\d{10}$/.test(nbr)
}
export function not_nbr(
nbr?: null | string
) {
return !is_nbr(nbr)
}
export function is_intro(
intro?: null | Id["intro"]
) {

View File

@@ -1,8 +1,7 @@
import { Usr } from "./typ.ts"
import { coll, DocC, DocD, DocR, DocU, Update } from "../db.ts"
import { is_nbr, not_nbr } from "../ont/sms.ts"
import { not_adm } from "../ont/adm.ts"
import { is_id, not_id, not_nam, not_intro } from "./is.ts"
import { is_id, not_id, not_nam, is_nbr, not_nbr, not_intro } from "./is.ts"
export async function usr_c(
nbr: NonNullable<Usr["nbr"]>,

View File

@@ -2,17 +2,6 @@ import { to_hex } from "./base.ts"
import { digest, key, sign } from "./crypt.ts"
import { utc_date } from "./utc.ts"
export function is_nbr(
nbr?: null | string
): nbr is string {
return typeof nbr === "string" && /^1\d{10}$/.test(nbr)
}
export function not_nbr(
nbr?: null | string
) {
return !is_nbr(nbr)
}
const tc: {
id: string,
key: string,

View File

@@ -4,7 +4,7 @@ import { act_c, act_d, act_r, act_u } from "../src/eid/act.ts"
import { agd_c, agd_d, agd_r, agd_u } from "../src/eid/agd.ts"
import { aut_c, aut_d, aut_r, aut_u } from "../src/eid/aut.ts"
import { id, idnam, nid_of_adm } from "../src/eid/id.ts"
import { is_id, is_intro, is_nam, not_id, not_intro, not_nam, is_rol, not_rol } from "../src/eid/is.ts"
import { is_id, is_intro, is_nam, not_id, not_intro, not_nam, is_rol, not_rol, is_nbr, not_nbr } from "../src/eid/is.ts"
import { nrec, rec_c, rec_d, rec_r, rec_u, rol } from "../src/eid/rec.ts"
import { soc_c, soc_d, sidnam, soc_r, soc_u } from "../src/eid/soc.ts"
import { usr_c, usr_r, usr_u, usr_d } from "../src/eid/usr.ts"
@@ -46,6 +46,11 @@ Deno.test("id", async () => {
await Promise.all(sid.map(soc_d))
})
Deno.test("nbr", () => {
assert(is_nbr("11111111111"))
assert(not_nbr(undefined) && not_nbr(null) && not_nbr("") && not_nbr("123"))
})
Deno.test("usr", async () => {
const nbr = "11111111114"
assert(null === await usr_r({ _id: 1 }, { nbr: 1 }))

View File

@@ -3,7 +3,6 @@ import { is_adm, is_adm1, is_adm2, not_adm, not_adm1, not_adm2 } from "../src/on
import { from_base64, from_hex, from_u8, to_base64, to_hex, to_u8 } from "../src/ont/base.ts"
import { digest } from "../src/ont/crypt.ts"
import { jwk_load, jwk_set, jwt_sign, jwt_verify } from "../src/ont/jwt.ts"
import { is_nbr, not_nbr } from "../src/ont/sms.ts"
import { utc_date, utc_h, utc_medium, utc_short } from "../src/ont/utc.ts"
Deno.test("base", () => {
@@ -39,11 +38,6 @@ Deno.test("adm", () => {
assert(not_adm2(undefined) && not_adm2(null) && not_adm2("") && not_adm2("四川"))
})
Deno.test("nbr", () => {
assert(is_nbr("11111111111"))
assert(not_nbr(undefined) && not_nbr(null) && not_nbr("") && not_nbr("123"))
})
Deno.test("dig", async () => {
const h = `${Math.floor(Date.now() % utc_h * Math.random())}`
const h1000 = await digest(h, 1000)

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,19 @@
import type { Id } from "../../src/eid/typ.ts"
import type { Aut, Id } from "../../src/eid/typ.ts"
import type { Pas } from "../../src/pra/pas.ts"
import type { Usr, Soc } from "../../src/pra/que.ts"
import { utc_medium } from "../../src/ont/utc.ts"
import { adm } from "../../src/ont/adm.ts"
import { Template } from "./template.ts"
import { pos, Template, utc_refresh } from "./template.ts"
import { is_aut } from "../../src/pra/con.ts"
import { Usr, Soc, hash } from "./article.ts"
import type { DocU } from "../../src/db.ts"
export function label(
el: HTMLElement,
s: string,
) {
const l = el.previousElementSibling as HTMLLabelElement
l.innerText = s
}
function selopt(
sel: HTMLSelectElement,
@@ -30,63 +39,83 @@ export function admsel(
t.adm1.addEventListener("change", () => selopt(t.adm2, adm.get(t.adm1.value)!))
}
export function idanchor(
id: number[],
idnam: Map<Id["_id"], Id["nam"]>,
el: HTMLElement,
export function ida(
t: HTMLElement,
pf: "" | "s" | "a",
nam: Map<Id["_id"], Id["nam"]>,
id?: number[],
) {
if (id.length === 0) { el.innerText = "无"; return }
if (!id) id = [...nam.keys()]
if (id.length === 0) return
id.forEach(id => {
const a = el.appendChild(document.createElement("a"))
const a = t.appendChild(document.createElement("a"))
a.href = `#${pf}${id}`
a.innerText = idnam.get(id) ?? `${id}`
a.innerText = nam.get(id) ?? `${pf}${id}`
})
}
function idmeta(
export function idmeta(
pas: Pas | null,
id: Omit<NonNullable<Usr | Soc>, "unam"> & { unam: Map<Id["_id"], Id["nam"]> },
t: Template["usr" | "soc"],
id: Usr | Soc,
): boolean {
let pro: null | "rej" | "ref" = null
if (id.rej.length >= 2) pro = "rej"
else if (id.ref.length < 2) pro = "ref"
const pub: boolean = pro === null || (pas !== null && is_aut(pas.aut, "pro_usr"))
if (pro === "rej") {
t.id.classList.add("red")
t.proc.classList.add("red")
} else if (pro === "ref") {
t.id.classList.add("green")
t.proc.classList.add("green")
} else t.proc.classList.add("gray")
const [rej, ref] = [id.rej.length >= 2, id.ref.length < 2]
const re: "rej" | "ref" | null = rej ? "rej" : ref ? "ref" : null
const { aut }: { aut?: Aut["aut"][0] } = {
...t.tid === "usr" ? { aut: "pre_usr" } : {},
...t.tid === "soc" ? { aut: "pre_soc" } : {},
}
const pub: boolean = re === null || (pas !== null && aut !== undefined && is_aut(pas.aut, aut))
t.adm.innerText = `${id.adm1} ${id.adm2}`
t.utc.innerText = `${utc_medium(id.utc)}`
idanchor(id.rej, id.unam, t.rej, "")
idanchor(id.ref, id.unam, t.ref, "")
ida(t.rej, "", id.unam, id.rej)
ida(t.ref, "", id.unam, id.ref)
if (id.rej.length >= 2) {
t.rej.classList.add("red")
t.rejc.classList.add("red")
} else t.rejc.classList.add("gray")
if (id.ref.length < 2) {
t.ref.classList.add("green")
t.refc.classList.add("green")
} else t.refc.classList.add("gray")
if (rej) [t.rej, t.rejc].forEach(el => el.classList.add("red"))
if (ref) [t.ref, t.refc].forEach(el => el.classList.add("green"))
if (re === "rej") [t.id, t.proc].forEach(el => el.classList.add("red"))
else if (re === "ref") [t.id, t.proc].forEach(el => el.classList.add("green"))
return pub
}
export function id(
pas: Pas | null,
ph: "" | "s",
id: Omit<NonNullable<Usr | Soc>, "unam"> & { unam: Map<Id["_id"], Id["nam"]> },
t: Template["usr" | "soc"],
): boolean {
t.idnam.href = `#${ph}${id._id}`
t.id.innerText = `${ph}${id._id}`
t.nam.innerText = id.nam
return idmeta(pas, id, t) || ph === "" && pas !== null && pas.id.uid === id._id
export function idnam(
t: Template["usr" | "soc" | "pre"],
id: string,
nam?: string,
) {
t.idnam.href = `#${id}`
t.id.innerText = id
if (hash === id) t.id.classList.add("active")
if (nam) t.nam.innerText = nam
}
export function pro(
pas: Pas,
t: Template["usr" | "soc"],
id: Usr | Soc,
re?: (r: Id["_id"]) => void,
) {
const [prorej, proref] = [!id.rej.includes(pas.id.uid), !id.ref.includes(pas.id.uid)]
t.prorej.innerText = prorej ? "反对" : "取消反对"
t.proref.innerText = proref ? "推荐" : "取消推荐"
if (re) {
const pid = {
...t.tid === "usr" ? { uid: id._id } : {},
...t.tid === "soc" ? { sid: id._id } : {},
}
t.prorej.addEventListener("click", async () => {
t.prorej.disabled = true
const c = await pos<DocU>("pro", { re: "rej", ...pid, pro: prorej })
if (c && c > 0) setTimeout(() => re(id._id), utc_refresh)
else t.prorej.disabled = false
})
t.proref.addEventListener("click", async () => {
t.proref.disabled = true
const c = await pos<DocU>("pro", { re: "ref", ...pid, pro: proref })
if (c && c > 0) setTimeout(() => re(id._id), utc_refresh)
else t.proref.disabled = false
})
} else t.prorej.disabled = t.proref.disabled = true
}

View File

@@ -1,14 +1,15 @@
export async function que<T>(
q: string
) {
const res = await fetch(`/q/${q}`)
const etag = res.headers.get("etag")?.substring(3)
const r = await fetch(`/q/${q}`)
const etag = r.headers.get("etag")?.substring(3)
if (etag) utc_etag = parseInt(etag)
return res.json() as T
return r.json() as T
}
export async function pos<T>(
f: string, b: Record<string, string | number | boolean>
f: string,
b: Record<string, string | number | boolean>,
) {
const res = await fetch(`/p/${f}`, {
method: "POST",
@@ -17,14 +18,16 @@ export async function pos<T>(
return res.json() as T
}
export const utc_refresh = 500
export let utc_etag = Date.now()
export const main = document.getElementById("main")!
export const main = document.getElementById("main")! as HTMLDivElement
export const pas_a = document.getElementById("pas")! as HTMLAnchorElement
const t: typeof document.createElement = (s: string) => document.createElement(s)
const template = {
pasact: {
tid: "pasact" as const,
nbr: t("input"), send: t("button"),
adm: t("section"), adm1: t("select"), adm2: t("select"),
pre: t("section"), actid: t("input"), act: t("button"),
@@ -33,6 +36,7 @@ const template = {
},
usr: {
tid: "usr" as const,
idnam: t("a"), id: t("code"), nam: t("span"),
adm: t("span"), utc: t("span"),
rej: t("span"), ref: t("span"),
@@ -46,6 +50,7 @@ const template = {
},
soc: {
tid: "soc" as const,
idnam: t("a"), id: t("code"), nam: t("span"),
adm: t("span"), utc: t("span"),
rej: t("span"), ref: t("span"),
@@ -56,6 +61,7 @@ const template = {
},
pre: {
tid: "pre" as const,
idnam: t("a"), id: t("code"), nam: t("span"),
meta: t("section"), pnam: t("input"), nbr: t("input"),
adm: t("section"), adm1: t("select"), adm2: t("select"),
@@ -64,6 +70,7 @@ const template = {
},
putusr: {
tid: "putusr" as const,
idnam: t("a"), id: t("code"),
nam: t("input"),
adm1: t("select"), adm2: t("select"),
@@ -72,6 +79,7 @@ const template = {
},
idnull: {
tid: "idnull" as const,
id: t("cod"),
meta: t("section")
}
@@ -90,5 +98,6 @@ export function bind<
c, t.querySelector(`.${c}`) as HTMLElement
])
]) as Template[T] & { bind: DocumentFragment }
b.tid = tid
return b
}

View File

@@ -155,6 +155,12 @@ section>p {
padding: 1ch;
}
section>span:empty::before,
section>p:empty::before {
color: var(--gray);
content: "无";
}
section>textarea {
box-sizing: border-box;
display: block;
@@ -180,6 +186,20 @@ section>select {
padding: 0.2ch 1ch;
}
section>span>a:not(:first-child):before,
section>p>a:not(:first-child):before {
content: "、";
color: initial;
}
@media (hover: hover) {
section>span>a:hover,
section>p>a:hover {
color: var(--amber);
}
}
div#main {
max-width: 1080px;
margin: 0 auto;
@@ -279,25 +299,19 @@ section.meta>span {
color: var(--black)
}
section.meta>span.red {
color: var(--red);
}
section.meta>span.green {
color: var(--green);
}
section.meta>span.gray {
section.meta>span.rejc,
section.meta>span.refc,
section.meta>span.proc {
color: var(--gray);
}
section.meta>span>a:hover {
color: var(--amber);
section.meta>span.red {
color: var(--red) !important;
}
section.meta>span>a:not(:first-child):before {
content: "、";
color: initial;
section.meta>span.green,
section.meta>span.green:empty::before {
color: var(--green) !important;
}
a.idnam {
@@ -318,10 +332,12 @@ a.idnam>code.id.active {
}
a.idnam>code.id.red {
border: none;
background: var(--red) !important;
}
a.idnam>code.id.green {
border: none;
background: var(--green) !important;
}

View File

@@ -1,7 +1,7 @@
<template id="pasact">
<article>
<section>
<a class="idnam"><code class="id active">pas</code> <span class="nam">用户登录与激活</span></a>
<a class="idnam" href="#pas"><code class="id active">pas</code> <span class="nam">用户登录与激活</span></a>
</section>
<hr>
<section class="flex">
@@ -145,7 +145,7 @@
<section class="meta"></section>
<hr>
<section class="flex">
<label>名称2-16个中文字符,不能重复</label>
<label>名称2-16个中文字符</label>
<input class="pnam" type="text" maxlength="16" required />
</section>
<section class="flex">
@@ -201,7 +201,7 @@
<template id="idnull">
<article>
<section>
<a class="idnam" href=""><code class="id active"></code> <span class="nam">无效操作</span></a>
<a class="idnam" href=""><code class="id active"></code> <span class="nam">无效链接</span></a>
</section>
<hr>
<section class="meta"></section>