feat: app index

v3
veypi 4 weeks ago
parent 6a85c9a7c4
commit 877b2a2021

@ -33,6 +33,9 @@ class davWraper {
this.prefix = prefix this.prefix = prefix
this.token = token this.token = token
this.client = webdav.createClient(host + prefix) this.client = webdav.createClient(host + prefix)
if (this.prefix.endsWith('/')) {
this.prefix = this.prefix.slice(0, -1)
}
} }
set(k: 'host' | 'token', value: string) { set(k: 'host' | 'token', value: string) {
if (value !== this[k]) { if (value !== this[k]) {
@ -58,6 +61,19 @@ class davWraper {
stat(path: string, options?: webdav.StatOptions) { stat(path: string, options?: webdav.StatOptions) {
return this.retry(() => this.client.stat(path, options)) return this.retry(() => this.client.stat(path, options))
} }
urlwrap(path: string) {
if (path.startsWith('/')) {
return this.prefix + path
} else {
return this.prefix + '/' + path
}
}
urlunwrap(url: string) {
if (url.startsWith(this.prefix)) {
return url.slice(this.prefix.length)
}
return url
}
private retry<T>(fn: () => Promise<T>): Promise<T> { private retry<T>(fn: () => Promise<T>): Promise<T> {
let retries = 0; let retries = 0;
function attempt(): Promise<T> { function attempt(): Promise<T> {

@ -13,6 +13,10 @@ import api from './api'
import fs from './fs' import fs from './fs'
export {
fs
}
export default new class { export default new class {
private ui?: ui private ui?: ui
constructor() { constructor() {

@ -5,8 +5,10 @@
* Distributed under terms of the MIT license. * Distributed under terms of the MIT license.
--> -->
<template> <template>
<div class="w-full h-full"> <div class="w-full h-full relative">
<!-- <div class="absolute bg-red-400 left-0 top-0 w-full h-full"></div> --> <OneIcon v-if='modelValue' @click="emits('update:modelValue', false)" class="go-edit" name='edit-square'>plus
</OneIcon>
<div class="w-full h-full veditor" :id="eid"></div> <div class="w-full h-full veditor" :id="eid"></div>
</div> </div>
</template> </template>
@ -36,20 +38,15 @@ let props = withDefaults(defineProps<{
) )
watch(computed(() => props.modelValue), (e) => { watch(computed(() => props.modelValue), (e) => {
set_mode(e) editor.switchModel(e ? 'previewOnly' : 'edit&preview')
}) })
watch(computed(() => props.content), (e) => { watch(computed(() => props.content), (e) => {
if (e) { if (e) {
console.log(e)
editor.setValue(e) editor.setValue(e)
} }
}) })
const set_mode = (preview: boolean) => {
editor.switchModel(preview ? 'previewOnly' : 'edit&preview')
}
const fileUpload = (f: File, cb: (url: string, params: any) => void) => { const fileUpload = (f: File, cb: (url: string, params: any) => void) => {
/** /**
* @param params.name 回填的alt信息 * @param params.name 回填的alt信息
@ -86,7 +83,7 @@ const init = () => {
let config = { let config = {
value: props.content, value: props.content,
id: props.eid, id: props.eid,
// isPreviewOnly: true, isPreviewOnly: props.modelValue,
callback: { callback: {
}, },
fileUpload: fileUpload, fileUpload: fileUpload,
@ -96,7 +93,6 @@ const init = () => {
// @ts-ignore // @ts-ignore
options.toolbars.customMenu.backMenu = backMenu options.toolbars.customMenu.backMenu = backMenu
editor = new Cherry(Object.assign({}, options, config)); editor = new Cherry(Object.assign({}, options, config));
set_mode(props.modelValue)
} }
@ -106,6 +102,20 @@ onMounted(() => {
</script> </script>
<style lang="scss"> <style lang="scss">
.go-edit {
position: absolute;
right: 2rem;
top: 1rem;
opacity: 0.5;
font-size: 1.5rem;
z-index: 10;
cursor: pointer;
&:hover {
opacity: 1;
}
}
.cherry-dialog { .cherry-dialog {
.cherry-dialog--body { .cherry-dialog--body {
bottom: 45px !important; bottom: 45px !important;

@ -187,15 +187,20 @@ const basicConfig: CherryOptions = {
insert: ['image', 'audio', 'video', 'link', 'hr', 'br', 'code', 'formula', 'toc', 'table', 'pdf', 'word', 'ruby'], insert: ['image', 'audio', 'video', 'link', 'hr', 'br', 'code', 'formula', 'toc', 'table', 'pdf', 'word', 'ruby'],
}, },
'graph', 'graph',
'togglePreview', ],
'export', toolbarRight: [
// @ts-ignore // @ts-ignore
'saveMenu', 'backMenu' 'backMenu', 'saveMenu',
'export',
'togglePreview',
], ],
// toolbarRight: [],
bubble: ['bold', 'italic', 'underline', 'strikethrough', 'sub', 'sup', 'quote', 'ruby', '|', 'size', 'color'], // array or false bubble: ['bold', 'italic', 'underline', 'strikethrough', 'sub', 'sup', 'quote', 'ruby', '|', 'size', 'color'], // array or false
// sidebar: false, // sidebar: false,
// float: false // float: false
toc: {
updateLocationHash: false, // 要不要更新URL的hash
defaultModel: 'pure', // pure: 精简模式/缩略模式,只有一排小点; full: 完整模式,会展示所有标题
},
customMenu: { customMenu: {
} as any, } as any,
}, },

@ -106,6 +106,7 @@ const responseFailed = (error: AxiosError) => {
} else if (response?.status == 500) { } else if (response?.status == 500) {
needRetry = false needRetry = false
} }
console.log(data, response?.status)
if (!needRetry) { if (!needRetry) {
return Promise.reject(data || response) return Promise.reject(data || response)
}; };

@ -6,9 +6,6 @@
--> -->
<template> <template>
<div> <div>
<div class="vbtn" v-if="preview_mode" @click="preview_mode = false">
<OneIcon name='edit-square'>plus</OneIcon>
</div>
<Editor style="" v-if="core.id" :eid="core.id + '.des'" v-model="preview_mode" :content="content" @save="save"> <Editor style="" v-if="core.id" :eid="core.id + '.des'" v-model="preview_mode" :content="content" @save="save">
</Editor> </Editor>
</div> </div>
@ -16,7 +13,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { models } from '#imports'; import type { models } from '#imports';
import oaer from '@veypi/oaer' import { fs } from '@veypi/oaer'
let props = withDefaults(defineProps<{ let props = withDefaults(defineProps<{
@ -27,40 +24,37 @@ let props = withDefaults(defineProps<{
let preview_mode = ref(true) let preview_mode = ref(true)
let content = ref('编辑') let content = ref('')
watch(computed(() => props.core.id), () => {
sync()
})
const sync = () => { const sync = () => {
if (props.core.des) { if (props.core.des) {
console.log(props.core.des) fs.app.getFileContents(fs.app.urlunwrap(props.core.des), { format: 'text' }).then((e) => {
oaer.fs().app.getFileContents("/net/go.mod", { format: 'text' }).then((e) => {
content.value = e as string content.value = e as string
}) })
} }
} }
watch(computed(() => props.core.id), () => {
sync()
}, { immediate: true })
const save = (des: string) => { const save = (des: string) => {
oaer.fs().app.putFileContents("/info/des.md", des).then((e) => { let furl = `/info/appdes/${props.core.id}.md`
console.log(e) fs.app.putFileContents(furl, des).then((e) => {
furl = fs.app.urlwrap(furl)
if (props.core.des !== furl) {
api.app.Patch(props.core.id, { des: furl }).then((e) => {
preview_mode.value = true
props.core.des = furl
})
} else {
preview_mode.value = true
}
}) })
// oafs.upload([a], props.core.id).then(url => {
// api.app.update(props.core.id, { des: url[0] }).then(e => {
// preview_mode.value = true
// props.core.des = url[0]
// }).catch(e => {
// // msg.Warn(": " + e)
// })
// }).catch(e => {
// // msg.Warn(": " + e)
// })
} }
onMounted(() => { onMounted(() => {
sync()
}) })
</script> </script>

Loading…
Cancel
Save