feat: 暂存
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCounterStore } from '@/stores/counter';
|
import { useCounterStore } from '@/stores/counter.ts';
|
||||||
|
|
||||||
import { useDark, useToggle } from '@vueuse/core';
|
import { useDark, useToggle } from '@vueuse/core';
|
||||||
const isDark = useDark();
|
const isDark = useDark();
|
||||||
@@ -20,6 +20,8 @@ const store = useCounterStore();
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
|
|
||||||
<SvgIcon icon="lock"></SvgIcon>
|
<SvgIcon icon="lock"></SvgIcon>
|
||||||
|
|
||||||
|
<ChangeTheme />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
19
src/components/i18n/SelectLanguage.vue
Normal file
19
src/components/i18n/SelectLanguage.vue
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<template>
|
||||||
|
<el-dropdown trigger="click">
|
||||||
|
<SvgIcon pointer icon="language"></SvgIcon>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item @click="changeLang('zh')">中文</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click="changeLang('en')">English</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { locale } = useI18n();
|
||||||
|
const changeLang = (lang: string) => {
|
||||||
|
locale.value = lang;
|
||||||
|
localStorage.setItem('lang', lang);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
``
|
||||||
33
src/components/icon/SvgIcon.vue
Normal file
33
src/components/icon/SvgIcon.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
prefix: {
|
||||||
|
type: String,
|
||||||
|
default: 'icon',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
|
pointer: {
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbolId = computed(() => {
|
||||||
|
return `#${props.prefix}-${props.icon}`;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-icon :size="size" :color="color" :style="props.pointer ? { cursor: 'pointer' } : {}">
|
||||||
|
<svg aria-hidden="true">
|
||||||
|
<use :xlink:href="symbolId" :fill="color" />
|
||||||
|
</svg>
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
8
src/components/icon/data.d.ts
vendored
Normal file
8
src/components/icon/data.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
declare namespace TYPE {
|
||||||
|
type SvgIconType = {
|
||||||
|
icon: string;
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
prefix?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
6
src/components/icon/useSvgIcon.ts
Normal file
6
src/components/icon/useSvgIcon.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { VNode } from 'vue';
|
||||||
|
import SvgIcon from './SvgIcon.vue';
|
||||||
|
|
||||||
|
export const useSvgIcon = (props: TYPE.SvgIconType): VNode => {
|
||||||
|
return h(SvgIcon, props);
|
||||||
|
};
|
||||||
66
src/components/theme/ChangeTheme.vue
Normal file
66
src/components/theme/ChangeTheme.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useDark } from '@vueuse/core';
|
||||||
|
import { useSvgIcon } from '../icon/useSvgIcon';
|
||||||
|
const isDark = useDark();
|
||||||
|
|
||||||
|
const darkIcon = useSvgIcon({ icon: 'dark', color: '#fde047' });
|
||||||
|
const lightIcon = useSvgIcon({ icon: 'light', color: '#fde047' });
|
||||||
|
|
||||||
|
// 添加动画效果
|
||||||
|
function toggleTheme(event: MouseEvent) {
|
||||||
|
const isAppearanceTransition =
|
||||||
|
// @ts-expect-error 实验性质方法
|
||||||
|
document.startViewTransition &&
|
||||||
|
!window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if (!isAppearanceTransition || !event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const x = event.clientX;
|
||||||
|
const y = event.clientY;
|
||||||
|
const endRadius = Math.hypot(
|
||||||
|
Math.max(x, innerWidth - x),
|
||||||
|
Math.max(y, innerHeight - y)
|
||||||
|
);
|
||||||
|
// @ts-expect-error: Transition API
|
||||||
|
const transition = document.startViewTransition(async () => {
|
||||||
|
isDark.value = !isDark.value;
|
||||||
|
await nextTick();
|
||||||
|
});
|
||||||
|
transition.ready.then(() => {
|
||||||
|
const clipPath = [
|
||||||
|
`circle(0px at ${x}px ${y}px)`,
|
||||||
|
`circle(${endRadius}px at ${x}px ${y}px)`,
|
||||||
|
];
|
||||||
|
document.documentElement.animate(
|
||||||
|
{
|
||||||
|
clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: 4500,
|
||||||
|
easing: 'ease-in',
|
||||||
|
pseudoElement: isDark.value
|
||||||
|
? '::view-transition-old(root)'
|
||||||
|
: '::view-transition-new(root)',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<!-- :border-color="blackColor"
|
||||||
|
:active-color="blackColor" -->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-switch
|
||||||
|
@click.stop="
|
||||||
|
(e:MouseEvent) => {
|
||||||
|
toggleTheme(e);
|
||||||
|
}
|
||||||
|
"
|
||||||
|
:before-change="() => false"
|
||||||
|
v-model="isDark"
|
||||||
|
inline-prompt
|
||||||
|
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #a1a1a1"
|
||||||
|
:active-icon="darkIcon"
|
||||||
|
:inactive-icon="lightIcon"
|
||||||
|
></el-switch>
|
||||||
|
</template>
|
||||||
@@ -5,6 +5,7 @@ import { createMemoryHistory, createRouter } from 'vue-router';
|
|||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import HelloWorld from './components/HelloWorld.vue';
|
import HelloWorld from './components/HelloWorld.vue';
|
||||||
|
|
||||||
|
// 引入黑色主题
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css';
|
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||||
import Demo from './components/Demo.vue';
|
import Demo from './components/Demo.vue';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
|
|||||||
11
src/stores/global.ts
Normal file
11
src/stores/global.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export const useGlobalStore = defineStore('global', () => {
|
||||||
|
const count = ref(0);
|
||||||
|
const doubleCount = computed(() => count.value * 2);
|
||||||
|
function increment() {
|
||||||
|
count.value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { count, doubleCount, increment };
|
||||||
|
});
|
||||||
145
src/style.css
145
src/style.css
@@ -1,79 +1,110 @@
|
|||||||
:root {
|
/* 清除 HTML 默认样式的 CSS Reset */
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* 使字体大小在浏览器上更容易控制 */
|
||||||
|
font-size: 100%;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
font-weight: 400;
|
}
|
||||||
|
|
||||||
color-scheme: light dark;
|
body {
|
||||||
color: rgba(255, 255, 255, 0.87);
|
/* 确保 body 占满整个屏幕高度 */
|
||||||
background-color: #242424;
|
min-height: 100vh;
|
||||||
|
text-rendering: optimizeSpeed;
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
video,
|
||||||
|
canvas,
|
||||||
|
svg {
|
||||||
|
/* 确保多媒体元素不超过其容器 */
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
/* 去除表单元素的默认样式,使用继承字体 */
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-weight: 500;
|
/* 清除链接的默认下划线和颜色 */
|
||||||
color: #646cff;
|
text-decoration: none;
|
||||||
text-decoration: inherit;
|
color: inherit;
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #535bf2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
ul,
|
||||||
margin: 0;
|
ol {
|
||||||
display: flex;
|
/* 清除列表的默认缩进和样式 */
|
||||||
place-items: center;
|
list-style: none;
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
table {
|
||||||
font-size: 3.2em;
|
/* 确保表格的布局正确 */
|
||||||
line-height: 1.1;
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保所有元素都有一致的外边距、填充和边界盒模型 */
|
||||||
|
* {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
/* 为所有浏览器设定一致的滚动行为 */
|
||||||
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
border-radius: 8px;
|
/* 去除按钮的默认边框 */
|
||||||
border: 1px solid transparent;
|
background: none;
|
||||||
padding: 0.6em 1.2em;
|
border: none;
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
border-color: #646cff;
|
|
||||||
}
|
|
||||||
button:focus,
|
|
||||||
button:focus-visible {
|
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
blockquote,
|
||||||
padding: 2em;
|
q {
|
||||||
|
/* 取消 blockquote 和 q 的默认引用样式 */
|
||||||
|
quotes: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
blockquote::before,
|
||||||
max-width: 1280px;
|
blockquote::after,
|
||||||
margin: 0 auto;
|
q::before,
|
||||||
padding: 2rem;
|
q::after {
|
||||||
text-align: center;
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
// 添加主题切换动画
|
||||||
:root {
|
::view-transition-new(root),
|
||||||
color: #213547;
|
::view-transition-old(root) {
|
||||||
background-color: #ffffff;
|
animation: none;
|
||||||
}
|
mix-blend-mode: normal;
|
||||||
a:hover {
|
}
|
||||||
color: #747bff;
|
|
||||||
}
|
::view-transition-old(root) {
|
||||||
button {
|
z-index: 1;
|
||||||
background-color: #f9f9f9;
|
}
|
||||||
}
|
|
||||||
|
::view-transition-new(root) {
|
||||||
|
z-index: 2147483646;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark::view-transition-old(root) {
|
||||||
|
z-index: 2147483646;
|
||||||
|
}
|
||||||
|
html.dark::view-transition-new(root) {
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/vite-env.d.ts
vendored
12
src/vite-env.d.ts
vendored
@@ -1 +1,13 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. /// <reference types="vite/client" />:
|
||||||
|
• 这行代码是一个三斜线指令,它告诉 TypeScript 编译器去引用 vite/client 的类型声明。
|
||||||
|
• Vite 自带的 vite/client 模块中包含了 Vite 环境下常用的全局变量和工具的类型声明。比如:
|
||||||
|
• import.meta: Vite 扩展了 ES Modules 中的 import.meta 对象,在 Vite 项目中有额外的属性如 import.meta.env,该属性中包含了一些 Vite 的环境变量。
|
||||||
|
• import.meta.env: 通过它,你可以访问定义在 .env 文件或 VITE_ 前缀的自定义环境变量。这些变量在 vite/client 模块中有类型定义,能提供良好的开发体验和自动完成支持。
|
||||||
|
2. vite-env.d.ts 文件的作用:
|
||||||
|
• 这是一个全局的类型声明文件,通常用于声明在整个项目中可以使用的全局类型。
|
||||||
|
• 该文件通常位于项目的根目录下,它使 TypeScript 知道 Vite 环境下的一些特殊语法(如 import.meta)以及任何自定义的全局类型。
|
||||||
|
• 这个文件的存在确保在使用 Vite 特定功能(例如动态导入、环境变量)时,不会出现 TypeScript 编译错误,并能获得相应的类型提示。
|
||||||
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user