feat: vif

v3
veypi 3 weeks ago
parent d4a57c8cf0
commit e819b138ae

@ -12,8 +12,10 @@ const info = () => {
}>('/', {}) }>('/', {})
} }
const set_base_host = webapi.set_base_host
export { export {
apitoken apitoken,
set_base_host
} }
export default { export default {

@ -175,6 +175,12 @@ export const webapi = {
'content-type': 'application/json;charset=UTF-8', 'content-type': 'application/json;charset=UTF-8',
}, },
})), })),
set_base_host(host: string) {
if (host === '' || host === '/') {
return
}
webapi.client.defaults.baseURL = host + '/api/'
},
Get<T>(url: string, req: data): Promise<T> { Get<T>(url: string, req: data): Promise<T> {
return webapi.client.request<T, any>(Object.assign({ method: 'get', url: url }, transData(req))) return webapi.client.request<T, any>(Object.assign({ method: 'get', url: url }, transData(req)))
}, },

@ -228,6 +228,24 @@ div[voa] {
} }
.voa-fs {
padding: 0 1rem;
.voa-fs-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.fsdir {
.fsdir-header {
display: flex;
justify-content: space-between;
align-items: center;
}
}
}
.voa-logo { .voa-logo {
background-image: url("../favicon.svg"); background-image: url("../favicon.svg");
background-size: cover; background-size: cover;

@ -19,7 +19,7 @@ export default v('voa-account', [
v('voa-ah-2')] v('voa-ah-2')]
), ),
v('voa-account-body', [ v('voa-account-body', [
v('voa-ab-ico', [v({ typ: 'img', attrs: { 'src': () => `${logic.user.icon}` } })]), v('voa-ab-ico', [v({ typ: 'img', attrs: { 'src': () => `${logic.urlwarp(logic.user.icon)}` } })]),
v('voa-ab-info', [ v('voa-ab-info', [
v('voa-abi-1', [v('span', '昵称:'), v('span', () => logic.user.nickname)]), v('voa-abi-1', [v('span', '昵称:'), v('span', () => logic.user.nickname)]),
v('voa-abi-2', [v('span', '账户:'), v('span', () => logic.user.username)]), v('voa-abi-2', [v('span', '账户:'), v('span', () => logic.user.username)]),

@ -32,7 +32,7 @@ export default () => {
vfor(apps, vfor(apps,
(data) => v({ (data) => v({
class: 'voa-apps-box', class: 'voa-apps-box',
children: [v('img', '', { src: () => data.icon })], children: [v('img', '', { src: () => logic.urlwarp(data.icon) })],
onclick: () => { onclick: () => {
logic.goto(`/app/${data.id}`) logic.goto(`/app/${data.id}`)
} }

@ -0,0 +1,43 @@
/*
* fs.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-11-04 17:11
* Distributed under terms of the GPL license.
*/
import { v } from "../v2dom";
import logic from '../logic'
import api from "../api";
import fstree from "./fstree";
export default () => {
return v({
class: 'voa-fs',
children: [
v('voa-fs-header',
[
v({
class: 'voa-item-title',
children: ['我的文件'],
onclick: () => {
logic.goto('/')
}
}),
v({
class: 'voa-fs-subtxt', children: '获取密钥', onclick: () => {
api.token.Post({
refresh: logic.token.refresh.raw(),
user_id: logic.token.refresh.uid!
}).then(e => {
console.log(e)
})
}
})
]
),
v('voa-fs-body', fstree('/'))
]
})
}

@ -0,0 +1,37 @@
/*
* fstree.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-11-04 17:18
* Distributed under terms of the GPL license.
*/
import v, { proxy, vfor } from "../v2dom"
import fs, { FileStat } from '../fs'
import { vif } from "../v2dom/v2dom"
const dirTree = (url: string) => {
let treeItems = proxy.Watch([] as FileStat[])
let expand = proxy.Watch({ value: false })
let child = v('',
() =>
expand.value && treeItems.length ? vfor(treeItems, (item) => v('div', item.basename), undefined, '123') : [])
return v('fsdir', [
v({
class: 'fsdir-header',
children: [v('div', () => expand.value + ''), v('div', url)],
onclick: () => {
expand.value = !expand.value
if (expand.value) {
fs.user.getDirectoryContents(url).then((e: any) => {
treeItems.splice(0)
treeItems.push(...e)
})
}
}
}),
vif([() => expand.value && treeItems.length, () => child], [1, () => v('div', 123)])
])
}
export default dirTree

@ -22,7 +22,7 @@ export default class {
let frame_user = v({ let frame_user = v({
class: 'voa-on', class: 'voa-on',
vclass: [() => logic.ready ? 'voa-scale-in' : 'voa-scale-off'], vclass: [() => logic.ready ? 'voa-scale-in' : 'voa-scale-off'],
children: [() => `<img src="${logic.user.icon || logic.token.refresh.icon}" />`], children: [() => `<img src="${logic.urlwarp(logic.user.icon || logic.token.refresh.icon!)}" />`],
onclick: () => { onclick: () => {
logic.getDetailUser() logic.getDetailUser()
if (!this.slide) { if (!this.slide) {

@ -9,6 +9,7 @@ import account from './account'
import app from './app' import app from './app'
import logic from '../logic' import logic from '../logic'
import bus from '../bus' import bus from '../bus'
import fsdom from './fsdom'
/* /*
mask mask
@ -37,14 +38,15 @@ export default class {
bus.emit('logout') bus.emit('logout')
} }
}) })
console.log(logic.user)
this.main = v({ this.main = v({
class: 'voa-slide-main', class: 'voa-slide-main',
children: [ children: [
account, account,
v({ class: 'voa-sm-separate' }), v({ class: 'voa-sm-separate' }),
app(), app(),
v({ class: 'voa-sm-separate' }) v({ class: 'voa-sm-separate' }),
fsdom(),
v({ class: 'voa-sm-separate' }),
] ]
}) })
this.body = v({ this.body = v({

@ -7,18 +7,10 @@
import * as webdav from 'webdav' import * as webdav from 'webdav'
export { type FileStat } from 'webdav'
import { proxy } from "./v2dom"; import { proxy } from "./v2dom";
import logic from "./logic"; import logic from "./logic";
export interface fileProps {
filename: string,
basename: string,
lastmod: string,
size: number,
type: "directory" | "file",
etag: string
}
function delay(ms: number) { function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms)); return new Promise(resolve => setTimeout(resolve, ms));
} }
@ -32,19 +24,20 @@ class davWraper {
this.host = host this.host = host
this.prefix = prefix this.prefix = prefix
this.token = token this.token = token
this.client = webdav.createClient(host + prefix) this.client = webdav.createClient(this.host + this.prefix)
this.set(host, prefix, token)
}
set(host: string, prefix: string, token: string) {
this.host = host
this.prefix = prefix
this.token = token
this.client = webdav.createClient(this.host + this.prefix)
if (this.prefix.endsWith('/')) { if (this.prefix.endsWith('/')) {
this.prefix = this.prefix.slice(0, -1) this.prefix = this.prefix.slice(0, -1)
} }
} this.client.setHeaders({
set(k: 'host' | 'token' | 'prefix', value: string) { authorization: "bearer " + this.token
if (value !== this[k]) { })
this[k] = value
this.client = webdav.createClient(this.host + this.prefix)
this.client.setHeaders({
authorization: "bearer " + this.token
})
}
} }
putFileContents(filename: string, data: string | webdav.BufferLike, options?: webdav.PutFileContentsOptions) { putFileContents(filename: string, data: string | webdav.BufferLike, options?: webdav.PutFileContentsOptions) {
return this.retry(() => this.client.putFileContents(filename, data, options)) return this.retry(() => this.client.putFileContents(filename, data, options))
@ -95,17 +88,12 @@ const user = new davWraper(logic.Host(), '/fs/u/', token)
const app = new davWraper(logic.Host(), '/fs/a/' + logic.oa_id, token) const app = new davWraper(logic.Host(), '/fs/a/' + logic.oa_id, token)
export const set_host = (h: string) => {
user.set('host', h)
app.set('host', h)
}
const sync = () => { const sync = () => {
if (logic.token.oa.isVaild()) { if (logic.token.oa.isVaild()) {
let t = logic.token.oa.raw() let t = logic.token.oa.raw()
// console.warn('sync oafs token: ' + t) // console.warn('sync oafs token: ' + t)
user.set('token', t) user.set(logic.Host(), '/fs/u/', t)
app.set('token', t) app.set(logic.Host(), '/fs/a/' + logic.app_id, t)
app.set('prefix', '/fs/a/' + logic.app_id)
} }
} }
proxy.Listen(() => { proxy.Listen(() => {

@ -152,17 +152,23 @@ const logic = proxy.Watch({
}, },
host: window.location.origin, host: window.location.origin,
Host() { Host() {
return this.host return logic.host
}, },
goto(url: string) { urlwarp(url: string) {
let h = logic.host
if (!url) {
return ''
}
if (url.startsWith('http')) { if (url.startsWith('http')) {
window.location.href = url return url
return
} }
if (!url.startsWith('/')) { if (!url.startsWith('/')) {
url = '/' + url url = '/' + url
} }
window.location.href = this.host + url return h + url
},
goto(url: string) {
window.location.href = logic.urlwarp(url)
}, },
}) })

@ -9,7 +9,7 @@ import './assets/css/oaer.scss'
import bus from './bus' import bus from './bus'
import logic from './logic' import logic from './logic'
import ui from './components' import ui from './components'
import api from './api' import api, { set_base_host } from './api'
import fs from './fs' import fs from './fs'
@ -33,6 +33,7 @@ export default new class {
init(host?: string, code?: string) { init(host?: string, code?: string) {
if (host) { if (host) {
logic.host = host logic.host = host
set_base_host(host)
} }
if (code) { if (code) {
logic.token.refresh.set(code) logic.token.refresh.set(code)

@ -85,7 +85,15 @@ function DomListen(dom: HTMLElement, fn: () => void) {
const isProxy = Symbol("isProxy") const isProxy = Symbol("isProxy")
const DataID = Symbol("DataID") const DataID = Symbol("DataID")
function Watch<T extends Object>(data: T) { // @ts-ignore
window.$u = (id: number) => {
callbackCache[id][1]()
}
function Ref<T>(data: T): { value: T } {
return Watch({ value: data })
}
function Watch<T extends Object>(data: T, debug = false) {
const did = GenUniqueID() const did = GenUniqueID()
let isArray = false let isArray = false
if (Object.prototype.toString.call(data) === '[object Array]') { if (Object.prototype.toString.call(data) === '[object Array]') {
@ -106,7 +114,7 @@ function Watch<T extends Object>(data: T) {
if (value[isProxy]) { if (value[isProxy]) {
return value return value
} else { } else {
let newValue = Watch(value) let newValue = Watch(value, debug)
Reflect.set(target, key, newValue, receiver) Reflect.set(target, key, newValue, receiver)
return newValue return newValue
} }
@ -124,12 +132,16 @@ function Watch<T extends Object>(data: T) {
listeners[lkey].push(idx) listeners[lkey].push(idx)
} }
} }
// console.log(`${did} get ${key.toString()}:${value} ${idx}`) if (debug) {
console.log(`${did} get ${key.toString()}:${value} ${idx}`)
}
return value; return value;
}, },
set(target: Object, key: string | symbol, newValue: any, receiver: any) { set(target: Object, key: string | symbol, newValue: any, receiver: any) {
// console.log(`${did} set ${key.toString()} ${newValue}`)
const result = Reflect.set(target, key, newValue, receiver); const result = Reflect.set(target, key, newValue, receiver);
if (debug) {
console.log(`${did} set ${key.toString()} ${newValue}`, listeners, result)
}
if (result && listen_tags.length === 0) { if (result && listen_tags.length === 0) {
let lkey = key let lkey = key
if (isArray) { if (isArray) {
@ -158,4 +170,4 @@ function Watch<T extends Object>(data: T) {
return res return res
} }
export default { Watch, Listen, DomListen, ForceUpdate, DataID, GenUniqueID, } export default { Watch, Listen, Ref, DomListen, ForceUpdate, DataID, GenUniqueID, }

@ -24,8 +24,11 @@ interface buildOpts<T> {
const typs = ['div', 'img', 'span', 'p', 'a', 'input'] const typs = ['div', 'img', 'span', 'p', 'a', 'input']
type vforChild<T> = { obj: T[], fn: (d: T, idx?: number) => HTMLElement, iterID: string, ifnone: HTMLElement } type vforChild<T> = { obj: T[], fn: (d: T, idx?: number) => HTMLElement, iterID: string, ifnone: HTMLElement }
type computedFn = () => string | HTMLElement type vIfCondition = boolean | number | string | undefined | null | (() => vIfCondition)
type childTyp<T> = HTMLElement | vforChild<T> | string | computedFn type vIfStatment = HTMLElement | (() => HTMLElement)
type vIfChild = { ifID: string, rules: [vIfCondition, vIfStatment][] }
type computedFn = () => number | string | HTMLElement | vforChild<any> | []
type childTyp<T> = HTMLElement | vforChild<T> | string | number | computedFn | vIfChild
type attrs = { [key: string]: string | number | (() => string) } type attrs = { [key: string]: string | number | (() => string) }
export const v = <T>(opts: buildOpts<T> | string, children?: childTyp<T> | childTyp<T>[], attrs?: attrs) => { export const v = <T>(opts: buildOpts<T> | string, children?: childTyp<T> | childTyp<T>[], attrs?: attrs) => {
@ -128,6 +131,8 @@ function handleChild(dom: HTMLElement, c: any, is_only = false, is_listen = fals
} }
} else if (c && c.iterID) { } else if (c && c.iterID) {
handleChildVfor(dom, c as vforChild<any>, is_listen) handleChildVfor(dom, c as vforChild<any>, is_listen)
} else if (c && c.ifID) {
handleChildVif(dom, c as vIfChild, is_listen)
} else if (c instanceof Element) { } else if (c instanceof Element) {
dom.appendChild(c) dom.appendChild(c)
} else if (typeof c === 'undefined' || c === null) { } else if (typeof c === 'undefined' || c === null) {
@ -136,8 +141,55 @@ function handleChild(dom: HTMLElement, c: any, is_only = false, is_listen = fals
} }
} }
export const vfor = <T>(obj: T[], fn: (d: T, idx?: number) => HTMLElement, ifnone?: () => HTMLElement): vforChild<T> => { function handleChildVif(dom: HTMLElement, data: vIfChild, is_listen = false) {
let iterID = proxy.GenUniqueID() let fn = () => {
let target = emptyDom()
for (let r of data.rules) {
let c = Boolean(r[0])
if (typeof r[0] === 'function') {
c = Boolean(r[0]())
}
if (c) {
if (typeof r[1] === 'function') {
target = r[1]()
} else {
target = r[1]
}
break
}
}
let before: Element | null = null
for (let domc of dom.children) {
if (domc.getAttribute('vbind-if') === data.ifID) {
before = domc
}
}
target.setAttribute('vbind-if', data.ifID)
if (before) {
dom.replaceChild(target, before)
} else {
dom.appendChild(target)
}
}
if (is_listen) {
fn()
} else {
proxy.DomListen(dom, fn)
}
}
export const vif = (...rules: [vIfCondition, vIfStatment][]): vIfChild => {
let ifID = proxy.GenUniqueID()
return {
ifID,
rules
}
}
export const vfor = <T>(obj: T[], fn: (d: T, idx?: number) => HTMLElement, ifnone?: () => HTMLElement, iterID?: string): vforChild<T> => {
if (!iterID) {
iterID = proxy.GenUniqueID()
}
let dom = document.createElement('div') as HTMLElement let dom = document.createElement('div') as HTMLElement
dom.style.display = 'none' dom.style.display = 'none'
dom.style.visibility = 'hidden' dom.style.visibility = 'hidden'
@ -238,3 +290,11 @@ function handleChildVfor(dom: HTMLElement, data: vforChild<any>, is_listen = fal
} }
function emptyDom() {
let dom = document.createElement('div') as HTMLElement
dom.style.display = 'none'
dom.style.visibility = 'hidden'
dom.style.height = '0'
dom.style.width = '0'
return dom
}

Loading…
Cancel
Save