mirror of https://github.com/veypi/OneAuth.git
update
parent
5e341aeef5
commit
33b1342f64
@ -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,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,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,7 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
<script lang="js" setup>
|
||||
</script>
|
||||
<style scoped>
|
||||
</style>
|
@ -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,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>昵称:    {{ usr.Nickname }}</span>
|
||||
<span>账户:    {{ usr.Username }}</span>
|
||||
<span>邮箱:    {{ 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,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
Loading…
Reference in New Issue