The script supports multiple URL matching

This commit is contained in:
wanghongenpin
2025-09-23 22:43:28 +08:00
parent 9869c00afc
commit f37330a517
9 changed files with 261 additions and 115 deletions

View File

@@ -335,7 +335,7 @@ class ScriptEdit extends StatefulWidget {
class _ScriptEditState extends State<ScriptEdit> {
late CodeController script;
late TextEditingController nameController;
late TextEditingController urlController;
late List<TextEditingController> urlControllers;
AppLocalizations get localizations => AppLocalizations.of(context)!;
@@ -344,14 +344,18 @@ class _ScriptEditState extends State<ScriptEdit> {
super.initState();
script = CodeController(language: javascript, text: widget.script ?? ScriptManager.template);
nameController = TextEditingController(text: widget.scriptItem?.name ?? widget.title);
urlController = TextEditingController(text: widget.scriptItem?.url ?? widget.url);
final urls = widget.scriptItem?.urls ?? (widget.url != null && widget.url!.isNotEmpty ? [widget.url!] : []);
urlControllers =
urls.isNotEmpty ? urls.map((u) => TextEditingController(text: u)).toList() : [TextEditingController()];
}
@override
void dispose() {
script.dispose();
nameController.dispose();
urlController.dispose();
for (final c in urlControllers) {
c.dispose();
}
super.dispose();
}
@@ -361,72 +365,130 @@ class _ScriptEditState extends State<ScriptEdit> {
bool isCN = Localizations.localeOf(context) == const Locale.fromSubtags(languageCode: 'zh');
return AlertDialog(
scrollable: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
titlePadding: const EdgeInsets.only(left: 15, top: 5, right: 15),
title: Row(children: [
Text(localizations.scriptEdit, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
const SizedBox(width: 10),
Text.rich(TextSpan(
text: localizations.useGuide,
style: const TextStyle(color: Colors.blue, fontSize: 14),
recognizer: TapGestureRecognizer()
..onTap = () => DesktopMultiWindow.invokeMethod(
0,
"launchUrl",
isCN
? 'https://gitee.com/wanghongenpin/proxypin/wikis/%E8%84%9A%E6%9C%AC'
: 'https://github.com/wanghongenpin/proxypin/wiki/Script'))),
const Expanded(child: Align(alignment: Alignment.topRight, child: CloseButton()))
]),
actionsPadding: const EdgeInsets.only(right: 10, bottom: 10),
actions: [
ElevatedButton(onPressed: () => Navigator.of(context).pop(), child: Text(localizations.cancel)),
FilledButton(
onPressed: () async {
if (!(formKey.currentState as FormState).validate()) {
FlutterToastr.show("${localizations.name} URL ${localizations.cannotBeEmpty}", context,
position: FlutterToastr.top);
return;
}
//新增
if (widget.scriptItem == null) {
var scriptItem = ScriptItem(true, nameController.text, urlController.text);
await (await ScriptManager.instance).addScript(scriptItem, script.text);
} else {
widget.scriptItem?.name = nameController.text;
widget.scriptItem?.url = urlController.text;
widget.scriptItem?.urlReg = null;
(await ScriptManager.instance).updateScript(widget.scriptItem!, script.text);
}
_refreshScript();
if (context.mounted) {
Navigator.of(context).maybePop(true);
}
},
child: Text(localizations.save)),
],
content: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
textField("${localizations.name}:", nameController, localizations.pleaseEnter),
const SizedBox(height: 10),
textField("URL:", urlController, "github.com/api/*", keyboardType: TextInputType.url),
const SizedBox(height: 10),
Text("${localizations.script}:"),
const SizedBox(height: 5),
SizedBox(
width: 850,
height: 380,
child: CodeTheme(
data: CodeThemeData(styles: monokaiSublimeTheme),
child: SingleChildScrollView(
child: CodeField(textStyle: const TextStyle(fontSize: 13), controller: script))))
],
)));
scrollable: true,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
titlePadding: const EdgeInsets.only(left: 15, top: 5, right: 15),
title: Row(children: [
Text(localizations.scriptEdit, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
const SizedBox(width: 10),
Text.rich(TextSpan(
text: localizations.useGuide,
style: const TextStyle(color: Colors.blue, fontSize: 14),
recognizer: TapGestureRecognizer()
..onTap = () => DesktopMultiWindow.invokeMethod(
0,
"launchUrl",
isCN
? 'https://gitee.com/wanghongenpin/proxypin/wikis/%E8%84%9A%E6%9C%AC'
: 'https://github.com/wanghongenpin/proxypin/wiki/Script'))),
const Expanded(child: Align(alignment: Alignment.topRight, child: CloseButton()))
]),
actionsPadding: const EdgeInsets.only(right: 10, bottom: 10),
actions: [
ElevatedButton(onPressed: () => Navigator.of(context).pop(), child: Text(localizations.cancel)),
FilledButton(
onPressed: () async {
if (!(formKey.currentState as FormState).validate()) {
FlutterToastr.show("${localizations.name} URL ${localizations.cannotBeEmpty}", context,
position: FlutterToastr.top);
return;
}
final urls = urlControllers.map((c) => c.text.trim()).where((u) => u.isNotEmpty).toSet().toList();
if (urls.isEmpty) {
FlutterToastr.show("URL ${localizations.cannotBeEmpty}", context, position: FlutterToastr.top);
return;
}
if (widget.scriptItem == null) {
var scriptItem = ScriptItem(true, nameController.text, urls);
await (await ScriptManager.instance).addScript(scriptItem, script.text);
} else {
widget.scriptItem?.name = nameController.text;
widget.scriptItem?.urls = urls;
widget.scriptItem?.urlRegs = null;
(await ScriptManager.instance).updateScript(widget.scriptItem!, script.text);
}
_refreshScript();
if (context.mounted) {
Navigator.of(context).maybePop(true);
}
},
child: Text(localizations.save)),
],
content: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
textField("${localizations.name}:", nameController, localizations.pleaseEnter),
const SizedBox(height: 3),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Text("URL(s):"),
const SizedBox(width: 8),
IconButton(
icon: const Icon(Icons.add_circle_outline, size: 20),
tooltip: localizations.add,
onPressed: () {
setState(() {
urlControllers.add(TextEditingController());
});
},
),
],
),
...List.generate(
urlControllers.length,
(i) => Row(
children: [
Expanded(
child: TextFormField(
controller: urlControllers[i],
validator: (val) => val?.isNotEmpty == true ? null : "",
keyboardType: TextInputType.url,
decoration: InputDecoration(
hintText: "github.com/api/*",
hintStyle: const TextStyle(fontSize: 14, color: Colors.grey),
contentPadding: const EdgeInsets.all(10),
errorStyle: const TextStyle(height: 0, fontSize: 0),
focusedBorder: focusedBorder(),
isDense: true,
border: const OutlineInputBorder(),
),
),
),
if (urlControllers.length > 1)
IconButton(
icon: const Icon(Icons.remove_circle_outline, color: Colors.red),
tooltip: localizations.delete,
onPressed: () {
setState(() {
urlControllers[i].dispose();
urlControllers.removeAt(i);
});
},
),
],
)),
],
),
const SizedBox(height: 10),
Text(
"${localizations.script}:",
),
const SizedBox(height: 5),
SizedBox(
width: 850,
height: 380,
child: CodeTheme(
data: CodeThemeData(styles: monokaiSublimeTheme),
child: SingleChildScrollView(
child: CodeField(textStyle: const TextStyle(fontSize: 13), controller: script))))
],
)),
);
}
Widget textField(String label, TextEditingController controller, String hint, {TextInputType? keyboardType}) {
@@ -439,6 +501,7 @@ class _ScriptEditState extends State<ScriptEdit> {
keyboardType: keyboardType,
decoration: InputDecoration(
hintText: hint,
hintStyle: const TextStyle(fontSize: 14, color: Colors.grey),
contentPadding: const EdgeInsets.all(10),
errorStyle: const TextStyle(height: 0, fontSize: 0),
focusedBorder: focusedBorder(),
@@ -573,13 +636,13 @@ class _ScriptListState extends State<ScriptList> {
_refreshScript();
}))),
const SizedBox(width: 20),
Expanded(child: Text(list[index].url, style: const TextStyle(fontSize: 13))),
Expanded(child: Text(list[index].urls.join(', '), style: const TextStyle(fontSize: 13))),
],
)));
});
}
showGlobalMenu(Offset offset) {
void showGlobalMenu(Offset offset) {
showContextMenu(context, offset, items: [
PopupMenuItem(height: 35, child: Text(localizations.newBuilt), onTap: () => showEdit()),
PopupMenuItem(height: 35, child: Text(localizations.export), onTap: () => export(selected.toList())),