master
veypi 3 years ago
parent 5e341aeef5
commit 33b1342f64

@ -0,0 +1,54 @@
package file
import (
"github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/libs/auth"
"github.com/veypi/OneAuth/libs/oerr"
"github.com/veypi/OneAuth/oalib"
"github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc"
"github.com/veypi/utils/log"
"net/http"
"strconv"
)
/**
* @name: user
* @author: veypi <i@veypi.com>
* @date: 2021-12-04 11:49
* @descriptionuser
**/
func appFileChecker(w http.ResponseWriter, r *http.Request) (prefix string, mountPoint string, ownerID string, actorID string, err error) {
m := w.(OneBD.Meta)
uuid := m.Params("uuid")
p := &oalib.PayLoad{}
h := r.Header.Get("auth_token")
if h == "" {
h = m.Query("auth_token")
}
log.Warn().Msgf("|%s|%s|", r.Header.Get("auth_token"), m.Query("auth_token"))
var ok bool
ok, err = p.ParseToken(h, cfg.CFG.APPKey)
if !ok {
err = oerr.NoAuth
return
}
l := p.GetAuth(auth.APP, uuid)
if !l.CanRead() {
err = oerr.NoAuth
}
if !l.CanDelete() && r.Method == rfc.MethodDelete {
err = oerr.NoAuth
}
if !l.CanUpdate() && (r.Method == "PUT" || r.Method == "MKCOL" || r.Method == "COPY" || r.Method == "MOVE") {
err = oerr.NoAuth
}
if err != nil {
return
}
actorID = strconv.Itoa(int(p.ID))
ownerID = uuid
mountPoint = uuid
prefix = cfg.CFG.FileUrlPrefix + "/app/" + uuid + "/"
return
}

@ -30,6 +30,7 @@
"path-posix": "^1.0.0" "path-posix": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@veypi/oaer": "file:src/oaer",
"@types/node": "^16.11.12", "@types/node": "^16.11.12",
"@vitejs/plugin-vue": "^1.9.3", "@vitejs/plugin-vue": "^1.9.3",
"less": "^4.1.2", "less": "^4.1.2",

@ -2,19 +2,39 @@ import axios from 'axios'
import {store} from '@/store' import {store} from '@/store'
function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function) { function getQueryVariable(variable: string) {
let query = window.location.search.substring(1)
let vars = query.split('&')
for (let i = 0; i < vars.length; i++) {
let pair = vars[i].split('=')
if (pair[0] == variable) {
return pair[1]
}
}
return ''
}
function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function, header?: any) {
let headers = {
auth_token: localStorage.auth_token || decodeURIComponent(getQueryVariable('token') as string),
}
if (header) {
headers = Object.assign(headers, header)
}
return axios({ return axios({
url: url, url: url,
params: query, params: query,
data: data, data: data,
method: method, method: method,
headers: { headers: headers,
auth_token: localStorage.auth_token
}
}).then((res: any) => { }).then((res: any) => {
if ('auth_token' in res.headers) { if ('auth_token' in res.headers) {
localStorage.auth_token = res.headers.auth_token localStorage.auth_token = res.headers.auth_token
} }
if ('redirect_url' in res.headers) {
window.location.href = res.headers.redirect_url
return
}
if (method === 'HEAD') { if (method === 'HEAD') {
success(res.headers) success(res.headers)
} else { } else {
@ -42,24 +62,24 @@ function baseRequests(url: string, method: any = 'GET', query: any, data: any, s
} }
const ajax = { const ajax = {
get(url: '', data = {}, success = {}, fail?: Function) { get(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'GET', data, {}, success, fail) return baseRequests(url, 'GET', data, {}, success, fail, header)
}, },
head(url: '', data = {}, success = {}, fail?: Function) { head(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'HEAD', data, {}, success, fail) return baseRequests(url, 'HEAD', data, {}, success, fail, header)
}, },
delete(url: '', data = {}, success = {}, fail?: Function) { delete(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'DELETE', data, {}, success, fail) return baseRequests(url, 'DELETE', data, {}, success, fail, header)
}, },
post(url: '', data = {}, success = {}, fail?: Function) { post(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'POST', {}, data, success, fail) return baseRequests(url, 'POST', {}, data, success, fail, header)
}, },
put(url: '', data = {}, success = {}, fail?: Function) { put(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'PUT', {}, data, success, fail) return baseRequests(url, 'PUT', {}, data, success, fail, header)
},
patch(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'PATCH', {}, data, success, fail, header)
}, },
patch(url: '', data = {}, success = {}, fail?: Function) {
return baseRequests(url, 'PATCH', {}, data, success, fail)
}
} }
export default ajax export default ajax

