<script setup> import { onMounted, onUnmounted, ref, useSlots } from 'vue' const slots = useSlots() const slides = ref([]) const originalSlidesLength = ref(0) const currentIndex = ref(0) const isDragging = ref(false) const startX = ref(0) const currentTranslate = ref(0) const prevTranslate = ref(0) const props = defineProps({ autoPlay: { type: Boolean, default: false } }) onMounted(() => { const originalSlides = slots.default()[0].children.map((slide, index) => ({ content: slide, key: index })) originalSlidesLength.value = originalSlides.length slides.value = [...originalSlides, originalSlides[0]] startAutoPlay() }) let autoPlayInterval const startAutoPlay = () => { if (!props.autoPlay) return autoPlayInterval = setInterval(() => { goToNextSlide() }, 3000) } const goToNextSlide = () => { if (currentIndex.value < slides.value.length - 2) { currentIndex.value += 1 } else { currentIndex.value = 0 updateTranslate(true) return } updateTranslate() } const startDrag = (event) => { isDragging.value = true startX.value = event.clientX prevTranslate.value = currentTranslate.value clearInterval(autoPlayInterval) } const onDrag = (event) => { if (isDragging.value) { const currentX = event.clientX const diff = currentX - startX.value currentTranslate.value = prevTranslate.value + (diff / window.innerWidth) * 100 } } const endDrag = () => { if (isDragging.value) { isDragging.value = false const movedBy = currentTranslate.value - prevTranslate.value if (movedBy < -10 && currentIndex.value < slides.value.length - 2) { currentIndex.value += 1 } else if (movedBy > 10 && currentIndex.value > 0) { currentIndex.value -= 1 } else if (movedBy > 10 && currentIndex.value === 0) { currentIndex.value = slides.value.length - 2 } else if (movedBy < -10 && currentIndex.value === slides.value.length - 2) { currentIndex.value = 0 } updateTranslate() startAutoPlay() } } const updateTranslate = (instant = false) => { prevTranslate.value = -currentIndex.value * 100 currentTranslate.value = prevTranslate.value if (instant) { requestAnimationFrame(() => { currentTranslate.value = prevTranslate.value }) } } const goToSlide = (index) => { currentIndex.value = index updateTranslate() } onUnmounted(() => { clearInterval(autoPlayInterval) }) </script> <template> <div class="carousel-container"> <div class="carousel" @mousedown="startDrag" @mousemove="onDrag" @mouseup="endDrag" @mouseleave="endDrag" :style="{ transform: `translateX(${currentTranslate}%)`, transition: isDragging ? 'none' : 'transform 0.3s ease' }" > <div v-for="(slide, index) in slides" :key="slide.key" class="slide"> <component :is="slide.content"></component> </div> </div> <div class="indicators"> <span v-for="(indicator, index) in slides.slice(0, originalSlidesLength)" :key="index" class="indicator" :class="{ active: currentIndex === index || (currentIndex === slides.length - 1 && index === 0) }" @click="goToSlide(index)" ></span> </div> </div> </template> <style scoped> .carousel-container { position: relative; width: 100%; height: 100%; margin: 0 auto; overflow: hidden; padding-bottom: 30px; } .carousel { height: 100%; display: flex; } .slide { min-width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; font-size: 2em; color: white; user-select: none; } .indicators { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; gap: 10px; } .indicator { width: 10px; height: 6px; background-color: rgba(255, 255, 255, 0.5); cursor: pointer; transition: background-color 0.3s ease; } .indicator.active { background-color: rgba(255, 255, 255, 1); } </style>
如何使用 unplugin-auto-import 和 unplugin-vue-components 自动导入组件、ui库、单文件组件
自动导入Vue 、Vue-route、pinia、AntdV、Vant
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import vueDevTools from 'vite-plugin-vue-devtools' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { VantResolver } from '@vant/auto-import-resolver' import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers' import GetLoginTemplate from './src/utils/GetLoginTemplate' import { AntDesignIconResolver,AntDesignApiResolver } from './src/utils/AntDesignResolver' export default defineConfig({ server: { host: '', proxy: { '/api': 'http://localhost:4000' } }, plugins: [ vue(), vueJsx(), vueDevTools(), AutoImport({ dirs: ['@/utils/Composable/**'], eslintrc: { enabled: true, filepath: '.eslintrc-auto-import.json', globalsPropValue: true }, imports: ['vue', 'vue-router', 'pinia'], include: [/\.[tj]sx?$/, /\.vue$/, /\.vue\?vue/, /\.md$/], dts: 'auto-imports.d.ts', resolvers: [ VantResolver(), AntDesignVueResolver({ importStyle: 'less' }), AntDesignApiResolver() ] }), Components({ dirs: ['./src/components/**', './src/utils/Composable/**', './src/Layout/**'], extensions: ['vue'], exclude: [/node_modules/, /\.git/, /\.nuxt/, /dist/, /public/, /test/, /mocks/], include: [/\.vue$/, /\.vue\?vue/, /\.md$/, /\.ts$/, /\.js$/, /\.jsx$/, /\.tsx$/], deep: true, dts: 'components.d.ts', resolvers: [ VantResolver(), AntDesignVueResolver({ importStyle: 'less' }), AntDesignIconResolver() ] }) ], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, css: { preprocessorOptions: { scss: { additionalData: '@import "@/assets/variables.scss";' } } } })继续阅读“如何使用 unplugin-auto-import 和 unplugin-vue-components 自动导入组件、ui库、单文件组件”
Macos安装conda 切换源
下载安装
https://docs.anaconda.com/miniconda/
切换源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
初始化虚拟环境
cd work_dir
conda create -n space_name python=3.8
conda activate space_name
如何通过 openvpn 连接服务器
sudo curl -O https://raw.githubusercontent.com/angristan/openvpn-install/master/openvpn-install.sh
sudo chmod +x openvpn-install.sh
sudo ./openvpn-install.sh
根据提示选择

