style
This commit is contained in:
+31
-1
@@ -1,15 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watchEffect, provide } from 'vue'
|
||||
import { ref, watchEffect, provide, onMounted } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
import ThemeToggle from './components/ThemeToggle.vue'
|
||||
|
||||
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(() => {
|
||||
document.documentElement.classList.toggle('dark', isDarkMode.value)
|
||||
})
|
||||
|
||||
provide('isDarkMode', isDarkMode)
|
||||
provide('setColorMode', setColorMode)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -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>
|
||||
@@ -1,24 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { inject } from 'vue'
|
||||
import { SunIcon, MoonIcon } from 'lucide-vue-next'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
}>()
|
||||
const isDarkMode = inject('isDarkMode') as { value: boolean }
|
||||
const setColorMode = inject('setColorMode') as (isDark: boolean) => void
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: boolean): void
|
||||
}>()
|
||||
|
||||
const isDarkMode = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (value) => emit('update:modelValue', value)
|
||||
})
|
||||
const toggleColorMode = () => {
|
||||
setColorMode(!isDarkMode.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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="isDarkMode ? 'bg-gray-800 text-yellow-300' : 'bg-white text-gray-800'"
|
||||
>
|
||||
|
||||
+40
-10
@@ -48,7 +48,7 @@
|
||||
<!-- 文件上传区域 -->
|
||||
<div
|
||||
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="[
|
||||
isDarkMode
|
||||
? 'bg-gray-800 bg-opacity-50 border-gray-600 hover:border-indigo-500'
|
||||
@@ -65,6 +65,9 @@
|
||||
@change="handleFileUpload"
|
||||
ref="fileInput"
|
||||
/>
|
||||
<div class="absolute inset-0 w-full h-full" v-if="uploadProgress > 0">
|
||||
<BorderProgressBar :progress="uploadProgress" />
|
||||
</div>
|
||||
<UploadCloudIcon
|
||||
:class="[
|
||||
'w-16 h-16 transition-colors duration-300',
|
||||
@@ -172,32 +175,59 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject } from 'vue'
|
||||
import { ref, inject } from 'vue'
|
||||
import { UploadCloudIcon, SendIcon } from 'lucide-vue-next'
|
||||
import { useRouter } from 'vue-router'
|
||||
import BorderProgressBar from '../components/BorderProgressBar.vue'
|
||||
|
||||
const router = useRouter()
|
||||
// 修改这里:使用 inject 来获取 isDarkMode
|
||||
const isDarkMode = inject('isDarkMode')
|
||||
|
||||
const sendType = ref('file')
|
||||
const selectedFile:any = ref(null)
|
||||
const selectedFile = ref<File | null>(null)
|
||||
const textContent = ref('')
|
||||
const fileInput:any = ref(null)
|
||||
const fileInput = ref<HTMLInputElement | null>(null)
|
||||
const expirationMethod = ref('time')
|
||||
const expirationTime = ref('')
|
||||
const expirationViews = ref('')
|
||||
const uploadProgress = ref(0)
|
||||
|
||||
const triggerFileUpload = () => {
|
||||
fileInput.value.click()
|
||||
fileInput.value?.click()
|
||||
}
|
||||
|
||||
const handleFileUpload = (event:any) => {
|
||||
selectedFile.value = event.target.files[0]
|
||||
const handleFileUpload = (event: Event) => {
|
||||
const target = event.target as HTMLInputElement
|
||||
if (target.files && target.files.length > 0) {
|
||||
selectedFile.value = target.files[0]
|
||||
simulateFileUpload()
|
||||
}
|
||||
}
|
||||
|
||||
const handleFileDrop = (event:any) => {
|
||||
selectedFile.value = event.dataTransfer.files[0]
|
||||
const handleFileDrop = (event: DragEvent) => {
|
||||
if (event.dataTransfer?.files && event.dataTransfer.files.length > 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 = () => {
|
||||
if (sendType.value === 'file' && !selectedFile.value) {
|
||||
alert('请选择要上传的文件')
|
||||
|
||||
Reference in New Issue
Block a user