@ -1,4 +1,4 @@
import {store} from "@/store"; import {store} from "@/store"
export type SuccessFunction<T> = (e: any) => void; export type SuccessFunction<T> = (e: any) => void;
export type FailedFunction<T> = (e: any) => void; export type FailedFunction<T> = (e: any) => void;
@ -12,11 +12,13 @@ export class Interface {
private readonly method: Function private readonly method: Function
private readonly api: string private readonly api: string
private readonly data: any private readonly data: any
private readonly header: any
constructor(method: Function, api: string, data?: any) { constructor(method: Function, api: string, data?: any, headers?: any) {
this.method = method this.method = method
this.api = api this.api = api
this.data = data this.data = data
this.header = headers
} }
Start(success?: SuccessFunction<any>, fail?: FailedFunction<any>) { Start(success?: SuccessFunction<any>, fail?: FailedFunction<any>) {
@ -56,6 +58,6 @@ export class Interface {
newFail(data) newFail(data)
} }
} }
this.method(this.api, this.data, newSuccess, newFail) this.method(this.api, this.data, newSuccess, newFail, this.header)
} }
} }

@ -2,7 +2,7 @@
<div class="core rounded-2xl p-3"> <div class="core rounded-2xl p-3">
<div class="grid gap-4 grid-cols-5"> <div class="grid gap-4 grid-cols-5">
<div class="col-span-2"> <div class="col-span-2">
<n-avatar style="--color: none;" @click="Go" round :size="80" :src="util.addTokenOf(core.Icon)"> <n-avatar style="--color: none;" @click="Go" round :size="80" :src="core.Icon">
</n-avatar> </n-avatar>
</div> </div>
<div class="col-span-3 grid grid-cols-1 items-center text-left"> <div class="col-span-3 grid grid-cols-1 items-center text-left">

@ -1,7 +1,7 @@
<template> <template>
<base-frame style="line-height:40px" v-model="shown" :isDark="IsDark"> <base-frame style="line-height:40px" v-model="shown" :isDark="IsDark">
<div class="flex"> <div class="flex">
<n-avatar style="--color: none" :src="util.addTokenOf($store.state.user.local.Icon)" round></n-avatar> <n-avatar style="--color: none" :src="$store.state.user.local.Icon" round></n-avatar>
</div> </div>
<template v-slot:main> <template v-slot:main>
<div style="height: 100%"> <div style="height: 100%">
@ -14,7 +14,7 @@
</div> </div>
<div class="grid grid-cols-4 gap-4 h-20"> <div class="grid grid-cols-4 gap-4 h-20">
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<n-avatar size="50" :src="util.addTokenOf($store.state.user.local.Icon)" round></n-avatar> <n-avatar size="50" :src="$store.state.user.local.Icon" round></n-avatar>
</div> </div>
<div class="col-span-2 text-xs grid grid-cols-1 items-center" style=""> <div class="col-span-2 text-xs grid grid-cols-1 items-center" style="">
<span>昵称: &ensp;&ensp; {{ $store.state.user.local.Nickname }}</span> <span>昵称: &ensp;&ensp; {{ $store.state.user.local.Nickname }}</span>

@ -40,7 +40,7 @@
</div> </div>
<div v-if="$store.state.user.ready" <div v-if="$store.state.user.ready"
class="h-full flex justify-center items-center mr-5"> class="h-full flex justify-center items-center mr-5">
<avatar></avatar> <OAer @logout="$store.commit('user/logout')" :is-dark="IsDark"></OAer>
</div> </div>
</div> </div>
</n-layout-header> </n-layout-header>
@ -77,6 +77,8 @@ import {onMounted, ref} from 'vue'
import {useStore} from '@/store' import {useStore} from '@/store'
import {useRouter} from 'vue-router' import {useRouter} from 'vue-router'
import util from '@/libs/util' import util from '@/libs/util'
import {OAer, Cfg} from '@/oaer'
Cfg.token.value = util.getToken()
let store = useStore() let store = useStore()
let router = useRouter() let router = useRouter()