下载配置文件
导入客户端即可连接


注意:如果是云服务器需要开放对应的tcp/udp端口
TS实现一个简单的 pick 方法

function pick<T, U extends keyof T>(data: T, keys: U[]): { [K in U]: T[K] } { const obj: any = {}; for (const key of keys) { obj[key] = data[key]; } return obj; } const user = { name: 'Tom', age: 19, }; pick(user, ['name', 'age']);
scp远程复制
注:如果使用 root 用户,需要配置sshd_config文件 打开root远程登录
ListenAddress 改为 0.0.0.0 或 ip地址
PermitRootLogin 改为 yes
使用 systemctl restart sshd 重启
一、 从 本地 复制到 远程
1. 复制文件
(1.)不修改文件名
scp local_file remote_username@remote_ip:remote_folder
(2.)修改文件名
scp local_file remote_username@remote_ip:remote_file
2. 复制目录
scp -r local_folder remote_username@remote_ip:remote_folder
二、从 远程 复制到 本地
1. 从 复制文件
(1.)不修改文件名
scp remote_username@remote_ip:remote_folder local_file
(2.)修改文件名
scp remote_username@remote_ip:remote_file local_file
2. 复制目录
scp -r remote_username@remote_ip:remote_folder local_folder
Docker 网络代理配置
[Service]
Environment=”HTTP_PROXY=http://192.168.0.49:3128″
Environment=”HTTPS_PROXY=http://192.168.0.49:3128″
mkdir /etc/systemd/system/docker.service.d && vim /etc/systemd/system/docker.service.d/http-proxy.conf
brew update 更新失败
cd "$(brew --repo)"
git remote set-url origin https://mirrors.ustc.edu.cn/brew.git
cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"
git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git
重新执行 brew update
Spring Boot 中使用 SseEmitter 实现服务器推送消息
Spring Boot 中使用 SseEmitter 实现服务器推送消息的功能,可以通过维护一个连接池来记录当前连接数。具体步骤如下:
- 定义一个 ConcurrentHashMap 用于保存连接池,key 为用户 id 或其它唯一标识符,value 为 SseEmitter 对象。
swiftCopy codeprivate final ConcurrentHashMap<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
继续阅读“Spring Boot 中使用 SseEmitter 实现服务器推送消息” 关于ConcurrentHashMap
ConcurrentHashMap 是 Java 并发包中的一个线程安全的哈希表实现,可以在多线程环境下高效地并发读写,而不需要使用显式的同步机制(如 synchronized 关键字)。
ConcurrentHashMap 的主要特点如下:
- 并发读写:ConcurrentHashMap 可以同时支持多个线程并发读取和写入数据,而不需要加锁。在读操作中,ConcurrentHashMap 采用了读写分离的技术,将数据分成了多个段,每个段都有自己的锁,不同线程对不同段的数据进行读取时不会互相干扰。
- 高效性能:ConcurrentHashMap 在多线程并发读写时可以保持高效性能,相比于同步的 HashMap,在并发读写时能够提供更高的吞吐量和更低的延迟。
- 线程安全:ConcurrentHashMap 是线程安全的,多个线程可以同时对其进行读写操作,不需要额外的同步机制,因此避免了死锁和其他并发问题。
- 支持高并发:ConcurrentHashMap 适用于高并发场景,可以应对大量的读写操作,并且具有较好的可伸缩性。
- 可扩展性:ConcurrentHashMap 支持动态扩容和收缩,可以根据实际情况自动调整内部数据结构的大小。
总之,ConcurrentHashMap 是一种高效、线程安全、支持高并发和可扩展的哈希表实现,适用于多线程环境下的并发读写操作。