update oaer

master
veypi 1 year ago
parent 664bf9c922
commit 7ef7e15e6e

16
oab/Cargo.lock generated

@ -19,6 +19,21 @@ dependencies = [
"tracing",
]
[[package]]
name = "actix-cors"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e"
dependencies = [
"actix-utils",
"actix-web",
"derive_more",
"futures-util",
"log",
"once_cell",
"smallvec",
]
[[package]]
name = "actix-files"
version = "0.6.2"
@ -1882,6 +1897,7 @@ dependencies = [
name = "oab"
version = "0.1.0"
dependencies = [
"actix-cors",
"actix-files",
"actix-multipart",
"actix-web",

@ -46,4 +46,5 @@ dav-server = {version = "0.5.7", features = ["default","actix-compat"]}
http = "0.2.9"
http-auth-basic = "0.3.3"
actix-multipart = "0.6.1"
actix-cors = "0.6.4"

@ -156,6 +156,8 @@ pub async fn register(
let mut rng = rand::thread_rng();
let idx: i64 = rng.gen_range(1..221);
u.icon = Some(format!("/media/icon/usr/{:04}.jpg", idx));
u.space = 300;
u.used = 0;
u.into()
}
};

@ -83,6 +83,7 @@ pub struct AppState {
pub log_pack_compress: Option<String>,
pub log_level: Option<String>,
pub jwt_secret: Option<String>,
pub user_init_space: i64,
#[serde(skip)]
pub _sqlx: Option<Pool<sqlx::MySql>>,
@ -134,6 +135,7 @@ impl AppState {
jwt_secret: None,
_sqlx: None,
_db: None,
user_init_space: 300,
}
}
pub fn save(&self) {}

@ -53,11 +53,15 @@ async fn web(data: AppState) -> Result<()> {
)
.into()
});
let cors = actix_cors::Cors::default()
.allow_any_origin()
.allow_any_method();
let app = App::new();
app.wrap(logger)
.wrap(middleware::Compress::default())
.app_data(web::Data::new(data.clone()))
.service(fs::Files::new("/media", data.media_path.clone()).show_files_listing())
.wrap(cors)
.service(
web::scope("api")
.wrap(

@ -16,6 +16,8 @@
"@quasar/extras": "^1.16.4",
"@veypi/msg": "^0.1.0",
"@veypi/one-icon": "2",
"@veypi/oaer": "file:~/test/oaer",
"animate.css": "^4.1.1",
"axios": "^1.2.1",
"js-base64": "^3.7.5",
"mitt": "^3.0.1",

@ -16,6 +16,9 @@ module.exports = configure(function(/* ctx */) {
return {
resolve: {
},
define: {
_global: ({})
},
eslint: {
fix: true,

@ -10,6 +10,13 @@
import { boot } from 'quasar/wrappers'
import '@veypi/msg/index.css'
import '../assets/icon.js'
import s from '@veypi/oaer'
// import { Cfg } from 'src/oaer'
// Cfg.host.value = 'http://' + window.location.host
// Cfg.token.value = localStorage.getItem('auth_token') || ''
// Cfg.uuid.value = 'FR9P5t8debxc11aFF'
// "async" is optional;
// more info on params: https://v2.quasar.dev/quasar-cli/boot-files

@ -16,7 +16,7 @@
<q-icon class="mx-2" size="1.5rem" @click="$q.dark.toggle"
:name="$q.dark.mode ? 'light_mode' : 'dark_mode'"></q-icon>
<OAer @logout="user.logout" :is-dark="$q.dark.mode"></OAer>
<OAer @logout="user.logout" :is-dark="$q.dark.mode as boolean"></OAer>
</q-toolbar>
<!-- <q-toolbar class=""> -->
<!-- <q-icon @click="toggleLeftDrawer" class="cursor-pointer" name="menu" size="sm"></q-icon> -->
@ -56,8 +56,7 @@ import { useRouter } from 'vue-router';
import Menu from 'src/components/menu.vue'
import { useAppStore } from 'src/stores/app';
import { useUserStore } from 'src/stores/user';
import { OAer, Cfg } from "src/oaer";
Cfg.token.value = util.getToken();
import { OAer } from "src/oaer";
const app = useAppStore()
const user = useUserStore()

@ -0,0 +1,48 @@
/*
* @name: app
* @author: veypi <i@veypi.com>
* @date: 2021-11-17 14:44
* @descriptionap
* @update: 2021-11-17 14:44
*/
import ajax from './axios'
import { Cfg } from './setting'
export default {
local: () => Cfg.BaseUrl() + '/app/',
self() {
return ajax.get(this.local(), { option: 'oa' })
},
getKey(uuid: string) {
return ajax.get(this.local() + uuid, { option: 'key' })
},
create(name: string, icon: string) {
return ajax.post(this.local(), { name, icon })
},
get(uuid: string) {
return ajax.get(this.local() + uuid)
},
list() {
return ajax.get(this.local())
},
update(uuid: string, props: any) {
return ajax.patch(this.local() + uuid, props)
},
user(uuid: string) {
if (uuid === '') {
uuid = '-'
}
return {
local: () => this.local() + uuid + '/user/',
list(id: string) {
return ajax.get(this.local() + id)
},
add(uid: number) {
return ajax.post(this.local() + uid)
},
update(uid: number, status: string) {
return ajax.patch(this.local() + uid, { status })
},
}
},
}

@ -0,0 +1,86 @@
/*
* axios.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-09-22 20:22
* Distributed under terms of the MIT license.
*/
import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios';
import msg from '@veypi/msg'
// Be careful when using SSR for cross-request state pollution
// due to creating a Singleton instance here;
// If any client changes this (global) instance, it might be a
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const proxy = axios.create({
withCredentials: true,
headers: {
'content-type': 'application/json;charset=UTF-8',
},
});
// 请求拦截
const beforeRequest = (config: any) => {
// 设置 token
const token = localStorage.getItem('auth_token')
// NOTE 添加自定义头部
token && (config.headers.auth_token = token)
// config.headers['auth_token'] = ''
return config
}
proxy.interceptors.request.use(beforeRequest)
// 响应拦截器
const responseSuccess = (response: AxiosResponse) => {
// eslint-disable-next-line yoda
// 这里没有必要进行判断axios 内部已经判断
// const isOk = 200 <= response.status && response.status < 300
let data = response.data
if (response.config.method === 'head') {
data = JSON.parse(JSON.stringify(response.headers))
}
return Promise.resolve(data)
}
const responseFailed = (error: AxiosError) => {
const { response } = error
if (!window.navigator.onLine) {
alert('没有网络')
return Promise.reject(new Error('请检查网络连接'))
}
console.log(response)
return Promise.reject(response?.data || response?.headers.error)
}
proxy.interceptors.response.use(responseSuccess, responseFailed)
const ajax = {
get(url: string, data = {}, header?: any) {
return proxy.get<any, any>(url, { params: data, headers: header })
},
head(url: string, data = {}, header?: any) {
return proxy.head<any, any>(url, { params: data, headers: header })
},
delete(url: string, data = {}, header?: any) {
return proxy.delete<any, any>(url, { params: data, headers: header })
},
post(url: string, data = {}, header?: any) {
return proxy.post<any, any>(url, data, { headers: header })
},
put(url: string, data = {}, header?: any) {
return proxy.put<any, any>(url, data, { headers: header })
},
patch(url: string, data = {}, header?: any) {
return proxy.patch<any, any>(url, data, { headers: header })
},
}
export default ajax

@ -0,0 +1,28 @@
/*
* @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
},
goto(url: string) {
if (!url.startsWith('/')) {
url = '/' + url
}
window.location.href = this.host.value + '/#' + url
},
userFileUrl() {
return (this.host.value || window.location.host) + '/file/'
},
}

@ -0,0 +1,44 @@
/*
* user.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-05 15:37
* Distributed under terms of the MIT license.
*/
import { Base64 } from 'js-base64'
import ajax from './axios'
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 ajax.post(this.local(), data)
},
login(username: string, password: string) {
return ajax.head(this.local() + username, {
typ: 'username',
password: Base64.encode(password),
})
},
search(q: string) {
return ajax.get(this.local(), { username: q })
},
get(id: number) {
return ajax.get(this.local() + id)
},
list() {
return ajax.get(this.local())
},
update(id: number, props: any) {
return ajax.patch(this.local() + id, props)
},
}

@ -0,0 +1,26 @@
<template>
<div class="w-full px-3">
<div class="h-16 flex justify-between items-center">
<span style="">我的应用</span>
<span @click="Cfg.goto('/')" 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="Cfg.goto(ap.redirect)" v-if="ap.id !== Cfg.uuid.value">
<img class="oa_avator" v-if="ap.icon" :src="Cfg.host.value +
ap.icon" alt="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'
withDefaults(defineProps<{
apps: modelsApp[]
}>(), {})
</script>
<style scoped></style>

@ -1,25 +1,25 @@
<template>
<div class="w-full px-3">
<div class="h-16 flex justify-between items-center">
<span style="color: #777">
<span style="">
我的云盘
</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" />
{{ 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 @click="showModal = true" type="primary">获取挂载链接</div>
</div>
<n-modal v-model:show="showModal">
<n-card style="width: 600px;" title="云盘挂载地址" :bordered="false" size="huge">
<template #header-extra>复制</template>
{{ Cfg.userFileUrl() }}
<template #footer> 挂载说明</template>
</n-card>
</n-modal>
<!-- <n-modal v-model:show="showModal"> -->
<!-- <n-card style="width: 600px;" title="云盘挂载地址" :bordered="false" size="huge"> -->
<!-- <template #header-extra>复制</template> -->
<!-- {{ Cfg.userFileUrl() }} -->
<!-- <template #footer> 挂载说明</template> -->
<!-- </n-card> -->
<!-- </n-modal> -->
<hr class="mt-10" style="border:none;border-top:1px solid #777;">
</div>
</template>
@ -34,8 +34,8 @@ let props = withDefaults(defineProps<{
usr: modelsUser
}>(), {})
let client = createClient('http://127.0.0.1:4001/file/usr/',
{ headers: { auth_token: localStorage.getItem('auth_token') as string } })
let client = createClient(Cfg.userFileUrl(),
{ headers: { auth_token: Cfg.token.value as string } })
onMounted(() => {
client.stat('').then((e) => {
console.log(e)

@ -18,8 +18,7 @@
</transition>
<div class="right-main">
<transition appear enter-active-class="animate__slideInDown">
<div class="right-main-core animate__animated animate__faster"
:style="{'background': backgound}">
<div class="right-main-core animate__animated animate__faster" :style="{ 'background': backgound }">
<slot name="main"></slot>
</div>
</transition>

@ -5,20 +5,10 @@
* @descriptionindex
*/
import { App } from 'vue'
import 'animate.css'
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?: any): void {
if (this.installed) {
return
}
this.installed = true
vue.component('OAer', OAer)
},
}

@ -0,0 +1,135 @@
<template>
<BaseFrame :class="[isDark ? 'oa_light' : 'oa_dark']" v-model="shown" :is-dark="isDark">
<template #title>
{{ self.name }}
</template>
<div class="flex justify-center items-center">
<img class="oa_avatar mx-2" :src="Cfg.host.value + usr.icon" alt="Avatar" />
</div>
<template #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="">我的账户</span>
<span @click="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">
<img class="oa_avatar mx-2" :src="Cfg.host.value + usr.icon" alt="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 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]))
console.log(data)
if (data.id) {
api.user.get(data.id).then(e => {
console.log(e)
usr.value = e
ofApps.value = []
for (let v of e.Apps) {
if (v.Status === 'ok') {
ofApps.value.push(v.App)
}
if (v.App.id === Cfg.uuid.value) {
self.value = v.App
}
}
emits('load', e)
}).catch(e => {
console.log(e)
evt.emit('logout')
})
} else {
evt.emit('logout')
}
}
evt.on('logout', () => {
emits('logout')
})
</script>
<style>
.oa_light {
color: #eee;
}
.oa_dark {
color: #333;
}
.oa_avatar {
vertical-align: middle;
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
}
</style>

@ -0,0 +1,53 @@
/*
* @name: index
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 17:36
* @descriptionindex
*/
export interface modelsApp {
created: string
updated: string
delete_flag: boolean
des: string
hide: boolean
icon: string
id: string
name: string
redirect: string
role_id: string
status: number
user_count: number
au: modelsAppUser
}
export enum AUStatus {
OK = 0,
Disabled = 1,
Applying = 2,
Deny = 3,
}
export interface modelsAppUser {
app_id: string
user_id: string
status: AUStatus
}
export interface modelsUser {
id: string
created: string
updated: string
delete_flag: boolean
username: string
nickname: string
email: string
phone: string
icon: string
status: number
used: number
space: number
}

@ -1,28 +0,0 @@
{
"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"
}

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

@ -0,0 +1,10 @@
/* eslint-disable */
/// <reference types="vite/client" />
// Mocks all files ending in `.vue` showing them as plain Vue instances
declare module '*.vue' {
import type { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}

@ -1,86 +0,0 @@
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

@ -1,31 +0,0 @@
/*
* @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)
},
}
},
}

@ -1,65 +0,0 @@
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)
}
}

@ -1,22 +0,0 @@
/*
* @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
},
userFileUrl() {
return (this.host.value || window.location.href) + '/file/usr/'
},
}

@ -1,33 +0,0 @@
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)
},
}

@ -1,36 +0,0 @@
<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>

@ -1,121 +0,0 @@
<template>
<BaseFrame v-model="shown" :is-dark="isDark">
<template #title>
{{ self.Name }}
</template>
<div class="flex justify-center items-center">
<n-avatar style="--color: none" :src="Cfg.host.value + usr.Icon"
round></n-avatar>
</div>
<template #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
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>

@ -1,126 +0,0 @@
/*
* @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 enum AppJoin {
Auto = 0,
Disabled = 1,
Applying = 2,
}
export interface App {
created: string
updated: string
delete_flag: boolean
des: string
hide: boolean
icon: string
id: string
join_method: AppJoin
name: string
redirect: string
role_id: string
status: number
user_count: number
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: App[]
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
}
export interface modelsSimpleAuth {
Level: number
RID: string
RUID: string
}
export interface modelsAuth {
App?: App
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?: App
AppUUID: string
Auths: null
CreatedAt: string
DeletedAt: null
ID: number
Name: string
Tag: string
UpdatedAt: string
UserCount: number
}
export interface modelsResource {
App?: App
AppUUID: string
CreatedAt: string
DeletedAt: null
Des: string
ID: number
Name: string
UpdatedAt: string
}

@ -1,72 +0,0 @@
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')
}

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

@ -1,15 +0,0 @@
{
"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"]
}

@ -1,7 +0,0 @@
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

@ -7,11 +7,10 @@
import { defineStore } from 'pinia';
import { useQuasar } from 'quasar'
export const useAppStore = defineStore('app', {
state: () => ({
id: '',
id: 'FR9P5t8debxc11aFF',
is_dark: false,
title: '',
}),

@ -23,10 +23,10 @@ export const useUserStore = defineStore('user', {
},
actions: {
logout() {
this.ready = false
localStorage.removeItem('auth_token')
const r = useRouter()
r.push({ name: 'login' })
// this.ready = false
// localStorage.removeItem('auth_token')
// const r = useRouter()
// r.push({ name: 'login' })
},
fetchUserData() {
let token = localStorage.getItem('auth_token')?.split('.');
@ -35,7 +35,6 @@ export const useUserStore = defineStore('user', {
}
let data = JSON.parse(Base64.decode(token[1]))
if (data.id) {
console.log(data)
this.auth = NewAuths(data.access)
api.user.get(data.id).then((e: modelsUser) => {
this.id = e.id

@ -615,6 +615,11 @@ ajv@^8.0.1:
require-from-string "^2.0.2"
uri-js "^4.2.2"
animate.css@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/animate.css/-/animate.css-4.1.1.tgz#614ec5a81131d7e4dc362a58143f7406abd68075"
integrity sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==
ansi-escapes@^4.2.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"

Loading…
Cancel
Save