@ -28,7 +28,7 @@ function click() {
file.value.dispatchEvent(new MouseEvent('click')) file.value.dispatchEvent(new MouseEvent('click'))
} }
let prefix = '/file/app/' + store.state.oauuid + '/' let prefix = '/file/public/app/' + store.state.oauuid + '/'
let client = createClient(prefix, let client = createClient(prefix,
{headers: {auth_token: localStorage.getItem('auth_token')}}) {headers: {auth_token: localStorage.getItem('auth_token')}})

@ -0,0 +1,28 @@
{
"name": "@veypi/oaer",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"mitt": "^3.0.0",
"vue": "^3.2.20"
},
"devDependencies": {
"@veypi/one-icon": "2.0.6",
"@vitejs/plugin-vue": "^1.9.3",
"autoprefixer": "^9.8.8",
"axios": "^0.24.0",
"js-base64": "^3.7.2",
"naive-ui": "^2.19.11",
"postcss": "^7.0.39",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@2.1.0",
"typescript": "^4.4.3",
"vite": "^2.6.4",
"vue-tsc": "^0.3.0"
},
"author": "veypi",
"license": "MIT"
}

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

@ -0,0 +1,86 @@
import axios from 'axios'
import evt from '../evt'
import {Cfg} from './setting'
function getQueryVariable(variable: string) {
let query = window.location.search.substring(1)
let vars = query.split('&')
for (let i = 0; i < vars.length; i++) {
let pair = vars[i].split('=')
if (pair[0] == variable) {
return pair[1]
}
}
return ''
}
function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function, header?: any) {
let headers = {
auth_token: Cfg.token.value || decodeURIComponent(getQueryVariable('token') as string),
uuid: Cfg.uuid.value,
}
if (header) {
headers = Object.assign(headers, header)
}
return axios({
url: url,
params: query,
data: data,
method: method,
headers: headers,
}).then((res: any) => {
if ('auth_token' in res.headers) {
Cfg.token.value = res.headers.auth_token
}
if ('redirect_url' in res.headers) {
window.location.href = res.headers.redirect_url
return
}
if (method === 'HEAD') {
success(res.headers)
} else {
success(res.data)
}
})
.catch((e: any) => {
if (e.response && e.response.status === 401) {
evt.emit('logout')
return
}
console.log(e)
if (e.response && e.response.status === 500) {
return
}
if (typeof fail === 'function') {
fail(e.response)
} else if (e.response && e.response.status === 400) {
console.log(400)
} else {
console.log(e.request)
}
})
}
const ajax = {
get(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'GET', data, {}, success, fail, header)
},
head(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'HEAD', data, {}, success, fail, header)
},
delete(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'DELETE', data, {}, success, fail, header)
},
post(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'POST', {}, data, success, fail, header)
},
put(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'PUT', {}, data, success, fail, header)
},
patch(url: '', data = {}, success = {}, fail?: Function, header?: any) {
return baseRequests(url, 'PATCH', {}, data, success, fail, header)
},
}
export default ajax

@ -0,0 +1,31 @@
/*
* @name: app
* @author: veypi <i@veypi.com>
* @date: 2021-11-17 14:44
* @descriptionap
* @update: 2021-11-17 14:44
*/
import {Interface} from './interface'
import ajax from './ajax'
import {Cfg} from './setting'
export default {
local: () => Cfg.BaseUrl() + 'app/',
get(uuid: string) {
return new Interface(ajax.get, this.local() + uuid)
},
list() {
return new Interface(ajax.get, this.local())
},
user(uuid: string) {
if (uuid === '') {
uuid = '-'
}
return {
local: () => this.local() + uuid + '/user/',
list(uid: number) {
return new Interface(ajax.get, this.local() + uid)
},
}
},
}

@ -0,0 +1,18 @@
/*
* Copyright (C) 2019 light <veypi@light-laptop>
*
* Distributed under terms of the MIT license.
*/
import user from './user'
import app from './app'
import {Cfg} from './setting'
const api = {
user: user,
app: app,
}
export {api, Cfg}
export default api

