初始化
This commit is contained in:
54
frontend/src/views/Settings.vue
Normal file
54
frontend/src/views/Settings.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { FileSliders, Database, Plus, Trash2, Upload, BarChart3, Folder, Languages, SunMoon, CheckCircle2, Info, Copy } from 'lucide-vue-next'
|
||||
import { call, isNative } from '../api'
|
||||
import { useAppStore } from '../store'
|
||||
|
||||
const tab=ref(new URLSearchParams(location.search).get('settingsTab')||'rules'),rules=ref([]),form=reactive({pattern:'',category:'custom'})
|
||||
const settings=reactive({theme:'dark',locale:'zh-CN',gitScope:'current',databasePath:'',autoRefresh:true,glassOpacity:55,loadingStyle:'fullscreen-orbit'})
|
||||
const store=useAppStore(),{locale}=useI18n(),native=isNative(),dbMessage=ref('')
|
||||
const groups=computed(()=>Object.groupBy?Object.groupBy(rules.value,x=>x.category):rules.value.reduce((a,x)=>((a[x.category]??=[]).push(x),a),{}))
|
||||
const loadingOptions=[
|
||||
{value:'fullscreen-orbit',title:'能量轨道',desc:'环形粒子、扫描光束和能量核心'},
|
||||
{value:'fullscreen-grid',title:'数据矩阵',desc:'流动数据网格和聚合节点'},
|
||||
{value:'fullscreen-warp',title:'光速跃迁',desc:'深空隧道、放射光束和跃迁环'},
|
||||
{value:'bar',title:'底部进度条',desc:'保留当前页面,只显示底部进度'}
|
||||
]
|
||||
|
||||
async function load(){
|
||||
rules.value=await call('GetRules')
|
||||
const [saved,bootstrap]=await Promise.all([call('GetSettings'),call('GetBootstrapStatus')])
|
||||
Object.assign(settings,saved)
|
||||
settings.databasePath=bootstrap.databasePath||saved.databasePath||bootstrap.defaultPath||''
|
||||
}
|
||||
async function add(){if(!form.pattern)return;await call('AddRule',form.pattern,form.category);form.pattern='';await load()}
|
||||
async function remove(r){if(!r.builtin){await call('DeleteRule',r.id);await load()}}
|
||||
async function save(){await call('SaveSettings',{...settings,databasePath:''});store.applyAppearance(settings);apply();localStorage.setItem('cc-settings',JSON.stringify(settings))}
|
||||
function apply(){locale.value=settings.locale;let theme=settings.theme;if(theme==='system')theme=matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';document.documentElement.dataset.theme=theme;document.documentElement.style.setProperty('--glass-user-opacity',String((settings.glassOpacity||55)/100))}
|
||||
async function migrate(){
|
||||
dbMessage.value=''
|
||||
try{
|
||||
const p=await call('SelectInitialDatabaseFile',settings.databasePath)
|
||||
if(!p)return
|
||||
await call('MigrateDatabase',p)
|
||||
const status=await call('GetBootstrapStatus')
|
||||
settings.databasePath=status.databasePath
|
||||
dbMessage.value='数据库已迁移并切换到新位置'
|
||||
store.showToast({type:'success',text:dbMessage.value})
|
||||
}catch(e){dbMessage.value=String(e);store.showToast({type:'error',text:'数据库迁移失败'})}
|
||||
}
|
||||
async function copyPath(){try{await navigator.clipboard.writeText(settings.databasePath);store.showToast({type:'success',text:'数据库路径已复制'})}catch{store.showToast({type:'error',text:'无法复制路径'})}}
|
||||
async function clear(mode){const id=mode==='project'?Number(prompt('项目 ID')):0;if(mode==='project'&&!id)return;if(confirm('此操作不可恢复,确认继续?')){await call('ClearData',mode,id);await store.refresh()}}
|
||||
watch(()=>[settings.theme,settings.locale,settings.glassOpacity,settings.loadingStyle],save)
|
||||
onMounted(async()=>{await load();apply()})
|
||||
</script>
|
||||
|
||||
<template><div class="page settings-page">
|
||||
<header class="page-head"><div><h1>设置</h1><p>管理排除规则、界面和数据库配置</p></div></header>
|
||||
<div class="tabs settings-tabs"><button :class="{active:tab==='rules'}" @click="tab='rules'"><FileSliders/>排除规则</button><button :class="{active:tab==='appearance'}" @click="tab='appearance'"><SunMoon/>界面设置</button><button :class="{active:tab==='database'}" @click="tab='database'"><Database/>数据管理</button></div>
|
||||
<template v-if="tab==='rules'"><section class="panel rule-add"><h2><Plus/>添加排除规则</h2><div><input v-model="form.pattern" placeholder="例如 *.log, cache, temp" @keyup.enter="add"/><select v-model="form.category"><option value="general">通用</option><option value="php">PHP</option><option value="go">Go</option><option value="vue">Vue/JS</option><option value="custom">自定义</option></select><button class="btn primary" @click="add"><Plus/>添加</button></div><small>支持 * 通配符;目录名会在任意层级匹配</small></section><section v-for="(items,name) in groups" :key="name" class="panel rule-group"><h2>{{name}} <small>{{items.length}} 条规则</small></h2><div><button v-for="r in items" :key="r.id" :class="{builtin:r.builtin}" @click="remove(r)">{{r.pattern}}<small v-if="r.builtin">默认</small><Trash2 v-else/></button></div></section></template>
|
||||
<template v-else-if="tab==='appearance'"><section class="panel form-panel"><h2><Languages/>语言与主题</h2><label>界面语言<select v-model="settings.locale"><option value="zh-CN">简体中文</option><option value="en">English</option></select></label><label>主题<select v-model="settings.theme"><option value="dark">暗色</option><option value="light">浅色</option><option value="system">跟随系统</option></select></label><label>Git 默认范围<select v-model="settings.gitScope"><option value="current">当前分支</option><option value="all">所有分支</option></select></label><div class="loading-style-setting"><span>统计 Loading 样式</span><div class="loading-style-grid" role="radiogroup" aria-label="统计 Loading 样式"><button v-for="option in loadingOptions" :key="option.value" type="button" role="radio" :aria-checked="settings.loadingStyle===option.value" :class="['loading-style-card',option.value,{active:settings.loadingStyle===option.value}]" @click="settings.loadingStyle=option.value"><span class="loading-style-preview" aria-hidden="true"><i/></span><b>{{option.title}}</b><small>{{option.desc}}</small></button></div></div><label class="opacity-setting"><span>卡片透明度 <b>{{settings.glassOpacity}}%</b></span><input v-model.number="settings.glassOpacity" type="range" min="30" max="75" step="1"/></label></section></template>
|
||||
<template v-else><section class="panel database-panel"><div class="db-title"><h2><Database/>数据库位置</h2><span class="db-connected"><CheckCircle2/>已连接</span></div><div v-if="!native" class="preview-notice"><Info/><div><b>当前是浏览器预览模式</b><small>浏览器无法访问本地数据库和文件选择器,请运行 code-count.exe 使用数据库功能。</small></div></div><div class="db-path"><span>当前位置</span><code :title="settings.databasePath">{{settings.databasePath||'未获取到数据库路径'}}</code><button title="复制路径" :disabled="!settings.databasePath" @click="copyPath"><Copy/></button></div><p v-if="dbMessage" class="db-message">{{dbMessage}}</p><button class="btn secondary migrate" :disabled="!native" @click="migrate"><Upload/>选择新位置并迁移</button></section>
|
||||
<section class="panel danger-zone"><h2><Trash2/>清空数据</h2><p>选择要清空的数据类型,此操作不可恢复。</p><div><button @click="clear('stats')"><BarChart3/><span><b>清空统计数据</b><small>删除分析记录,保留项目配置</small></span></button><button @click="clear('project')"><Folder/><span><b>清空单个项目</b><small>删除指定项目的统计数据</small></span></button><button class="danger" @click="clear('all')"><Trash2/><span><b>清空所有数据</b><small>删除所有项目和分析记录</small></span></button></div></section></template>
|
||||
</div></template>
|
||||
Reference in New Issue
Block a user