This commit is contained in:
Lan
2024-09-25 20:42:00 +08:00
parent 8f735949b3
commit 4e9ec4618a
4 changed files with 232 additions and 24 deletions
+31 -1
View File
@@ -1,15 +1,45 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watchEffect, provide } from 'vue' import { ref, watchEffect, provide, onMounted } from 'vue'
import { RouterView } from 'vue-router' import { RouterView } from 'vue-router'
import ThemeToggle from './components/ThemeToggle.vue' import ThemeToggle from './components/ThemeToggle.vue'
const isDarkMode = ref(false) const isDarkMode = ref(false)
// 检查系统颜色模式
const checkSystemColorScheme = () => {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
}
// 从本地存储获取用户之前的选择
const getUserPreference = () => {
const storedPreference = localStorage.getItem('colorMode')
if (storedPreference) {
return storedPreference === 'dark'
}
return null
}
// 设置颜色模式
const setColorMode = (isDark: boolean) => {
isDarkMode.value = isDark
localStorage.setItem('colorMode', isDark ? 'dark' : 'light')
}
onMounted(() => {
const userPreference = getUserPreference()
if (userPreference !== null) {
setColorMode(userPreference)
} else {
setColorMode(checkSystemColorScheme())
}
})
watchEffect(() => { watchEffect(() => {
document.documentElement.classList.toggle('dark', isDarkMode.value) document.documentElement.classList.toggle('dark', isDarkMode.value)
}) })
provide('isDarkMode', isDarkMode) provide('isDarkMode', isDarkMode)
provide('setColorMode', setColorMode)
</script> </script>
<template> <template>
+154
View File
@@ -0,0 +1,154 @@
<template>
<div class="border-progress-container" ref="container">
<canvas ref="canvas" class="border-progress-canvas"></canvas>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue';
const props = defineProps<{
progress: number;
}>();
const container = ref<HTMLDivElement | null>(null);
const canvas = ref<HTMLCanvasElement | null>(null);
let ctx: CanvasRenderingContext2D | null = null;
const drawProgress = () => {
if (!ctx || !canvas.value || !container.value) return;
const width = container.value.clientWidth;
const height = container.value.clientHeight;
canvas.value.width = width;
canvas.value.height = height;
const borderWidth = 4; // 边框宽度
const cornerRadius = 8; // 拐角半径
ctx.lineWidth = borderWidth;
// 创建渐变
const gradient = ctx.createLinearGradient(0, 0, width, height);
gradient.addColorStop(0, '#4f46e5'); // 靛蓝色
gradient.addColorStop(0.5, '#7c3aed'); // 紫色
gradient.addColorStop(1, '#db2777'); // 粉色
// 绘制背景
ctx.strokeStyle = 'rgba(229, 231, 235, 0.2)'; // 淡灰色半透明
drawRoundedRect(ctx, borderWidth / 2, borderWidth / 2, width - borderWidth, height - borderWidth, cornerRadius);
ctx.stroke();
// 计算进度
const totalLength = (width + height) * 2 - 8 * cornerRadius + 2 * Math.PI * cornerRadius;
const progressLength = (totalLength * props.progress) / 100;
// 绘制进度
ctx.strokeStyle = gradient;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
let remainingLength = progressLength;
const halfBorder = borderWidth / 2;
const innerWidth = width - borderWidth;
const innerHeight = height - borderWidth;
// 上边
if (remainingLength > 0) {
const topLength = Math.min(innerWidth - 2 * cornerRadius, remainingLength);
ctx.moveTo(cornerRadius + halfBorder, halfBorder);
ctx.lineTo(topLength + cornerRadius + halfBorder, halfBorder);
remainingLength -= topLength;
}
// 右上角
if (remainingLength > 0) {
const angle = Math.min(Math.PI / 2, remainingLength / cornerRadius);
ctx.arc(innerWidth - cornerRadius + halfBorder, cornerRadius + halfBorder, cornerRadius, -Math.PI / 2, angle - Math.PI / 2, false);
remainingLength -= angle * cornerRadius;
}
// 右边
if (remainingLength > 0) {
const rightLength = Math.min(innerHeight - 2 * cornerRadius, remainingLength);
ctx.lineTo(innerWidth + halfBorder, rightLength + cornerRadius + halfBorder);
remainingLength -= rightLength;
}
// 右下角
if (remainingLength > 0) {
const angle = Math.min(Math.PI / 2, remainingLength / cornerRadius);
ctx.arc(innerWidth - cornerRadius + halfBorder, innerHeight - cornerRadius + halfBorder, cornerRadius, 0, angle, false);
remainingLength -= angle * cornerRadius;
}
// 下边
if (remainingLength > 0) {
const bottomLength = Math.min(innerWidth - 2 * cornerRadius, remainingLength);
ctx.lineTo(innerWidth - bottomLength - cornerRadius + halfBorder, innerHeight + halfBorder);
remainingLength -= bottomLength;
}
// 左下角
if (remainingLength > 0) {
const angle = Math.min(Math.PI / 2, remainingLength / cornerRadius);
ctx.arc(cornerRadius + halfBorder, innerHeight - cornerRadius + halfBorder, cornerRadius, Math.PI / 2, Math.PI / 2 + angle, false);
remainingLength -= angle * cornerRadius;
}
// 左边
if (remainingLength > 0) {
const leftLength = Math.min(innerHeight - 2 * cornerRadius, remainingLength);
ctx.lineTo(halfBorder, innerHeight - leftLength - cornerRadius + halfBorder);
remainingLength -= leftLength;
}
// 左上角
if (remainingLength > 0) {
const angle = Math.min(Math.PI / 2, remainingLength / cornerRadius);
ctx.arc(cornerRadius + halfBorder, cornerRadius + halfBorder, cornerRadius, Math.PI, Math.PI + angle, false);
}
ctx.stroke();
};
function drawRoundedRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.arcTo(x + width, y, x + width, y + radius, radius);
ctx.lineTo(x + width, y + height - radius);
ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius);
ctx.lineTo(x + radius, y + height);
ctx.arcTo(x, y + height, x, y + height - radius, radius);
ctx.lineTo(x, y + radius);
ctx.arcTo(x, y, x + radius, y, radius);
ctx.closePath();
}
onMounted(() => {
if (canvas.value) {
ctx = canvas.value.getContext('2d');
drawProgress();
}
});
watch(() => props.progress, drawProgress);
</script>
<style scoped>
.border-progress-container {
position: relative;
width: 100%;
height: 100%;
}
.border-progress-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: all 0.3s ease;
}
</style>
+7 -13
View File
@@ -1,24 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { inject } from 'vue'
import { SunIcon, MoonIcon } from 'lucide-vue-next' import { SunIcon, MoonIcon } from 'lucide-vue-next'
const props = defineProps<{ const isDarkMode = inject('isDarkMode') as { value: boolean }
modelValue: boolean const setColorMode = inject('setColorMode') as (isDark: boolean) => void
}>()
const emit = defineEmits<{ const toggleColorMode = () => {
(e: 'update:modelValue', value: boolean): void setColorMode(!isDarkMode.value)
}>() }
const isDarkMode = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
</script> </script>
<template> <template>
<button <button
@click="isDarkMode = !isDarkMode" @click="toggleColorMode"
class="fixed top-4 right-4 p-2 rounded-full transition-all duration-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transform hover:rotate-180" class="fixed top-4 right-4 p-2 rounded-full transition-all duration-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transform hover:rotate-180"
:class="isDarkMode ? 'bg-gray-800 text-yellow-300' : 'bg-white text-gray-800'" :class="isDarkMode ? 'bg-gray-800 text-yellow-300' : 'bg-white text-gray-800'"
> >
+39 -9
View File
@@ -48,7 +48,7 @@
<!-- 文件上传区域 --> <!-- 文件上传区域 -->
<div <div
v-if="sendType === 'file'" v-if="sendType === 'file'"
class="rounded-xl p-6 flex flex-col items-center justify-center border-2 border-dashed transition-all duration-300 group cursor-pointer" class="rounded-xl p-6 flex flex-col items-center justify-center border-2 border-dashed transition-all duration-300 group cursor-pointer relative"
:class="[ :class="[
isDarkMode isDarkMode
? 'bg-gray-800 bg-opacity-50 border-gray-600 hover:border-indigo-500' ? 'bg-gray-800 bg-opacity-50 border-gray-600 hover:border-indigo-500'
@@ -65,6 +65,9 @@
@change="handleFileUpload" @change="handleFileUpload"
ref="fileInput" ref="fileInput"
/> />
<div class="absolute inset-0 w-full h-full" v-if="uploadProgress > 0">
<BorderProgressBar :progress="uploadProgress" />
</div>
<UploadCloudIcon <UploadCloudIcon
:class="[ :class="[
'w-16 h-16 transition-colors duration-300', 'w-16 h-16 transition-colors duration-300',
@@ -172,32 +175,59 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, inject } from 'vue' import { ref, inject } from 'vue'
import { UploadCloudIcon, SendIcon } from 'lucide-vue-next' import { UploadCloudIcon, SendIcon } from 'lucide-vue-next'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import BorderProgressBar from '../components/BorderProgressBar.vue'
const router = useRouter() const router = useRouter()
// 修改这里:使用 inject 来获取 isDarkMode
const isDarkMode = inject('isDarkMode') const isDarkMode = inject('isDarkMode')
const sendType = ref('file') const sendType = ref('file')
const selectedFile:any = ref(null) const selectedFile = ref<File | null>(null)
const textContent = ref('') const textContent = ref('')
const fileInput:any = ref(null) const fileInput = ref<HTMLInputElement | null>(null)
const expirationMethod = ref('time') const expirationMethod = ref('time')
const expirationTime = ref('') const expirationTime = ref('')
const expirationViews = ref('') const expirationViews = ref('')
const uploadProgress = ref(0)
const triggerFileUpload = () => { const triggerFileUpload = () => {
fileInput.value.click() fileInput.value?.click()
} }
const handleFileUpload = (event:any) => { const handleFileUpload = (event: Event) => {
selectedFile.value = event.target.files[0] const target = event.target as HTMLInputElement
if (target.files && target.files.length > 0) {
selectedFile.value = target.files[0]
simulateFileUpload()
}
} }
const handleFileDrop = (event:any) => { const handleFileDrop = (event: DragEvent) => {
if (event.dataTransfer?.files && event.dataTransfer.files.length > 0) {
selectedFile.value = event.dataTransfer.files[0] selectedFile.value = event.dataTransfer.files[0]
simulateFileUpload()
}
} }
const simulateFileUpload = () => {
uploadProgress.value = 0
const duration = 2000; // 总动画时长(毫秒)
const steps = 100; // 动画步数
const stepDuration = duration / steps;
const stepIncrement = 100 / steps;
const animate = (step: number) => {
if (step <= steps) {
uploadProgress.value = step * stepIncrement;
setTimeout(() => animate(step + 1), stepDuration);
}
};
animate(0);
}
const handleSubmit = () => { const handleSubmit = () => {
if (sendType.value === 'file' && !selectedFile.value) { if (sendType.value === 'file' && !selectedFile.value) {
alert('请选择要上传的文件') alert('请选择要上传的文件')