@ -0,0 +1,65 @@
import evt from '../evt'
export type SuccessFunction<T> = (e: any) => void;
export type FailedFunction<T> = (e: any) => void;
const Code = {
42011: '无操作权限',
22031: '资源不存在 或 您无权操作该资源',
}
export class Interface {
private readonly method: Function
private readonly api: string
private readonly data: any
private readonly header: any
constructor(method: Function, api: string, data?: any, headers?: any) {
this.method = method
this.api = api
this.data = data
this.header = headers
}
Start(success?: SuccessFunction<any>, fail?: FailedFunction<any>) {
const newFail = function (data: any) {
if (data) {
if (data.code === 40001) {
// no login
evt.emit('logout')
return
// @ts-ignore
} else if (data.code === 42011 && window.$msg) {
// @ts-ignore
window.$msg.warning('无权限')
}
}
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
if (data && data.code && Code[data.code]) {
}
if (fail) {
fail(data.err)
// @ts-ignore
} else if (window.$msg) {
// @ts-ignore
window.$msg.warning(data.err)
}
}
const newSuccess = function (data: any) {
if (Number(data.status) === 1) {
if (success) {
success(data.content)
// @ts-ignore
} else if (window.$msg) {
// @ts-ignore
window.$msg.warning('ok')
}
} else {
newFail(data)
}
}
this.method(this.api, this.data, newSuccess, newFail, this.header)
}
}

@ -0,0 +1,19 @@
/*
* @name: setting
* @author: veypi <i@veypi.com>
* @date: 2021-11-17 15:45
* @descriptionsetting
* @update: 2021-11-17 15:45
*/
import {ref} from 'vue'
export let Cfg= {
token: ref(''),
uuid: ref(''),
host: ref(''),
prefix: '/api/',
BaseUrl() {
return this.host.value + this.prefix
},
}

@ -0,0 +1,33 @@
import {Base64} from 'js-base64'
import {Interface} from './interface'
import ajax from './ajax'
import {Cfg} from './setting'
export default {
local: () => Cfg.BaseUrl() + 'user/',
register(username: string, password: string, prop?: any) {
const data = Object.assign({
username: username,
password: Base64.encode(password),
}, prop)
return new Interface(ajax.post, this.local(), data)
},
login(username: string, password: string) {
return new Interface(ajax.head, this.local() + username, {
UidType: 'username',
password: Base64.encode(password),
})
},
search(q: string) {
return new Interface(ajax.get, this.local(), {username: q})
},
get(id: number) {
return new Interface(ajax.get, this.local() + id)
},
list() {
return new Interface(ajax.get, this.local())
},
update(id: number, props: any) {
return new Interface(ajax.patch, this.local() + id, props)
},
}

