parent
5b033246bc
commit
0605f89a67
|
|
@ -18,11 +18,11 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, ref, watch } from 'vue';
|
||||
import { useRoute, RouteLocationMatched } from 'vue-router';
|
||||
import { compile } from 'path-to-regexp';
|
||||
import router from '@/router';
|
||||
import { translateRouteTitleI18n } from '@/utils/i18n';
|
||||
import { onBeforeMount, ref, watch } from "vue";
|
||||
import { useRoute, RouteLocationMatched } from "vue-router";
|
||||
import { compile } from "path-to-regexp";
|
||||
import router from "@/router";
|
||||
import { translateRouteTitleI18n } from "@/utils/i18n";
|
||||
|
||||
const currentRoute = useRoute();
|
||||
const pathCompile = (path: string) => {
|
||||
|
|
@ -35,15 +35,15 @@ const breadcrumbs = ref([] as Array<RouteLocationMatched>);
|
|||
|
||||
function getBreadcrumb() {
|
||||
let matched = currentRoute.matched.filter(
|
||||
item => item.meta && item.meta.title
|
||||
(item) => item.meta && item.meta.title
|
||||
);
|
||||
const first = matched[0];
|
||||
if (!isDashboard(first)) {
|
||||
matched = [
|
||||
{ path: '/dashboard', meta: { title: 'dashboard' } } as any
|
||||
{ path: "/dashboard", meta: { title: "dashboard" } } as any,
|
||||
].concat(matched);
|
||||
}
|
||||
breadcrumbs.value = matched.filter(item => {
|
||||
breadcrumbs.value = matched.filter((item) => {
|
||||
return item.meta && item.meta.title && item.meta.breadcrumb !== false;
|
||||
});
|
||||
}
|
||||
|
|
@ -55,27 +55,27 @@ function isDashboard(route: RouteLocationMatched) {
|
|||
}
|
||||
return (
|
||||
name.toString().trim().toLocaleLowerCase() ===
|
||||
'Dashboard'.toLocaleLowerCase()
|
||||
"Dashboard".toLocaleLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
function handleLink(item: any) {
|
||||
const { redirect, path } = item;
|
||||
if (redirect) {
|
||||
router.push(redirect).catch(err => {
|
||||
router.push(redirect).catch((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
router.push(pathCompile(path)).catch(err => {
|
||||
router.push(pathCompile(path)).catch((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => currentRoute.path,
|
||||
path => {
|
||||
if (path.startsWith('/redirect/')) {
|
||||
(path) => {
|
||||
if (path.startsWith("/redirect/")) {
|
||||
return;
|
||||
}
|
||||
getBreadcrumb();
|
||||
|
|
@ -90,9 +90,9 @@ onBeforeMount(() => {
|
|||
<style lang="scss" scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
// 覆盖 element-plus 的样式
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 250 250"
|
||||
style="fill: #40c9c6; color: #fff"
|
||||
style="color: #fff; fill: #40c9c6"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
|
||||
|
|
@ -38,22 +38,25 @@
|
|||
100% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: none;
|
||||
}
|
||||
@media (width <= 500px) {
|
||||
.github-corner .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out;
|
||||
}
|
||||
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ defineProps({
|
|||
isActive: {
|
||||
required: true,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['toggleClick']);
|
||||
const emit = defineEmits(["toggleClick"]);
|
||||
|
||||
function toggleClick() {
|
||||
emit('toggleClick');
|
||||
emit("toggleClick");
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -38,6 +38,7 @@ function toggleClick() {
|
|||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: -4px;
|
||||
|
||||
&.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
require: false
|
||||
}
|
||||
require: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const inputValue = toRef(props, 'modelValue');
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const inputValue = toRef(props, "modelValue");
|
||||
|
||||
const visible = ref(false); // 弹窗显示状态
|
||||
|
||||
const iconNames: string[] = []; // 所有的图标名称集合
|
||||
|
||||
const filterValue = ref(''); // 筛选的值
|
||||
const filterValue = ref(""); // 筛选的值
|
||||
const filterIconNames = ref<string[]>([]); // 过滤后的图标名称集合
|
||||
|
||||
const iconSelectorRef = ref(null);
|
||||
|
|
@ -21,9 +21,9 @@ const iconSelectorRef = ref(null);
|
|||
* icon 加载
|
||||
*/
|
||||
function loadIcons() {
|
||||
const icons = import.meta.glob('../../assets/icons/*.svg');
|
||||
const icons = import.meta.glob("../../assets/icons/*.svg");
|
||||
for (const icon in icons) {
|
||||
const iconName = icon.split('assets/icons/')[1].split('.svg')[0];
|
||||
const iconName = icon.split("assets/icons/")[1].split(".svg")[0];
|
||||
iconNames.push(iconName);
|
||||
}
|
||||
filterIconNames.value = iconNames;
|
||||
|
|
@ -34,7 +34,7 @@ function loadIcons() {
|
|||
*/
|
||||
function handleFilter() {
|
||||
if (filterValue.value) {
|
||||
filterIconNames.value = iconNames.filter(iconName =>
|
||||
filterIconNames.value = iconNames.filter((iconName) =>
|
||||
iconName.includes(filterValue.value)
|
||||
);
|
||||
} else {
|
||||
|
|
@ -46,7 +46,7 @@ function handleFilter() {
|
|||
* icon 选择
|
||||
*/
|
||||
function handleSelect(iconName: string) {
|
||||
emit('update:modelValue', iconName);
|
||||
emit("update:modelValue", iconName);
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
|
|
@ -125,6 +125,7 @@ onMounted(() => {
|
|||
.el-divider--horizontal {
|
||||
margin: 10px auto !important;
|
||||
}
|
||||
|
||||
.iconselect-container {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
|
|
@ -137,18 +138,19 @@ onMounted(() => {
|
|||
margin-top: 10px;
|
||||
|
||||
.icon-item {
|
||||
cursor: pointer;
|
||||
width: 10%;
|
||||
margin: 0 10px 10px 0;
|
||||
padding: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: center;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
width: 10%;
|
||||
padding: 5px;
|
||||
margin: 0 10px 10px 0;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
transition: all 0.2s;
|
||||
transform: scaleX(1.1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,54 +14,54 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, PropType } from 'vue';
|
||||
import { scrollTo } from '@/utils/scroll-to';
|
||||
import { computed, PropType } from "vue";
|
||||
import { scrollTo } from "@/utils/scroll-to";
|
||||
|
||||
const props = defineProps({
|
||||
total: {
|
||||
required: true,
|
||||
type: Number as PropType<number>,
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
default: 1,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
default: 20,
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array as PropType<number[]>,
|
||||
default() {
|
||||
return [10, 20, 30, 50];
|
||||
}
|
||||
},
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
default: "total, sizes, prev, pager, next, jumper",
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
|
||||
const emit = defineEmits(["update:page", "update:limit", "pagination"]);
|
||||
|
||||
const currentPage = computed<number | undefined>({
|
||||
get: () => props.page,
|
||||
set: value => {
|
||||
emit('update:page', value);
|
||||
}
|
||||
set: (value) => {
|
||||
emit("update:page", value);
|
||||
},
|
||||
});
|
||||
|
||||
const pageSize = computed<number | undefined>({
|
||||
|
|
@ -69,12 +69,12 @@ const pageSize = computed<number | undefined>({
|
|||
return props.limit;
|
||||
},
|
||||
set(val) {
|
||||
emit('update:limit', val);
|
||||
}
|
||||
emit("update:limit", val);
|
||||
},
|
||||
});
|
||||
|
||||
function handleSizeChange(val: number) {
|
||||
emit('pagination', { page: currentPage, limit: val });
|
||||
emit("pagination", { page: currentPage, limit: val });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
|
|
@ -82,7 +82,7 @@ function handleSizeChange(val: number) {
|
|||
|
||||
function handleCurrentChange(val: number) {
|
||||
currentPage.value = val;
|
||||
emit('pagination', { page: val, limit: props.limit });
|
||||
emit("pagination", { page: val, limit: props.limit });
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800);
|
||||
}
|
||||
|
|
@ -92,6 +92,7 @@ function handleCurrentChange(val: number) {
|
|||
<style lang="scss" scoped>
|
||||
.pagination {
|
||||
padding: 12px;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,45 @@
|
|||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
|
||||
import { addClass, removeClass } from '@/utils/index';
|
||||
import { addClass, removeClass } from "@/utils/index";
|
||||
|
||||
const show = ref(false);
|
||||
|
||||
defineProps({
|
||||
buttonTop: {
|
||||
default: 250,
|
||||
type: Number
|
||||
}
|
||||
type: Number,
|
||||
},
|
||||
});
|
||||
|
||||
watch(show, value => {
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
addEventClick();
|
||||
}
|
||||
if (value) {
|
||||
addClass(document.body, 'showRightPanel');
|
||||
addClass(document.body, "showRightPanel");
|
||||
} else {
|
||||
removeClass(document.body, 'showRightPanel');
|
||||
removeClass(document.body, "showRightPanel");
|
||||
}
|
||||
});
|
||||
|
||||
function addEventClick() {
|
||||
window.addEventListener('click', closeSidebar, { passive: true });
|
||||
window.addEventListener("click", closeSidebar, { passive: true });
|
||||
}
|
||||
|
||||
function closeSidebar(evt: any) {
|
||||
// 主题选择点击不关闭
|
||||
let parent = evt.target.closest('.right-panel-container');
|
||||
let parent = evt.target.closest(".right-panel-container");
|
||||
if (!parent) {
|
||||
show.value = false;
|
||||
window.removeEventListener('click', closeSidebar);
|
||||
window.removeEventListener("click", closeSidebar);
|
||||
}
|
||||
}
|
||||
|
||||
const rightPanel = ref();
|
||||
|
||||
function insertToBody() {
|
||||
const body = document.querySelector('body') as any;
|
||||
const body = document.querySelector("body") as any;
|
||||
body.insertBefore(rightPanel.value, body.firstChild);
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ onBeforeUnmount(() => {
|
|||
<div
|
||||
class="right-panel-btn"
|
||||
:style="{
|
||||
top: buttonTop + 'px'
|
||||
top: buttonTop + 'px',
|
||||
}"
|
||||
@click="show = !show"
|
||||
>
|
||||
|
|
@ -75,38 +75,40 @@ onBeforeUnmount(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.showRightPanel {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: calc(100% - 15px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.right-panel-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
background: rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
.right-panel-container {
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.05);
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 100vh;
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
|
||||
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
transform: translate(100%);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.show {
|
||||
transition: all 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
|
||||
.right-panel-overlay {
|
||||
z-index: 99;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.right-panel-container {
|
||||
|
|
@ -115,19 +117,20 @@ onBeforeUnmount(() => {
|
|||
}
|
||||
|
||||
.right-panel-btn {
|
||||
background-color: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
position: absolute;
|
||||
left: -36px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
left: -36px;
|
||||
position: absolute;
|
||||
color: var(--el-color-white);
|
||||
text-align: center;
|
||||
border-radius: 6px 0 0 6px;
|
||||
cursor: pointer;
|
||||
background-color: var(--el-color-primary);
|
||||
border-radius: 6px 0 0 6px;
|
||||
|
||||
svg {
|
||||
vertical-align: -10px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: -10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -12,19 +12,19 @@
|
|||
const props = defineProps({
|
||||
prefix: {
|
||||
type: String,
|
||||
default: 'icon'
|
||||
default: "icon",
|
||||
},
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
color: {
|
||||
type: String
|
||||
type: String,
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: '1em'
|
||||
}
|
||||
default: "1em",
|
||||
},
|
||||
});
|
||||
|
||||
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
|
||||
|
|
@ -33,11 +33,11 @@ const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
|
|||
<style scoped>
|
||||
.svg-icon {
|
||||
display: inline-block;
|
||||
outline: none;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
|
||||
fill: currentColor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
|
||||
overflow: hidden;
|
||||
vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
|
||||
outline: none;
|
||||
fill: currentcolor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { UploadRawFile, UploadRequestOptions } from 'element-plus';
|
||||
import { uploadFileApi } from '@/api/file';
|
||||
import { UploadRawFile, UploadRequestOptions } from "element-plus";
|
||||
import { uploadFileApi } from "@/api/file";
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const imgUrl = computed<string | undefined>({
|
||||
|
|
@ -32,8 +32,8 @@ const imgUrl = computed<string | undefined>({
|
|||
},
|
||||
set(val) {
|
||||
// imgUrl改变时触发修改父组件绑定的v-model的值
|
||||
emit('update:modelValue', val);
|
||||
}
|
||||
emit("update:modelValue", val);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
@ -51,7 +51,7 @@ async function uploadFile(options: UploadRequestOptions): Promise<any> {
|
|||
*/
|
||||
function handleBeforeUpload(file: UploadRawFile) {
|
||||
if (file.size > 2 * 1048 * 1048) {
|
||||
ElMessage.warning('上传图片不能大于2M');
|
||||
ElMessage.warning("上传图片不能大于2M");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -60,19 +60,19 @@ function handleBeforeUpload(file: UploadRawFile) {
|
|||
|
||||
<style scoped>
|
||||
.single-uploader .single {
|
||||
display: block;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.single-uploader .el-upload {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
|
|
@ -81,10 +81,10 @@ function handleBeforeUpload(file: UploadRawFile) {
|
|||
}
|
||||
|
||||
.el-icon.single-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
||||
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
</script>
|
||||
|
|
@ -18,10 +18,11 @@ const tagsViewStore = useTagsViewStore();
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.app-main {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
/* 50= navbar 50 */
|
||||
min-height: calc(100vh - 50px);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: var(--el-bg-color-page);
|
||||
}
|
||||
|
|
@ -37,8 +38,8 @@ const tagsViewStore = useTagsViewStore();
|
|||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 84px;
|
||||
min-height: 100vh;
|
||||
padding-top: 84px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useAppStore } from "@/store/modules/app";
|
||||
import { useTagsViewStore } from "@/store/modules/tagsView";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
|
|
@ -20,10 +20,10 @@ function toggleSideBar() {
|
|||
|
||||
// 注销
|
||||
function logout() {
|
||||
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
userStore
|
||||
.logout()
|
||||
|
|
@ -76,19 +76,19 @@ function logout() {
|
|||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/">
|
||||
<el-dropdown-item>{{ $t('navbar.dashboard') }}</el-dropdown-item>
|
||||
<el-dropdown-item>{{ $t("navbar.dashboard") }}</el-dropdown-item>
|
||||
</router-link>
|
||||
<a target="_blank" href="https://github.com/hxrui">
|
||||
<el-dropdown-item>Github</el-dropdown-item>
|
||||
</a>
|
||||
<a target="_blank" href="https://gitee.com/haoxr">
|
||||
<el-dropdown-item>{{ $t('navbar.gitee') }}</el-dropdown-item>
|
||||
<el-dropdown-item>{{ $t("navbar.gitee") }}</el-dropdown-item>
|
||||
</a>
|
||||
<a target="_blank" href="https://www.cnblogs.com/haoxianrui/">
|
||||
<el-dropdown-item>{{ $t('navbar.document') }}</el-dropdown-item>
|
||||
<el-dropdown-item>{{ $t("navbar.document") }}</el-dropdown-item>
|
||||
</a>
|
||||
<el-dropdown-item divided @click="logout">
|
||||
{{ $t('navbar.logout') }}
|
||||
{{ $t("navbar.logout") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
|
|
@ -99,23 +99,24 @@ function logout() {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.navbar {
|
||||
background-color: #fff;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 1px #0003;
|
||||
|
||||
.navbar-setting-item {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
color: #5a5e66;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(249, 250, 251, 1);
|
||||
background: rgb(249 250 251 / 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
|
||||
import IconEpSunny from '~icons/ep/sunny';
|
||||
import IconEpMoon from '~icons/ep/moon';
|
||||
import IconEpSunny from "~icons/ep/sunny";
|
||||
import IconEpMoon from "~icons/ep/moon";
|
||||
|
||||
/**
|
||||
* 暗黑模式
|
||||
|
|
@ -15,30 +15,30 @@ const toggleDark = () => useToggle(isDark);
|
|||
* 切换布局
|
||||
*/
|
||||
function changeLayout(layout: string) {
|
||||
settingsStore.changeSetting({ key: 'layout', value: layout });
|
||||
window.document.body.setAttribute('layout', settingsStore.layout);
|
||||
settingsStore.changeSetting({ key: "layout", value: layout });
|
||||
window.document.body.setAttribute("layout", settingsStore.layout);
|
||||
}
|
||||
|
||||
// 主题颜色
|
||||
const themeColors = ref<string[]>([
|
||||
'#409EFF',
|
||||
'#304156',
|
||||
'#11a983',
|
||||
'#13c2c2',
|
||||
'#6959CD',
|
||||
'#f5222d'
|
||||
"#409EFF",
|
||||
"#304156",
|
||||
"#11a983",
|
||||
"#13c2c2",
|
||||
"#6959CD",
|
||||
"#f5222d",
|
||||
]);
|
||||
|
||||
/**
|
||||
* 切换主题颜色
|
||||
*/
|
||||
function changeThemeColor(color: string) {
|
||||
document.documentElement.style.setProperty('--el-color-primary', color);
|
||||
settingsStore.changeSetting({ key: 'layout', value: color });
|
||||
document.documentElement.style.setProperty("--el-color-primary", color);
|
||||
settingsStore.changeSetting({ key: "layout", value: color });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.document.body.setAttribute('layout', settingsStore.layout);
|
||||
window.document.body.setAttribute("layout", settingsStore.layout);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -133,6 +133,7 @@ onMounted(() => {
|
|||
<style lang="scss" scoped>
|
||||
.settings-container {
|
||||
padding: 16px;
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -141,67 +142,57 @@ onMounted(() => {
|
|||
height: 50px;
|
||||
|
||||
&-item {
|
||||
position: relative;
|
||||
width: 18%;
|
||||
height: 45px;
|
||||
background: #f0f2f5;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background: #f0f2f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&-item.is-active {
|
||||
border: 2px solid var(--el-color-primary);
|
||||
}
|
||||
&-left {
|
||||
div {
|
||||
&:nth-child(1) {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
background: #1b2a47;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 70%;
|
||||
height: 30%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 1px #888;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
&-mix div:nth-child(1) {
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
box-shadow: 0 0 1px #888;
|
||||
}
|
||||
|
||||
&-top {
|
||||
div {
|
||||
&:nth-child(1) {
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
box-shadow: 0 0 1px #888;
|
||||
}
|
||||
}
|
||||
&-mix div:nth-child(2) {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 30%;
|
||||
height: 70%;
|
||||
background: #1b2a47;
|
||||
box-shadow: 0 0 1px #888;
|
||||
}
|
||||
|
||||
&-mix {
|
||||
div {
|
||||
&:nth-child(1) {
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
box-shadow: 0 0 1px #888;
|
||||
}
|
||||
&-top div:nth-child(1) {
|
||||
width: 100%;
|
||||
height: 30%;
|
||||
background: #1b2a47;
|
||||
box-shadow: 0 0 1px #888;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
width: 30%;
|
||||
height: 70%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #1b2a47;
|
||||
box-shadow: 0 0 1px #888;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
&-left div:nth-child(1) {
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
background: #1b2a47;
|
||||
}
|
||||
|
||||
&-left div:nth-child(2) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 70%;
|
||||
height: 30%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 1px #888;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { useTagsViewStore, TagView } from '@/store/modules/tagsView';
|
||||
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
|
||||
|
||||
const tagAndTagSpacing = ref(4);
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
const emits = defineEmits(['scroll']);
|
||||
const emits = defineEmits(["scroll"]);
|
||||
const emitScroll = () => {
|
||||
emits('scroll');
|
||||
emits("scroll");
|
||||
};
|
||||
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
|
|
@ -16,10 +16,10 @@ const scrollWrapper = computed(
|
|||
);
|
||||
|
||||
onMounted(() => {
|
||||
scrollWrapper.value.addEventListener('scroll', emitScroll, true);
|
||||
scrollWrapper.value.addEventListener("scroll", emitScroll, true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
scrollWrapper.value.removeEventListener('scroll', emitScroll);
|
||||
scrollWrapper.value.removeEventListener("scroll", emitScroll);
|
||||
});
|
||||
|
||||
function handleScroll(e: WheelEvent) {
|
||||
|
|
@ -47,14 +47,14 @@ function moveToTarget(currentTag: TagView) {
|
|||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
|
||||
} else {
|
||||
const tagListDom = document.getElementsByClassName('tags-item');
|
||||
const tagListDom = document.getElementsByClassName("tags-item");
|
||||
const currentIndex = tagsViewStore.visitedViews.findIndex(
|
||||
item => item === currentTag
|
||||
(item) => item === currentTag
|
||||
);
|
||||
let prevTag = null;
|
||||
let nextTag = null;
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (k !== "length" && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (
|
||||
(tagListDom[k] as any).dataset.path ===
|
||||
tagsViewStore.visitedViews[currentIndex - 1].path
|
||||
|
|
@ -88,7 +88,7 @@ function moveToTarget(currentTag: TagView) {
|
|||
}
|
||||
|
||||
defineExpose({
|
||||
moveToTarget
|
||||
moveToTarget,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -105,19 +105,17 @@ defineExpose({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.scroll-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.el-scrollbar__wrap {
|
||||
height: 49px;
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,19 +5,19 @@ import {
|
|||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
ComponentInternalInstance
|
||||
} from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
ComponentInternalInstance,
|
||||
} from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
import path from 'path-browserify';
|
||||
import path from "path-browserify";
|
||||
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
import { translateRouteTitleI18n } from '@/utils/i18n';
|
||||
import { translateRouteTitleI18n } from "@/utils/i18n";
|
||||
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
import { useTagsViewStore, TagView } from '@/store/modules/tagsView';
|
||||
import ScrollPane from './ScrollPane.vue';
|
||||
import { usePermissionStore } from "@/store/modules/permission";
|
||||
import { useTagsViewStore, TagView } from "@/store/modules/tagsView";
|
||||
import ScrollPane from "./ScrollPane.vue";
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const router = useRouter();
|
||||
|
|
@ -42,30 +42,30 @@ watch(
|
|||
},
|
||||
{
|
||||
//初始化立即执行
|
||||
immediate: true
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const tagMenuVisible = ref(false); // 标签操作菜单显示状态
|
||||
watch(tagMenuVisible, value => {
|
||||
watch(tagMenuVisible, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', closeTagMenu);
|
||||
document.body.addEventListener("click", closeTagMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', closeTagMenu);
|
||||
document.body.removeEventListener("click", closeTagMenu);
|
||||
}
|
||||
});
|
||||
|
||||
function filterAffixTags(routes: any[], basePath = '/') {
|
||||
function filterAffixTags(routes: any[], basePath = "/") {
|
||||
let tags: TagView[] = [];
|
||||
|
||||
routes.forEach(route => {
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
meta: { ...route.meta },
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ function isFirstView() {
|
|||
return (
|
||||
(selectedTag.value as TagView).fullPath ===
|
||||
tagsViewStore.visitedViews[1].fullPath ||
|
||||
(selectedTag.value as TagView).fullPath === '/index'
|
||||
(selectedTag.value as TagView).fullPath === "/index"
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
|
|
@ -145,7 +145,7 @@ function refreshSelectedTag(view: TagView) {
|
|||
tagsViewStore.delCachedView(view);
|
||||
const { fullPath } = view;
|
||||
nextTick(() => {
|
||||
router.replace({ path: '/redirect' + fullPath }).catch(err => {
|
||||
router.replace({ path: "/redirect" + fullPath }).catch((err) => {
|
||||
console.warn(err);
|
||||
});
|
||||
});
|
||||
|
|
@ -158,11 +158,11 @@ function toLastView(visitedViews: TagView[], view?: any) {
|
|||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view.name === 'Dashboard') {
|
||||
if (view.name === "Dashboard") {
|
||||
// to reload home page
|
||||
router.replace({ path: '/redirect' + view.fullPath });
|
||||
router.replace({ path: "/redirect" + view.fullPath });
|
||||
} else {
|
||||
router.push('/');
|
||||
router.push("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,7 +210,7 @@ function closeAllTags(view: TagView) {
|
|||
function openTagMenu(tag: TagView, e: MouseEvent) {
|
||||
const menuMinWidth = 105;
|
||||
|
||||
console.log('test', proxy?.$el);
|
||||
console.log("test", proxy?.$el);
|
||||
|
||||
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = proxy?.$el.offsetWidth; // container width
|
||||
|
|
@ -300,18 +300,19 @@ onMounted(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.tags-container {
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
box-shadow: 0px 1px 1px var(--el-box-shadow-light);
|
||||
height: 34px;
|
||||
background-color: var(--el-bg-color);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
box-shadow: 0 1px 1px var(--el-box-shadow-light);
|
||||
|
||||
.tags-item {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
margin: 4px 0 0 5px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
padding: 3px 8px;
|
||||
font-size: 12px;
|
||||
margin: 4px 0 0 5px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
|
|
@ -326,46 +327,44 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
background-color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.tags-item-close {
|
||||
&:hover {
|
||||
background: rgb(0 0 0 / 0.16);
|
||||
}
|
||||
content: "";
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
&-close {
|
||||
border-radius: 100%;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
background: rgb(0 0 0 / 0.16);
|
||||
background: rgb(0 0 0 / 16%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag-menu {
|
||||
background: var(--el-bg-color-overlay);
|
||||
z-index: 99;
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
z-index: 99;
|
||||
font-size: 12px;
|
||||
background: var(--el-bg-color-overlay);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
|
||||
li {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: var(--el-fill-color-light);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, watchEffect } from 'vue';
|
||||
import { useWindowSize } from '@vueuse/core';
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components/index';
|
||||
import Sidebar from './components/Sidebar/index.vue';
|
||||
import RightPanel from '@/components/RightPanel/index.vue';
|
||||
import { computed, watchEffect } from "vue";
|
||||
import { useWindowSize } from "@vueuse/core";
|
||||
import { AppMain, Navbar, Settings, TagsView } from "./components/index";
|
||||
import Sidebar from "./components/Sidebar/index.vue";
|
||||
import RightPanel from "@/components/RightPanel/index.vue";
|
||||
|
||||
import { useAppStore } from '@/store/modules/app';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
import { useAppStore } from "@/store/modules/app";
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
|
||||
const { width } = useWindowSize();
|
||||
|
||||
|
|
@ -30,15 +30,15 @@ const classObj = computed(() => ({
|
|||
hideSidebar: !appStore.sidebar.opened,
|
||||
openSidebar: appStore.sidebar.opened,
|
||||
withoutAnimation: appStore.sidebar.withoutAnimation,
|
||||
mobile: appStore.device === 'mobile'
|
||||
mobile: appStore.device === "mobile",
|
||||
}));
|
||||
|
||||
watchEffect(() => {
|
||||
if (width.value < WIDTH) {
|
||||
appStore.toggleDevice('mobile');
|
||||
appStore.toggleDevice("mobile");
|
||||
appStore.closeSideBar(true);
|
||||
} else {
|
||||
appStore.toggleDevice('desktop');
|
||||
appStore.toggleDevice("desktop");
|
||||
|
||||
if (width.value >= 1200) {
|
||||
//大屏
|
||||
|
|
@ -84,15 +84,15 @@ function handleOutsideClick() {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.app-wrapper {
|
||||
&:after {
|
||||
content: '';
|
||||
&::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&.mobile.openSidebar {
|
||||
position: fixed;
|
||||
|
|
@ -108,20 +108,22 @@ function handleOutsideClick() {
|
|||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
html.dark{
|
||||
--menuBg:var(--el-bg-color-overlay);
|
||||
--menuText:#fff;
|
||||
--menuActiveText:var(--el-menu-active-color);
|
||||
--menuHover:rgba(0,0,0,.2);
|
||||
|
||||
html.dark {
|
||||
--menuBg: var(--el-bg-color-overlay);
|
||||
--menuText: #fff;
|
||||
--menuActiveText: var(--el-menu-active-color);
|
||||
--menuHover: rgb(0 0 0 / 20%);
|
||||
--subMenuBg: var(--el-menu-bg-color);
|
||||
--subMenuActiveText:var(--el-menu-active-color);
|
||||
--subMenuHover: rgba(0,0,0,.2);
|
||||
|
||||
--subMenuActiveText: var(--el-menu-active-color);
|
||||
--subMenuHover: rgb(0 0 0 / 20%);
|
||||
|
||||
.navbar {
|
||||
background-color: var(--el-bg-color);
|
||||
color: var(--el-text-color-regular);
|
||||
.navbar-setting-item:hover{
|
||||
background-color: var(--el-bg-color);
|
||||
|
||||
.navbar-setting-item:hover {
|
||||
background: var(--el-fill-color-light);
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel-btn{
|
||||
.right-panel-btn {
|
||||
background-color: var(--el-color-primary-dark);
|
||||
}
|
||||
.svg-icon,svg{
|
||||
|
||||
.svg-icon,
|
||||
svg {
|
||||
fill: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
|
||||
.sidebar-container{
|
||||
.sidebar-container {
|
||||
.el-menu-item.is-active .svg-icon {
|
||||
fill: var(--el-color-primary);
|
||||
fill: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
@import './sidebar.scss';
|
||||
@import './reset.scss';
|
||||
@import './dark.scss';
|
||||
|
||||
@import "./sidebar";
|
||||
@import "./reset";
|
||||
@import "./dark";
|
||||
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
.search {
|
||||
padding: 18px 0 0 10px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
padding: 20px;
|
||||
|
||||
.search {
|
||||
padding: 18px 0 0 10px;
|
||||
margin-bottom: 10px;
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-color: currentcolor;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
|
|
@ -13,69 +13,63 @@
|
|||
}
|
||||
|
||||
html {
|
||||
line-height: 1.5;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-tab-size: 4;
|
||||
tab-size: 4;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
line-height: inherit;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
|
||||
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||
line-height: inherit;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizelegibility;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
|
||||
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
|
||||
img,
|
||||
svg
|
||||
{
|
||||
svg {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: -0.15em; //因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果
|
||||
}
|
||||
|
||||
|
||||
ul,li{
|
||||
margin: 0;
|
||||
ul,
|
||||
li {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active ,
|
||||
div:focus
|
||||
{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:active,
|
||||
div:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
#app {
|
||||
.main-container {
|
||||
min-height: 100%;
|
||||
transition: margin-left 0.28s;
|
||||
margin-left: $sideBarWidth;
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
margin-left: $sideBarWidth;
|
||||
transition: margin-left 0.28s;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: width 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
background-color: $menuBg;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1001;
|
||||
width: $sideBarWidth !important;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: $menuBg;
|
||||
transition: width 0.28s;
|
||||
|
||||
// reset element-ui css
|
||||
.horizontal-collapse-transition {
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
}
|
||||
|
||||
.el-scrollbar__bar.is-vertical {
|
||||
right: 0px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
|
|
@ -46,7 +46,6 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
|
@ -57,9 +56,9 @@
|
|||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
height: 100%;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
// menu hover
|
||||
|
|
@ -69,11 +68,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.is-active>.el-sub-menu__title {
|
||||
.is-active > .el-sub-menu__title {
|
||||
color: $subMenuActiveText !important;
|
||||
}
|
||||
|
||||
& .nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
& .nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
& .el-sub-menu .el-menu-item {
|
||||
min-width: $sideBarWidth !important;
|
||||
background-color: $subMenuBg !important;
|
||||
|
|
@ -89,7 +88,7 @@
|
|||
width: 54px !important;
|
||||
|
||||
.svg-icon {
|
||||
margin-right: 0px;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +99,7 @@
|
|||
.el-sub-menu {
|
||||
overflow: hidden;
|
||||
|
||||
&>.el-sub-menu__title {
|
||||
& > .el-sub-menu__title {
|
||||
padding: 0 !important;
|
||||
|
||||
.svg-icon {
|
||||
|
|
@ -119,13 +118,13 @@
|
|||
|
||||
.el-menu--collapse {
|
||||
.el-sub-menu {
|
||||
&>.el-sub-menu__title {
|
||||
&>span {
|
||||
height: 0;
|
||||
& > .el-sub-menu__title {
|
||||
& > span {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -139,12 +138,12 @@
|
|||
// mobile responsive
|
||||
.mobile {
|
||||
.main-container {
|
||||
margin-left: 0px;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.sidebar-container {
|
||||
transition: transform 0.28s;
|
||||
width: $sideBarWidth !important;
|
||||
transition: transform 0.28s;
|
||||
}
|
||||
|
||||
&.hideSidebar {
|
||||
|
|
@ -157,7 +156,6 @@
|
|||
}
|
||||
|
||||
.withoutAnimation {
|
||||
|
||||
.main-container,
|
||||
.sidebar-container {
|
||||
transition: none;
|
||||
|
|
@ -167,7 +165,7 @@
|
|||
|
||||
// when menu collapsed
|
||||
.el-menu--vertical {
|
||||
&>.el-menu {
|
||||
& > .el-menu {
|
||||
.svg-icon {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
|
@ -178,7 +176,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.nest-menu .el-sub-menu>.el-sub-menu__title,
|
||||
.nest-menu .el-sub-menu > .el-sub-menu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
// you can use $subMenuHover
|
||||
|
|
@ -187,7 +185,7 @@
|
|||
}
|
||||
|
||||
// the scroll bar appears when the subMenu is too long
|
||||
>.el-menu--popup {
|
||||
> .el-menu--popup {
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
// 全局SCSS变量
|
||||
|
||||
:root{
|
||||
--menuBg:#304156;
|
||||
--menuText:#bfcbd9;
|
||||
--menuActiveText:#409eff;
|
||||
--menuHover:#263445;
|
||||
|
||||
:root {
|
||||
--menuBg: #304156;
|
||||
--menuText: #bfcbd9;
|
||||
--menuActiveText: #409eff;
|
||||
--menuHover: #263445;
|
||||
--subMenuBg: #1f2d3d;
|
||||
--subMenuActiveText: #f4f4f5;
|
||||
--subMenuHover: #001528;
|
||||
|
||||
}
|
||||
|
||||
|
||||
$menuBg: var(--menuBg);
|
||||
$menuText: var(--menuText);
|
||||
$menuActiveText: var(--menuActiveText);
|
||||
$menuHover: var(--menuHover);
|
||||
|
||||
$subMenuBg:var(--subMenuBg);
|
||||
$subMenuActiveText:var(--subMenuActiveText);
|
||||
$subMenuHover:var(--subMenuHover);
|
||||
$subMenuBg: var(--subMenuBg);
|
||||
$subMenuActiveText: var(--subMenuActiveText);
|
||||
$subMenuHover: var(--subMenuHover);
|
||||
|
||||
$sideBarWidth: 210px;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
<script lang="ts">
|
||||
export default { name: 'Dashboard' };
|
||||
export default { name: "Dashboard" };
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useTransition, TransitionPresets } from '@vueuse/core';
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useTransition, TransitionPresets } from "@vueuse/core";
|
||||
|
||||
import GithubCorner from '@/components/GithubCorner/index.vue';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
import BarChart from './components/BarChart.vue';
|
||||
import PieChart from './components/PieChart.vue';
|
||||
import RadarChart from './components/RadarChart.vue';
|
||||
import GithubCorner from "@/components/GithubCorner/index.vue";
|
||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||
import BarChart from "./components/BarChart.vue";
|
||||
import PieChart from "./components/PieChart.vue";
|
||||
import RadarChart from "./components/RadarChart.vue";
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
|
|
@ -18,15 +18,15 @@ const date: Date = new Date();
|
|||
|
||||
const greetings = computed(() => {
|
||||
if (date.getHours() >= 6 && date.getHours() < 8) {
|
||||
return '晨起披衣出草堂,轩窗已自喜微凉🌅!';
|
||||
return "晨起披衣出草堂,轩窗已自喜微凉🌅!";
|
||||
} else if (date.getHours() >= 8 && date.getHours() < 12) {
|
||||
return '上午好🌞!';
|
||||
return "上午好🌞!";
|
||||
} else if (date.getHours() >= 12 && date.getHours() < 18) {
|
||||
return '下午好☕!';
|
||||
return "下午好☕!";
|
||||
} else if (date.getHours() >= 18 && date.getHours() < 24) {
|
||||
return '晚上好🌃!';
|
||||
return "晚上好🌃!";
|
||||
} else if (date.getHours() >= 0 && date.getHours() < 6) {
|
||||
return '偷偷向银河要了一把碎星,只等你闭上眼睛撒入你的梦中,晚安🌛!';
|
||||
return "偷偷向银河要了一把碎星,只等你闭上眼睛撒入你的梦中,晚安🌛!";
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ const duration = 5000;
|
|||
const amount = ref(0);
|
||||
const amountOutput = useTransition(amount, {
|
||||
duration: duration,
|
||||
transition: TransitionPresets.easeOutExpo
|
||||
transition: TransitionPresets.easeOutExpo,
|
||||
});
|
||||
amount.value = 2000;
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ amount.value = 2000;
|
|||
const visitCount = ref(0);
|
||||
const visitCountOutput = useTransition(visitCount, {
|
||||
duration: duration,
|
||||
transition: TransitionPresets.easeOutExpo
|
||||
transition: TransitionPresets.easeOutExpo,
|
||||
});
|
||||
visitCount.value = 2000;
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ visitCount.value = 2000;
|
|||
const messageCount = ref(0);
|
||||
const messageCountOutput = useTransition(messageCount, {
|
||||
duration: duration,
|
||||
transition: TransitionPresets.easeOutExpo
|
||||
transition: TransitionPresets.easeOutExpo,
|
||||
});
|
||||
messageCount.value = 2000;
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ messageCount.value = 2000;
|
|||
const orderCount = ref(0);
|
||||
const orderCountOutput = useTransition(orderCount, {
|
||||
duration: duration,
|
||||
transition: TransitionPresets.easeOutExpo
|
||||
transition: TransitionPresets.easeOutExpo,
|
||||
});
|
||||
orderCount.value = 2000;
|
||||
</script>
|
||||
|
|
@ -215,34 +215,36 @@ orderCount.value = 2000;
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard-container {
|
||||
padding: 24px;
|
||||
position: relative;
|
||||
padding: 24px;
|
||||
|
||||
.user-avatar {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.github-corner {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
border: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.data-box {
|
||||
font-weight: bold;
|
||||
padding: 20px;
|
||||
color: var(--el-text-color-regular);
|
||||
background: var(--el-bg-color-overlay);
|
||||
box-shadow: var(--el-box-shadow-dark);
|
||||
border-color: var(--el-border-color);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 20px;
|
||||
font-weight: bold;
|
||||
color: var(--el-text-color-regular);
|
||||
background: var(--el-bg-color-overlay);
|
||||
border-color: var(--el-border-color);
|
||||
box-shadow: var(--el-box-shadow-dark);
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
fill: currentColor !important;
|
||||
fill: currentcolor !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<iframe
|
||||
id="apidocIframe"
|
||||
id="apidoc-iframe"
|
||||
src="https://www.apifox.cn/apidoc/shared-195e783f-4d85-4235-a038-eec696de4ea5"
|
||||
width="100%"
|
||||
frameborder="0"
|
||||
|
|
@ -11,12 +11,12 @@
|
|||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#apidocIframe {
|
||||
#apidoc-iframe {
|
||||
height: calc(100vh - 100px);
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
#apidocIframe {
|
||||
#apidoc-iframe {
|
||||
height: calc(100vh - 140px);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Page401'
|
||||
name: "Page401",
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, toRefs } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { reactive, toRefs } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const state = reactive({
|
||||
errGif: new URL(`../../assets/401_images/401.gif`, import.meta.url).href,
|
||||
|
||||
ewizardClap:
|
||||
'https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646',
|
||||
dialogVisible: false
|
||||
"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",
|
||||
dialogVisible: false,
|
||||
});
|
||||
|
||||
const { errGif, ewizardClap, dialogVisible } = toRefs(state);
|
||||
|
|
@ -72,20 +72,20 @@ function back() {
|
|||
margin: 100px auto;
|
||||
|
||||
.pan-back-btn {
|
||||
background: #008489;
|
||||
color: #fff;
|
||||
background: #008489;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.pan-gif {
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.pan-img {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.text-jumbo {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'Page404'
|
||||
name: "Page404",
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
function message() {
|
||||
return 'The webmaster said that you can not enter this page...';
|
||||
return "The webmaster said that you can not enter this page...";
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -60,10 +60,10 @@ function message() {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.wscn-http404-container {
|
||||
transform: translate(-50%, -50%);
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.wscn-http404 {
|
||||
|
|
@ -86,39 +86,39 @@ function message() {
|
|||
position: absolute;
|
||||
|
||||
&.left {
|
||||
width: 80px;
|
||||
top: 17px;
|
||||
left: 220px;
|
||||
width: 80px;
|
||||
opacity: 0;
|
||||
animation-name: cloudLeft;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.mid {
|
||||
width: 46px;
|
||||
top: 10px;
|
||||
left: 420px;
|
||||
width: 46px;
|
||||
opacity: 0;
|
||||
animation-name: cloudMid;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1.2s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.right {
|
||||
width: 62px;
|
||||
top: 100px;
|
||||
left: 500px;
|
||||
width: 62px;
|
||||
opacity: 0;
|
||||
animation-name: cloudRight;
|
||||
animation-duration: 2s;
|
||||
animation-timing-function: linear;
|
||||
animation-fill-mode: forwards;
|
||||
animation-delay: 1s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes cloudLeft {
|
||||
|
|
@ -209,24 +209,24 @@ function message() {
|
|||
overflow: hidden;
|
||||
|
||||
&__oops {
|
||||
margin-bottom: 20px;
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
line-height: 40px;
|
||||
color: #1482f0;
|
||||
opacity: 0;
|
||||
margin-bottom: 20px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&__headline {
|
||||
margin-bottom: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 24px;
|
||||
color: #222;
|
||||
font-weight: bold;
|
||||
opacity: 0;
|
||||
margin-bottom: 10px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.1s;
|
||||
|
|
@ -234,11 +234,11 @@ function message() {
|
|||
}
|
||||
|
||||
&__info {
|
||||
margin-bottom: 30px;
|
||||
font-size: 13px;
|
||||
line-height: 21px;
|
||||
color: grey;
|
||||
opacity: 0;
|
||||
margin-bottom: 30px;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.2s;
|
||||
|
|
@ -250,14 +250,14 @@ function message() {
|
|||
float: left;
|
||||
width: 110px;
|
||||
height: 36px;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
opacity: 0;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background: #1482f0;
|
||||
border-radius: 100px;
|
||||
opacity: 0;
|
||||
animation-name: slideUp;
|
||||
animation-duration: 0.5s;
|
||||
animation-delay: 0.3s;
|
||||
|
|
@ -266,13 +266,13 @@ function message() {
|
|||
|
||||
@keyframes slideUp {
|
||||
0% {
|
||||
transform: translateY(60px);
|
||||
opacity: 0;
|
||||
transform: translateY(60px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
class="login-form"
|
||||
>
|
||||
<div class="flex text-white items-center py-4">
|
||||
<span class="text-2xl flex-1 text-center">{{ $t('login.title') }}</span>
|
||||
<span class="text-2xl flex-1 text-center">{{ $t("login.title") }}</span>
|
||||
<lang-select style="color: #fff" />
|
||||
</div>
|
||||
|
||||
|
|
@ -77,30 +77,30 @@
|
|||
type="primary"
|
||||
class="w-full"
|
||||
@click.prevent="handleLogin"
|
||||
>{{ $t('login.login') }}
|
||||
>{{ $t("login.login") }}
|
||||
</el-button>
|
||||
|
||||
<!-- 账号密码提示 -->
|
||||
<div class="mt-4 text-white text-sm">
|
||||
<span>{{ $t('login.username') }}: admin</span>
|
||||
<span class="ml-4"> {{ $t('login.password') }}: 123456</span>
|
||||
<span>{{ $t("login.username") }}: admin</span>
|
||||
<span class="ml-4"> {{ $t("login.password") }}: 123456</span>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import router from '@/router';
|
||||
import LangSelect from '@/components/LangSelect/index.vue';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
import router from "@/router";
|
||||
import LangSelect from "@/components/LangSelect/index.vue";
|
||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||
|
||||
// 状态管理依赖
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
// API依赖
|
||||
import { LocationQuery, LocationQueryValue, useRoute } from 'vue-router';
|
||||
import { getCaptchaApi } from '@/api/auth';
|
||||
import { LoginData } from '@/api/auth/types';
|
||||
import { LocationQuery, LocationQueryValue, useRoute } from "vue-router";
|
||||
import { getCaptchaApi } from "@/api/auth";
|
||||
import { LoginData } from "@/api/auth/types";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const route = useRoute();
|
||||
|
|
@ -128,14 +128,14 @@ const captchaBase64 = ref();
|
|||
const loginFormRef = ref(ElForm);
|
||||
|
||||
const loginData = ref<LoginData>({
|
||||
username: 'admin',
|
||||
password: '123456'
|
||||
username: "admin",
|
||||
password: "123456",
|
||||
});
|
||||
|
||||
const loginRules = {
|
||||
username: [{ required: true, trigger: 'blur' }],
|
||||
password: [{ required: true, trigger: 'blur', validator: passwordValidator }],
|
||||
verifyCode: [{ required: true, trigger: 'blur' }]
|
||||
username: [{ required: true, trigger: "blur" }],
|
||||
password: [{ required: true, trigger: "blur", validator: passwordValidator }],
|
||||
verifyCode: [{ required: true, trigger: "blur" }],
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -143,7 +143,7 @@ const loginRules = {
|
|||
*/
|
||||
function passwordValidator(rule: any, value: any, callback: any) {
|
||||
if (value.length < 6) {
|
||||
callback(new Error('The password can not be less than 6 digits'));
|
||||
callback(new Error("The password can not be less than 6 digits"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
|
|
@ -154,7 +154,7 @@ function passwordValidator(rule: any, value: any, callback: any) {
|
|||
*/
|
||||
function checkCapslock(e: any) {
|
||||
const { key } = e;
|
||||
isCapslock.value = key && key.length === 1 && key >= 'A' && key <= 'Z';
|
||||
isCapslock.value = key && key.length === 1 && key >= "A" && key <= "Z";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -180,11 +180,11 @@ function handleLogin() {
|
|||
.then(() => {
|
||||
const query: LocationQuery = route.query;
|
||||
|
||||
const redirect = (query.redirect as LocationQueryValue) ?? '/';
|
||||
const redirect = (query.redirect as LocationQueryValue) ?? "/";
|
||||
|
||||
const otherQueryParams = Object.keys(query).reduce(
|
||||
(acc: any, cur: string) => {
|
||||
if (cur !== 'redirect') {
|
||||
if (cur !== "redirect") {
|
||||
acc[cur] = query[cur];
|
||||
}
|
||||
return acc;
|
||||
|
|
@ -212,10 +212,10 @@ onMounted(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.login-container {
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
background-color: #2d3a4b;
|
||||
min-height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #2d3a4b;
|
||||
|
||||
.login-form {
|
||||
width: 520px;
|
||||
|
|
@ -226,12 +226,12 @@ onMounted(() => {
|
|||
|
||||
.captcha {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
|
||||
img {
|
||||
height: 48px;
|
||||
width: 120px;
|
||||
height: 48px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
@ -239,8 +239,8 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.el-form-item {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
background: rgb(0 0 0 / 10%);
|
||||
border: 1px solid rgb(255 255 255 / 10%);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
|
@ -254,10 +254,10 @@ onMounted(() => {
|
|||
box-shadow: none;
|
||||
|
||||
.el-input__inner {
|
||||
background: transparent;
|
||||
border: 0px;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
caret-color: #fff;
|
||||
|
||||
&:-webkit-autofill {
|
||||
|
|
@ -270,9 +270,8 @@ onMounted(() => {
|
|||
&:-webkit-autofill:hover,
|
||||
&:-webkit-autofill:focus,
|
||||
&:-webkit-autofill:active {
|
||||
-webkit-transition-delay: 99999s;
|
||||
-webkit-transition: color 99999s ease-out,
|
||||
background-color 99999s ease-out;
|
||||
transition: color 99999s ease-out, background-color 99999s ease-out;
|
||||
transition-delay: 99999s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue