mirror of
https://github.com/jekip/naive-ui-admin.git
synced 2026-03-01 00:23:11 +08:00
更新0.1.1版本
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<n-dialog-provider>
|
||||
<DialogContent/>
|
||||
<n-notification-provider>
|
||||
<n-message-provider>
|
||||
<MessageContent/>
|
||||
<slot slot="default"></slot>
|
||||
</n-message-provider>
|
||||
</n-notification-provider>
|
||||
</n-dialog-provider>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, onMounted, onUnmounted } from 'vue'
|
||||
import { MessageContent } from '@/components/MessageContent'
|
||||
import { DialogContent } from '@/components/DialogContent'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Application',
|
||||
components: { MessageContent, DialogContent },
|
||||
setup() {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import AppProvider from './Application.vue'
|
||||
|
||||
export { AppProvider }
|
||||
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<span :style="{ color }">
|
||||
{{ value }}
|
||||
</span>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed, watchEffect, unref, onMounted, watch } from 'vue';
|
||||
import { useTransition, TransitionPresets } from '@vueuse/core';
|
||||
import { isNumber } from '@/utils/is';
|
||||
|
||||
const props = {
|
||||
startVal: { type: Number, default: 0 },
|
||||
endVal: { type: Number, default: 2021 },
|
||||
duration: { type: Number, default: 1500 },
|
||||
autoplay: { type: Boolean, default: true },
|
||||
decimals: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
validator(value: number) {
|
||||
return value >= 0;
|
||||
},
|
||||
},
|
||||
prefix: { type: String, default: '' },
|
||||
suffix: { type: String, default: '' },
|
||||
separator: { type: String, default: ',' },
|
||||
decimal: { type: String, default: '.' },
|
||||
/**
|
||||
* font color
|
||||
*/
|
||||
color: { type: String },
|
||||
/**
|
||||
* Turn on digital animation
|
||||
*/
|
||||
useEasing: { type: Boolean, default: true },
|
||||
/**
|
||||
* Digital animation
|
||||
*/
|
||||
transition: { type: String, default: 'linear' },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CountTo',
|
||||
props,
|
||||
emits: ['onStarted', 'onFinished'],
|
||||
setup(props, { emit }) {
|
||||
const source = ref(props.startVal);
|
||||
const disabled = ref(false);
|
||||
let outputValue = useTransition(source);
|
||||
|
||||
const value = computed(() => formatNumber(unref(outputValue)));
|
||||
|
||||
watchEffect(() => {
|
||||
source.value = props.startVal;
|
||||
});
|
||||
|
||||
watch([() => props.startVal, () => props.endVal], () => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
props.autoplay && start();
|
||||
});
|
||||
|
||||
function start() {
|
||||
run();
|
||||
source.value = props.endVal;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
source.value = props.startVal;
|
||||
run();
|
||||
}
|
||||
|
||||
function run() {
|
||||
outputValue = useTransition(source, {
|
||||
disabled,
|
||||
duration: props.duration,
|
||||
onFinished: () => emit('onFinished'),
|
||||
onStarted: () => emit('onStarted'),
|
||||
...(props.useEasing ? { transition: TransitionPresets[props.transition] } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
function formatNumber(num: number | string) {
|
||||
if (!num) {
|
||||
return '';
|
||||
}
|
||||
const { decimals, decimal, separator, suffix, prefix } = props;
|
||||
num = Number(num).toFixed(decimals);
|
||||
num += '';
|
||||
|
||||
const x = num.split('.');
|
||||
let x1 = x[0];
|
||||
const x2 = x.length > 1 ? decimal + x[1] : '';
|
||||
|
||||
const rgx = /(\d+)(\d{3})/;
|
||||
if (separator && !isNumber(separator)) {
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, '$1' + separator + '$2');
|
||||
}
|
||||
}
|
||||
return prefix + x1 + x2 + suffix;
|
||||
}
|
||||
|
||||
return { value, start, reset };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,4 @@
|
||||
import { withInstall } from '@/utils';
|
||||
import countTo from './CountTo.vue';
|
||||
|
||||
export const CountTo = withInstall(countTo);
|
||||
@@ -0,0 +1,3 @@
|
||||
import DialogContent from './index.vue'
|
||||
|
||||
export { DialogContent }
|
||||
@@ -0,0 +1,12 @@
|
||||
<template></template>
|
||||
<script lang="ts">
|
||||
import { useDialog } from 'naive-ui'
|
||||
|
||||
export default {
|
||||
name: 'DialogContent',
|
||||
setup() {
|
||||
//挂载在 window 方便与在js中使用
|
||||
window.$dialog = useDialog()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import MessageContent from './index.vue'
|
||||
|
||||
export { MessageContent }
|
||||
@@ -0,0 +1,12 @@
|
||||
<template></template>
|
||||
<script lang="ts">
|
||||
import { useMessage } from 'naive-ui'
|
||||
|
||||
export default {
|
||||
name: 'MessageContent',
|
||||
setup() {
|
||||
//挂载在 window 方便与在js中使用
|
||||
window.$message = useMessage()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,3 @@
|
||||
import LockScreen from './lockscreen.vue'
|
||||
|
||||
export { LockScreen }
|
||||
@@ -0,0 +1,304 @@
|
||||
<template>
|
||||
<div
|
||||
:class="{ onLockLogin: showLogin }"
|
||||
class="lockscreen"
|
||||
@keyup="onLockLogin(true)"
|
||||
@mousedown.stop
|
||||
@contextmenu.prevent
|
||||
>
|
||||
<template v-if="!showLogin">
|
||||
|
||||
<div class="lock-box">
|
||||
<div class="lock">
|
||||
<span class="lock-icon" title="解锁屏幕" @click="onLockLogin(true)">
|
||||
<n-icon>
|
||||
<lock-outlined/>
|
||||
</n-icon>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!--充电-->
|
||||
<recharge
|
||||
:battery="battery"
|
||||
:battery-status="batteryStatus"
|
||||
:calc-discharging-time="calcDischargingTime"
|
||||
></recharge>
|
||||
|
||||
<div class="local-time">
|
||||
<div class="time">{{ hour }}:{{ minute }}</div>
|
||||
<div class="date">{{ month }}月{{ day }}号,星期{{ week }}</div>
|
||||
</div>
|
||||
<div class="computer-status">
|
||||
<span :class="{ offline: !online }" class="network">
|
||||
<wifi-outlined class="network"/>
|
||||
</span>
|
||||
<api-outlined/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!--登录-->
|
||||
<template v-if="showLogin">
|
||||
<div class="login-box">
|
||||
<n-avatar :size="128">
|
||||
<n-icon>
|
||||
<user-outlined/>
|
||||
</n-icon>
|
||||
</n-avatar>
|
||||
<div class="username">{{ loginParams.username }}</div>
|
||||
<n-input
|
||||
type="password"
|
||||
autofocus
|
||||
v-model:value="loginParams.password"
|
||||
placeholder="请输入登录密码">
|
||||
<template #suffix>
|
||||
<n-icon @click="onLogin" style="cursor: pointer;">
|
||||
<LoadingOutlined v-if="loginLoading"/>
|
||||
<arrow-right-outlined v-else/>
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-input>
|
||||
|
||||
<div class="w-full flex" v-if="isLoginError">
|
||||
<span class="text-red-500">{{ errorMsg }}</span>
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-1 flex justify-around">
|
||||
<div><a @click="showLogin=false">返回</a></div>
|
||||
<div><a @click="goLogin">重新登录</a></div>
|
||||
<div><a @click="onLogin">进入系统</a></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, reactive, toRefs, computed } from 'vue'
|
||||
import { ResultEnum } from '@/enums/httpEnum'
|
||||
import recharge from './recharge.vue'
|
||||
import {
|
||||
LockOutlined,
|
||||
LoadingOutlined,
|
||||
UnlockOutlined,
|
||||
UserOutlined,
|
||||
ApiOutlined,
|
||||
ArrowRightOutlined,
|
||||
WifiOutlined,
|
||||
} from '@vicons/antd'
|
||||
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useOnline } from '@/hooks/useOnline'
|
||||
import { useTime } from '@/hooks/useTime'
|
||||
import { useBattery } from '@/hooks/useBattery'
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Lockscreen',
|
||||
components: {
|
||||
LockOutlined,
|
||||
LoadingOutlined,
|
||||
UnlockOutlined,
|
||||
UserOutlined,
|
||||
ArrowRightOutlined,
|
||||
ApiOutlined,
|
||||
WifiOutlined,
|
||||
recharge,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const useLockscreen = useLockscreenStore()
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 获取时间
|
||||
const { month, day, hour, minute, second, week } = useTime()
|
||||
const { online } = useOnline()
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const { battery, batteryStatus, calcDischargingTime } = useBattery()
|
||||
const { username } = userStore.getUserInfo || {}
|
||||
const state = reactive({
|
||||
showLogin: false,
|
||||
loginLoading: false, // 正在登录
|
||||
isLoginError: false, //密码错误
|
||||
errorMsg: '密码错误',
|
||||
loginParams: {
|
||||
username: username || '',
|
||||
password: ''
|
||||
}
|
||||
})
|
||||
|
||||
// 解锁登录
|
||||
const onLockLogin = (value: boolean) => (state.showLogin = value)
|
||||
|
||||
// 登录
|
||||
const onLogin = async () => {
|
||||
if (!state.loginParams.password.trim()) {
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
isLock: true,
|
||||
...state.loginParams
|
||||
}
|
||||
state.loginLoading = true
|
||||
const { code, result, message } = await userStore.login(params)
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
onLockLogin(false)
|
||||
useLockscreen.setLock(false)
|
||||
} else {
|
||||
state.errorMsg = message
|
||||
state.isLoginError = true
|
||||
}
|
||||
state.loginLoading = false
|
||||
}
|
||||
|
||||
//重新登录
|
||||
const goLogin = () => {
|
||||
onLockLogin(false)
|
||||
useLockscreen.setLock(false)
|
||||
router.replace({
|
||||
path: '/login',
|
||||
query: {
|
||||
redirect: route.fullPath
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
online,
|
||||
month,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
week,
|
||||
battery,
|
||||
batteryStatus,
|
||||
calcDischargingTime,
|
||||
onLockLogin,
|
||||
onLogin,
|
||||
goLogin
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.lockscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
background: #000;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
z-index: 9999;
|
||||
|
||||
&.onLockLogin {
|
||||
background-color: rgba(25, 28, 34, 0.88);
|
||||
backdrop-filter: blur(7px);
|
||||
}
|
||||
|
||||
.login-box {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
> * {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.lock-box {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 34px;
|
||||
z-index: 100;
|
||||
|
||||
.tips {
|
||||
color: white;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.lock {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
.lock-icon {
|
||||
cursor: pointer;
|
||||
|
||||
.anticon-unlock {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .anticon-unlock {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
&:hover .anticon-lock {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.local-time {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
left: 60px;
|
||||
font-family: helvetica;
|
||||
|
||||
.time {
|
||||
font-size: 70px;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.computer-status {
|
||||
position: absolute;
|
||||
bottom: 60px;
|
||||
right: 60px;
|
||||
font-size: 24px;
|
||||
|
||||
> * {
|
||||
margin-left: 14px;
|
||||
}
|
||||
|
||||
.network {
|
||||
position: relative;
|
||||
|
||||
&.offline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 2px;
|
||||
height: 28px;
|
||||
transform: translate(-50%, -50%) rotate(45deg);
|
||||
background-color: red;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="number">{{ battery.level }}%</div>
|
||||
<div class="contrast">
|
||||
<div class="circle"></div>
|
||||
<ul class="bubbles">
|
||||
<li v-for="i in 15" :key="i"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="charging">
|
||||
<div>{{ batteryStatus }}</div>
|
||||
<div v-show="Number.isFinite(battery.dischargingTime) && battery.dischargingTime != 0">
|
||||
剩余可使用时间:{{ calcDischargingTime }}
|
||||
</div>
|
||||
<span v-show="Number.isFinite(battery.chargingTime) && battery.chargingTime != 0">
|
||||
距离电池充满需要:{{ calcDischargingTime }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HuaweiCharge',
|
||||
// props: ['batteryStatus', 'battery', 'calcDischargingTime'],
|
||||
props: {
|
||||
battery: {
|
||||
// 电池对象
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
calcDischargingTime: {
|
||||
// 电池剩余时间可用时间
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
batteryStatus: {
|
||||
// 电池状态
|
||||
type: String,
|
||||
validator: (val: string) => ['充电中', '已充满', '已断开电源'].includes(val)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
position: absolute;
|
||||
bottom: 20vh;
|
||||
left: 50vw;
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
transform: translateX(-50%);
|
||||
|
||||
.number {
|
||||
position: absolute;
|
||||
top: 27%;
|
||||
z-index: 10;
|
||||
width: 300px;
|
||||
font-size: 32px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contrast {
|
||||
width: 300px;
|
||||
height: 400px;
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
filter: contrast(15) hue-rotate(0);
|
||||
animation: hueRotate 10s infinite linear;
|
||||
|
||||
.circle {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
filter: blur(8px);
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background-color: #00ff6f;
|
||||
border-radius: 42% 38% 62% 49% / 45%;
|
||||
content: '';
|
||||
transform: translate(-50%, -50%) rotate(0);
|
||||
animation: rotate 10s infinite linear;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
z-index: 10;
|
||||
width: 176px;
|
||||
height: 176px;
|
||||
background-color: #000;
|
||||
border-radius: 50%;
|
||||
content: '';
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
.bubbles {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
background-color: #00ff6f;
|
||||
border-radius: 100px 100px 0 0;
|
||||
filter: blur(5px);
|
||||
transform: translate(-50%, 0);
|
||||
|
||||
li {
|
||||
position: absolute;
|
||||
background: #00ff6f;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.charging {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@width: ~`Math.round(Math.random() * 100)` px;
|
||||
@left: calc(15px + `Math.round(Math.random(70))`);
|
||||
each(range(15), {
|
||||
.xiaoma-@{value} {
|
||||
height: (@value * 50px);
|
||||
}
|
||||
li:nth-child(@{index}) {
|
||||
top: 50%;
|
||||
left: @left;
|
||||
width: @width;
|
||||
height: @width;
|
||||
transform: translate(-50%, -50%);
|
||||
/*animation: moveToTop (Math.random(6) + 3s) ease-in-out -(Math.random(5000) / 1000s) infinite;*/
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@keyframes rotate {
|
||||
50% {
|
||||
border-radius: 45% / 42% 38% 58% 49%;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(720deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes moveToTop {
|
||||
90% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
transform: translate(-50%, -180px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes hueRotate {
|
||||
100% {
|
||||
filter: contrast(15) hue-rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user