@ -0,0 +1 @@
!function(t){var e,n,o,i,c,d='<svg><symbol id="icon-close" viewBox="0 0 1024 1024"><path d="M176.661601 817.172881C168.472798 825.644055 168.701706 839.149636 177.172881 847.338438 185.644056 855.527241 199.149636 855.298332 207.338438 846.827157L826.005105 206.827157C834.193907 198.355983 833.964998 184.850403 825.493824 176.661601 817.02265 168.472798 803.517069 168.701706 795.328267 177.172881L176.661601 817.172881Z" ></path><path d="M795.328267 846.827157C803.517069 855.298332 817.02265 855.527241 825.493824 847.338438 833.964998 839.149636 834.193907 825.644055 826.005105 817.172881L207.338438 177.172881C199.149636 168.701706 185.644056 168.472798 177.172881 176.661601 168.701706 184.850403 168.472798 198.355983 176.661601 206.827157L795.328267 846.827157Z" ></path></symbol><symbol id="icon-logout" viewBox="0 0 1024 1024"><path d="M856.8 389.8c-18.9-44.7-45.9-84.8-80.4-119.2-18.9-18.9-39.5-35.6-61.7-49.9-10-6.5-23.3 0.6-23.3 12.6 0 5.1 2.6 9.9 6.9 12.6 95 61.5 158 168.5 158 289.8 0 190.3-154.8 345-345 345s-345-154.8-345-345c0-122.4 64.1-230.2 160.5-291.4 4.4-2.8 7-7.6 7-12.7 0-11.8-13.1-19.1-23.1-12.8-23.2 14.7-44.8 32-64.6 51.8-34.4 34.4-61.5 74.5-80.4 119.2-19.6 46.2-29.5 95.3-29.5 146s9.9 99.7 29.5 146c18.9 44.7 45.9 84.8 80.4 119.2 34.4 34.4 74.5 61.5 119.2 80.4 46.2 19.6 95.3 29.5 146 29.5 50.6 0 99.7-9.9 146-29.5 44.7-18.9 84.8-45.9 119.2-80.4s61.5-74.5 80.4-119.2c19.6-46.2 29.5-95.3 29.5-146s-10-99.8-29.6-146z" fill="" ></path><path d="M512 431.1c-8.8 0-16-7.2-16-16V98.2c0-8.8 7.2-16 16-16s16 7.2 16 16V415c0 8.9-7.2 16.1-16 16.1z" fill="" ></path></symbol></svg>',s=(s=document.getElementsByTagName("script"))[s.length-1].getAttribute("data-injectcss"),l=function(t,e){e.parentNode.insertBefore(t,e)};if(s&&!t.__iconfont__svg__cssinject__){t.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(t){console&&console.log(t)}}function a(){c||(c=!0,o())}function r(){try{i.documentElement.doScroll("left")}catch(t){return void setTimeout(r,50)}a()}e=function(){var t,e;(e=document.createElement("div")).innerHTML=d,d=null,(t=e.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",e=t,(t=document.body).firstChild?l(e,t.firstChild):t.appendChild(e))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(e,0):(n=function(){document.removeEventListener("DOMContentLoaded",n,!1),e()},document.addEventListener("DOMContentLoaded",n,!1)):document.attachEvent&&(o=e,i=t.document,c=!1,r(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,a())})}(window);

@ -0,0 +1,36 @@
<template>
<div class="w-full px-3">
<div class="h-16 flex justify-between items-center">
<span style="color: #777">我的应用</span>
<span @click="Cfg.host.value?goto(Cfg.host.value):redirect('/')" class="cursor-pointer"
style="color:#f36828">应用中心</span>
</div>
<div class="grid grid-cols-5">
<template v-for="(ap,ai) of apps"
:key="ai">
<div class="mx-2" @click="redirect(ap.Host)" v-if="ap.UUID !== Cfg.uuid.value">
<n-avatar v-if="ap.Icon" size="2" :src="Cfg.host.value+ ap.Icon" round></n-avatar>
</div>
</template>
</div>
<hr class="mt-10" style="border:none;border-top:1px solid #777;">
</div>
</template>
<script lang="ts" setup>
import {modelsApp} from '../models'
import {Cfg} from '../api'
let props = withDefaults(defineProps<{
apps: modelsApp[]
}>(), {})
function goto(url: string) {
window.open(url, '_blank')
}
function redirect(url: string) {
window.location.href = url
}
</script>
<style scoped>
</style>

@ -0,0 +1,42 @@
<template>
<div class="w-full px-3">
<div class="h-16 flex justify-between items-center">
<span style="color: #777">
我的云盘
</span>
<span class="cursor-pointer"
style="color:#f36828">文件中心</span>
</div>
<div class="">
{{ usr.Used }} KB / {{ usr.Space }} GB
<n-progress
type="line"
color="#0f0"
rail-color="#fff"
:percentage="1"
indicator-text-color="#f00"
/>
</div>
<div class="flex justify-center">
<n-button @click="showModal=true" type="primary">获取挂载链接</n-button>
</div>
<n-modal v-model:show="showModal">
<n-card style="width: 600px;" title="模态框" :bordered="false" size="huge">
<template #header-extra> </template>
内容
<template #footer> 尾部 </template>
</n-card>
</n-modal>
<hr class="mt-10" style="border:none;border-top:1px solid #777;">
</div>
</template>
<script lang="ts" setup>
import {modelsUser} from '../models'
import {ref} from 'vue'
let showModal = ref(false)
let props = withDefaults(defineProps<{
usr: modelsUser
}>(), {})
</script>
<style scoped>
</style>

@ -0,0 +1,7 @@
<template>
<div></div>
</template>
<script lang="js" setup>
</script>
<style scoped>
</style>

@ -0,0 +1,12 @@
/*
* @name: index
* @author: veypi <i@veypi.com>
* @date: 2021-12-18 14:24
* @descriptionindex
*/
import mitt from 'mitt'
const emitter = mitt()
export default emitter

@ -0,0 +1,91 @@
<template>
<div>
<div @click="setValue(true)">
<slot>
</slot>
</div>
<div @click.self="setValue(false)" class="core" style="height: 100vh;width: 100vw;" v-if="props.modelValue">
<div style="height: 100%; width: 300px" class="core-right">
<transition appear enter-active-class="animate__slideInRight">
<div class="flex right-title animate__animated animate__faster px-3">
<div class="flex-grow text-left" style="font-size: 1.5rem">
<slot name="title"></slot>
</div>
<div class="flex-grow-0 flex items-center h-full">
<OneIcon @click="setValue(false)" color="#fff" style="font-size: 24px">close</OneIcon>
</div>
</div>
</transition>
<div class="right-main">
<transition appear enter-active-class="animate__slideInDown">
<div class="right-main-core animate__animated animate__faster"
:style="{'background': backgound}">
<slot name="main"></slot>
</div>
</transition>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {OneIcon} from '@veypi/one-icon'
import {computed, watch} from 'vue'
let emits = defineEmits<{
(e: 'update:modelValue', v: boolean): void
}>()
let props = withDefaults(defineProps<{
isDark?: boolean,
modelValue?: boolean
}>(), {})
let backgound = computed(() => {
return props.isDark ? '#222' : '#eee'
})
watch(props, () => {
})
function setValue(b: boolean) {
emits('update:modelValue', b)
}
</script>
<style scoped>
.core {
position: fixed;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 100;
}
.core-right {
position: absolute;
right: 0;
top: 0;
}
.right-main {
width: 100%;
height: calc(100% - 50px);
overflow: hidden;
}
.right-main-core {
height: 100%;
width: 100%;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
--animate-duration: 400ms;
}
.right-title {
width: 100%;
height: 50px;
line-height: 50px;
background: linear-gradient(90deg, #f74d22, #fa9243);
}
</style>

@ -0,0 +1,24 @@
/*
* @name: index
* @author: veypi <i@veypi.com>
* @date: 2021-12-18 13:16
* @descriptionindex
*/
import {App} from 'vue'
import OAer from './main.vue'
import './assets/icon.js'
import {Cfg, api} from './api'
export {OAer, Cfg, api}
export default {
installed: false,
install(vue: App, options?: {}): void {
if (this.installed) {
return
}
this.installed = true
vue.component('OAer', OAer)
},
}

@ -0,0 +1,125 @@
<template>
<BaseFrame v-model="shown" :is-dark="isDark">
<template #title>
{{self.Name}}
</template>
<slot>
<div class="flex justify-center items-center">
<n-avatar style="--color: none" :src="Cfg.host.value + usr.Icon"
round></n-avatar>
</div>
</slot>
<template v-slot:main>
<div style="height: 100%">
<div style="height: calc(100% - 50px)">
<div class="w-full px-3">
<div class="h-16 flex justify-between items-center">
<span style="color: #777">我的账户</span>
<span @click="$router.push({name: 'user_setting'});shown=false" class="cursor-pointer"
style="color:#f36828">账户中心</span>
</div>
<div class="grid grid-cols-4 gap-4 h-20">
<div class="flex items-center justify-center">
<n-avatar size="50" :src="Cfg.host.value+ usr.Icon" round></n-avatar>
</div>
<div class="col-span-2 text-xs grid grid-cols-1 items-center text-left" style="">
<span>昵称: &ensp;&ensp; {{ usr.Nickname }}</span>
<span>账户: &ensp;&ensp; {{ usr.Username }}</span>
<span>邮箱: &ensp;&ensp; {{ usr.Email }}</span>
</div>
<div class="">123</div>
</div>
<hr class="mt-10" style="border:none;border-top:1px solid #777;">
</div>
<File :usr="usr"></File>
<Apps :apps="ofApps"></Apps>
</div>
<hr style="border:none;border-top:2px solid #777;">
<div style="height: 48px">
<div @click="evt.emit('logout')"
class="w-full h-full flex justify-center items-center cursor-pointer transition duration-500 ease-in-out transform hover:scale-125">
<OneIcon :color="isDark?'#eee': '#333'" class="inline-block" style="font-size: 24px;">
logout
</OneIcon>
<div>
退出登录
</div>
</div>
</div>
</div>
</template>
</BaseFrame>
</template>
<script lang="ts" setup>
import BaseFrame from './frame.vue'
import Apps from './components/app.vue'
import File from './components/file.vue'
import {OneIcon} from '@veypi/one-icon'
import {computed, onMounted, ref, watch} from 'vue'
import {decode} from 'js-base64'
import {api, Cfg} from './api'
import evt from './evt'
import {modelsApp, modelsUser} from './models'
let shown = ref(false)
let emits = defineEmits<{
(e: 'logout'): void
(e: 'load', u: modelsUser): void
}>()
let props = withDefaults(defineProps<{
isDark?: boolean
}>(), {
isDark: false,
})
onMounted(() => {
fetchUserData()
})
let usr = ref<modelsUser>({} as modelsUser)
let ofApps = ref<modelsApp[]>([])
let self = ref<modelsApp>({} as modelsApp)
let token = computed(() => Cfg.token.value)
watch(token, () => {
fetchUserData()
})
function fetchUserData() {
let token = Cfg.token.value?.split('.')
if (!token || token.length !== 3) {
return false
}
let data = JSON.parse(decode(token[1]))
if (data.ID > 0) {
api.user.get(data.ID).Start(e => {
usr.value = e
console.log(e)
ofApps.value = []
for (let v of e.Apps) {
if (v.Status === 'ok') {
ofApps.value.push(v.App)
}
if (v.App.UUID === Cfg.uuid.value) {
self.value = v.App
}
}
emits('load', e)
}, e => {
console.log(e)
evt.emit('logout')
})
} else {
evt.emit('logout')
}
}
evt.on('logout', () => {
emits('logout')
})
</script>
<style scoped>
</style>

@ -0,0 +1,111 @@
/*
* @name: index
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 17:36
* @descriptionindex
*/
export interface modelsBread {
Index: number
Name: string
Type?: string
RName: string
RParams?: any
RQuery?: any
}
export interface modelsApp {
CreatedAt: string
UpdatedAt: string
DeletedAt: null
Creator: number
Des: string
EnableEmail: boolean
EnablePhone: boolean
EnableRegister: true
EnableUser: boolean
EnableUserKey: boolean
EnableWx: boolean
Hide: boolean
Host: string
Icon: string
InitRole: null
InitRoleID: number
Name: string
UUID: string
UserCount: number
UserKeyUrl: string
UserRefreshUrl: string
UserStatus: string
Users: null
}
export interface modelsUser {
// Index 前端缓存
Index?: number
Apps: modelsApp[]
Auths: null
CreatedAt: string
DeletedAt: null
ID: number
Icon: string
Position: string
Roles: null
Status: string
UpdatedAt: string
Username: string
Email: string
Nickname: string
Phone: string
Used: number
Space: number
}
export interface modelsSimpleAuth {
Level: number
RID: string
RUID: string
}
export interface modelsAuth {
App?: modelsApp
AppUUID: string
CreatedAt: string
DeletedAt: null
ID: number
Level: number
RID: string
RUID: string
Resource?: modelsResource
ResourceID: number
Role?: modelsRole
RoleID: number
UpdatedAt: string
User?: modelsUser
UserID?: number
}
export interface modelsRole {
App?: modelsApp
AppUUID: string
Auths: null
CreatedAt: string
DeletedAt: null
ID: number
Name: string
Tag: string
UpdatedAt: string
UserCount: number
}
export interface modelsResource {
App?: modelsApp
AppUUID: string
CreatedAt: string
DeletedAt: null
Des: string
ID: number
Name: string
UpdatedAt: string
}

@ -0,0 +1,72 @@
import {darkTheme} from 'naive-ui/lib/themes'
import {BuiltInGlobalTheme} from 'naive-ui/lib/themes/interface'
import {lightTheme} from 'naive-ui/lib/themes/light'
import {ref} from 'vue'
import {useOsTheme, GlobalThemeOverrides} from 'naive-ui'
interface builtIn extends BuiltInGlobalTheme {
overrides: GlobalThemeOverrides
me: {
lightBox: string,
lightBoxShadow: string
}
}
let light = lightTheme as builtIn
let dark = darkTheme as builtIn
let intputNone = {
color: 'url(0) no-repeat',
colorFocus: 'url(0) no-repeat',
colorFocusWarning: 'url(0) no-repeat',
colorFocusError: 'url(0) no-repeat',
}
light.overrides = {
Input: Object.assign({}, intputNone),
}
dark.overrides = {
Input: Object.assign({
border: '1px solid #aaa',
}, intputNone),
}
light.common.cardColor = '#f4f4f4'
light.common.bodyColor = '#eee'
dark.common.bodyColor = '#2e2e2e'
light.me = {
lightBox: '#f4f4f4',
lightBoxShadow: '18px 18px 36px #c6c6c6, -18px -18px 36px #fff',
}
dark.me = {
lightBox: '#2e2e2e',
lightBoxShadow: '21px 21px 42px #272727, -21px -21px 42px #353535',
}
export const OsThemeRef = useOsTheme()
let theme = 'light'
export let Theme = ref(light)
export let IsDark = ref(false)
function change(t: string) {
if (t === 'dark') {
theme = 'dark'
Theme.value = dark
} else {
theme = 'light'
Theme.value = light
}
IsDark.value = theme === 'dark'
}
export function ChangeTheme() {
if (IsDark.value) {
change('light')
} else {
change('dark')
}
}
if (OsThemeRef.value === 'dark') {
change('dark')
}

@ -0,0 +1,11 @@
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}

@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "esnext",
"useDefineForClassFields": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})

File diff suppressed because it is too large Load Diff

@ -67,6 +67,7 @@ export function ChangeTheme() {
} }
} }
console.log(OsThemeRef.value)
if (OsThemeRef.value === 'dark') { if (OsThemeRef.value === 'dark') {
change('dark') change('dark')
} }

