diff --git a/oaer/lib/assets/css/oaer.scss b/oaer/lib/assets/css/oaer.scss
index 5ae7a0a..1bdbc19 100644
--- a/oaer/lib/assets/css/oaer.scss
+++ b/oaer/lib/assets/css/oaer.scss
@@ -7,12 +7,21 @@
@import "./animate.scss";
-:root {
- --bg-base: #efefef;
- --fg-base: #090909
+
+div[voa] {
+ box-sizing: border-box;
+ --oabg-base: var(--bg-base, #efefef);
+ --oafg-base: var(--fg-base, #090909);
+ --oaheader-bg: linear-gradient(90deg, #6849E1, #b09ef4);
+
+ div {
+ box-sizing: border-box;
+ }
}
+
.voa {
+
user-select: none;
cursor: pointer;
height: inherit;
@@ -102,7 +111,7 @@
position: relative;
width: 100%;
height: 4rem;
- background: linear-gradient(90deg, #6849E1, #b09ef4);
+ background: var(--oaheader-bg);
z-index: 1;
}
@@ -110,13 +119,22 @@
position: relative;
width: 100%;
height: calc(100% - 4rem);
- background: var(--bg-base);
- color: var(--fg-base);
+ background: var(--oabg-base);
+ color: var(--oafg-base);
transform: translateY(-140%);
.voa-slide-main {
height: calc(100% - 3rem);
overflow: auto;
+ padding: 1rem;
+
+ .voa-sm-separate {
+ height: 1px;
+ margin: 1rem 0;
+ background: var(--oafg-base);
+ width: 100%;
+ opacity: 0.4;
+ }
}
.voa-slide-footer {
@@ -137,6 +155,43 @@
}
}
+.voa-account {
+ padding: 0 1rem;
+
+ .voa-account-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 3rem;
+ line-height: 3rem;
+ font-size: 1.25rem;
+ }
+
+ .voa-account-body {
+ width: 100%;
+ height: 7rem;
+ display: flex;
+ align-items: center;
+ gap: 2rem;
+
+ .voa-ab-ico {
+ display: contents;
+
+ img {
+ width: 4rem;
+ height: 4rem;
+ vertical-align: middle;
+ border-radius: 50%;
+ }
+ }
+
+ .voa-ab-info {
+ display: flex;
+ flex-direction: column;
+ }
+ }
+}
+
.voa-logo {
background-image: url("../favicon.svg");
diff --git a/oaer/lib/cfg.ts b/oaer/lib/cfg.ts
index 4072c72..0fbd324 100644
--- a/oaer/lib/cfg.ts
+++ b/oaer/lib/cfg.ts
@@ -12,6 +12,13 @@ let cfg = {
app_data: {},
+ user: {
+ username: 'asd',
+ nickname: '',
+ email: '',
+ phone: '',
+ icon: 'https://public.veypi.com/img/avatar/0001.jpg'
+ },
ready: false,
local_user: {},
@@ -20,7 +27,7 @@ let cfg = {
nats_pub_key: '',
prefix: '/api',
BaseUrl() {
- return this.host.value + this.prefix
+ return this.host + this.prefix
},
NatsHost() {
if (this._host_nats.startsWith('ws')) {
@@ -29,7 +36,7 @@ let cfg = {
return 'ws://' + this._host_nats
},
media(u: string) {
- return this.host.value + u
+ return this.host + u
},
goto(url: string) {
if (url.startsWith('http')) {
@@ -39,16 +46,16 @@ let cfg = {
if (!url.startsWith('/')) {
url = '/' + url
}
- window.location.href = this.host.value + url
+ window.location.href = this.host + url
},
Host() {
- return this.host.value || window.location.host
+ return this.host || window.location.host
},
userFileUrl() {
return '/fs/u/'
},
appFileUrl() {
- return `/fs/a/${this.uuid.value}/`
+ return `/fs/a/${this.uuid}/`
},
}
diff --git a/oaer/lib/components/account.ts b/oaer/lib/components/account.ts
index 0642d35..90f25c8 100644
--- a/oaer/lib/components/account.ts
+++ b/oaer/lib/components/account.ts
@@ -4,36 +4,23 @@
* 2024-10-22 22:07
* Distributed under terms of the GPL license.
*/
-import Base from './base'
+import b from './build'
+import cfg from '../cfg'
-export default class extends Base {
- doms: { [key: string]: HTMLElement }
+export default class {
main: HTMLElement
constructor() {
- super()
- this.main = this.build({
- class: 'voa-account'
- })
- let u = {
- username: 'asd',
- icon: 'https://public.veypi.com/img/avatar/0001.jpg'
- }
- this.main.innerHTML = `
-
-
-
-
-
-
-
昵称: ${u.username}
-
昵称: ${u.username}
-
昵称: ${u.username}
-
昵称: ${u.username}
-
-
-`
+ this.main = b('voa-account', [
+ b('voa-account-header', [b('voa-ah-1', '我的账户'), b('voa-ah-2')]),
+ b('voa-account-body', [
+ b('voa-ab-ico', [b('img', '', (d) => { d.setAttribute('src', cfg.user.icon) })]),
+ b('voa-ab-info', [
+ b('voa-abi-1', [b('span', '昵称:'), b('span', '', (d) => { d.innerHTML = cfg.user.nickname })]),
+ b('voa-abi-2', [b('span', '账户:'), b('span', '', (d) => { d.innerHTML = cfg.user.username })]),
+ b('voa-abi-3', [b('span', '邮箱:'), b('span', '', (d) => { d.innerHTML = cfg.user.email })]),
+ b('voa-abi-4', [b('span', '手机:'), b('span', '', (d) => { d.innerHTML = cfg.user.phone })]),
+ ])
+ ])
+ ])
}
}
diff --git a/oaer/lib/components/app.ts b/oaer/lib/components/app.ts
new file mode 100644
index 0000000..1926cf4
--- /dev/null
+++ b/oaer/lib/components/app.ts
@@ -0,0 +1,24 @@
+/*
+ * app.ts
+ * Copyright (C) 2024 veypi
+ * 2024-10-23 15:35
+ * Distributed under terms of the GPL license.
+ */
+
+import b from "./build";
+
+
+export default class {
+ main: HTMLElement
+ body: HTMLElement
+ constructor() {
+ this.body = b('voa-apps-body')
+ this.main = b({
+ class: 'voa-apps',
+ children: [
+ b({ class: 'voa-apps-header', children: [b('voa-apps-title')] }),
+ this.body
+ ]
+ })
+ }
+}
diff --git a/oaer/lib/components/base.ts b/oaer/lib/components/base.ts
deleted file mode 100644
index 67bf95f..0000000
--- a/oaer/lib/components/base.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * base.ts
- * Copyright (C) 2024 veypi
- * 2024-10-22 18:21
- * Distributed under terms of the GPL license.
- */
-interface buildOpts {
- id?: string
- typ?: 'div'
- class?: string
- style?: string
- innerHtml?: string
- onclick?: any
- children?: HTMLElement[]
-}
-
-export default class {
- class_prefix: string
- constructor(p?: string) {
- this.class_prefix = p || 'voa-'
- }
- build(opts: buildOpts) {
- let dom = document.createElement(opts.typ || 'div')
- if (opts.id) {
- dom.id = opts.id
- }
- if (opts.class) {
- this.addClass(dom, opts.class)
- }
- if (opts.innerHtml) {
- dom.innerHTML = opts.innerHtml
- }
- if (opts.onclick) {
- dom.onclick = opts.onclick
- }
- if (opts.children) {
- for (let c in opts.children) {
- dom.appendChild(opts.children[c])
- }
- }
- if (opts.style) {
- const regex = /([a-zA-Z-]+)\s*:\s*([^;]+);?/g;
- let match;
- while ((match = regex.exec(opts.style)) !== null) {
- const key = match[1].trim();
- const value = match[2].trim();
- console.log([key, value])
- dom.style.setProperty(key, value)
- }
- }
- return dom
- }
- addClass(dom: HTMLElement, c: string) {
- let items = c.split(' ')
- for (let i of items) {
- if (i.startsWith(this.class_prefix)) {
- dom.classList.add(i)
- } else {
- dom.classList.add(this.class_prefix + i)
- }
- }
- }
- removeClass(dom: HTMLElement, c: string) {
- let items = c.split(' ')
- for (let i of items) {
- if (i.startsWith(this.class_prefix)) {
- dom.classList.remove(i)
- } else {
- dom.classList.remove(this.class_prefix + i)
- }
- }
- }
-}
diff --git a/oaer/lib/components/build.ts b/oaer/lib/components/build.ts
new file mode 100644
index 0000000..9e028f1
--- /dev/null
+++ b/oaer/lib/components/build.ts
@@ -0,0 +1,84 @@
+/*
+ * build.ts
+ * Copyright (C) 2024 veypi
+ * 2024-10-23 15:54
+ * Distributed under terms of the GPL license.
+ */
+
+
+interface buildOpts {
+ id?: string
+ typ?: string
+ class?: string
+ style?: string
+ innerHtml?: string
+ onclick?: any
+ children?: HTMLElement[]
+ updator?: (p: HTMLElement) => void
+}
+
+const typs = ['div', 'img', 'span', 'p', 'a']
+const caches: (() => void)[] = []
+function update() {
+ for (let c of caches) {
+ c()
+ }
+}
+export { update }
+
+export default (opts: buildOpts | string, inner?: string | HTMLElement[], updator?: (p: HTMLElement) => void) => {
+ if (typeof opts === 'string') {
+ if (typs.indexOf(opts) >= 0) {
+ opts = { typ: opts }
+ } else {
+ opts = { class: opts }
+ }
+ }
+ if (inner) {
+ if (typeof inner == 'string') {
+ opts.innerHtml = inner
+ } else {
+ opts.children = inner
+ }
+ }
+ if (opts.updator) {
+ updator = opts.updator
+ }
+ let dom = document.createElement(opts.typ || 'div')
+ if (opts.id) {
+ dom.id = opts.id
+ }
+ if (opts.class) {
+ dom.classList.add(...opts.class.split(' '))
+ }
+ if (opts.innerHtml) {
+ dom.innerHTML = opts.innerHtml
+ }
+ if (opts.onclick) {
+ dom.onclick = opts.onclick
+ }
+ if (opts.children) {
+ for (let c in opts.children) {
+ dom.appendChild(opts.children[c])
+ }
+ }
+ if (opts.style) {
+ const regex = /([a-zA-Z-]+)\s*:\s*([^;]+);?/g;
+ let match: any
+ while ((match = regex.exec(opts.style)) !== null) {
+ const key = match[1].trim();
+ const value = match[2].trim();
+ console.log([key, value])
+ dom.style.setProperty(key, value)
+ }
+ }
+ dom.setAttribute('voa', '1')
+ if (updator) {
+ caches.push(() => {
+ updator(dom)
+ })
+ updator(dom)
+ }
+ return dom
+}
+
diff --git a/oaer/lib/components/index.ts b/oaer/lib/components/index.ts
index f7025de..6266e95 100644
--- a/oaer/lib/components/index.ts
+++ b/oaer/lib/components/index.ts
@@ -6,15 +6,14 @@
*/
import slide from './slide'
-import Base from './base'
+import b from './build'
import bus from '../bus'
-export default class extends Base {
+export default class {
slide: slide
- frame: HTMLDivElement
- frame_login?: HTMLDivElement
- frame_user?: HTMLDivElement
- constructor(frame: HTMLDivElement) {
- super()
+ frame: HTMLElement
+ frame_login?: HTMLElement
+ frame_user?: HTMLElement
+ constructor(frame: HTMLElement) {
this.frame = frame
this.frame.classList.add('voa')
this.slide = new slide()
@@ -25,12 +24,12 @@ export default class extends Base {
})
}
mount_login() {
- this.frame_login = this.build({
- class: 'off hover-line-b scale-in',
+ this.frame_login = b({
+ class: 'voa-off voa-hover-line-b voa-scale-in',
innerHtml: '登录',
onclick: () => {
console.log('click login')
- this.addClass(this.frame_login!, 'scale-off')
+ this.frame_login?.classList.add('voa-scale-off')
this.mount_user()
}
})
@@ -41,10 +40,10 @@ export default class extends Base {
}
mount_user() {
let icon = 'https://public.veypi.com/img/avatar/0001.jpg'
- this.frame_user = this.build({
- class: 'on scale-in',
+ this.frame_user = b({
+ class: 'voa-on voa-scale-in',
innerHtml: `
-
+
`,
onclick: () => {
this.slide.show()
diff --git a/oaer/lib/components/slide.ts b/oaer/lib/components/slide.ts
index fce4866..20fa365 100644
--- a/oaer/lib/components/slide.ts
+++ b/oaer/lib/components/slide.ts
@@ -4,7 +4,7 @@
* 2024-10-22 17:57
* Distributed under terms of the GPL license.
*/
-import Base from './base'
+import b from './build'
import bus from '../bus'
import account from './account'
@@ -17,41 +17,43 @@ mask
footer
*
* */
-export default class extends Base {
- mask: HTMLDivElement
- slide: HTMLDivElement
- header: HTMLDivElement
+export default class {
+ mask: HTMLElement
+ slide: HTMLElement
+ header: HTMLElement
body: HTMLElement
main: HTMLElement
footer: HTMLElement
constructor() {
- super()
- this.header = this.build({
- class: 'slide-header animate-slow',
+ this.header = b({
+ class: 'voa-slide-header voa-animate-slow',
})
- this.footer = this.build({
- class: 'slide-footer',
+ this.footer = b({
+ class: 'voa-slide-footer',
innerHtml: 'logout',
onclick: () => {
bus.emit('logout')
}
})
- this.main = this.build({
- class: 'slide-main',
- children: [new account().main]
+ this.main = b({
+ class: 'voa-slide-main',
+ children: [
+ new account().main,
+ b({ class: 'voa-sm-separate' })
+ ]
})
- this.body = this.build({
- class: 'slide-body animate-slow',
+ this.body = b({
+ class: 'voa-slide-body voa-animate-slow',
style: 'animation-delay: 300ms',
children: [this.main, this.footer]
})
- this.slide = this.build({
+ this.slide = b({
id: 'voa-slide',
- class: 'slide',
+ class: 'voa-slide',
children: [this.header, this.body]
})
- this.mask = this.build({
- class: 'slide-mask',
+ this.mask = b({
+ class: 'voa-slide-mask',
style: 'visibility: hidden',
children: [this.slide],
onclick: (e: MouseEvent) => {
@@ -64,12 +66,12 @@ export default class extends Base {
}
show() {
this.mask.style.visibility = 'visible'
- this.addClass(this.header, 'slidein-right')
- this.addClass(this.body, 'slidein-up')
+ this.header.classList.add('voa-slidein-right')
+ this.body.classList.add('voa-slidein-up')
}
hide() {
this.mask.style.visibility = 'hidden'
- this.removeClass(this.header, 'slidein-right')
- this.removeClass(this.body, 'slidein-up')
+ this.header.classList.remove('voa-slidein-right')
+ this.body.classList.remove('voa-slidein-up')
}
}