diff --git a/package.v2.json b/package.v2.json index 6e12279..53ba249 100644 --- a/package.v2.json +++ b/package.v2.json @@ -434,11 +434,12 @@ "name": "IMDb源", "description": "让探索和推荐支持IMDb数据源。", "labels": "探索", - "version": "1.4.2", + "version": "1.4.3", "icon": "IMDb_IOS-OSX_App.png", "author": "wumode", "level": 1, "history": { + "v1.4.3": "为仪表盘组件添加缓存", "v1.4.2": "优化小屏幕组件显示", "v1.4.1": "优化亮色主题显示", "v1.4.0":"添加仪表盘组件: IMDb 编辑精选", @@ -455,11 +456,12 @@ "name": "Clash Rule Provider", "description": "随时为Clash添加一些额外的规则。", "labels": "工具", - "version": "1.2.3", + "version": "1.2.4", "icon": "Mihomo_Meta_A.png", "author": "wumode", "level": 1, "history": { + "v1.2.4": "支持geo规则补全; 代理组编辑", "v1.2.3": "修复规则集名称错误", "v1.2.2": "展示更多信息; 修复交互问题", "v1.2.1": "修复配置模板错误", diff --git a/plugins.v2/clashruleprovider/__init__.py b/plugins.v2/clashruleprovider/__init__.py index ab0a7a9..2cf7a1d 100644 --- a/plugins.v2/clashruleprovider/__init__.py +++ b/plugins.v2/clashruleprovider/__init__.py @@ -26,9 +26,9 @@ from app.log import logger from app.plugins import _PluginBase from app.schemas.types import NotificationType from app.utils.http import RequestUtils -from app.plugins.clashruleprovider.clash_rule_parser import ClashRuleParser, Converter -from app.plugins.clashruleprovider.clash_rule_parser import Action, RuleType, ClashRule, MatchRule, LogicRule -from app.plugins.clashruleprovider.clash_rule_parser import ProxyGroup, RuleProvider +from app.plugins.clashruleprovider.clashruleparser import ClashRuleParser, Converter +from app.plugins.clashruleprovider.clashruleparser import Action, RuleType, ClashRule, MatchRule, LogicRule +from app.plugins.clashruleprovider.clashruleparser import ProxyGroup, RuleProvider class ClashRuleProvider(_PluginBase): @@ -39,7 +39,7 @@ class ClashRuleProvider(_PluginBase): # 插件图标 plugin_icon = "Mihomo_Meta_A.png" # 插件版本 - plugin_version = "1.2.3" + plugin_version = "1.2.4" # 插件作者 plugin_author = "wumode" # 作者主页 @@ -75,10 +75,10 @@ class ClashRuleProvider(_PluginBase): _discard_rules: bool = False _enable_acl4ssr: bool = False _dashboard_components: List[str] = [] - _clash_template_yaml = '' + _clash_template_yaml: str = '' + _hint_geo_dat: bool = False # 插件数据 - # 综合多个订阅的配置 _top_rules: List[str] = [] _ruleset_rules: List[str] = [] _rule_provider: Dict[str, Any] = {} @@ -98,6 +98,7 @@ class ClashRuleProvider(_PluginBase): _clash_template: Optional[Dict[str, Any]] = None _scheduler: Optional[BackgroundScheduler] = None _countries: Optional[List[Dict[str, str]]] = None + _geo_rules: Dict[str, List[str]] = {'geoip': [], 'geosite': []} def init_plugin(self, config: dict = None): self._ruleset_rules = self.get_data("ruleset_rules") @@ -138,6 +139,7 @@ class ClashRuleProvider(_PluginBase): self._enable_acl4ssr = config.get("enable_acl4ssr") or False self._dashboard_components = config.get("dashboard_components") or [] self._clash_template_yaml = config.get("clash_template") or '' + self._hint_geo_dat = config.get("hint_geo_dat", False) self._clash_rule_parser = ClashRuleParser() self._ruleset_rule_parser = ClashRuleParser() self._clash_template = {} @@ -168,10 +170,15 @@ class ClashRuleProvider(_PluginBase): # 更新订阅 self._scheduler.add_job(self.refresh_subscriptions, "date", run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=2)) + if self._hint_geo_dat: + self._scheduler.add_job(self.__refresh_geo_dat, "date", + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=3)) + else: + self._geo_rules = {'geoip': [], 'geosite': []} # 更新acl4ssr if self._enable_acl4ssr: self._scheduler.add_job(self.__refresh_acl4ssr, "date", - run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=5)) + run_date=datetime.now(tz=pytz.timezone(settings.TZ)) + timedelta(seconds=4)) else: self._acl4ssr_providers = {} @@ -337,6 +344,14 @@ class ClashRuleProvider(_PluginBase): "summary": "添加一个代理组", "description": "添加一个代理组" }, + { + "path": "/proxy-group", + "endpoint": self.update_proxy_group, + "methods": ["PUT"], + "auth": "bear", + "summary": "更新一个代理组", + "description": "更新一个代理组" + }, { "path": "/ruleset", "endpoint": self.get_ruleset, @@ -570,6 +585,7 @@ class ClashRuleProvider(_PluginBase): "data": {"state": self._enabled, "ruleset_prefix": self._ruleset_prefix, "clash": {"rule_size": rule_size}, + "geoRules": self._geo_rules, "subscription_info": self._subscription_info, "sub_url": f"{self._movie_pilot_url}/api/v1/plugin/ClashRuleProvider/config?" f"apikey={settings.API_TOKEN}"}} @@ -604,7 +620,7 @@ class ClashRuleProvider(_PluginBase): res = self.delete_rule_by_priority(params.get('priority'), self._ruleset_rule_parser) if res: self.__add_notification_job( - f"{self._ruleset_prefix}{res.action.value if isinstance(res.action, Action) else res.action}") + [f"{self._ruleset_prefix}{res.action.value if isinstance(res.action, Action) else res.action}",]) else: self.delete_rule_by_priority(params.get('priority'), self._clash_rule_parser) return schemas.Response(success=True, message='') @@ -632,7 +648,7 @@ class ClashRuleProvider(_PluginBase): try: if params.get('type') == 'ruleset': self.__reorder_rules(self._ruleset_rule_parser, moved_priority, target_priority) - self.__add_notification_job(f"{self._ruleset_prefix}{params.get('rule_data').get('action')}") + self.__add_notification_job([f"{self._ruleset_prefix}{params.get('rule_data').get('action')}"]) else: self.__reorder_rules(self._clash_rule_parser, moved_priority, target_priority) except Exception as e: @@ -652,11 +668,15 @@ class ClashRuleProvider(_PluginBase): if not self._enabled: return {"success": False, "message": ""} if params.get('type') == 'ruleset': + original_rule = self._ruleset_rule_parser.get_rule_at_priority(params.get('priority')) res = self.update_rule_by_priority(params.get('rule_data'), params.get('priority'), self._ruleset_rule_parser) if res: - self.__add_notification_job(f"{self._ruleset_prefix}{params.get('rule_data').get('action')}") + ruleset_to_notify = [f"{self._ruleset_prefix}{params.get('rule_data').get('action')}"] + if params.get('rule_data').get('action') != original_rule.action: + ruleset_to_notify.append(f"{self._ruleset_prefix}{original_rule.action}") + self.__add_notification_job(ruleset_to_notify) else: res = self.update_rule_by_priority(params.get('rule_data'), params.get('priority'), self._clash_rule_parser) return {"success": bool(res), "message": None} @@ -667,7 +687,7 @@ class ClashRuleProvider(_PluginBase): if params.get('type') == 'ruleset': res = self.add_rule_by_priority(params.get('rule_data'), self._ruleset_rule_parser) if res: - self.__add_notification_job(f"{self._ruleset_prefix}{params.get('rule_data').get('action')}") + self.__add_notification_job([f"{self._ruleset_prefix}{params.get('rule_data').get('action')}",]) else: res = self.add_rule_by_priority(params.get('rule_data'), self._clash_rule_parser) return schemas.Response(success=bool(res), message='') @@ -844,6 +864,37 @@ class ClashRuleProvider(_PluginBase): self.save_data('proxy_groups', self._proxy_groups) return schemas.Response(success=True) + def update_proxy_group(self, params: Dict[str, Any]) -> schemas.Response: + if not self._enabled: + return schemas.Response(success=False, message='') + proxy_group = params.get('proxy_group', {}) + name = params.get('name') + if not name or not proxy_group: + return schemas.Response(success=False, message='Invalid params') + try: + ProxyGroup.parse_obj(proxy_group) + except Exception as e: + error_message = f"Failed to parse proxy group: Invalid data={proxy_group}, error={repr(e)}" + logger.error(error_message) + return schemas.Response(success=False, message=str(error_message)) + index = next((i for i, x in enumerate(self._proxy_groups) if x.get('name') == name), None) + # whether new name exists + new_name_index = next((i for i, x in enumerate(self._proxy_groups) if x.get('name') == proxy_group.get('name')), + None + ) + if new_name_index and new_name_index != index: + return schemas.Response(success=False, + message=f"The proxy group name {proxy_group.get('name')} already exists") + new_item = {} + for k, v in proxy_group.items(): + if v == '': + continue + if v is None: + continue + new_item[k] = v + self._proxy_groups[index] = new_item + return schemas.Response(success=True) + def delete_proxy_group(self, params: dict = Body(...)) -> schemas.Response: if not self._enabled: return schemas.Response(success=False, message='') @@ -1015,7 +1066,7 @@ class ClashRuleProvider(_PluginBase): ) def __refresh_acl4ssr(self): - logger.info(f"Refreshing ACL4SSR") + logger.info(f"Refreshing ACL4SSR ...") # 配置参数 owner = 'ACL4SSR' repo = 'ACL4SSR' @@ -1038,6 +1089,26 @@ class ClashRuleProvider(_PluginBase): self._acl4ssr_providers[name] = provider self.save_data('acl4ssr_providers', self._acl4ssr_providers) + def __refresh_geo_dat(self): + logger.info(f"Refreshing Geo Rules ...") + owner = 'MetaCubeX' + repo = 'meta-rules-dat' + branch = 'meta' + api_url = f"https://api.github.com/repos/{owner}/{repo}/contents/geo" + resp = RequestUtils().get_res(api_url, headers=settings.GITHUB_HEADERS, params={'ref': branch}) + for path in resp.json(): + if path["type"] == "dir" and path["name"] in self._geo_rules: + tree_sha = path["sha"] + url = f"https://api.github.com/repos/{owner}/{repo}/git/trees/{tree_sha}" + res = RequestUtils().get_res(url, headers=settings.GITHUB_HEADERS, params={'ref': branch}) + if not res: + continue + tree = res.json() + yaml_files = [item["path"][:item["path"].rfind('.')] for item in tree["tree"] if + item["type"] == "blob" and item['path'].endswith((".yaml", ".yml"))] + self._geo_rules[path["name"]] = yaml_files + print(len(self._geo_rules['geosite'])) + def refresh_subscriptions(self) -> Dict[str, bool]: """ 更新全部订阅链接 @@ -1157,15 +1228,16 @@ class ClashRuleProvider(_PluginBase): return continents_names[country['continent']] return None - def __add_notification_job(self, ruleset: str): - if ruleset in self._rule_provider: - self._scheduler.add_job(self.notify_clash, "date", - run_date=datetime.now( - tz=pytz.timezone(settings.TZ)) + timedelta(seconds=self._refresh_delay), - args=[ruleset], - id='CRP-notify-clash', - replace_existing=True - ) + def __add_notification_job(self, ruleset_names: List[str]): + for ruleset in ruleset_names: + if ruleset in self._rule_provider: + self._scheduler.add_job(self.notify_clash, "date", + run_date=datetime.now( + tz=pytz.timezone(settings.TZ)) + timedelta(seconds=self._refresh_delay), + args=[ruleset], + id=f'CRP-notify-clash{ruleset}', + replace_existing=True + ) def __remove_nodes_by_keywords(self, clash_config: Dict[str, Any]) -> Dict[str, Any]: removed_proxies = [] diff --git a/plugins.v2/clashruleprovider/clash_rule_parser.py b/plugins.v2/clashruleprovider/clashruleparser.py similarity index 98% rename from plugins.v2/clashruleprovider/clash_rule_parser.py rename to plugins.v2/clashruleprovider/clashruleparser.py index 7365a19..266a944 100644 --- a/plugins.v2/clashruleprovider/clash_rule_parser.py +++ b/plugins.v2/clashruleprovider/clashruleparser.py @@ -141,6 +141,11 @@ ProxyGroupUnion = Union[SelectGroup, RelayGroup, FallbackGroup, UrlTestGroup, Lo class ProxyGroup(BaseModel): __root__: ProxyGroupUnion +class AdditionalParam(Enum): + NO_RESOLVE = 'no-resolve' + SRC = 'src' + + class RuleType(Enum): """Enumeration of all supported Clash rule types""" DOMAIN = "DOMAIN" @@ -201,14 +206,10 @@ class ClashRule: rule_type: RuleType payload: str action: Union[Action, str] # Can be Action enum or custom proxy group name - additional_params: Optional[List[str]] = None + additional_params: Optional[AdditionalParam] = None raw_rule: str = "" priority: int = 0 - def __post_init__(self): - if self.additional_params is None: - self.additional_params = [] - def condition_string(self) -> str: return f"{self.rule_type.value},{self.payload}" @@ -282,6 +283,8 @@ class ClashRuleParser: rule = ClashRuleParser._parse_match_rule(raw_rule) else: raw_rule = f"{clash_rule.get('type')},{clash_rule.get('payload')},{clash_rule.get('action')}" + if clash_rule.get('additional_params'): + raw_rule += f",{clash_rule.get('additional_params')}" rule = ClashRuleParser._parse_regular_rule(raw_rule) if rule and 'priority' in clash_rule: rule.priority = clash_rule['priority'] @@ -310,7 +313,7 @@ class ClashRuleParser: """Parse a regular (non-logic) rule""" parts = line.split(',') - if len(parts) < 3: + if len(parts) < 3 or len(parts) > 4: raise ValueError(f"Invalid rule format: {line}") rule_type_str = parts[0].upper() @@ -320,7 +323,7 @@ class ClashRuleParser: if not payload or not rule_type_str: raise ValueError(f"Invalid rule format: {line}") - additional_params = parts[3:] if len(parts) > 3 else [] + additional_params = parts[3] if len(parts) > 3 else None # Validate rule type try: @@ -543,6 +546,12 @@ class ClashRuleParser: self.insert_rule_at_priority(clash_rule, clash_rule.priority) return True + def get_rule_at_priority(self, priority: int) -> Optional[Union[ClashRule, LogicRule, MatchRule]]: + for rule in self.rules: + if rule.priority == priority: + return rule + return None + def remove_rule_at_priority(self, priority: int) -> Optional[Union[ClashRule, LogicRule, MatchRule]]: """Remove rule at specific priority and adjust remaining priorities""" rule_to_remove = None diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BAzyOCdJ.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BAzyOCdJ.css deleted file mode 100644 index edd0ed0..0000000 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BAzyOCdJ.css +++ /dev/null @@ -1,4 +0,0 @@ - -.plugin-config[data-v-106fe9f8] { - margin: 0 auto; -} diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BWBZ5vgt.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-HFXrSiMi.js similarity index 93% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BWBZ5vgt.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-HFXrSiMi.js index 1ec1b56..462cf5d 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-BWBZ5vgt.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-HFXrSiMi.js @@ -74,7 +74,8 @@ const defaultConfig = { discard_rules: false, enable_acl4ssr: false, dashboard_components: [], - clash_template: '' + clash_template: '', + hint_geo_dat: false, }; // 响应式配置对象 @@ -271,7 +272,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[27] || (_cache[27] = [ + default: _withCtx(() => _cache[28] || (_cache[28] = [ _createTextVNode("mdi-close") ])), _: 1 @@ -282,7 +283,7 @@ return (_ctx, _cache) => { ]), default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[26] || (_cache[26] = [ + default: _withCtx(() => _cache[27] || (_cache[27] = [ _createTextVNode("Clash Rule Provider 插件配置") ])), _: 1 @@ -308,11 +309,11 @@ return (_ctx, _cache) => { ref_key: "form", ref: form, modelValue: isFormValid.value, - "onUpdate:modelValue": _cache[20] || (_cache[20] = $event => ((isFormValid).value = $event)), + "onUpdate:modelValue": _cache[21] || (_cache[21] = $event => ((isFormValid).value = $event)), onSubmit: _withModifiers(saveConfig, ["prevent"]) }, { default: _withCtx(() => [ - _cache[41] || (_cache[41] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "基本设置", -1)), + _cache[42] || (_cache[42] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "基本设置", -1)), _createVNode(_component_v_row, null, { default: _withCtx(() => [ _createVNode(_component_v_col, { @@ -386,7 +387,7 @@ return (_ctx, _cache) => { ]), _: 1 }), - _cache[42] || (_cache[42] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)), + _cache[43] || (_cache[43] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "订阅配置", -1)), _createVNode(_component_v_row, null, { default: _withCtx(() => [ _createVNode(_component_v_col, { cols: "12" }, { @@ -450,7 +451,7 @@ return (_ctx, _cache) => { ]), _: 1 }), - _cache[43] || (_cache[43] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)), + _cache[44] || (_cache[44] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "Clash 面板设置", -1)), _createVNode(_component_v_row, null, { default: _withCtx(() => [ _createVNode(_component_v_col, { cols: "12" }, { @@ -466,7 +467,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "primary" }, { - default: _withCtx(() => _cache[28] || (_cache[28] = [ + default: _withCtx(() => _cache[29] || (_cache[29] = [ _createTextVNode("mdi-web") ])), _: 1 @@ -495,7 +496,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "warning" }, { - default: _withCtx(() => _cache[29] || (_cache[29] = [ + default: _withCtx(() => _cache[30] || (_cache[30] = [ _createTextVNode("mdi-key") ])), _: 1 @@ -525,7 +526,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[30] || (_cache[30] = [ + default: _withCtx(() => _cache[31] || (_cache[31] = [ _createTextVNode("mdi-view-dashboard") ])), _: 1 @@ -539,7 +540,7 @@ return (_ctx, _cache) => { ]), _: 1 }), - _cache[44] || (_cache[44] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)), + _cache[45] || (_cache[45] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "MoviePilot 设置", -1)), _createVNode(_component_v_row, null, { default: _withCtx(() => [ _createVNode(_component_v_col, { cols: "12" }, { @@ -555,7 +556,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "success" }, { - default: _withCtx(() => _cache[31] || (_cache[31] = [ + default: _withCtx(() => _cache[32] || (_cache[32] = [ _createTextVNode("mdi-movie") ])), _: 1 @@ -569,7 +570,7 @@ return (_ctx, _cache) => { ]), _: 1 }), - _cache[45] || (_cache[45] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)), + _cache[46] || (_cache[46] = _createElementVNode("div", { class: "text-subtitle-1 font-weight-bold mt-4 mb-2" }, "执行设置", -1)), _createVNode(_component_v_row, null, { default: _withCtx(() => [ _createVNode(_component_v_col, { cols: "12" }, { @@ -583,7 +584,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[32] || (_cache[32] = [ + default: _withCtx(() => _cache[33] || (_cache[33] = [ _createTextVNode("mdi-clock-time-four-outline") ])), _: 1 @@ -634,7 +635,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[33] || (_cache[33] = [ + default: _withCtx(() => _cache[34] || (_cache[34] = [ _createTextVNode("mdi-refresh") ])), _: 1 @@ -658,12 +659,12 @@ return (_ctx, _cache) => { _createVNode(_component_v_expansion_panel_title, null, { default: _withCtx(() => [ _createVNode(_component_v_icon, { class: "mr-2" }, { - default: _withCtx(() => _cache[34] || (_cache[34] = [ + default: _withCtx(() => _cache[35] || (_cache[35] = [ _createTextVNode("mdi-cog") ])), _: 1 }), - _cache[35] || (_cache[35] = _createTextVNode(" 高级选项 ")) + _cache[36] || (_cache[36] = _createTextVNode(" 高级选项 ")) ]), _: 1 }), @@ -687,6 +688,22 @@ return (_ctx, _cache) => { ]), _: 1 }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: config.hint_geo_dat, + "onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ((config.hint_geo_dat) = $event)), + label: "Geo规则补全", + color: "primary", + inset: "", + hint: "获取官方Geo数据库, 并在输入时补全" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), _createVNode(_component_v_col, { cols: "12", md: "3" @@ -694,7 +711,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: config.enable_acl4ssr, - "onUpdate:modelValue": _cache[15] || (_cache[15] = $event => ((config.enable_acl4ssr) = $event)), + "onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ((config.enable_acl4ssr) = $event)), label: "ACL4SSR规则集", color: "primary", inset: "", @@ -710,7 +727,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: config.group_by_region, - "onUpdate:modelValue": _cache[16] || (_cache[16] = $event => ((config.group_by_region) = $event)), + "onUpdate:modelValue": _cache[17] || (_cache[17] = $event => ((config.group_by_region) = $event)), label: "按大洲分组节点", color: "primary", inset: "", @@ -718,27 +735,6 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]) ]), _: 1 - }), - _createVNode(_component_v_col, { cols: "3" }, { - default: _withCtx(() => [ - _createVNode(_component_v_btn, { - color: "primary", - onClick: openClashTemplateDialog, - class: "mr-2" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[36] || (_cache[36] = [ - _createTextVNode("mdi-import") - ])), - _: 1 - }), - _cache[37] || (_cache[37] = _createTextVNode(" Clash 配置模板 ")) - ]), - _: 1 - }) - ]), - _: 1 }) ]), _: 1 @@ -747,12 +743,12 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_col, { cols: "12", - md: "4" + md: "3" }, { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: config.ruleset_prefix, - "onUpdate:modelValue": _cache[17] || (_cache[17] = $event => ((config.ruleset_prefix) = $event)), + "onUpdate:modelValue": _cache[18] || (_cache[18] = $event => ((config.ruleset_prefix) = $event)), label: "规则集前缀", variant: "outlined", placeholder: "📂<=", @@ -761,6 +757,33 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "info" }, { + default: _withCtx(() => _cache[37] || (_cache[37] = [ + _createTextVNode("mdi-palette") + ])), + _: 1 + }) + ]), + _: 1 + }, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "3" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: config.acl4ssr_prefix, + "onUpdate:modelValue": _cache[19] || (_cache[19] = $event => ((config.acl4ssr_prefix) = $event)), + label: "ACL4SSR 规则集前缀", + variant: "outlined", + placeholder: "🗂️=>", + rules: [v => !!v || '规则集前缀不能为空'], + hint: "ACL4SSR 规则集前缀" + }, { + "prepend-inner": _withCtx(() => [ + _createVNode(_component_v_icon, { color: "primary" }, { default: _withCtx(() => _cache[38] || (_cache[38] = [ _createTextVNode("mdi-palette") ])), @@ -774,39 +797,12 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_col, { cols: "12", - md: "4" - }, { - default: _withCtx(() => [ - _createVNode(_component_v_text_field, { - modelValue: config.acl4ssr_prefix, - "onUpdate:modelValue": _cache[18] || (_cache[18] = $event => ((config.acl4ssr_prefix) = $event)), - label: "ACL4SSR 规则集前缀", - variant: "outlined", - placeholder: "🗂️=>", - rules: [v => !!v || '规则集前缀不能为空'], - hint: "ACL4SSR 规则集前缀" - }, { - "prepend-inner": _withCtx(() => [ - _createVNode(_component_v_icon, { color: "primary" }, { - default: _withCtx(() => _cache[39] || (_cache[39] = [ - _createTextVNode("mdi-palette") - ])), - _: 1 - }) - ]), - _: 1 - }, 8, ["modelValue", "rules"]) - ]), - _: 1 - }), - _createVNode(_component_v_col, { - cols: "12", - md: "4" + md: "3" }, { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: config.refresh_delay, - "onUpdate:modelValue": _cache[19] || (_cache[19] = $event => ((config.refresh_delay) = $event)), + "onUpdate:modelValue": _cache[20] || (_cache[20] = $event => ((config.refresh_delay) = $event)), modelModifiers: { number: true }, label: "刷新延迟", variant: "outlined", @@ -819,7 +815,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "info" }, { - default: _withCtx(() => _cache[40] || (_cache[40] = [ + default: _withCtx(() => _cache[39] || (_cache[39] = [ _createTextVNode("mdi-clock-outline") ])), _: 1 @@ -829,6 +825,27 @@ return (_ctx, _cache) => { }, 8, ["modelValue", "rules"]) ]), _: 1 + }), + _createVNode(_component_v_col, { cols: "3" }, { + default: _withCtx(() => [ + _createVNode(_component_v_btn, { + color: "primary", + onClick: openClashTemplateDialog, + class: "mr-2" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, { left: "" }, { + default: _withCtx(() => _cache[40] || (_cache[40] = [ + _createTextVNode("mdi-import") + ])), + _: 1 + }), + _cache[41] || (_cache[41] = _createTextVNode(" Clash 配置模板 ")) + ]), + _: 1 + }) + ]), + _: 1 }) ]), _: 1 @@ -854,7 +871,7 @@ return (_ctx, _cache) => { class: "mb-6", variant: "tonal" }, { - default: _withCtx(() => _cache[46] || (_cache[46] = [ + default: _withCtx(() => _cache[47] || (_cache[47] = [ _createTextVNode(" 配置说明参考: "), _createElementVNode("a", { href: "https://github.com/wumode/MoviePilot-Plugins/tree/main/plugins.v2/clashruleprovider/README.md", @@ -871,12 +888,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[47] || (_cache[47] = [ + default: _withCtx(() => _cache[48] || (_cache[48] = [ _createTextVNode("mdi-view-dashboard-edit") ])), _: 1 }), - _cache[48] || (_cache[48] = _createTextVNode(" 规则 ")) + _cache[49] || (_cache[49] = _createTextVNode(" 规则 ")) ]), _: 1 }), @@ -884,7 +901,7 @@ return (_ctx, _cache) => { color: "secondary", onClick: resetForm }, { - default: _withCtx(() => _cache[49] || (_cache[49] = [ + default: _withCtx(() => _cache[50] || (_cache[50] = [ _createTextVNode("重置") ])), _: 1 @@ -894,7 +911,7 @@ return (_ctx, _cache) => { onClick: testConnection, loading: testing.value }, { - default: _withCtx(() => _cache[50] || (_cache[50] = [ + default: _withCtx(() => _cache[51] || (_cache[51] = [ _createTextVNode("测试连接") ])), _: 1 @@ -906,7 +923,7 @@ return (_ctx, _cache) => { onClick: saveConfig, loading: saving.value }, { - default: _withCtx(() => _cache[51] || (_cache[51] = [ + default: _withCtx(() => _cache[52] || (_cache[52] = [ _createTextVNode(" 保存配置 ") ])), _: 1 @@ -921,7 +938,7 @@ return (_ctx, _cache) => { variant: "tonal", closable: "", class: "ma-4 mt-0", - "onClick:close": _cache[21] || (_cache[21] = $event => (testResult.show = false)) + "onClick:close": _cache[22] || (_cache[22] = $event => (testResult.show = false)) }, { default: _withCtx(() => [ _createElementVNode("div", _hoisted_2, [ @@ -946,14 +963,14 @@ return (_ctx, _cache) => { ]), _createVNode(_component_v_dialog, { modelValue: clashTemplateDialog.value, - "onUpdate:modelValue": _cache[25] || (_cache[25] = $event => ((clashTemplateDialog).value = $event)), + "onUpdate:modelValue": _cache[26] || (_cache[26] = $event => ((clashTemplateDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[52] || (_cache[52] = [ + default: _withCtx(() => _cache[53] || (_cache[53] = [ _createTextVNode("Clash 配置模板") ])), _: 1 @@ -962,14 +979,14 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: clashTemplateType.value, - "onUpdate:modelValue": _cache[22] || (_cache[22] = $event => ((clashTemplateType).value = $event)), + "onUpdate:modelValue": _cache[23] || (_cache[23] = $event => ((clashTemplateType).value = $event)), items: ['YAML'], label: "配置类型", class: "mb-4" }, null, 8, ["modelValue"]), _createVNode(_component_v_textarea, { modelValue: clashTemplateContent.value, - "onUpdate:modelValue": _cache[23] || (_cache[23] = $event => ((clashTemplateContent).value = $event)), + "onUpdate:modelValue": _cache[24] || (_cache[24] = $event => ((clashTemplateContent).value = $event)), label: "配置内容", "auto-grow": "", placeholder: "mixed-port: 7890", @@ -985,9 +1002,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { text: "", - onClick: _cache[24] || (_cache[24] = $event => (clashTemplateDialog.value = false)) + onClick: _cache[25] || (_cache[25] = $event => (clashTemplateDialog.value = false)) }, { - default: _withCtx(() => _cache[53] || (_cache[53] = [ + default: _withCtx(() => _cache[54] || (_cache[54] = [ _createTextVNode("取消") ])), _: 1 @@ -996,7 +1013,7 @@ return (_ctx, _cache) => { color: "primary", onClick: saveClashTemplate }, { - default: _withCtx(() => _cache[54] || (_cache[54] = [ + default: _withCtx(() => _cache[55] || (_cache[55] = [ _createTextVNode("确定") ])), _: 1 @@ -1015,6 +1032,6 @@ return (_ctx, _cache) => { } }; -const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-106fe9f8"]]); +const ConfigComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-03cdd879"]]); export { ConfigComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-s4NWY2Hy.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-s4NWY2Hy.css new file mode 100644 index 0000000..0b9bec0 --- /dev/null +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Config-s4NWY2Hy.css @@ -0,0 +1,4 @@ + +.plugin-config[data-v-03cdd879] { + margin: 0 auto; +} diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BFVr4jq_.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BDSt5WaH.js similarity index 99% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BFVr4jq_.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BDSt5WaH.js index 9d41b2d..65c146c 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BFVr4jq_.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-BDSt5WaH.js @@ -15460,13 +15460,13 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ _createElementVNode("div", _hoisted_9, [ clashInfo.value.version.meta ? (_openBlock(), _createBlock(_component_v_img, { key: 0, - src: "/api/v1/system/img/0?imgurl=https://raw.githubusercontent.com/MetaCubeX/mihomo/refs/heads/Meta/Meta.png", + src: "/api/v1/system/img/1?cache=1&imgurl=https://raw.githubusercontent.com/MetaCubeX/mihomo/refs/heads/Meta/Meta.png", alt: "Logo", "max-height": "48", contain: "" })) : (_openBlock(), _createBlock(_component_v_img, { key: 1, - src: "/api/v1/system/img/0?imgurl=https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/refs/heads/main/icons/Clash_A.png", + src: "/api/v1/system/img/1?cache=1&imgurl=https://raw.githubusercontent.com/jxxghp/MoviePilot-Plugins/refs/heads/main/icons/Clash_A.png", alt: "Logo", "max-height": "48", contain: "" @@ -15514,6 +15514,6 @@ const _sfc_main = /* @__PURE__ */ _defineComponent({ } }); -const DashboardComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-9093476d"]]); +const DashboardComponent = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-de7a088e"]]); export { DashboardComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-D6WU_Ejn.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-vS9Qm2ZB.css similarity index 51% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-D6WU_Ejn.css rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-vS9Qm2ZB.css index 6a17a70..3e49ef2 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-D6WU_Ejn.css +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Dashboard-vS9Qm2ZB.css @@ -1,4 +1,4 @@ -.dashboard-widget[data-v-9093476d] { +.dashboard-widget[data-v-de7a088e] { font-family: 'Inter', sans-serif; } diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-Bt1EwqOk.js b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-BNbsW3Id.js similarity index 87% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-Bt1EwqOk.js rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-BNbsW3Id.js index 57c477e..253736e 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-Bt1EwqOk.js +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-BNbsW3Id.js @@ -7,7 +7,7 @@ function isNothing(subject) { } -function isObject(subject) { +function isObject$1(subject) { return (typeof subject === 'object') && (subject !== null); } @@ -53,7 +53,7 @@ function isNegativeZero(number) { var isNothing_1 = isNothing; -var isObject_1 = isObject; +var isObject_1 = isObject$1; var toArray_1 = toArray; var repeat_1 = repeat; var isNegativeZero_1 = isNegativeZero; @@ -3849,6 +3849,392 @@ var jsYaml = { safeDump: safeDump }; +var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +/** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + +/** Used as the `TypeError` message for "Functions" methods. */ +var FUNC_ERROR_TEXT = 'Expected a function'; + +/** Used as references for various `Number` constants. */ +var NAN = 0 / 0; + +/** `Object#toString` result references. */ +var symbolTag = '[object Symbol]'; + +/** Used to match leading and trailing whitespace. */ +var reTrim = /^\s+|\s+$/g; + +/** Used to detect bad signed hexadecimal string values. */ +var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + +/** Used to detect binary string values. */ +var reIsBinary = /^0b[01]+$/i; + +/** Used to detect octal string values. */ +var reIsOctal = /^0o[0-7]+$/i; + +/** Built-in method references without a dependency on `root`. */ +var freeParseInt = parseInt; + +/** Detect free variable `global` from Node.js. */ +var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; + +/** Detect free variable `self`. */ +var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + +/** Used as a reference to the global object. */ +var root = freeGlobal || freeSelf || Function('return this')(); + +/** Used for built-in method references. */ +var objectProto = Object.prototype; + +/** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ +var objectToString = objectProto.toString; + +/* Built-in method references for those with the same name as other `lodash` methods. */ +var nativeMax = Math.max, + nativeMin = Math.min; + +/** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ +var now = function() { + return root.Date.now(); +}; + +/** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ +function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + result = wait - timeSinceLastCall; + + return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; +} + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); +} + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return !!value && typeof value == 'object'; +} + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); +} + +/** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ +function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); +} + +var lodash_debounce = debounce; + +const debounce$1 = /*@__PURE__*/getDefaultExportFromCjs(lodash_debounce); + const {createTextVNode:_createTextVNode,resolveComponent:_resolveComponent,withCtx:_withCtx,createVNode:_createVNode,toDisplayString:_toDisplayString,openBlock:_openBlock,createBlock:_createBlock,createCommentVNode:_createCommentVNode,createElementVNode:_createElementVNode,withModifiers:_withModifiers,normalizeClass:_normalizeClass,mergeProps:_mergeProps,renderList:_renderList,Fragment:_Fragment,createElementBlock:_createElementBlock} = await importShared('vue'); @@ -3909,7 +4295,7 @@ const _hoisted_39 = { style: {"position":"absolute","right":"0","bottom":"0"} }; const _hoisted_40 = { class: "d-flex flex-column justify-space-between gap-1" }; const _hoisted_41 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; const _hoisted_42 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; -const _hoisted_43 = { }; +const _hoisted_43 = { class: "text-white" }; const _hoisted_44 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; const _hoisted_45 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; const _hoisted_46 = { class: "d-flex justify-space-between text-body-2 border-b pb-1" }; @@ -4159,7 +4545,7 @@ const newProxyGroup = ref({ url: '', lazy: true, interval: 300, - timeout: 50000, + timeout: 5000, 'disable-udp': false, filter: '', 'include-all': false, @@ -4170,6 +4556,10 @@ const newProxyGroup = ref({ tolerance: null, strategy: null, 'expected-status': '*', + hidden: false, + icon: '', + use: null, + 'max-failed-times': 5, }); // 组件状态 @@ -4180,6 +4570,10 @@ const rulesetRules = ref([]); const extraRuleProviders = ref([]); const status = ref('running'); const rulesetPrefix = ref('Custom_'); +const geoRules = ref({ + geoip: [], + geosite: [], +}); const lastUpdated = ref(''); const refreshingSubscription = ref(false); const yamlDialog = ref(false); @@ -4192,6 +4586,7 @@ const proxyGroupDialog = ref(false); const ruleDialog = ref(false); const ruleProviderDialog = ref(false); const editingPriority = ref(null); +const editingProxyGroupName = ref(null); const editingRuleProviderName = ref(null); const editingType = ref('top'); // 记录当前编辑的规则类型('top' 或 'ruleset') const newRule = ref({ @@ -4238,8 +4633,8 @@ const ruleProviderNames = computed(() => { // 规则类型和动作选项 const ruleTypes = computed(() => { const allTypes = [ - 'DOMAIN', 'DOMAIN-SUFFIX', 'DOMAIN-KEYWORD', 'DOMAIN-REGEX', 'GEOSITE', - 'IP-CIDR', 'IP-CIDR6', 'IP-SUFFIX', 'IP-ASN', 'GEOIP', + 'DOMAIN', 'DOMAIN-SUFFIX', 'DOMAIN-KEYWORD', 'DOMAIN-REGEX', 'GEOSITE', 'GEOIP', + 'IP-CIDR', 'IP-CIDR6', 'IP-SUFFIX', 'IP-ASN', 'SRC-GEOIP', 'SRC-IP-ASN', 'SRC-IP-CIDR', 'SRC-IP-SUFFIX', 'DST-PORT', 'SRC-PORT', 'IN-PORT', 'IN-TYPE', 'IN-USER', 'IN-NAME', 'PROCESS-PATH', 'PROCESS-PATH-REGEX', 'PROCESS-NAME', 'PROCESS-NAME-REGEX', @@ -4257,7 +4652,7 @@ const importExtraProxiesPlaceholderText = computed(() => { ? 'proxies: []' : 'vless://xxxx'; }); -const proxyGroupTypes = ref(['select', 'url-test', 'fallback', 'load-balance', 'relay']); +const proxyGroupTypes = ref(['select', 'url-test', 'fallback', 'load-balance']); ref(['Direct', 'Reject', 'RejectDrop', 'Compatible', 'Pass', 'Dns', 'Relay', 'Selector', 'Fallback', 'URLTest', 'LoadBalance', 'Shadowsocks', 'ShadowsocksR', 'Snell', 'Socks5', 'Http', 'Vmess', 'Vless', 'Trojan', 'Hysteria', 'Hysteria2', 'WireGuard', 'Tuic', 'Ssh',]); @@ -4272,6 +4667,60 @@ const actions = computed(() => [ 'DIRECT', 'REJECT', 'REJECT-DROP', 'PASS', 'COMPATIBLE', ...customOutbounds.value.map(outbound => outbound.name) ]); + +ref(''); +const filteredGeoItems = ref([]); +const geoSearch = ref(''); +const geoIPSearch = ref(''); +const geoFilterLoading = ref(false); + +// 当输入框失去焦点时,将当前搜索词设置为选中项(如果它不在候选列表中) +const onGeoSiteBlur = () => { + if (!filteredGeoItems.value.includes(geoSearch.value)) { + newRule.value.payload = geoSearch.value; + } +}; +const onGeoIPBlur = () => { + if (!filteredGeoItems.value.includes(geoIPSearch.value)) { + newRule.value.payload = geoIPSearch.value; + } +}; +const performFilter = debounce$1((val) => { + if (!val) { + filteredGeoItems.value = []; + geoFilterLoading.value = false; + return + } + geoFilterLoading.value = true; + filteredGeoItems.value = geoRules.value.geosite.filter(item => + item.toLowerCase().includes(val.toLowerCase()) + ); + geoFilterLoading.value = false; +}, 200); // 20ms debounce + +const performGeoIPFilter = debounce$1((val) => { + if (!val) { + filteredGeoItems.value = []; + geoFilterLoading.value = false; + return + } + geoFilterLoading.value = true; + filteredGeoItems.value = geoRules.value.geoip.filter(item => + item.toLowerCase().includes(val.toLowerCase()) + ); + geoFilterLoading.value = false; +}, 200); // 20ms debounce + +const onGeoSearch = (val) => { + geoSearch.value = val; + performFilter(val); +}; + +const onGeoIPSearch = (val) => { + geoIPSearch.value = val; + performGeoIPFilter(val); +}; + const subscriptionInfo = ref({ download: 0, upload: 0, @@ -4427,6 +4876,7 @@ const showProxyGroupYaml = (proxyGroup) => { }; function openAddProxyGroupDialog() { + editingProxyGroupName.value = null; newProxyGroup.value = { name: '', type: 'select', @@ -4434,7 +4884,7 @@ function openAddProxyGroupDialog() { url: 'https://www.gstatic.com/generate_204', lazy: true, interval: 300, - timeout: 50000, + timeout: 5000, 'disable-udp': false, filter: '', 'include-all': false, @@ -4445,6 +4895,10 @@ function openAddProxyGroupDialog() { 'exclude-type': '', tolerance: null, strategy: null, + hidden: false, + icon: '', + use: null, + 'max-failed-times': 5, }; proxyGroupDialog.value = true; } @@ -4461,13 +4915,44 @@ function editRule(priority, type = 'top') { type: rule.type, payload: rule.payload, action: rule.action, - additional_params: rule.additional_params?.join(', ') || '', + additional_params: rule.additional_params || '', priority: rule.priority }; ruleDialog.value = true; } } +function editProxyGroup(name) { + const proxyGroup = proxyGroups.value.find(p => p.name === name); + if (proxyGroup) { + editingProxyGroupName.value = name; + newProxyGroup.value = { + name: proxyGroup.name, + type: proxyGroup.type, + proxies: proxyGroup?.proxies || [], + url: proxyGroup?.url || '', + lazy: proxyGroup?.lazy ?? true, + interval: proxyGroup?.interval ?? 300, + timeout: proxyGroup?.timeout ?? 5000, + 'disable-udp': proxyGroup?.['disable-udp'] ?? false, + filter: proxyGroup?.filter, + 'include-all': proxyGroup?.['include-all'] ?? false, + 'include-all-proxies': proxyGroup?.['include-all-proxies'] ?? false, + 'include-all-providers': proxyGroup?.['include-all-providers'] ?? false, + 'exclude-filter': proxyGroup?.['exclude-filter'] || '', + 'exclude-type': proxyGroup?.['exclude-type'] || '', + tolerance: proxyGroup?.tolerance ?? null, + strategy: proxyGroup?.strategy ?? null, + 'expected-status': proxyGroup?.['expected-status'] || '*', + hidden: proxyGroup?.hidden ?? false, + icon: proxyGroup?.icon || '', + use: proxyGroup?.use || null, + 'max-failed-times': proxyGroup?.['max-failed-times'] ?? 5, + }; + proxyGroupDialog.value = true; + } +} + function editRuleProvider(name) { const ruleProvider = extraRuleProviders.value.find(r => r.name === name); if (ruleProvider) { @@ -4557,17 +5042,20 @@ async function importExtraProxiesFun() { async function saveProxyGroups() { const {valid} = await proxyGroupsForm.value.validate(); + const action = editingProxyGroupName.value === null ? '添加代理组' : '更新代理组'; if (!valid) return; try { const requestData = { proxy_group: newProxyGroup.value, + name: editingProxyGroupName.value, }; - const result = await props.api.post('/plugin/ClashRuleProvider/proxy-group', requestData); + const method = editingProxyGroupName.value === null ? 'post' : 'put'; + const result = await props.api[method]('/plugin/ClashRuleProvider/proxy-group', requestData); if (!result.success) { - error.value = '导入代理组失败: ' + (result.message || '未知错误'); + error.value = action + '失败: ' + (result.message || '未知错误'); snackbar.value = { show: true, - message: '导入代理组失败', + message: action + '失败', color: 'error' }; return @@ -4576,14 +5064,14 @@ async function saveProxyGroups() { await refreshData(); snackbar.value = { show: true, - message: '更新代理组成功', + message: action + '成功', color: 'success' }; } catch (err) { - error.value = '导入代理组失败: ' + (err.message || '未知错误'); + error.value = action + '失败: ' + (err.message || '未知错误'); snackbar.value = { show: true, - message: '导入代理组失败', + message: action + '失败', color: 'error' }; } @@ -4593,6 +5081,13 @@ const ruleForm = ref(null); const proxyGroupsForm = ref(null); const ruleProvidersForm = ref(null); +function closeRuleDialog() { + ruleDialog.value = false; + geoSearch.value = ''; + geoIPSearch.value = ''; + filteredGeoItems.value = []; +} + // 保存规则 async function saveRule() { const {valid} = await ruleForm.value.validate(); @@ -4604,15 +5099,15 @@ async function saveRule() { rule_data: { ...newRule.value, additional_params: newRule.value.additional_params - ? newRule.value.additional_params.split(',').map(param => param.trim()).filter(param => param) - : [] + ? newRule.value.additional_params + : null } }; const method = editingPriority.value === null ? 'post' : 'put'; await props.api[method]('/plugin/ClashRuleProvider/rule', requestData); - ruleDialog.value = false; + closeRuleDialog(); await refreshData(); // 显示成功提示 @@ -4812,13 +5307,13 @@ async function refreshData() { } clashInfo.value = state?.data?.clash ?? clashInfo.value; rulesetPrefix.value = state?.data?.ruleset_prefix || '📂<='; + geoRules.value = state?.data?.geoRules ?? geoRules.value; rules.value = response?.data.rules || []; rulesetRules.value = response_ruleset?.data.rules || []; customOutbounds.value = outboundsResponse?.data.outbound || []; extraRuleProviders.value = providersResponse?.data || []; proxyGroups.value = proxyGroupsResponse?.data.proxy_groups || []; extraProxies.value = extraProxiesResponse?.data.extra_proxies || []; - // extraRuleProviders.value = extraRuleProvidersResponse?.data.rule_providers || []; lastUpdated.value = new Date().toLocaleString(); // 刷新后恢复面板状态 @@ -4879,6 +5374,7 @@ return (_ctx, _cache) => { const _component_v_spacer = _resolveComponent("v-spacer"); const _component_v_card_actions = _resolveComponent("v-card-actions"); const _component_v_snackbar = _resolveComponent("v-snackbar"); + const _component_v_autocomplete = _resolveComponent("v-autocomplete"); const _component_v_form = _resolveComponent("v-form"); const _component_v_dialog = _resolveComponent("v-dialog"); const _component_v_switch = _resolveComponent("v-switch"); @@ -4898,7 +5394,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[76] || (_cache[76] = [ + default: _withCtx(() => _cache[80] || (_cache[80] = [ _createTextVNode("mdi-close") ])), _: 1 @@ -4909,7 +5405,7 @@ return (_ctx, _cache) => { ]), default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[75] || (_cache[75] = [ + default: _withCtx(() => _cache[79] || (_cache[79] = [ _createTextVNode("Clash Rule Provider") ])), _: 1 @@ -4945,31 +5441,31 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[77] || (_cache[77] = [ + default: _withCtx(() => _cache[81] || (_cache[81] = [ _createTextVNode("规则集规则") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[78] || (_cache[78] = [ + default: _withCtx(() => _cache[82] || (_cache[82] = [ _createTextVNode("置顶规则") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[79] || (_cache[79] = [ + default: _withCtx(() => _cache[83] || (_cache[83] = [ _createTextVNode("代理组") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[80] || (_cache[80] = [ + default: _withCtx(() => _cache[84] || (_cache[84] = [ _createTextVNode("出站代理") ])), _: 1 }), _createVNode(_component_v_tab, null, { - default: _withCtx(() => _cache[81] || (_cache[81] = [ + default: _withCtx(() => _cache[85] || (_cache[85] = [ _createTextVNode("规则集合") ])), _: 1 @@ -4987,7 +5483,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_3, [ _createElementVNode("div", _hoisted_4, [ _createElementVNode("div", _hoisted_5, [ - _cache[84] || (_cache[84] = _createElementVNode("div", { class: "text-h6" }, "规则集规则", -1)), + _cache[88] || (_cache[88] = _createElementVNode("div", { class: "text-h6" }, "规则集规则", -1)), _createElementVNode("div", _hoisted_6, [ _createVNode(_component_v_text_field, { modelValue: searchRulesetRule.value, @@ -5006,12 +5502,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[82] || (_cache[82] = [ + default: _withCtx(() => _cache[86] || (_cache[86] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[83] || (_cache[83] = _createTextVNode(" 添加规则 ")) + _cache[87] || (_cache[87] = _createTextVNode(" 添加规则 ")) ]), _: 1 }) @@ -5040,7 +5536,7 @@ return (_ctx, _cache) => { }, [ _createElementVNode("td", null, [ _createVNode(_component_v_icon, { class: "drag-handle" }, { - default: _withCtx(() => _cache[85] || (_cache[85] = [ + default: _withCtx(() => _cache[89] || (_cache[89] = [ _createTextVNode("mdi-drag") ])), _: 1 @@ -5071,7 +5567,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[86] || (_cache[86] = [ + default: _withCtx(() => _cache[90] || (_cache[90] = [ _createTextVNode("mdi-pencil") ])), _: 1 @@ -5088,7 +5584,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[87] || (_cache[87] = [ + default: _withCtx(() => _cache[91] || (_cache[91] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5128,7 +5624,7 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "items", "search", "page", "items-per-page", "items-per-page-options"]), - _cache[88] || (_cache[88] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对规则集中规则的修改可以在Clash中立即生效。 ", -1)) + _cache[92] || (_cache[92] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对规则集中规则的修改可以在Clash中立即生效。 ", -1)) ]) ]), _: 1 @@ -5138,7 +5634,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_11, [ _createElementVNode("div", _hoisted_12, [ _createElementVNode("div", _hoisted_13, [ - _cache[93] || (_cache[93] = _createElementVNode("div", { class: "text-h6" }, "置顶规则", -1)), + _cache[97] || (_cache[97] = _createElementVNode("div", { class: "text-h6" }, "置顶规则", -1)), _createElementVNode("div", _hoisted_14, [ _createVNode(_component_v_text_field, { modelValue: searchTopRule.value, @@ -5158,12 +5654,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[89] || (_cache[89] = [ + default: _withCtx(() => _cache[93] || (_cache[93] = [ _createTextVNode("mdi-import") ])), _: 1 }), - _cache[90] || (_cache[90] = _createTextVNode(" 导入规则 ")) + _cache[94] || (_cache[94] = _createTextVNode(" 导入规则 ")) ]), _: 1 }), @@ -5173,12 +5669,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[91] || (_cache[91] = [ + default: _withCtx(() => _cache[95] || (_cache[95] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[92] || (_cache[92] = _createTextVNode(" 添加规则 ")) + _cache[96] || (_cache[96] = _createTextVNode(" 添加规则 ")) ]), _: 1 }) @@ -5207,7 +5703,7 @@ return (_ctx, _cache) => { }, [ _createElementVNode("td", null, [ _createVNode(_component_v_icon, { class: "drag-handle" }, { - default: _withCtx(() => _cache[94] || (_cache[94] = [ + default: _withCtx(() => _cache[98] || (_cache[98] = [ _createTextVNode("mdi-drag") ])), _: 1 @@ -5238,7 +5734,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[95] || (_cache[95] = [ + default: _withCtx(() => _cache[99] || (_cache[99] = [ _createTextVNode("mdi-pencil") ])), _: 1 @@ -5256,7 +5752,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[96] || (_cache[96] = [ + default: _withCtx(() => _cache[100] || (_cache[100] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5270,7 +5766,7 @@ return (_ctx, _cache) => { activator: "parent", location: "top" }, { - default: _withCtx(() => _cache[97] || (_cache[97] = [ + default: _withCtx(() => _cache[101] || (_cache[101] = [ _createTextVNode(" 根据规则集自动添加 ") ])), _: 1 @@ -5308,8 +5804,8 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "search", "items", "page", "items-per-page"]), - _cache[98] || (_cache[98] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *置顶规则用于管理来自规则集的匹配规则,这些规则会动态更新。 ", -1)), - _cache[99] || (_cache[99] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对置顶规则的修改只有Clash更新配置后才会生效。 ", -1)) + _cache[102] || (_cache[102] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *置顶规则用于管理来自规则集的匹配规则,这些规则会动态更新。 ", -1)), + _cache[103] || (_cache[103] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, " *对置顶规则的修改只有Clash更新配置后才会生效。 ", -1)) ]) ]), _: 1 @@ -5319,7 +5815,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_19, [ _createElementVNode("div", _hoisted_20, [ _createElementVNode("div", _hoisted_21, [ - _cache[102] || (_cache[102] = _createElementVNode("div", { class: "text-h6" }, "代理组", -1)), + _cache[106] || (_cache[106] = _createElementVNode("div", { class: "text-h6" }, "代理组", -1)), _createElementVNode("div", _hoisted_22, [ _createVNode(_component_v_btn, { color: "primary", @@ -5327,12 +5823,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[100] || (_cache[100] = [ + default: _withCtx(() => _cache[104] || (_cache[104] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[101] || (_cache[101] = _createTextVNode(" 添加代理组 ")) + _cache[105] || (_cache[105] = _createTextVNode(" 添加代理组 ")) ]), _: 1 }) @@ -5355,6 +5851,24 @@ return (_ctx, _cache) => { _createElementVNode("td", null, _toDisplayString(item.type), 1), _createElementVNode("td", null, _toDisplayString(item.source), 1), _createElementVNode("td", null, [ + _createVNode(_component_v_btn, { + icon: "", + size: "small", + color: "primary", + variant: "text", + onClick: $event => (editProxyGroup(item.name)), + disabled: !isManual(item.source) + }, { + default: _withCtx(() => [ + _createVNode(_component_v_icon, null, { + default: _withCtx(() => _cache[107] || (_cache[107] = [ + _createTextVNode("mdi-pencil") + ])), + _: 1 + }) + ]), + _: 2 + }, 1032, ["onClick", "disabled"]), _createVNode(_component_v_btn, { icon: "", size: "small", @@ -5364,7 +5878,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[103] || (_cache[103] = [ + default: _withCtx(() => _cache[108] || (_cache[108] = [ _createTextVNode("mdi-code-json") ])), _: 1 @@ -5382,7 +5896,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[104] || (_cache[104] = [ + default: _withCtx(() => _cache[109] || (_cache[109] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5396,7 +5910,7 @@ return (_ctx, _cache) => { activator: "parent", location: "top" }, { - default: _withCtx(() => _cache[105] || (_cache[105] = [ + default: _withCtx(() => _cache[110] || (_cache[110] = [ _createTextVNode(" 非手动添加 ") ])), _: 1 @@ -5434,7 +5948,7 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "items", "page", "items-per-page"]), - _cache[106] || (_cache[106] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) + _cache[111] || (_cache[111] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) ]) ]), _: 1 @@ -5444,7 +5958,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_26, [ _createElementVNode("div", _hoisted_27, [ _createElementVNode("div", _hoisted_28, [ - _cache[109] || (_cache[109] = _createElementVNode("div", { class: "text-h6" }, "出站代理", -1)), + _cache[114] || (_cache[114] = _createElementVNode("div", { class: "text-h6" }, "出站代理", -1)), _createElementVNode("div", _hoisted_29, [ _createVNode(_component_v_btn, { color: "primary", @@ -5452,12 +5966,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[107] || (_cache[107] = [ + default: _withCtx(() => _cache[112] || (_cache[112] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[108] || (_cache[108] = _createTextVNode(" 导入节点 ")) + _cache[113] || (_cache[113] = _createTextVNode(" 导入节点 ")) ]), _: 1 }) @@ -5491,7 +6005,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[110] || (_cache[110] = [ + default: _withCtx(() => _cache[115] || (_cache[115] = [ _createTextVNode("mdi-code-json") ])), _: 1 @@ -5508,9 +6022,9 @@ return (_ctx, _cache) => { disabled: !isManual(item.source) }, { default: _withCtx(() => [ - _cache[112] || (_cache[112] = _createTextVNode(" > ")), + _cache[117] || (_cache[117] = _createTextVNode(" > ")), _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[111] || (_cache[111] = [ + default: _withCtx(() => _cache[116] || (_cache[116] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5524,7 +6038,7 @@ return (_ctx, _cache) => { activator: "parent", location: "top" }, { - default: _withCtx(() => _cache[113] || (_cache[113] = [ + default: _withCtx(() => _cache[118] || (_cache[118] = [ _createTextVNode(" 非手动添加 ") ])), _: 1 @@ -5562,7 +6076,7 @@ return (_ctx, _cache) => { ]), _: 1 }, 8, ["headers", "items", "page", "items-per-page"]), - _cache[114] || (_cache[114] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) + _cache[119] || (_cache[119] = _createElementVNode("div", { class: "text-caption text-grey mt-2" }, null, -1)) ]) ]), _: 1 @@ -5572,7 +6086,7 @@ return (_ctx, _cache) => { _createElementVNode("div", _hoisted_33, [ _createElementVNode("div", _hoisted_34, [ _createElementVNode("div", _hoisted_35, [ - _cache[117] || (_cache[117] = _createElementVNode("div", { class: "text-h6" }, "规则集合", -1)), + _cache[122] || (_cache[122] = _createElementVNode("div", { class: "text-h6" }, "规则集合", -1)), _createElementVNode("div", _hoisted_36, [ _createVNode(_component_v_text_field, { modelValue: searchRuleProviders.value, @@ -5591,12 +6105,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[115] || (_cache[115] = [ + default: _withCtx(() => _cache[120] || (_cache[120] = [ _createTextVNode("mdi-plus") ])), _: 1 }), - _cache[116] || (_cache[116] = _createTextVNode(" 添加规则集合 ")) + _cache[121] || (_cache[121] = _createTextVNode(" 添加规则集合 ")) ]), _: 1 }) @@ -5632,9 +6146,9 @@ return (_ctx, _cache) => { disabled: !isManual(item.source) }, { default: _withCtx(() => [ - _cache[119] || (_cache[119] = _createTextVNode(" > ")), + _cache[124] || (_cache[124] = _createTextVNode(" > ")), _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[118] || (_cache[118] = [ + default: _withCtx(() => _cache[123] || (_cache[123] = [ _createTextVNode("mdi-pencil") ])), _: 1 @@ -5652,7 +6166,7 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[120] || (_cache[120] = [ + default: _withCtx(() => _cache[125] || (_cache[125] = [ _createTextVNode("mdi-delete") ])), _: 1 @@ -5666,7 +6180,7 @@ return (_ctx, _cache) => { activator: "parent", location: "top" }, { - default: _withCtx(() => _cache[121] || (_cache[121] = [ + default: _withCtx(() => _cache[126] || (_cache[126] = [ _createTextVNode(" 非手动添加 ") ])), _: 1 @@ -5724,7 +6238,7 @@ return (_ctx, _cache) => { _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, { class: "text-h6 font-weight-medium" }, { - default: _withCtx(() => _cache[122] || (_cache[122] = [ + default: _withCtx(() => _cache[127] || (_cache[127] = [ _createTextVNode("状态信息") ])), _: 1 @@ -5733,7 +6247,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createElementVNode("div", _hoisted_40, [ _createElementVNode("div", _hoisted_41, [ - _cache[123] || (_cache[123] = _createElementVNode("span", null, "状态", -1)), + _cache[128] || (_cache[128] = _createElementVNode("span", null, "状态", -1)), _createVNode(_component_v_chip, { size: "small", color: status.value === 'running' ? 'success' : 'warning' @@ -5745,23 +6259,23 @@ return (_ctx, _cache) => { }, 8, ["color"]) ]), _createElementVNode("div", _hoisted_42, [ - _cache[124] || (_cache[124] = _createElementVNode("span", null, "订阅配置规则数", -1)), + _cache[129] || (_cache[129] = _createElementVNode("span", null, "订阅配置规则数", -1)), _createElementVNode("span", _hoisted_43, _toDisplayString(subscriptionInfo.value.rule_size), 1) ]), _createElementVNode("div", _hoisted_44, [ - _cache[125] || (_cache[125] = _createElementVNode("span", null, "置顶规则数", -1)), + _cache[130] || (_cache[130] = _createElementVNode("span", null, "置顶规则数", -1)), _createElementVNode("span", null, _toDisplayString(sortedRules.value.length), 1) ]), _createElementVNode("div", _hoisted_45, [ - _cache[126] || (_cache[126] = _createElementVNode("span", null, "规则集规则数", -1)), + _cache[131] || (_cache[131] = _createElementVNode("span", null, "规则集规则数", -1)), _createElementVNode("span", null, _toDisplayString(sortedRulesetRules.value.length), 1) ]), _createElementVNode("div", _hoisted_46, [ - _cache[127] || (_cache[127] = _createElementVNode("span", null, "代理组数", -1)), + _cache[132] || (_cache[132] = _createElementVNode("span", null, "代理组数", -1)), _createElementVNode("span", null, _toDisplayString(proxyGroups.value.length), 1) ]), _createElementVNode("div", _hoisted_47, [ - _cache[128] || (_cache[128] = _createElementVNode("span", null, "最后更新", -1)), + _cache[133] || (_cache[133] = _createElementVNode("span", null, "最后更新", -1)), _createElementVNode("span", null, _toDisplayString(lastUpdated.value), 1) ]) ]) @@ -5783,7 +6297,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_card_title, { class: "d-flex justify-space-between align-center" }, { default: _withCtx(() => [ - _cache[130] || (_cache[130] = _createElementVNode("span", { class: "text-h6 font-weight-medium" }, "订阅链接", -1)), + _cache[135] || (_cache[135] = _createElementVNode("span", { class: "text-h6 font-weight-medium" }, "订阅链接", -1)), _createVNode(_component_v_tooltip, { location: "top", text: "复制链接" @@ -5799,7 +6313,7 @@ return (_ctx, _cache) => { }), { default: _withCtx(() => [ _createVNode(_component_v_icon, null, { - default: _withCtx(() => _cache[129] || (_cache[129] = [ + default: _withCtx(() => _cache[134] || (_cache[134] = [ _createTextVNode("mdi-content-copy") ])), _: 1 @@ -5823,12 +6337,12 @@ return (_ctx, _cache) => { color: "grey", class: "mr-2" }, { - default: _withCtx(() => _cache[131] || (_cache[131] = [ + default: _withCtx(() => _cache[136] || (_cache[136] = [ _createTextVNode("mdi-link") ])), _: 1 }), - _cache[132] || (_cache[132] = _createElementVNode("span", { class: "text-grey-darken-1" }, "原始链接:", -1)) + _cache[137] || (_cache[137] = _createElementVNode("span", { class: "text-grey-darken-1" }, "原始链接:", -1)) ]), _createElementVNode("div", _hoisted_50, [ (Object.keys(subscriptionsInfo.value).length > 0) @@ -5849,7 +6363,7 @@ return (_ctx, _cache) => { ]), _createElementVNode("div", _hoisted_52, [ _createVNode(_component_v_icon, { color: "blue" }, { - default: _withCtx(() => _cache[133] || (_cache[133] = [ + default: _withCtx(() => _cache[138] || (_cache[138] = [ _createTextVNode("mdi-arrow-down-bold") ])), _: 1 @@ -5860,12 +6374,12 @@ return (_ctx, _cache) => { color: "primary", class: "mr-2" }, { - default: _withCtx(() => _cache[134] || (_cache[134] = [ + default: _withCtx(() => _cache[139] || (_cache[139] = [ _createTextVNode("mdi-link-variant") ])), _: 1 }), - _cache[135] || (_cache[135] = _createElementVNode("span", { class: "text-grey-darken-1" }, "生成链接:", -1)) + _cache[140] || (_cache[140] = _createElementVNode("span", { class: "text-grey-darken-1" }, "生成链接:", -1)) ]), _createElementVNode("div", _hoisted_54, [ _createElementVNode("a", { @@ -5898,12 +6412,12 @@ return (_ctx, _cache) => { _createVNode(_component_v_expansion_panel_title, null, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[136] || (_cache[136] = [ + default: _withCtx(() => _cache[141] || (_cache[141] = [ _createTextVNode("mdi-cloud-download") ])), _: 1 }), - _cache[137] || (_cache[137] = _createElementVNode("span", { class: "text-subtitle-1 font-weight-medium" }, "订阅管理", -1)) + _cache[142] || (_cache[142] = _createElementVNode("span", { class: "text-subtitle-1 font-weight-medium" }, "订阅管理", -1)) ]), _: 1 }), @@ -5916,7 +6430,7 @@ return (_ctx, _cache) => { variant: "tonal", class: "mb-4" }, { - default: _withCtx(() => _cache[138] || (_cache[138] = [ + default: _withCtx(() => _cache[143] || (_cache[143] = [ _createTextVNode(" 暂无订阅信息,请先添加订阅链接 ") ])), _: 1 @@ -6002,11 +6516,11 @@ return (_ctx, _cache) => { : _createCommentVNode("", true) ]), _createElementVNode("div", _hoisted_58, [ - _cache[139] || (_cache[139] = _createElementVNode("span", null, "已用流量:", -1)), + _cache[144] || (_cache[144] = _createElementVNode("span", null, "已用流量:", -1)), _createElementVNode("strong", null, _toDisplayString(formatBytes(info.download + info.upload)), 1) ]), _createElementVNode("div", _hoisted_59, [ - _cache[140] || (_cache[140] = _createElementVNode("span", null, "剩余流量:", -1)), + _cache[145] || (_cache[145] = _createElementVNode("span", null, "剩余流量:", -1)), _createElementVNode("strong", null, _toDisplayString(formatBytes(info.total - info.download)), 1) ]), _createVNode(_component_v_progress_linear, { @@ -6035,12 +6549,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[141] || (_cache[141] = [ + default: _withCtx(() => _cache[146] || (_cache[146] = [ _createTextVNode("mdi-cloud-sync") ])), _: 1 }), - _cache[142] || (_cache[142] = _createTextVNode(" 更新订阅 ")) + _cache[147] || (_cache[147] = _createTextVNode(" 更新订阅 ")) ]), _: 2 }, 1032, ["onClick", "loading"]) @@ -6074,12 +6588,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[143] || (_cache[143] = [ + default: _withCtx(() => _cache[148] || (_cache[148] = [ _createTextVNode("mdi-refresh") ])), _: 1 }), - _cache[144] || (_cache[144] = _createTextVNode(" 刷新数据 ")) + _cache[149] || (_cache[149] = _createTextVNode(" 刷新数据 ")) ]), _: 1 }, 8, ["loading"]), @@ -6090,12 +6604,12 @@ return (_ctx, _cache) => { }, { default: _withCtx(() => [ _createVNode(_component_v_icon, { left: "" }, { - default: _withCtx(() => _cache[145] || (_cache[145] = [ + default: _withCtx(() => _cache[150] || (_cache[150] = [ _createTextVNode("mdi-cog") ])), _: 1 }), - _cache[146] || (_cache[146] = _createTextVNode(" 配置 ")) + _cache[151] || (_cache[151] = _createTextVNode(" 配置 ")) ]), _: 1 }) @@ -6119,7 +6633,7 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_dialog, { modelValue: ruleDialog.value, - "onUpdate:modelValue": _cache[34] || (_cache[34] = $event => ((ruleDialog).value = $event)), + "onUpdate:modelValue": _cache[35] || (_cache[35] = $event => ((ruleDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ @@ -6147,29 +6661,69 @@ return (_ctx, _cache) => { required: "", class: "mb-4" }, null, 8, ["modelValue", "items"]), - (newRule.value.type !== 'RULE-SET') - ? (_openBlock(), _createBlock(_component_v_text_field, { + (newRule.value.type === 'RULE-SET') + ? (_openBlock(), _createBlock(_component_v_select, { key: 0, modelValue: newRule.value.payload, "onUpdate:modelValue": _cache[28] || (_cache[28] = $event => ((newRule.value.payload) = $event)), - label: "内容", - required: "", - rules: payloadRules.value, - class: "mb-4" - }, null, 8, ["modelValue", "rules"])) - : (_openBlock(), _createBlock(_component_v_select, { - key: 1, - modelValue: newRule.value.payload, - "onUpdate:modelValue": _cache[29] || (_cache[29] = $event => ((newRule.value.payload) = $event)), items: ruleProviderNames.value, label: "选择规则集", required: "", rules: [(v) => !!v || '请选择一个有效的规则集',], class: "mb-4" - }, null, 8, ["modelValue", "items", "rules"])), + }, null, 8, ["modelValue", "items", "rules"])) + : (newRule.value.type === 'GEOSITE') + ? (_openBlock(), _createBlock(_component_v_autocomplete, { + key: 1, + modelValue: newRule.value.payload, + "onUpdate:modelValue": _cache[29] || (_cache[29] = $event => ((newRule.value.payload) = $event)), + search: geoSearch.value, + items: filteredGeoItems.value, + loading: geoFilterLoading.value, + "hide-no-data": "", + "hide-selected": "", + label: "内容", + "no-filter": "", + solo: "", + "custom-filter": () => true, + clearable: "", + "onUpdate:search": onGeoSearch, + onBlur: onGeoSiteBlur, + class: "mb-4", + rules: payloadRules.value + }, null, 8, ["modelValue", "search", "items", "loading", "rules"])) + : (newRule.value.type === 'GEOIP') + ? (_openBlock(), _createBlock(_component_v_autocomplete, { + key: 2, + modelValue: newRule.value.payload, + "onUpdate:modelValue": _cache[30] || (_cache[30] = $event => ((newRule.value.payload) = $event)), + search: geoIPSearch.value, + items: filteredGeoItems.value, + loading: geoFilterLoading.value, + "hide-no-data": "", + "hide-selected": "", + label: "内容", + "no-filter": "", + solo: "", + "custom-filter": () => true, + clearable: "", + "onUpdate:search": onGeoIPSearch, + onBlur: onGeoIPBlur, + class: "mb-4", + rules: payloadRules.value + }, null, 8, ["modelValue", "search", "items", "loading", "rules"])) + : (_openBlock(), _createBlock(_component_v_text_field, { + key: 3, + modelValue: newRule.value.payload, + "onUpdate:modelValue": _cache[31] || (_cache[31] = $event => ((newRule.value.payload) = $event)), + label: "内容", + required: "", + rules: payloadRules.value, + class: "mb-4" + }, null, 8, ["modelValue", "rules"])), _createVNode(_component_v_select, { modelValue: newRule.value.action, - "onUpdate:modelValue": _cache[30] || (_cache[30] = $event => ((newRule.value.action) = $event)), + "onUpdate:modelValue": _cache[32] || (_cache[32] = $event => ((newRule.value.action) = $event)), items: actions.value, label: "出站", required: "", @@ -6177,9 +6731,9 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "items"]), (showAdditionalParams.value) ? (_openBlock(), _createBlock(_component_v_select, { - key: 2, + key: 4, modelValue: newRule.value.additional_params, - "onUpdate:modelValue": _cache[31] || (_cache[31] = $event => ((newRule.value.additional_params) = $event)), + "onUpdate:modelValue": _cache[33] || (_cache[33] = $event => ((newRule.value.additional_params) = $event)), label: "附加参数", items: additionalParamOptions.value, clearable: "", @@ -6190,9 +6744,9 @@ return (_ctx, _cache) => { : _createCommentVNode("", true), (editingPriority.value !== null) ? (_openBlock(), _createBlock(_component_v_text_field, { - key: 3, + key: 5, modelValue: newRule.value.priority, - "onUpdate:modelValue": _cache[32] || (_cache[32] = $event => ((newRule.value.priority) = $event)), + "onUpdate:modelValue": _cache[34] || (_cache[34] = $event => ((newRule.value.priority) = $event)), modelModifiers: { number: true }, type: "number", label: "优先级", @@ -6208,9 +6762,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[33] || (_cache[33] = $event => (ruleDialog.value = false)) + onClick: closeRuleDialog }, { - default: _withCtx(() => _cache[147] || (_cache[147] = [ + default: _withCtx(() => _cache[152] || (_cache[152] = [ _createTextVNode("取消") ])), _: 1 @@ -6219,7 +6773,7 @@ return (_ctx, _cache) => { color: "primary", type: "submit" }, { - default: _withCtx(() => _cache[148] || (_cache[148] = [ + default: _withCtx(() => _cache[153] || (_cache[153] = [ _createTextVNode("保存") ])), _: 1 @@ -6238,7 +6792,7 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: proxyGroupDialog.value, - "onUpdate:modelValue": _cache[52] || (_cache[52] = $event => ((proxyGroupDialog).value = $event)), + "onUpdate:modelValue": _cache[56] || (_cache[56] = $event => ((proxyGroupDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ @@ -6251,9 +6805,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[149] || (_cache[149] = [ - _createTextVNode(_toDisplayString('添加代理组')) - ])), + default: _withCtx(() => [ + _createTextVNode(_toDisplayString(editingProxyGroupName.value === null ? '添加代理组' : '编辑代理组'), 1) + ]), _: 1 }), _createVNode(_component_v_card_text, null, { @@ -6267,12 +6821,12 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.name, - "onUpdate:modelValue": _cache[35] || (_cache[35] = $event => ((newProxyGroup.value.name) = $event)), + "onUpdate:modelValue": _cache[36] || (_cache[36] = $event => ((newProxyGroup.value.name) = $event)), label: "name", required: "", - class: "mb-4", hint: "策略组的名字", - rules: [v => !!v || 'Name不能为空'] + rules: [v => !!v || 'Name不能为空'], + class: "mb-4" }, null, 8, ["modelValue", "rules"]) ]), _: 1 @@ -6284,7 +6838,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: newProxyGroup.value.type, - "onUpdate:modelValue": _cache[36] || (_cache[36] = $event => ((newProxyGroup.value.type) = $event)), + "onUpdate:modelValue": _cache[37] || (_cache[37] = $event => ((newProxyGroup.value.type) = $event)), label: "type", items: proxyGroupTypes.value, required: "", @@ -6299,76 +6853,120 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_select, { modelValue: newProxyGroup.value.proxies, - "onUpdate:modelValue": _cache[37] || (_cache[37] = $event => ((newProxyGroup.value.proxies) = $event)), + "onUpdate:modelValue": _cache[38] || (_cache[38] = $event => ((newProxyGroup.value.proxies) = $event)), label: "proxies", items: actions.value, multiple: "", chips: "", - class: "mb-4", - hint: "引入出站代理或其他策略组" + clearable: "", + hint: "引入出站代理或其他策略组", + class: "mb-4" }, null, 8, ["modelValue", "items"]), _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.url, - "onUpdate:modelValue": _cache[38] || (_cache[38] = $event => ((newProxyGroup.value.url) = $event)), + "onUpdate:modelValue": _cache[39] || (_cache[39] = $event => ((newProxyGroup.value.url) = $event)), label: "url", - class: "mb-4", hint: "健康检查测试地址", rules: urlRules, - clearable: "" + clearable: "", + class: "mb-4" }, null, 8, ["modelValue"]), (newProxyGroup.value.type === 'url-test') ? (_openBlock(), _createBlock(_component_v_text_field, { key: 0, modelValue: newProxyGroup.value.tolerance, - "onUpdate:modelValue": _cache[39] || (_cache[39] = $event => ((newProxyGroup.value.tolerance) = $event)), + "onUpdate:modelValue": _cache[40] || (_cache[40] = $event => ((newProxyGroup.value.tolerance) = $event)), modelModifiers: { number: true }, label: "tolerance (ms)", variant: "outlined", type: "number", min: "10", - class: "mb-4", hint: "节点切换容差", - rules: [v => v >=10 || '检查间隔需不小于0'] + rules: [v => v >=10 || '检查间隔需不小于0'], + class: "mb-4" }, null, 8, ["modelValue", "rules"])) : _createCommentVNode("", true), (newProxyGroup.value.type === 'load-balance') ? (_openBlock(), _createBlock(_component_v_select, { key: 1, modelValue: newProxyGroup.value.strategy, - "onUpdate:modelValue": _cache[40] || (_cache[40] = $event => ((newProxyGroup.value.strategy) = $event)), + "onUpdate:modelValue": _cache[41] || (_cache[41] = $event => ((newProxyGroup.value.strategy) = $event)), label: "strategy", items: strategyTypes.value, - class: "mb-4", - hint: "负载均衡策略" + hint: "负载均衡策略", + class: "mb-4" }, null, 8, ["modelValue", "items"])) : _createCommentVNode("", true), + _createVNode(_component_v_row, null, { + default: _withCtx(() => [ + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: newProxyGroup.value.filter, + "onUpdate:modelValue": _cache[42] || (_cache[42] = $event => ((newProxyGroup.value.filter) = $event)), + label: "filter", + hint: "筛选满足关键词或正则表达式的节点" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: newProxyGroup.value['exclude-filter'], + "onUpdate:modelValue": _cache[43] || (_cache[43] = $event => ((newProxyGroup.value['exclude-filter']) = $event)), + label: "exclude-filter", + hint: "排除满足关键词或正则表达式的节点" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: newProxyGroup.value['exclude-type'], + "onUpdate:modelValue": _cache[44] || (_cache[44] = $event => ((newProxyGroup.value['exclude-type']) = $event)), + label: "exclude-type", + hint: "不支持正则表达式,通过 | 分割", + class: "mb-4" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: newProxyGroup.value['expected-status'], + "onUpdate:modelValue": _cache[45] || (_cache[45] = $event => ((newProxyGroup.value['expected-status']) = $event)), + label: "expected-status", + hint: "健康检查时期望的 HTTP 响应状态码", + class: "mb-4" + }, null, 8, ["modelValue"]) + ]), + _: 1 + }) + ]), + _: 1 + }), _createVNode(_component_v_text_field, { - modelValue: newProxyGroup.value.filter, - "onUpdate:modelValue": _cache[41] || (_cache[41] = $event => ((newProxyGroup.value.filter) = $event)), - label: "filter", - class: "mb-4", - hint: "筛选满足关键词或正则表达式的节点" - }, null, 8, ["modelValue"]), - _createVNode(_component_v_text_field, { - modelValue: newProxyGroup.value['exclude-filter'], - "onUpdate:modelValue": _cache[42] || (_cache[42] = $event => ((newProxyGroup.value['exclude-filter']) = $event)), - label: "exclude-filter", - class: "mb-4", - hint: "排除满足关键词或正则表达式的节点" - }, null, 8, ["modelValue"]), - _createVNode(_component_v_text_field, { - modelValue: newProxyGroup.value['exclude-type'], - "onUpdate:modelValue": _cache[43] || (_cache[43] = $event => ((newProxyGroup.value['exclude-type']) = $event)), - label: "exclude-type", - class: "mb-4", - hint: "不支持正则表达式,通过 | 分割" - }, null, 8, ["modelValue"]), - _createVNode(_component_v_text_field, { - modelValue: newProxyGroup.value['expected-status'], - "onUpdate:modelValue": _cache[44] || (_cache[44] = $event => ((newProxyGroup.value['expected-status']) = $event)), - label: "expected-status", - class: "mb-4", - hint: "健康检查时期望的 HTTP 响应状态码" + modelValue: newProxyGroup.value.icon, + "onUpdate:modelValue": _cache[46] || (_cache[46] = $event => ((newProxyGroup.value.icon) = $event)), + label: "icon", + clearable: "", + hint: "在 api 返回icon所输入的字符串", + class: "mb-4" }, null, 8, ["modelValue"]), _createVNode(_component_v_row, null, { default: _withCtx(() => [ @@ -6379,7 +6977,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.interval, - "onUpdate:modelValue": _cache[45] || (_cache[45] = $event => ((newProxyGroup.value.interval) = $event)), + "onUpdate:modelValue": _cache[47] || (_cache[47] = $event => ((newProxyGroup.value.interval) = $event)), modelModifiers: { number: true }, label: "interval", variant: "outlined", @@ -6391,7 +6989,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "warning" }, { - default: _withCtx(() => _cache[150] || (_cache[150] = [ + default: _withCtx(() => _cache[154] || (_cache[154] = [ _createTextVNode("mdi-timer") ])), _: 1 @@ -6409,7 +7007,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newProxyGroup.value.timeout, - "onUpdate:modelValue": _cache[46] || (_cache[46] = $event => ((newProxyGroup.value.timeout) = $event)), + "onUpdate:modelValue": _cache[48] || (_cache[48] = $event => ((newProxyGroup.value.timeout) = $event)), modelModifiers: { number: true }, label: "timeout", variant: "outlined", @@ -6421,7 +7019,7 @@ return (_ctx, _cache) => { }, { "prepend-inner": _withCtx(() => [ _createVNode(_component_v_icon, { color: "warning" }, { - default: _withCtx(() => _cache[151] || (_cache[151] = [ + default: _withCtx(() => _cache[155] || (_cache[155] = [ _createTextVNode("mdi-timer") ])), _: 1 @@ -6437,6 +7035,25 @@ return (_ctx, _cache) => { }), _createVNode(_component_v_row, null, { default: _withCtx(() => [ + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_text_field, { + modelValue: newProxyGroup.value['max-failed-times'], + "onUpdate:modelValue": _cache[49] || (_cache[49] = $event => ((newProxyGroup.value['max-failed-times']) = $event)), + modelModifiers: { number: true }, + label: "max-failed-times", + variant: "outlined", + type: "number", + min: "0", + hint: "最大失败次数", + rules: [v => v >= 0 || '最大失败次数必须大于等于0'] + }, null, 8, ["modelValue", "rules"]) + ]), + _: 1 + }), _createVNode(_component_v_col, { cols: "12", md: "6" @@ -6444,7 +7061,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['lazy'], - "onUpdate:modelValue": _cache[47] || (_cache[47] = $event => ((newProxyGroup.value['lazy']) = $event)), + "onUpdate:modelValue": _cache[50] || (_cache[50] = $event => ((newProxyGroup.value['lazy']) = $event)), label: "lazy", inset: "", hint: "未选择到当前策略组时,不进行测试", @@ -6460,7 +7077,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['disable-udp'], - "onUpdate:modelValue": _cache[48] || (_cache[48] = $event => ((newProxyGroup.value['disable-udp']) = $event)), + "onUpdate:modelValue": _cache[51] || (_cache[51] = $event => ((newProxyGroup.value['disable-udp']) = $event)), label: "disable-udp", inset: "", hint: "禁用该策略组的UDP", @@ -6468,6 +7085,22 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]) ]), _: 1 + }), + _createVNode(_component_v_col, { + cols: "12", + md: "6" + }, { + default: _withCtx(() => [ + _createVNode(_component_v_switch, { + modelValue: newProxyGroup.value.hidden, + "onUpdate:modelValue": _cache[52] || (_cache[52] = $event => ((newProxyGroup.value.hidden) = $event)), + label: "hidden", + inset: "", + hint: "在 api 返回hidden状态", + "persistent-hint": "" + }, null, 8, ["modelValue"]) + ]), + _: 1 }) ]), _: 1 @@ -6481,7 +7114,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['include-all'], - "onUpdate:modelValue": _cache[49] || (_cache[49] = $event => ((newProxyGroup.value['include-all']) = $event)), + "onUpdate:modelValue": _cache[53] || (_cache[53] = $event => ((newProxyGroup.value['include-all']) = $event)), label: "include-all", inset: "", hint: "引入所有出站代理以及代理集合", @@ -6497,7 +7130,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_switch, { modelValue: newProxyGroup.value['include-all-proxies'], - "onUpdate:modelValue": _cache[50] || (_cache[50] = $event => ((newProxyGroup.value['include-all-proxies']) = $event)), + "onUpdate:modelValue": _cache[54] || (_cache[54] = $event => ((newProxyGroup.value['include-all-proxies']) = $event)), label: "include-all-proxies", inset: "", hint: "引入所有出站代理", @@ -6518,7 +7151,7 @@ return (_ctx, _cache) => { class: "mb-6", variant: "tonal" }, { - default: _withCtx(() => _cache[152] || (_cache[152] = [ + default: _withCtx(() => _cache[156] || (_cache[156] = [ _createTextVNode(" 参考"), _createElementVNode("a", { href: "https://wiki.metacubex.one/config/proxy-groups/", @@ -6532,9 +7165,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[51] || (_cache[51] = $event => (proxyGroupDialog.value = false)) + onClick: _cache[55] || (_cache[55] = $event => (proxyGroupDialog.value = false)) }, { - default: _withCtx(() => _cache[153] || (_cache[153] = [ + default: _withCtx(() => _cache[157] || (_cache[157] = [ _createTextVNode("取消") ])), _: 1 @@ -6543,7 +7176,7 @@ return (_ctx, _cache) => { color: "primary", type: "submit" }, { - default: _withCtx(() => _cache[154] || (_cache[154] = [ + default: _withCtx(() => _cache[158] || (_cache[158] = [ _createTextVNode("保存") ])), _: 1 @@ -6562,15 +7195,15 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: yamlDialog.value, - "onUpdate:modelValue": _cache[55] || (_cache[55] = $event => ((yamlDialog).value = $event)), + "onUpdate:modelValue": _cache[59] || (_cache[59] = $event => ((yamlDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, { class: "headline" }, { - default: _withCtx(() => _cache[155] || (_cache[155] = [ - _createTextVNode("YAML配置") + default: _withCtx(() => _cache[159] || (_cache[159] = [ + _createTextVNode("YAML 配置") ])), _: 1 }), @@ -6585,18 +7218,18 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "primary", - onClick: _cache[53] || (_cache[53] = $event => (copyToClipboard(displayedYaml.value))) + onClick: _cache[57] || (_cache[57] = $event => (copyToClipboard(displayedYaml.value))) }, { - default: _withCtx(() => _cache[156] || (_cache[156] = [ + default: _withCtx(() => _cache[160] || (_cache[160] = [ _createTextVNode("复制") ])), _: 1 }), _createVNode(_component_v_btn, { color: "primary", - onClick: _cache[54] || (_cache[54] = $event => (yamlDialog.value = false)) + onClick: _cache[58] || (_cache[58] = $event => (yamlDialog.value = false)) }, { - default: _withCtx(() => _cache[157] || (_cache[157] = [ + default: _withCtx(() => _cache[161] || (_cache[161] = [ _createTextVNode("关闭") ])), _: 1 @@ -6612,14 +7245,14 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: importRuleDialog.value, - "onUpdate:modelValue": _cache[59] || (_cache[59] = $event => ((importRuleDialog).value = $event)), + "onUpdate:modelValue": _cache[63] || (_cache[63] = $event => ((importRuleDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[158] || (_cache[158] = [ + default: _withCtx(() => _cache[162] || (_cache[162] = [ _createTextVNode("导入规则") ])), _: 1 @@ -6628,7 +7261,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: importRules.value.type, - "onUpdate:modelValue": _cache[56] || (_cache[56] = $event => ((importRules.value.type) = $event)), + "onUpdate:modelValue": _cache[60] || (_cache[60] = $event => ((importRules.value.type) = $event)), items: importRuleTypes, label: "内容格式", required: "", @@ -6636,7 +7269,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_textarea, { modelValue: importRules.value.payload, - "onUpdate:modelValue": _cache[57] || (_cache[57] = $event => ((importRules.value.payload) = $event)), + "onUpdate:modelValue": _cache[61] || (_cache[61] = $event => ((importRules.value.payload) = $event)), label: "内容", required: "", placeholder: "rules: []", @@ -6652,7 +7285,7 @@ return (_ctx, _cache) => { class: "mb-4", variant: "tonal" }, { - default: _withCtx(() => _cache[159] || (_cache[159] = [ + default: _withCtx(() => _cache[163] || (_cache[163] = [ _createTextVNode(" 请输入 Clash 规则中的 "), _createElementVNode("strong", null, "rules", -1), _createTextVNode(" 字段,例如:"), @@ -6673,9 +7306,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[58] || (_cache[58] = $event => (importRuleDialog.value = false, error.value=null)) + onClick: _cache[62] || (_cache[62] = $event => (importRuleDialog.value = false, error.value=null)) }, { - default: _withCtx(() => _cache[160] || (_cache[160] = [ + default: _withCtx(() => _cache[164] || (_cache[164] = [ _createTextVNode("取消") ])), _: 1 @@ -6684,7 +7317,7 @@ return (_ctx, _cache) => { color: "primary", onClick: importRule }, { - default: _withCtx(() => _cache[161] || (_cache[161] = [ + default: _withCtx(() => _cache[165] || (_cache[165] = [ _createTextVNode("导入") ])), _: 1 @@ -6700,14 +7333,14 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: importExtraProxiesDialog.value, - "onUpdate:modelValue": _cache[63] || (_cache[63] = $event => ((importExtraProxiesDialog).value = $event)), + "onUpdate:modelValue": _cache[67] || (_cache[67] = $event => ((importExtraProxiesDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ _createVNode(_component_v_card, null, { default: _withCtx(() => [ _createVNode(_component_v_card_title, null, { - default: _withCtx(() => _cache[162] || (_cache[162] = [ + default: _withCtx(() => _cache[166] || (_cache[166] = [ _createTextVNode("导入节点") ])), _: 1 @@ -6716,7 +7349,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_select, { modelValue: importExtraProxies.value.type, - "onUpdate:modelValue": _cache[60] || (_cache[60] = $event => ((importExtraProxies.value.type) = $event)), + "onUpdate:modelValue": _cache[64] || (_cache[64] = $event => ((importExtraProxies.value.type) = $event)), items: importProxiesTypes, label: "内容格式", required: "", @@ -6724,7 +7357,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_textarea, { modelValue: importExtraProxies.value.payload, - "onUpdate:modelValue": _cache[61] || (_cache[61] = $event => ((importExtraProxies.value.payload) = $event)), + "onUpdate:modelValue": _cache[65] || (_cache[65] = $event => ((importExtraProxies.value.payload) = $event)), label: "内容", required: "", placeholder: importExtraProxiesPlaceholderText.value, @@ -6741,7 +7374,7 @@ return (_ctx, _cache) => { class: "mb-4", variant: "tonal" }, { - default: _withCtx(() => _cache[163] || (_cache[163] = [ + default: _withCtx(() => _cache[167] || (_cache[167] = [ _createTextVNode(" 请输入 Clash 规则中的 "), _createElementVNode("strong", null, "proxies", -1), _createTextVNode(" 字段,例如:"), @@ -6763,7 +7396,7 @@ return (_ctx, _cache) => { class: "mb-4", variant: "tonal" }, { - default: _withCtx(() => _cache[164] || (_cache[164] = [ + default: _withCtx(() => _cache[168] || (_cache[168] = [ _createTextVNode(" 请输入 V2Ray 格式的节点链接,例如:"), _createElementVNode("br", null, null, -1), _createElementVNode("code", null, "vmess://xxxx", -1), @@ -6781,9 +7414,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[62] || (_cache[62] = $event => (importExtraProxiesDialog.value=false, error.value=null)) + onClick: _cache[66] || (_cache[66] = $event => (importExtraProxiesDialog.value=false, error.value=null)) }, { - default: _withCtx(() => _cache[165] || (_cache[165] = [ + default: _withCtx(() => _cache[169] || (_cache[169] = [ _createTextVNode("取消") ])), _: 1 @@ -6792,7 +7425,7 @@ return (_ctx, _cache) => { color: "primary", onClick: importExtraProxiesFun }, { - default: _withCtx(() => _cache[166] || (_cache[166] = [ + default: _withCtx(() => _cache[170] || (_cache[170] = [ _createTextVNode("导入") ])), _: 1 @@ -6808,7 +7441,7 @@ return (_ctx, _cache) => { }, 8, ["modelValue"]), _createVNode(_component_v_dialog, { modelValue: ruleProviderDialog.value, - "onUpdate:modelValue": _cache[74] || (_cache[74] = $event => ((ruleProviderDialog).value = $event)), + "onUpdate:modelValue": _cache[78] || (_cache[78] = $event => ((ruleProviderDialog).value = $event)), "max-width": "600" }, { default: _withCtx(() => [ @@ -6830,7 +7463,7 @@ return (_ctx, _cache) => { default: _withCtx(() => [ _createVNode(_component_v_text_field, { modelValue: newRuleProvider.value.name, - "onUpdate:modelValue": _cache[64] || (_cache[64] = $event => ((newRuleProvider.value.name) = $event)), + "onUpdate:modelValue": _cache[68] || (_cache[68] = $event => ((newRuleProvider.value.name) = $event)), label: "name", required: "", rules: [v => !!v || '名称不能为空'], @@ -6838,7 +7471,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "rules"]), _createVNode(_component_v_select, { modelValue: newRuleProvider.value.type, - "onUpdate:modelValue": _cache[65] || (_cache[65] = $event => ((newRuleProvider.value.type) = $event)), + "onUpdate:modelValue": _cache[69] || (_cache[69] = $event => ((newRuleProvider.value.type) = $event)), items: ruleProviderTypes, label: "type", required: "", @@ -6849,7 +7482,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 0, modelValue: newRuleProvider.value.url, - "onUpdate:modelValue": _cache[66] || (_cache[66] = $event => ((newRuleProvider.value.url) = $event)), + "onUpdate:modelValue": _cache[70] || (_cache[70] = $event => ((newRuleProvider.value.url) = $event)), label: "url", required: "", rules: [(v) => !!v || 'URL 不能为空', (v) => isValidUrl(v) || '请输入有效的 URL',], @@ -6861,7 +7494,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_text_field, { key: 1, modelValue: newRuleProvider.value.path, - "onUpdate:modelValue": _cache[67] || (_cache[67] = $event => ((newRuleProvider.value.path) = $event)), + "onUpdate:modelValue": _cache[71] || (_cache[71] = $event => ((newRuleProvider.value.path) = $event)), label: "path", required: "", rules: [v => !!v || '当类型为文件时,路径不能为空'], @@ -6871,7 +7504,7 @@ return (_ctx, _cache) => { : _createCommentVNode("", true), _createVNode(_component_v_text_field, { modelValue: newRuleProvider.value.interval, - "onUpdate:modelValue": _cache[68] || (_cache[68] = $event => ((newRuleProvider.value.interval) = $event)), + "onUpdate:modelValue": _cache[72] || (_cache[72] = $event => ((newRuleProvider.value.interval) = $event)), modelModifiers: { number: true }, label: "interval", class: "mb-4", @@ -6883,7 +7516,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue", "rules"]), _createVNode(_component_v_select, { modelValue: newRuleProvider.value.behavior, - "onUpdate:modelValue": _cache[69] || (_cache[69] = $event => ((newRuleProvider.value.behavior) = $event)), + "onUpdate:modelValue": _cache[73] || (_cache[73] = $event => ((newRuleProvider.value.behavior) = $event)), items: ruleProviderBehaviorTypes, label: "behavior", class: "mb-4", @@ -6891,7 +7524,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_select, { modelValue: newRuleProvider.value.format, - "onUpdate:modelValue": _cache[70] || (_cache[70] = $event => ((newRuleProvider.value.format) = $event)), + "onUpdate:modelValue": _cache[74] || (_cache[74] = $event => ((newRuleProvider.value.format) = $event)), items: ruleProviderFormatTypes, label: "format", class: "mb-4", @@ -6899,7 +7532,7 @@ return (_ctx, _cache) => { }, null, 8, ["modelValue"]), _createVNode(_component_v_text_field, { modelValue: newRuleProvider.value['size-limit'], - "onUpdate:modelValue": _cache[71] || (_cache[71] = $event => ((newRuleProvider.value['size-limit']) = $event)), + "onUpdate:modelValue": _cache[75] || (_cache[75] = $event => ((newRuleProvider.value['size-limit']) = $event)), modelModifiers: { number: true }, label: "size-limit", class: "mb-4", @@ -6913,7 +7546,7 @@ return (_ctx, _cache) => { ? (_openBlock(), _createBlock(_component_v_combobox, { key: 2, modelValue: newRuleProvider.value.payload, - "onUpdate:modelValue": _cache[72] || (_cache[72] = $event => ((newRuleProvider.value.payload) = $event)), + "onUpdate:modelValue": _cache[76] || (_cache[76] = $event => ((newRuleProvider.value.payload) = $event)), multiple: "", chips: "", "closable-chips": "", @@ -6947,9 +7580,9 @@ return (_ctx, _cache) => { _createVNode(_component_v_spacer), _createVNode(_component_v_btn, { color: "secondary", - onClick: _cache[73] || (_cache[73] = $event => (ruleProviderDialog.value = false, error.value=null)) + onClick: _cache[77] || (_cache[77] = $event => (ruleProviderDialog.value = false, error.value=null)) }, { - default: _withCtx(() => _cache[167] || (_cache[167] = [ + default: _withCtx(() => _cache[171] || (_cache[171] = [ _createTextVNode("取消") ])), _: 1 @@ -6958,7 +7591,7 @@ return (_ctx, _cache) => { color: "primary", type: "submit" }, { - default: _withCtx(() => _cache[168] || (_cache[168] = [ + default: _withCtx(() => _cache[172] || (_cache[172] = [ _createTextVNode("保存") ])), _: 1 @@ -6980,6 +7613,6 @@ return (_ctx, _cache) => { } }; -const PageComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-a60e548e"]]); +const PageComponent = /*#__PURE__*/_export_sfc(_sfc_main, [['__scopeId',"data-v-40c8d8f5"]]); export { PageComponent as default }; diff --git a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-eylW2iyA.css b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DMGoo00L.css similarity index 68% rename from plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-eylW2iyA.css rename to plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DMGoo00L.css index a1b3179..19567d2 100644 --- a/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-eylW2iyA.css +++ b/plugins.v2/clashruleprovider/dist/assets/__federation_expose_Page-DMGoo00L.css @@ -1,13 +1,13 @@ -.plugin-page[data-v-a60e548e] { +.plugin-page[data-v-40c8d8f5] { margin: 0 auto; } /* 使卡片等宽并适应移动端 */ -.d-flex.flex-wrap[data-v-a60e548e] { +.d-flex.flex-wrap[data-v-40c8d8f5] { gap: 16px; } -.url-display[data-v-a60e548e] { +.url-display[data-v-40c8d8f5] { word-break: break-all; padding: 8px; background: rgba(0, 0, 0, 0.05); @@ -16,19 +16,19 @@ /* 移动端堆叠布局 */ @media (max-width: 768px) { -.d-flex.flex-wrap[data-v-a60e548e] { +.d-flex.flex-wrap[data-v-40c8d8f5] { flex-direction: column; } } /* Add visual distinction between sections */ -.ruleset-section[data-v-a60e548e] { +.ruleset-section[data-v-40c8d8f5] { border: 1px solid #e0e0e0; border-radius: 4px; padding: 16px; background-color: #f5f5f5; } -.top-section[data-v-a60e548e] { +.top-section[data-v-40c8d8f5] { border: 1px solid #e0e0e0; border-radius: 4px; padding: 16px; @@ -36,15 +36,15 @@ } /* Optional: Add different border colors to further distinguish */ -.ruleset-section[data-v-a60e548e] { +.ruleset-section[data-v-40c8d8f5] { border-left: 4px solid #2196F3; /* Blue accent */ } -.top-section[data-v-a60e548e] { +.top-section[data-v-40c8d8f5] { border-left: 4px solid #4CAF50; /* Green accent */ } -.drag-handle[data-v-a60e548e] { +.drag-handle[data-v-40c8d8f5] { cursor: move; } -.gap-2[data-v-a60e548e] { +.gap-2[data-v-40c8d8f5] { gap: 8px; } diff --git a/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js b/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js index f55aa17..4672a18 100644 --- a/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js +++ b/plugins.v2/clashruleprovider/dist/assets/remoteEntry.js @@ -2,14 +2,14 @@ const currentImports = {}; const exportSet = new Set(['Module', '__esModule', 'default', '_export_sfc']); let moduleMap = { "./Page":()=>{ - dynamicLoadingCss(["__federation_expose_Page-eylW2iyA.css"], false, './Page'); - return __federation_import('./__federation_expose_Page-Bt1EwqOk.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, + dynamicLoadingCss(["__federation_expose_Page-DMGoo00L.css"], false, './Page'); + return __federation_import('./__federation_expose_Page-BNbsW3Id.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, "./Config":()=>{ - dynamicLoadingCss(["__federation_expose_Config-BAzyOCdJ.css"], false, './Config'); - return __federation_import('./__federation_expose_Config-BWBZ5vgt.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, + dynamicLoadingCss(["__federation_expose_Config-s4NWY2Hy.css"], false, './Config'); + return __federation_import('./__federation_expose_Config-HFXrSiMi.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)}, "./Dashboard":()=>{ - dynamicLoadingCss(["__federation_expose_Dashboard-D6WU_Ejn.css"], false, './Dashboard'); - return __federation_import('./__federation_expose_Dashboard-BFVr4jq_.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},}; + dynamicLoadingCss(["__federation_expose_Dashboard-vS9Qm2ZB.css"], false, './Dashboard'); + return __federation_import('./__federation_expose_Dashboard-BDSt5WaH.js').then(module =>Object.keys(module).every(item => exportSet.has(item)) ? () => module.default : () => module)},}; const seen = {}; const dynamicLoadingCss = (cssFilePaths, dontAppendStylesToHead, exposeItemName) => { const metaUrl = import.meta.url; diff --git a/plugins.v2/imdbsource/__init__.py b/plugins.v2/imdbsource/__init__.py index 8493119..297b4cd 100644 --- a/plugins.v2/imdbsource/__init__.py +++ b/plugins.v2/imdbsource/__init__.py @@ -2,6 +2,8 @@ from typing import Optional, Any, List, Dict, Tuple from datetime import datetime import re +from apscheduler.schedulers.background import BackgroundScheduler + from app.core.config import settings from app.core.event import eventmanager, Event from app.plugins import _PluginBase @@ -20,7 +22,7 @@ class ImdbSource(_PluginBase): # 插件图标 plugin_icon = "IMDb_IOS-OSX_App.png" # 插件版本 - plugin_version = "1.4.2" + plugin_version = "1.4.3" # 插件作者 plugin_author = "wumode" # 作者主页 @@ -41,7 +43,9 @@ class ImdbSource(_PluginBase): # 私有属性 _imdb_helper = None _cache = {"discover": [], "trending": [], "trending_in_anime": [], "trending_in_sitcom": [], - "trending_in_documentary": [], "imdb_top_250": []} + "trending_in_documentary": [], "imdb_top_250": [], "staff_picks": {}} + _img_proxy_prefix = '' + _scheduler: Optional[BackgroundScheduler] = None def init_plugin(self, config: dict = None): if config: @@ -55,6 +59,10 @@ class ImdbSource(_PluginBase): settings.SECURITY_IMAGE_DOMAINS.append("media-amazon.com") if "media-imdb.com" not in settings.SECURITY_IMAGE_DOMAINS: settings.SECURITY_IMAGE_DOMAINS.append("media-imdb.com") + if self._enabled: + self._scheduler = BackgroundScheduler(timezone=settings.TZ) + self._scheduler.start() + self._scheduler.add_job(self.__cache_staff_picks, trigger='date', run_date=None) def get_state(self) -> bool: return self._enabled @@ -85,15 +93,15 @@ class ImdbSource(_PluginBase): if not self._staff_picks: return None - def year_and_type(entry: Dict) -> Tuple[MediaType, str, str]: - title = next((t for t in titles if t.get("id") == entry.get('ttconst')), None) + def year_and_type(imdb_entry: Dict) -> Tuple[MediaType, str, str]: + title = next((t for t in titles if t.get("id") == imdb_entry.get('ttconst')), None) if not title: return MediaType.MOVIE, datetime.now().date().strftime("%Y"), '' media_id = title.get('titleType', {}).get('id') release_year = title.get('releaseYear', {}).get('year') or datetime.now().date().strftime("%Y") media_type = ImdbSource.title_id_to_mtype(media_id) - plot = title.get("plot", {}).get("plotText", {}).get("plainText", '') - return media_type, release_year, plot + media_plot = title.get("plot", {}).get("plotText", {}).get("plainText", '') + return media_type, release_year, media_plot # 列配置 size_config = { @@ -112,16 +120,15 @@ class ImdbSource(_PluginBase): "border": False } # 获取流行越势数据 - entries = self._imdb_helper.staff_picks() - items = None - if entries: - items = self._imdb_helper.vertical_list_page_items( - titles=[entry.get('ttconst', '') for entry in entries], - names=[item for entry in entries for item in entry.get("relatedconst", [])], - images=[entry.get('rmconst', '') for entry in entries], - ) - - if not entries or not items: + entries = self._cache['staff_picks'].get('entries') + imdb_items = self._cache['staff_picks'].get('imdb_items') + if not entries or not imdb_items: + self.__cache_staff_picks() + entries = self._cache['staff_picks'].get('entries') + imdb_items = self._cache['staff_picks'].get('imdb_items') + else: + self._scheduler.add_job(self.__cache_staff_picks, trigger='date', run_date=None) + if not entries or not imdb_items: elements = [ { 'component': 'VCard', @@ -145,19 +152,21 @@ class ImdbSource(_PluginBase): } ] return cols, attrs, elements - images = items.get('images') or [] - names = items.get('names') or [] - titles = items.get('titles') or [] + images = imdb_items.get('images') or [] + names = imdb_items.get('names') or [] + titles = imdb_items.get('titles') or [] contents = [] for entry in entries: cast = [name for related in entry.get('relatedconst', []) for name in names if name.get('id') == related] mtype, year, plot = year_and_type(entry) mp_url = f"/media?mediaid=imdb:{entry.get('ttconst')}&title='{entry.get('name')}'&year={year}&type={mtype.value}" + primary_img_url = next((f"{image.get('url')}" for image in images + if image.get("id") == entry.get('rmconst')), '') + primary_img_url = f'{self._img_proxy_prefix}{primary_img_url}' item1 = { 'component': 'VCarouselItem', 'props': { - 'src': next((f"{image.get('url')}" for image in images - if image.get("id") == entry.get('rmconst')), None), + 'src': primary_img_url, 'cover': True, 'position': 'center', }, @@ -225,8 +234,8 @@ class ImdbSource(_PluginBase): { 'component': 'VImg', 'props': { - 'src': cs.get('primaryImage', {}).get('url', - ''), + 'src': f"{self._img_proxy_prefix}" + f"{cs.get('primaryImage', {}).get('url', '')}", 'alt': cs.get('nameText', {}).get('text', 'Avatar'), 'cover': True } @@ -251,6 +260,7 @@ class ImdbSource(_PluginBase): } poster_url = next((f"{title.get('primaryImage', {}).get('url')}" for title in titles if title.get("id") == entry.get('ttconst')), None) + poster_url = f"{self._img_proxy_prefix}{poster_url}" poster_com = { 'component': 'VImg', 'props': { @@ -332,8 +342,7 @@ class ImdbSource(_PluginBase): item2 = { 'component': 'VCarouselItem', 'props': { - 'src': next((f"{image.get('url')}" for image in images - if image.get("id") == entry.get('rmconst')), None), + 'src': primary_img_url, 'cover': True, 'position': 'center' }, @@ -529,8 +538,20 @@ class ImdbSource(_PluginBase): """ pass - @staticmethod - def __movie_to_media(movie_info: dict) -> schemas.MediaInfo: + def __cache_staff_picks(self): + entries = self._imdb_helper.staff_picks() + imdb_items = None + if entries: + imdb_items = self._imdb_helper.vertical_list_page_items( + titles=[entry.get('ttconst', '') for entry in entries], + names=[item for entry in entries for item in entry.get("relatedconst", [])], + images=[entry.get('rmconst', '') for entry in entries], + ) + if not entries or not imdb_items: + return + self._cache['staff_picks'] = {'entries': entries, 'imdb_items': imdb_items} + + def __movie_to_media(self, movie_info: dict) -> schemas.MediaInfo: title = "" if movie_info.get("titleText"): title = movie_info.get("titleText", {}).get("text", "") @@ -542,6 +563,7 @@ class ImdbSource(_PluginBase): primary_image = movie_info.get("primaryImage").get("url") if primary_image: poster_path = primary_image.replace('@._V1', '@._V1_QL75_UY414_CR6,0,280,414_') + poster_path = f"{self._img_proxy_prefix}{poster_path}" vote_average = 0 if movie_info.get("ratingsSummary"): vote_average = movie_info.get("ratingsSummary").get("aggregateRating") @@ -565,8 +587,7 @@ class ImdbSource(_PluginBase): imdb_id=movie_info.get("id") ) - @staticmethod - def __series_to_media(series_info: dict) -> schemas.MediaInfo: + def __series_to_media(self, series_info: dict) -> schemas.MediaInfo: title = "" if series_info.get("titleText"): title = series_info.get("titleText", {}).get("text", "") @@ -578,6 +599,7 @@ class ImdbSource(_PluginBase): primary_image = series_info.get("primaryImage").get("url") if primary_image: poster_path = primary_image.replace('@._V1', '@._V1_QL75_UY414_CR6,0,280,414_') + poster_path = f"{self._img_proxy_prefix}{poster_path}" vote_average = 0 if series_info.get("ratingsSummary"): vote_average = series_info.get("ratingsSummary").get("aggregateRating") diff --git a/plugins.v2/imdbsource/imdbhelper.py b/plugins.v2/imdbsource/imdbhelper.py index 8ae1868..40903ec 100644 --- a/plugins.v2/imdbsource/imdbhelper.py +++ b/plugins.v2/imdbsource/imdbhelper.py @@ -411,4 +411,4 @@ class ImdbHelper: if error: logger.error(f"Error querying VerticalListPageItems: {error}") return None - return data \ No newline at end of file + return data diff --git a/plugins.v2/tobypasstrackers/__init__.py b/plugins.v2/tobypasstrackers/__init__.py index c89781a..e538edf 100644 --- a/plugins.v2/tobypasstrackers/__init__.py +++ b/plugins.v2/tobypasstrackers/__init__.py @@ -25,7 +25,7 @@ class ToBypassTrackers(_PluginBase): # 插件名称 plugin_name = "绕过Trackers" # 插件描述 - plugin_desc = "提供tracker服务器IP地址列表,帮助IPv6连接绕过OpenClash" + plugin_desc = "提供tracker服务器IP地址列表,帮助IPv6连接绕过OpenClash。" # 插件图标 plugin_icon = "Clash_A.png" # 插件版本