@ -1,7 +1,7 @@
<template> <template>
<siderframe> <siderframe>
<template v-slot:avatar> <template v-slot:avatar>
<n-avatar style="--color:none" @click="util.goto(app.Host)" :src="util.addTokenOf(app.Icon)" round size="large"></n-avatar> <n-avatar style="--color:none" @click="util.goto(app.Host)" :src="app.Icon" round size="large"></n-avatar>
</template> </template>
<template #title>{{ app.Name }}</template> <template #title>{{ app.Name }}</template>
<template #subtitle>{{ app.Des }}</template> <template #subtitle>{{ app.Des }}</template>

@ -26,7 +26,7 @@
:url="uuid + '.ico'" :url="uuid + '.ico'"
@success="handleFinish" @success="handleFinish"
> >
<n-avatar size="large" round :src="util.addTokenOf(data.Icon)"> <n-avatar size="large" round :src="data.Icon">
</n-avatar> </n-avatar>
</uploader> </uploader>
</div> </div>

@ -31,10 +31,10 @@
</n-form-item> </n-form-item>
<n-form-item required label="icon" path="icon"> <n-form-item required label="icon" path="icon">
<uploader <uploader
:url="new Date().toLocaleDateString()" url="test.ico"
@success="(e) => {temp_app.icon = e}" @success="(e) => {temp_app.icon = e}"
> >
<n-avatar size="large" round :src="util.addTokenOf(temp_app.icon)"> <n-avatar size="large" round :src="temp_app.icon">
</n-avatar> </n-avatar>
</uploader> </uploader>
</n-form-item> </n-form-item>

