Files
mihomo-patchbay/web/components/DropFile.vue
T
im0o 013f537eaa 订阅和规则组支持从文件导入,配置页与表单一批打磨
文件导入走两步:内容先经新端点 POST /api/provider-files 写进内核工作
目录的 providers/ 下(路径白名单校验,只认两个路径助手生成的形状),
成了再写 file 类型的声明——顺序反了内核重载会去读不存在的文件。
file 类型没有地址和自动更新(内核靠文件监视跟进),详情页照此展示,
编辑按钮禁用。ini 导入的取文件按钮换成同一个拖放模块。

订阅的更新间隔改成开关语义:内核只在 interval > 0 时才起定时拉取
(component/resource/fetcher.go:226-228),关掉开关就是不写这个键。
配置页加「更新所有订阅」,详情的「立刻更新」改叫「更新订阅」。

规则表从零开始写入时自动垫上内网直连——保留地址不直连的话第一条
代理规则一生效,访问局域网的流量就全进了代理;按钮改成「补全内网
直连」,只在有缺口时出现。

所有表单对话框点遮罩关闭;失败横幅右侧加关闭按钮。
2026-08-12 00:46:17 +08:00

88 lines
2.1 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* 拖拽或点选一个文件,读出文本内容交给调用方。
*
* 无状态:只发 load 事件,选没选、选了什么由调用方自己记——三个用它的表单
* 对「选中之后长什么样」要求各不相同(订阅显示文件名待提交,ini 导入直接
* 灌进文本框),状态放这里反而要为每种用法开口子。
*/
import { ref } from 'vue'
import IconUpload from '~icons/ri/upload-cloud-2-line'
const props = defineProps<{
/** 原生 accept 串,如 ".yaml,.yml,.txt" */
accept?: string
}>()
const emit = defineEmits<{ load: [name: string, content: string] }>()
const input = ref<HTMLInputElement | null>(null)
const dragging = ref(false)
async function take(file: File | undefined) {
if (!file) return
emit('load', file.name, await file.text())
}
function onDrop(event: DragEvent) {
dragging.value = false
void take(event.dataTransfer?.files[0])
}
function onPick(event: Event) {
const el = event.target as HTMLInputElement
void take(el.files?.[0])
// 同一个文件选第二次也要触发 change
el.value = ''
}
</script>
<template>
<button
type="button"
:class="['zone', dragging && 'is-over']"
@click="input?.click()"
@dragover.prevent="dragging = true"
@dragleave="dragging = false"
@drop.prevent="onDrop"
>
<IconUpload class="ico" aria-hidden="true" />
<span>拖拽文件到这里,或点击选择</span>
<input ref="input" type="file" :accept="props.accept" hidden @change="onPick" />
</button>
</template>
<style scoped>
.zone {
display: flex;
align-items: center;
justify-content: center;
gap: var(--s-2);
width: 100%;
padding: var(--s-4);
border: 1px dashed var(--c-border-strong);
border-radius: var(--r-sm);
background: none;
color: var(--c-text-soft);
font-size: var(--t-sm);
cursor: pointer;
user-select: none;
transition:
border-color 0.15s ease,
color 0.15s ease,
background-color 0.15s ease;
}
.zone:hover,
.zone.is-over {
border-color: var(--c-accent);
color: var(--c-accent-text);
background: var(--c-accent-soft);
}
.ico {
width: 18px;
height: 18px;
}
</style>