715 lines
19 KiB
Vue
715 lines
19 KiB
Vue
<template>
|
||
<div class="file-system">
|
||
<a-space direction="vertical" style="width: 100%" :size="16">
|
||
|
||
<!-- 路径输入 -->
|
||
<a-card title="📂 文件浏览">
|
||
<a-space direction="vertical" :size="12" style="width: 100%">
|
||
<a-input-group>
|
||
<a-auto-complete
|
||
v-model="filePath"
|
||
:data="pathHistory"
|
||
placeholder="输入文件或目录路径 (如: C:\Users 或 /home/user)"
|
||
style="flex: 1"
|
||
@select="onPathSelect"
|
||
/>
|
||
<a-button @click="browseDirectory">
|
||
<template #icon>
|
||
<icon-folder />
|
||
</template>
|
||
浏览
|
||
</a-button>
|
||
<a-button type="primary" @click="listDirectory" :loading="fileLoading">
|
||
<template #icon>
|
||
<icon-list />
|
||
</template>
|
||
列出目录
|
||
</a-button>
|
||
</a-input-group>
|
||
|
||
<!-- 常用路径快捷按钮 -->
|
||
<a-space wrap>
|
||
<a-tag
|
||
v-for="shortcut in commonPaths"
|
||
:key="shortcut.path"
|
||
@click="openPath(shortcut.path)"
|
||
style="cursor: pointer"
|
||
>
|
||
<template #icon>
|
||
<icon-forward />
|
||
</template>
|
||
{{ shortcut.name }}
|
||
</a-tag>
|
||
</a-space>
|
||
</a-space>
|
||
</a-card>
|
||
|
||
<!-- 收藏的文件 -->
|
||
<a-card title="⭐ 收藏夹" v-if="favoriteFiles.length > 0">
|
||
<a-space wrap>
|
||
<a-tag
|
||
v-for="fav in favoriteFiles"
|
||
:key="fav.path"
|
||
closable
|
||
@close="removeFavorite(fav.path)"
|
||
@click="openFavoriteFile(fav.path)"
|
||
style="cursor: pointer; margin-bottom: 4px; padding: 4px 8px"
|
||
>
|
||
<template #icon>
|
||
<span style="font-size: 16px">{{ fav.is_dir ? '📁' : '📄' }}</span>
|
||
</template>
|
||
{{ fav.name }}
|
||
</a-tag>
|
||
</a-space>
|
||
</a-card>
|
||
|
||
<!-- 文件列表和编辑器 -->
|
||
<a-row :gutter="16">
|
||
<a-col :span="12">
|
||
<a-card title="📋 文件列表" style="height: 100%">
|
||
<a-list
|
||
:data="fileList"
|
||
:loading="fileLoading"
|
||
:bordered="false"
|
||
style="max-height: 500px; overflow-y: auto"
|
||
:pagination="false"
|
||
>
|
||
<template #item="{ item }">
|
||
<a-list-item class="file-item-compact">
|
||
<a-list-item-meta>
|
||
<template #title>
|
||
<div class="file-item-content">
|
||
<span class="file-icon">{{ getFileIcon(item) }}</span>
|
||
<a @click="selectFile(item.path)" class="file-name-compact">{{ item.name }}</a>
|
||
<span class="file-info-inline">
|
||
<span v-if="!item.is_dir" class="file-size">{{ formatBytes(item.size) }}</span>
|
||
<span class="file-time">{{ item.mod_time }}</span>
|
||
</span>
|
||
<a-button
|
||
type="text"
|
||
size="mini"
|
||
@click.stop="toggleFavorite(item)"
|
||
class="favorite-btn"
|
||
>
|
||
<template #icon>
|
||
<icon-star-fill v-if="isFavorite(item.path)" />
|
||
<icon-star v-else />
|
||
</template>
|
||
</a-button>
|
||
</div>
|
||
</template>
|
||
</a-list-item-meta>
|
||
</a-list-item>
|
||
</template>
|
||
</a-list>
|
||
</a-card>
|
||
</a-col>
|
||
|
||
<a-col :span="12">
|
||
<a-card title="📝 文件内容">
|
||
<a-space direction="vertical" :size="8" style="width: 100%">
|
||
<div
|
||
class="file-content-wrapper"
|
||
:style="{ height: fileContentHeight + 'px' }"
|
||
>
|
||
<a-textarea
|
||
v-model="fileContent"
|
||
class="file-content-textarea"
|
||
placeholder="文件内容将显示在这里,点击文件列表中的文件即可查看"
|
||
/>
|
||
</div>
|
||
<div
|
||
class="resize-handle"
|
||
@mousedown="startResize"
|
||
title="拖拽调整高度"
|
||
>
|
||
<div class="resize-handle-bar"></div>
|
||
</div>
|
||
<a-space>
|
||
<a-button type="primary" @click="readFile" :loading="fileLoading">
|
||
<template #icon>
|
||
<icon-file />
|
||
</template>
|
||
读取
|
||
</a-button>
|
||
<a-button @click="writeFile" :loading="fileLoading">
|
||
<template #icon>
|
||
<icon-save />
|
||
</template>
|
||
保存
|
||
</a-button>
|
||
<a-button status="danger" @click="deleteFile" :loading="fileLoading">
|
||
<template #icon>
|
||
<icon-delete />
|
||
</template>
|
||
删除
|
||
</a-button>
|
||
<a-button @click="clearContent">
|
||
<template #icon>
|
||
<icon-eraser />
|
||
</template>
|
||
清空
|
||
</a-button>
|
||
</a-space>
|
||
</a-space>
|
||
</a-card>
|
||
</a-col>
|
||
</a-row>
|
||
</a-space>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, onMounted, ref, watch } from 'vue'
|
||
import { Message, Modal } from '@arco-design/web-vue'
|
||
import {
|
||
IconFolder,
|
||
IconList,
|
||
IconForward,
|
||
IconStarFill,
|
||
IconStar,
|
||
IconFile,
|
||
IconSave,
|
||
IconDelete,
|
||
IconEraser
|
||
} from '@arco-design/web-vue/es/icon'
|
||
import {
|
||
listDir,
|
||
readFile as readFileApi,
|
||
writeFile as writeFileApi,
|
||
deletePath
|
||
} from '@/api'
|
||
|
||
// localStorage 键名
|
||
const STORAGE_KEYS = {
|
||
FILE_PATH: 'filesystem-file-path',
|
||
FILE_LIST: 'filesystem-file-list',
|
||
FILE_CONTENT: 'filesystem-file-content',
|
||
PATH_HISTORY: 'filesystem-path-history',
|
||
FILE_CONTENT_HEIGHT: 'filesystem-file-content-height',
|
||
FAVORITE_FILES: 'filesystem-favorite-files'
|
||
}
|
||
|
||
// 常用路径快捷方式
|
||
const commonPaths = ref([])
|
||
const systemPaths = ref({})
|
||
|
||
// 加载常用系统路径
|
||
const loadCommonPaths = async () => {
|
||
try {
|
||
const paths = await window.go.main.App.GetCommonPaths()
|
||
systemPaths.value = paths
|
||
|
||
const platform = window.navigator.platform
|
||
if (platform.includes('Win')) {
|
||
// 基础路径
|
||
const pathList = [
|
||
{ name: '🖥️ 桌面', path: paths.desktop },
|
||
{ name: '📁 文档', path: paths.documents },
|
||
{ name: '📥 下载', path: paths.downloads },
|
||
{ name: '💾 用户目录', path: paths.home }
|
||
]
|
||
|
||
// 动态添加所有盘符(按字母顺序)
|
||
const drives = []
|
||
for (const key in paths) {
|
||
if (key.startsWith('root_')) {
|
||
const driveLetter = key.substring(5)
|
||
drives.push({
|
||
letter: driveLetter,
|
||
path: paths[key]
|
||
})
|
||
}
|
||
}
|
||
drives.sort((a, b) => a.letter.localeCompare(b.letter))
|
||
|
||
// 添加盘符到路径列表
|
||
drives.forEach(drive => {
|
||
pathList.push({
|
||
name: `💿 ${drive.letter}盘`,
|
||
path: drive.path
|
||
})
|
||
})
|
||
|
||
commonPaths.value = pathList
|
||
} else {
|
||
commonPaths.value = [
|
||
{ name: '🖥️ 桌面', path: paths.desktop },
|
||
{ name: '📁 文档', path: paths.documents },
|
||
{ name: '📥 下载', path: paths.downloads },
|
||
{ name: '🏠 主目录', path: paths.home },
|
||
{ name: '📂 根目录', path: '/' }
|
||
]
|
||
}
|
||
} catch (error) {
|
||
console.error('加载常用路径失败:', error)
|
||
// 降级方案:使用默认路径
|
||
commonPaths.value = [
|
||
{ name: '💿 C盘', path: 'C:\\' },
|
||
{ name: '💿 D盘', path: 'D:\\' }
|
||
]
|
||
}
|
||
}
|
||
|
||
// 状态
|
||
const filePath = ref('')
|
||
const fileContent = ref('')
|
||
const fileList = ref([])
|
||
const fileLoading = ref(false)
|
||
const pathHistory = ref([])
|
||
const fileContentHeight = ref(250)
|
||
const favoriteFiles = ref([])
|
||
|
||
// 格式化文件大小
|
||
const formatBytes = (bytes) => {
|
||
if (!bytes) return '0 B'
|
||
const unit = 1024
|
||
if (bytes < unit) return bytes + ' B'
|
||
const exp = Math.floor(Math.log(bytes) / Math.log(unit))
|
||
return (bytes / Math.pow(unit, exp)).toFixed(2) + ' ' + 'KMGTPE'[exp - 1] + 'B'
|
||
}
|
||
|
||
// 获取文件图标(根据文件类型)
|
||
const getFileIcon = (item) => {
|
||
if (item.is_dir) {
|
||
return '📁'
|
||
}
|
||
|
||
const ext = item.name.split('.').pop()?.toLowerCase() || ''
|
||
|
||
// 图片文件
|
||
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'heic', 'heif']
|
||
if (imageExts.includes(ext)) return '🖼️'
|
||
|
||
// 视频文件
|
||
const videoExts = ['mp4', 'avi', 'mkv', 'mov', 'wmv', 'flv', 'webm', 'm4v', 'rmvb', '3gp']
|
||
if (videoExts.includes(ext)) return '🎬'
|
||
|
||
// 音频文件
|
||
const audioExts = ['mp3', 'wav', 'flac', 'aac', 'ogg', 'wma', 'm4a', 'opus']
|
||
if (audioExts.includes(ext)) return '🎵'
|
||
|
||
// 文档文件
|
||
const docExts = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'rtf', 'odt', 'ods', 'odp']
|
||
if (docExts.includes(ext)) {
|
||
if (ext === 'pdf') return '📕'
|
||
if (['doc', 'docx'].includes(ext)) return '📘'
|
||
if (['xls', 'xlsx'].includes(ext)) return '📗'
|
||
if (['ppt', 'pptx'].includes(ext)) return '📙'
|
||
if (ext === 'txt') return '📃'
|
||
return '📄'
|
||
}
|
||
|
||
// 压缩文件
|
||
const archiveExts = ['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'z', 'cab', 'iso']
|
||
if (archiveExts.includes(ext)) return '📦'
|
||
|
||
// 代码文件
|
||
const codeExts = ['js', 'ts', 'jsx', 'tsx', 'vue', 'py', 'java', 'c', 'cpp', 'h', 'go', 'rs', 'php', 'rb', 'cs', 'swift', 'kt', 'scala', 'html', 'css', 'scss', 'less', 'json', 'xml', 'yaml', 'yml', 'sql', 'sh', 'bat', 'ps1']
|
||
if (codeExts.includes(ext)) return '💻'
|
||
|
||
// 数据库文件
|
||
const dbExts = ['db', 'sqlite', 'mdb', 'accdb']
|
||
if (dbExts.includes(ext)) return '🗄️'
|
||
|
||
// 可执行文件
|
||
const exeExts = ['exe', 'msi', 'app', 'dmg', 'deb', 'rpm']
|
||
if (exeExts.includes(ext)) return '⚙️'
|
||
|
||
// 字体文件
|
||
const fontExts = ['ttf', 'otf', 'woff', 'woff2', 'eot']
|
||
if (fontExts.includes(ext)) return '🔤'
|
||
|
||
// 默认文件图标
|
||
return '📄'
|
||
}
|
||
|
||
// 列出目录
|
||
const listDirectory = async () => {
|
||
if (!filePath.value) {
|
||
Message.error('请输入目录路径')
|
||
return
|
||
}
|
||
|
||
addToHistory(filePath.value)
|
||
fileLoading.value = true
|
||
try {
|
||
fileList.value = await listDir(filePath.value)
|
||
} catch (error) {
|
||
console.error('列出目录失败:', error)
|
||
Message.error('列出目录失败: ' + (error.message || error))
|
||
} finally {
|
||
fileLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 路径选择
|
||
const onPathSelect = (value) => {
|
||
filePath.value = value
|
||
listDirectory()
|
||
}
|
||
|
||
// 打开路径
|
||
const openPath = (path) => {
|
||
filePath.value = path
|
||
listDirectory()
|
||
}
|
||
|
||
// 浏览目录
|
||
const browseDirectory = () => {
|
||
const path = prompt('请输入目录路径(例如: C:\\Users 或 /home/user)')
|
||
if (path) {
|
||
filePath.value = path
|
||
listDirectory()
|
||
}
|
||
}
|
||
|
||
// 选择文件
|
||
const selectFile = (path) => {
|
||
filePath.value = path
|
||
addToHistory(path)
|
||
}
|
||
|
||
// 读取文件
|
||
const readFile = async () => {
|
||
if (!filePath.value) {
|
||
Message.error('请输入文件路径')
|
||
return
|
||
}
|
||
|
||
addToHistory(filePath.value)
|
||
fileLoading.value = true
|
||
try {
|
||
fileContent.value = await readFileApi(filePath.value)
|
||
Message.success('文件读取成功')
|
||
} catch (error) {
|
||
console.error('读取文件失败:', error)
|
||
Message.error('读取文件失败: ' + (error.message || error))
|
||
} finally {
|
||
fileLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 写入文件
|
||
const writeFile = async () => {
|
||
if (!filePath.value) {
|
||
Message.error('请输入文件路径')
|
||
return
|
||
}
|
||
|
||
fileLoading.value = true
|
||
try {
|
||
await writeFileApi(filePath.value, fileContent.value)
|
||
Message.success('文件保存成功')
|
||
} catch (error) {
|
||
console.error('写入文件失败:', error)
|
||
Message.error('写入文件失败: ' + (error.message || error))
|
||
} finally {
|
||
fileLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 删除文件
|
||
const deleteFile = async () => {
|
||
if (!filePath.value) {
|
||
Message.error('请输入文件路径')
|
||
return
|
||
}
|
||
|
||
Modal.confirm({
|
||
title: '确认删除',
|
||
content: `确定要删除 ${filePath.value} 吗?此操作不可恢复!`,
|
||
onOk: async () => {
|
||
fileLoading.value = true
|
||
try {
|
||
await deletePath(filePath.value)
|
||
Message.success('删除成功')
|
||
// 清空状态
|
||
filePath.value = ''
|
||
fileContent.value = ''
|
||
fileList.value = []
|
||
// 重新列出目录
|
||
if (pathHistory.value.length > 0) {
|
||
filePath.value = pathHistory.value[0]
|
||
listDirectory()
|
||
}
|
||
} catch (error) {
|
||
console.error('删除失败:', error)
|
||
Message.error('删除失败: ' + (error.message || error))
|
||
} finally {
|
||
fileLoading.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 清空内容
|
||
const clearContent = () => {
|
||
fileContent.value = ''
|
||
Message.info('内容已清空')
|
||
}
|
||
|
||
// 拖拽调整高度
|
||
const startResize = (e) => {
|
||
const startY = e.clientY
|
||
const startHeight = fileContentHeight.value
|
||
|
||
const onMouseMove = (moveEvent) => {
|
||
const deltaY = moveEvent.clientY - startY
|
||
const newHeight = startHeight + deltaY
|
||
if (newHeight >= 150 && newHeight <= 800) {
|
||
fileContentHeight.value = newHeight
|
||
}
|
||
}
|
||
|
||
const onMouseUp = () => {
|
||
document.removeEventListener('mousemove', onMouseMove)
|
||
document.removeEventListener('mouseup', onMouseUp)
|
||
saveToStorage(STORAGE_KEYS.FILE_CONTENT_HEIGHT, fileContentHeight.value.toString())
|
||
}
|
||
|
||
document.addEventListener('mousemove', onMouseMove)
|
||
document.addEventListener('mouseup', onMouseUp)
|
||
}
|
||
|
||
// 历史记录
|
||
const addToHistory = (path) => {
|
||
if (!path || path.trim() === '') return
|
||
|
||
const index = pathHistory.value.indexOf(path)
|
||
if (index > -1) {
|
||
pathHistory.value.splice(index, 1)
|
||
}
|
||
|
||
pathHistory.value.unshift(path)
|
||
if (pathHistory.value.length > 20) {
|
||
pathHistory.value = pathHistory.value.slice(0, 20)
|
||
}
|
||
|
||
saveToStorage(STORAGE_KEYS.PATH_HISTORY, pathHistory.value)
|
||
}
|
||
|
||
// 收藏功能
|
||
const isFavorite = (path) => {
|
||
return favoriteFiles.value.some(fav => fav.path === path)
|
||
}
|
||
|
||
const toggleFavorite = (item) => {
|
||
const index = favoriteFiles.value.findIndex(fav => fav.path === item.path)
|
||
|
||
if (index > -1) {
|
||
favoriteFiles.value.splice(index, 1)
|
||
Message.info('已取消收藏: ' + item.name)
|
||
} else {
|
||
favoriteFiles.value.push({
|
||
path: item.path,
|
||
name: item.name,
|
||
is_dir: item.is_dir
|
||
})
|
||
Message.success('已收藏: ' + item.name)
|
||
}
|
||
|
||
saveToStorage(STORAGE_KEYS.FAVORITE_FILES, favoriteFiles.value)
|
||
}
|
||
|
||
const removeFavorite = (path) => {
|
||
const index = favoriteFiles.value.findIndex(fav => fav.path === path)
|
||
if (index > -1) {
|
||
const name = favoriteFiles.value[index].name
|
||
favoriteFiles.value.splice(index, 1)
|
||
saveToStorage(STORAGE_KEYS.FAVORITE_FILES, favoriteFiles.value)
|
||
Message.info('已取消收藏: ' + name)
|
||
}
|
||
}
|
||
|
||
const openFavoriteFile = (path) => {
|
||
filePath.value = path
|
||
addToHistory(path)
|
||
|
||
const fav = favoriteFiles.value.find(f => f.path === path)
|
||
if (fav && fav.is_dir) {
|
||
listDirectory()
|
||
} else {
|
||
readFile()
|
||
}
|
||
}
|
||
|
||
// localStorage 操作
|
||
const loadFromStorage = () => {
|
||
try {
|
||
const savedPath = localStorage.getItem(STORAGE_KEYS.FILE_PATH)
|
||
const savedFileList = localStorage.getItem(STORAGE_KEYS.FILE_LIST)
|
||
const savedFileContent = localStorage.getItem(STORAGE_KEYS.FILE_CONTENT)
|
||
const savedHistory = localStorage.getItem(STORAGE_KEYS.PATH_HISTORY)
|
||
const savedHeight = localStorage.getItem(STORAGE_KEYS.FILE_CONTENT_HEIGHT)
|
||
const savedFavorites = localStorage.getItem(STORAGE_KEYS.FAVORITE_FILES)
|
||
|
||
if (savedPath) filePath.value = savedPath
|
||
if (savedFileList) fileList.value = JSON.parse(savedFileList)
|
||
if (savedFileContent) fileContent.value = savedFileContent
|
||
if (savedHistory) pathHistory.value = JSON.parse(savedHistory)
|
||
if (savedFavorites) favoriteFiles.value = JSON.parse(savedFavorites)
|
||
if (savedHeight) {
|
||
const height = parseInt(savedHeight)
|
||
if (!isNaN(height) && height >= 150 && height <= 800) {
|
||
fileContentHeight.value = height
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('从 localStorage 加载数据失败:', error)
|
||
}
|
||
}
|
||
|
||
const saveToStorage = (key, value) => {
|
||
try {
|
||
if (typeof value === 'string') {
|
||
localStorage.setItem(key, value)
|
||
} else {
|
||
localStorage.setItem(key, JSON.stringify(value))
|
||
}
|
||
} catch (error) {
|
||
console.error('保存到 localStorage 失败:', error)
|
||
}
|
||
}
|
||
|
||
// 监听数据变化自动保存
|
||
watch(filePath, (newPath) => {
|
||
saveToStorage(STORAGE_KEYS.FILE_PATH, newPath)
|
||
})
|
||
|
||
watch(fileContent, (newContent) => {
|
||
saveToStorage(STORAGE_KEYS.FILE_CONTENT, newContent)
|
||
})
|
||
|
||
watch(fileList, (newList) => {
|
||
saveToStorage(STORAGE_KEYS.FILE_LIST, newList)
|
||
}, { deep: true })
|
||
|
||
onMounted(() => {
|
||
loadFromStorage()
|
||
loadCommonPaths()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.file-system {
|
||
padding: 16px;
|
||
}
|
||
|
||
/* 紧凑型文件列表项 */
|
||
.file-item-compact {
|
||
padding: 4px 0 !important;
|
||
margin: 0 !important;
|
||
}
|
||
|
||
.file-item-content {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.file-item-content:hover {
|
||
background: var(--color-fill-2);
|
||
}
|
||
|
||
.file-icon {
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
width: 24px;
|
||
text-align: center;
|
||
}
|
||
|
||
.file-name-compact {
|
||
flex: 1;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.file-info-inline {
|
||
display: flex;
|
||
gap: 12px;
|
||
font-size: 11px;
|
||
color: var(--color-text-3);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.file-size {
|
||
color: var(--color-text-3);
|
||
}
|
||
|
||
.file-time {
|
||
color: var(--color-text-3);
|
||
}
|
||
|
||
.favorite-btn {
|
||
flex-shrink: 0;
|
||
padding: 0 4px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 移除列表项的默认边距和内边距 */
|
||
.file-item-compact :deep(.arco-list-item-meta) {
|
||
padding: 0;
|
||
margin: 0;
|
||
}
|
||
|
||
.file-item-compact :deep(.arco-list-item-meta-title) {
|
||
margin: 0;
|
||
}
|
||
|
||
.file-item-compact :deep(.arco-list-item) {
|
||
padding: 0;
|
||
margin: 0;
|
||
border: none;
|
||
}
|
||
|
||
.file-content-wrapper {
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: height 0.1s ease;
|
||
border: 1px solid var(--color-border-2);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.file-content-textarea {
|
||
width: 100%;
|
||
height: 100%;
|
||
resize: none;
|
||
border: none;
|
||
}
|
||
|
||
.resize-handle {
|
||
height: 8px;
|
||
cursor: ns-resize;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: var(--color-fill-2);
|
||
border-radius: 4px;
|
||
margin: 4px 0;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.resize-handle:hover {
|
||
background: var(--color-fill-3);
|
||
}
|
||
|
||
.resize-handle-bar {
|
||
width: 40px;
|
||
height: 3px;
|
||
background: var(--color-border-3);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.resize-handle:hover .resize-handle-bar {
|
||
background: rgb(var(--primary-6));
|
||
}
|
||
</style>
|