@ -23,7 +23,7 @@
:url="user.ID+'.ico'" :url="user.ID+'.ico'"
@success="handleFinish" @success="handleFinish"
> >
<n-avatar size="large" round :src="util.addTokenOf(user.Icon)"> <n-avatar size="large" round :src="user.Icon">
</n-avatar> </n-avatar>
</uploader> </uploader>
</n-form-item> </n-form-item>
@ -68,7 +68,7 @@ import api from '@/api'
import {useMessage} from 'naive-ui' import {useMessage} from 'naive-ui'
import {modelsUser} from '@/models' import {modelsUser} from '@/models'
import util from '@/libs/util' import util from '@/libs/util'
import uploader from '@/components/uploader' import Uploader from '@/components/uploader'
let msg = useMessage() let msg = useMessage()
let store = useStore() let store = useStore()

@ -166,6 +166,12 @@
dependencies: dependencies:
"@types/yargs-parser" "*" "@types/yargs-parser" "*"
"@veypi/oaer@file:src/oaer":
version "1.0.0"
dependencies:
mitt "^3.0.0"
vue "^3.2.20"
"@veypi/one-icon@2.0.6": "@veypi/one-icon@2.0.6":
version "2.0.6" version "2.0.6"
resolved "https://registry.yarnpkg.com/@veypi/one-icon/-/one-icon-2.0.6.tgz#158c692971848524cd59db1a61d88805d2e45646" resolved "https://registry.yarnpkg.com/@veypi/one-icon/-/one-icon-2.0.6.tgz#158c692971848524cd59db1a61d88805d2e45646"
@ -1289,6 +1295,11 @@ minimist@^1.1.1:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mitt@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd"
integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
modern-normalize@^1.0.0: modern-normalize@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7"

Loading…
Cancel
Save