mirror of
https://github.com/wanghongenpin/proxypin.git
synced 2026-06-03 17:25:48 +08:00
请求重写导出、导入
This commit is contained in:
@@ -1,7 +1,17 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:file_selector/file_selector.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/components/request_rewrite_manager.dart';
|
||||
import 'package:network_proxy/network/util/logger.dart';
|
||||
import 'package:network_proxy/ui/component/utils.dart';
|
||||
import 'package:network_proxy/ui/component/widgets.dart';
|
||||
import 'package:network_proxy/ui/mobile/setting/rewrite/rewrite_update.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'rewrite/rewrite_replace.dart';
|
||||
|
||||
@@ -40,14 +50,19 @@ class _MobileRequestRewriteState extends State<MobileRequestRewrite> {
|
||||
body: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Wrap(alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||
const Text("是否启用请求重写"),
|
||||
SwitchWidget(value: enabled, scale: 0.8, onChanged: (val) => enabled = val),
|
||||
const Expanded(child: SizedBox()),
|
||||
FilledButton.icon(icon: const Icon(Icons.add, size: 18), onPressed: add, label: const Text("添加")),
|
||||
const SizedBox(width: 10),
|
||||
FilledButton.icon(
|
||||
icon: const Icon(Icons.input_rounded, size: 18),
|
||||
style: ElevatedButton.styleFrom(padding: const EdgeInsets.only(left: 20, right: 20)),
|
||||
onPressed: import,
|
||||
label: const Text("导入"),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(child: RequestRuleList(widget.requestRewrites)),
|
||||
@@ -55,6 +70,35 @@ class _MobileRequestRewriteState extends State<MobileRequestRewrite> {
|
||||
)));
|
||||
}
|
||||
|
||||
//导入
|
||||
import() async {
|
||||
final XFile? file = await openFile();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
List json = jsonDecode(utf8.decode(await file.readAsBytes()));
|
||||
|
||||
for (var item in json) {
|
||||
var rule = RequestRewriteRule.formJson(item);
|
||||
var items = (item['items'] as List).map((e) => RewriteItem.fromJson(e)).toList();
|
||||
widget.requestRewrites.addRule(rule, items);
|
||||
}
|
||||
widget.requestRewrites.flushRequestRewriteConfig();
|
||||
|
||||
if (context.mounted) {
|
||||
FlutterToastr.show("导入成功", context);
|
||||
}
|
||||
setState(() {});
|
||||
} catch (e, t) {
|
||||
logger.e('导入失败 $file', error: e, stackTrace: t);
|
||||
if (context.mounted) {
|
||||
FlutterToastr.show("导入失败 $e", context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add([int currentIndex = -1]) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (_) => const RewriteRule())).then((rule) {
|
||||
if (rule != null) {
|
||||
@@ -75,10 +119,12 @@ class RequestRuleList extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _RequestRuleListState extends State<RequestRuleList> {
|
||||
int selected = -1;
|
||||
Set<int> selected = HashSet<int>();
|
||||
late List<RequestRewriteRule> rules;
|
||||
bool changed = false;
|
||||
|
||||
bool multiple = false;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
@@ -96,29 +142,78 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 30),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||
color: Colors.white,
|
||||
backgroundBlendMode: BlendMode.colorBurn),
|
||||
child: Scrollbar(
|
||||
child: ListView(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
return Scaffold(
|
||||
persistentFooterButtons: [multiple ? globalMenu() : const SizedBox()],
|
||||
body: Container(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 30),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||
color: Colors.white,
|
||||
backgroundBlendMode: BlendMode.colorBurn),
|
||||
child: Scrollbar(
|
||||
child: ListView(
|
||||
children: [
|
||||
Container(width: 80, padding: const EdgeInsets.only(left: 10), child: const Text("名称")),
|
||||
const SizedBox(width: 30, child: Text("启用", textAlign: TextAlign.center)),
|
||||
const VerticalDivider(),
|
||||
const Expanded(child: Text("URL")),
|
||||
const SizedBox(width: 60, child: Text("行为", textAlign: TextAlign.center)),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(width: 80, padding: const EdgeInsets.only(left: 10), child: const Text("名称")),
|
||||
const SizedBox(width: 30, child: Text("启用", textAlign: TextAlign.center)),
|
||||
const VerticalDivider(),
|
||||
const Expanded(child: Text("URL")),
|
||||
const SizedBox(width: 60, child: Text("行为", textAlign: TextAlign.center)),
|
||||
],
|
||||
),
|
||||
const Divider(thickness: 0.5),
|
||||
Column(children: rows(widget.requestRewrites.rules))
|
||||
],
|
||||
),
|
||||
const Divider(thickness: 0.5),
|
||||
Column(children: rows(widget.requestRewrites.rules))
|
||||
],
|
||||
)));
|
||||
))));
|
||||
}
|
||||
|
||||
globalMenu() {
|
||||
return Stack(children: [
|
||||
Container(
|
||||
height: 50,
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||
color: Colors.white,
|
||||
backgroundBlendMode: BlendMode.colorBurn)),
|
||||
Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Center(
|
||||
child: TextButton(
|
||||
onPressed: () {},
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
export(selected.toList());
|
||||
setState(() {
|
||||
selected.clear();
|
||||
multiple = false;
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.share, size: 18),
|
||||
label: const Text("导出")),
|
||||
const SizedBox(width: 15),
|
||||
TextButton.icon(
|
||||
onPressed: () => removeRewrite(),
|
||||
icon: const Icon(Icons.delete, size: 18),
|
||||
label: const Text("删除")),
|
||||
const SizedBox(width: 15),
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
multiple = false;
|
||||
selected.clear();
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.cancel, size: 18),
|
||||
label: const Text("取消")),
|
||||
]))))
|
||||
]);
|
||||
}
|
||||
|
||||
List<Widget> rows(List<RequestRewriteRule> list) {
|
||||
@@ -131,6 +226,14 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
hoverColor: primaryColor.withOpacity(0.3),
|
||||
onLongPress: () => showMenus(index),
|
||||
onTap: () async {
|
||||
if (multiple) {
|
||||
setState(() {
|
||||
if (!selected.add(index)) {
|
||||
selected.remove(index);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
var rule = widget.requestRewrites.rules[index];
|
||||
var rewriteItems = await widget.requestRewrites.getRewriteItems(rule);
|
||||
if (!mounted) return;
|
||||
@@ -143,7 +246,7 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
color: selected == index
|
||||
color: selected.contains(index)
|
||||
? primaryColor.withOpacity(0.8)
|
||||
: index.isEven
|
||||
? Colors.grey.withOpacity(0.1)
|
||||
@@ -179,14 +282,23 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
//点击菜单
|
||||
showMenus(int index) {
|
||||
setState(() {
|
||||
selected = index;
|
||||
selected.add(index);
|
||||
});
|
||||
|
||||
showModalBottomSheet(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(10))),
|
||||
context: context,
|
||||
enableDrag: true,
|
||||
builder: (ctx) {
|
||||
return Wrap(alignment: WrapAlignment.center, children: [
|
||||
BottomSheetItem(
|
||||
text: '多选',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
multiple = true;
|
||||
});
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
BottomSheetItem(
|
||||
text: "编辑",
|
||||
onPressed: () async {
|
||||
@@ -202,6 +314,8 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
}
|
||||
});
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
BottomSheetItem(text: "分享", onPressed: () => export([index])),
|
||||
const Divider(thickness: 0.5, height: 1),
|
||||
BottomSheetItem(
|
||||
text: rules[index].enabled ? "禁用" : "启用",
|
||||
@@ -209,32 +323,71 @@ class _RequestRuleListState extends State<RequestRuleList> {
|
||||
rules[index].enabled = !rules[index].enabled;
|
||||
changed = true;
|
||||
}),
|
||||
const Divider(thickness: 0.5, height: 1),
|
||||
const Divider(thickness: 0.5),
|
||||
BottomSheetItem(
|
||||
text: "删除",
|
||||
onPressed: () async {
|
||||
widget.requestRewrites.removeIndex([index]);
|
||||
await widget.requestRewrites.removeIndex([index]);
|
||||
widget.requestRewrites.flushRequestRewriteConfig();
|
||||
if (mounted) FlutterToastr.show('删除成功', context);
|
||||
}),
|
||||
Container(color: Theme.of(context).hoverColor, height: 8),
|
||||
TextButton(
|
||||
child: Container(
|
||||
height: 42,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: const Text("取消", textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
child: Container(
|
||||
height: 48,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: const Text("取消", textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
]);
|
||||
}).then((value) => setState(() {
|
||||
selected = -1;
|
||||
}));
|
||||
}).then((value) {
|
||||
if (multiple) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
selected.remove(index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
DataCell cell(Widget child) {
|
||||
return DataCell(child);
|
||||
//导出js
|
||||
Future<void> export(List<int> indexes) async {
|
||||
if (indexes.isEmpty) return;
|
||||
|
||||
String fileName = 'proxypin-rewrites.config';
|
||||
|
||||
var list = [];
|
||||
for (var index in indexes) {
|
||||
var rule = widget.requestRewrites.rules[index];
|
||||
var json = rule.toJson();
|
||||
json.remove("rewritePath");
|
||||
json['items'] = await widget.requestRewrites.getRewriteItems(rule);
|
||||
list.add(json);
|
||||
}
|
||||
|
||||
final XFile file = XFile.fromData(utf8.encode(jsonEncode(list)), mimeType: 'config');
|
||||
await Share.shareXFiles([file], subject: fileName);
|
||||
if (context.mounted) FlutterToastr.show('导出成功', context);
|
||||
}
|
||||
|
||||
//删除
|
||||
Future<void> removeRewrite() async {
|
||||
if (selected.isEmpty) return;
|
||||
return showConfirmDialog(context, content: '是否删除${selected.length}条规则?', onConfirm: () async {
|
||||
var list = selected.toList();
|
||||
list.sort((a, b) => b.compareTo(a));
|
||||
for (var value in list) {
|
||||
await widget.requestRewrites.removeIndex([value]);
|
||||
}
|
||||
widget.requestRewrites.flushRequestRewriteConfig();
|
||||
setState(() {
|
||||
multiple = false;
|
||||
selected.clear();
|
||||
});
|
||||
if (mounted) FlutterToastr.show('删除成功', context);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,7 +439,16 @@ class _RewriteRuleState extends State<RewriteRule> {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: const Text("请求重写规则", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
|
||||
title: Row(children: [
|
||||
const Text("请求重写规则", style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500)),
|
||||
const SizedBox(width: 20),
|
||||
Text.rich(TextSpan(
|
||||
text: '使用文档',
|
||||
style: const TextStyle(color: Colors.blue, fontSize: 14),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => launchUrl(Uri.parse(
|
||||
'https://gitee.com/wanghongenpin/network-proxy-flutter/wikis/%E8%AF%B7%E6%B1%82%E9%87%8D%E5%86%99')))),
|
||||
]),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text("保存"),
|
||||
@@ -350,27 +512,40 @@ class _RewriteRuleState extends State<RewriteRule> {
|
||||
contentPadding: EdgeInsets.only(left: 7, right: 7),
|
||||
),
|
||||
items: RuleType.values.map((e) => DropdownMenuItem(value: e, child: Text(e.label))).toList(),
|
||||
onChanged: (val) => ruleType = val!,
|
||||
onChanged: (val) {
|
||||
ruleType = val!;
|
||||
items = ruleType == widget.rule?.type ? widget.items : [];
|
||||
},
|
||||
)),
|
||||
const SizedBox(width: 10),
|
||||
TextButton(
|
||||
onPressed: () => showEdit(rule), child: const Text("点击编辑", style: TextStyle(fontSize: 16))),
|
||||
]),
|
||||
const SizedBox(height: 10),
|
||||
if (items?.isNotEmpty == true && ruleType != RuleType.redirect)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 50),
|
||||
child: Text("替换: ${items?.where((it) => it.enabled).map((e) => e.type.label).join(" ")}",
|
||||
style: const TextStyle(color: Colors.grey))),
|
||||
Padding(padding: const EdgeInsets.only(left: 60), child: getDescribe()),
|
||||
]))),
|
||||
);
|
||||
}
|
||||
|
||||
Widget getDescribe() {
|
||||
if (items?.isNotEmpty == true && (ruleType == RuleType.requestReplace || ruleType == RuleType.responseReplace)) {
|
||||
return Text("替换: ${items?.where((it) => it.enabled).map((e) => e.type.label).join(" ")}",
|
||||
style: const TextStyle(color: Colors.grey));
|
||||
}
|
||||
|
||||
if (ruleType == RuleType.requestUpdate || ruleType == RuleType.responseUpdate) {
|
||||
return Text("${items?.length}条修改", style: const TextStyle(color: Colors.grey));
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
void showEdit(RequestRewriteRule rule) async {
|
||||
if (!mounted) return;
|
||||
Navigator.of(context)
|
||||
.push(MaterialPageRoute(
|
||||
builder: (context) => RewriteReplaceWidget(subtitle: urlInput.text, items: items, ruleType: ruleType)))
|
||||
builder: (context) => ruleType == RuleType.requestUpdate || ruleType == RuleType.responseUpdate
|
||||
? RewriteUpdateWidget(subtitle: urlInput.text, items: items, ruleType: ruleType)
|
||||
: RewriteReplaceWidget(subtitle: urlInput.text, items: items, ruleType: ruleType)))
|
||||
.then((value) {
|
||||
if (value is List<RewriteItem>) {
|
||||
setState(() {
|
||||
|
||||
347
lib/ui/mobile/setting/rewrite/rewrite_update.dart
Normal file
347
lib/ui/mobile/setting/rewrite/rewrite_update.dart
Normal file
@@ -0,0 +1,347 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_toastr/flutter_toastr.dart';
|
||||
import 'package:network_proxy/network/components/request_rewrite_manager.dart';
|
||||
import 'package:network_proxy/ui/component/widgets.dart';
|
||||
|
||||
class RewriteUpdateWidget extends StatefulWidget {
|
||||
final String subtitle;
|
||||
final RuleType ruleType;
|
||||
final List<RewriteItem>? items;
|
||||
|
||||
const RewriteUpdateWidget({super.key, required this.subtitle, required this.ruleType, this.items});
|
||||
|
||||
@override
|
||||
State<RewriteUpdateWidget> createState() => _RewriteUpdateState();
|
||||
}
|
||||
|
||||
class _RewriteUpdateState extends State<RewriteUpdateWidget> {
|
||||
List<RewriteItem> items = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.items?.isNotEmpty == true) {
|
||||
items.addAll(widget.items!);
|
||||
return;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
add();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(widget.ruleType.label, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w500)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(items);
|
||||
},
|
||||
child: const Text("完成")),
|
||||
const SizedBox(width: 10)
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ListView(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(widget.subtitle, style: const TextStyle(fontSize: 13, color: Colors.grey)),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [IconButton(onPressed: add, icon: const Icon(Icons.add)), const SizedBox(width: 10)],
|
||||
))
|
||||
],
|
||||
),
|
||||
UpdateList(items: items, ruleType: widget.ruleType),
|
||||
],
|
||||
)));
|
||||
}
|
||||
|
||||
add() {
|
||||
showDialog(context: context, builder: (context) => RewriteUpdateAddDialog(ruleType: widget.ruleType)).then((value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
items.add(value);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class RewriteUpdateAddDialog extends StatefulWidget {
|
||||
final RewriteItem? item;
|
||||
final RuleType ruleType;
|
||||
|
||||
const RewriteUpdateAddDialog({super.key, this.item, required this.ruleType});
|
||||
|
||||
@override
|
||||
State<RewriteUpdateAddDialog> createState() => _RewriteUpdateAddState();
|
||||
}
|
||||
|
||||
class _RewriteUpdateAddState extends State<RewriteUpdateAddDialog> {
|
||||
late RewriteType rewriteType;
|
||||
GlobalKey formKey = GlobalKey<FormState>();
|
||||
late RewriteItem rewriteItem;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
rewriteType = widget.item?.type ?? RewriteType.updateBody;
|
||||
rewriteItem = widget.item ?? RewriteItem(rewriteType, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isDelete = rewriteType == RewriteType.removeQueryParam || rewriteType == RewriteType.removeHeader;
|
||||
bool isUpdate =
|
||||
[RewriteType.updateBody, RewriteType.updateHeader, RewriteType.updateQueryParam].contains(rewriteType);
|
||||
|
||||
String keyTips = "";
|
||||
String valueTips = "";
|
||||
if (isDelete) {
|
||||
keyTips = "匹配规则";
|
||||
valueTips = "为空表示匹配全部";
|
||||
} else if (rewriteType == RewriteType.updateQueryParam || rewriteType == RewriteType.updateHeader) {
|
||||
keyTips = rewriteType == RewriteType.updateQueryParam ? "name=123" : "Content-Type: application/json";
|
||||
valueTips = rewriteType == RewriteType.updateQueryParam ? "name=456" : "Content-Type: application/xml";
|
||||
}
|
||||
|
||||
var typeList = widget.ruleType == RuleType.requestUpdate ? RewriteType.updateRequest : RewriteType.updateResponse;
|
||||
|
||||
return AlertDialog(
|
||||
title: const Text("添加修改",
|
||||
style: TextStyle(fontSize: 15, fontWeight: FontWeight.w500), textAlign: TextAlign.center),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.of(context).pop(), child: const Text("取消")),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (!(formKey.currentState as FormState).validate()) {
|
||||
FlutterToastr.show("缺少配置", context, position: FlutterToastr.center);
|
||||
return;
|
||||
}
|
||||
(formKey.currentState as FormState).save();
|
||||
rewriteItem.type = rewriteType;
|
||||
Navigator.of(context).pop(rewriteItem);
|
||||
},
|
||||
child: const Text("确定")),
|
||||
],
|
||||
content: SizedBox(
|
||||
width: 320,
|
||||
height: 180,
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: ListView(children: [
|
||||
Row(
|
||||
children: [
|
||||
const Text('类型'),
|
||||
const SizedBox(width: 15),
|
||||
SizedBox(
|
||||
width: 120,
|
||||
child: DropdownButtonFormField<RewriteType>(
|
||||
value: rewriteType,
|
||||
focusColor: Colors.transparent,
|
||||
itemHeight: 48,
|
||||
decoration: const InputDecoration(
|
||||
contentPadding: EdgeInsets.all(10), isDense: true, border: InputBorder.none),
|
||||
items: typeList
|
||||
.map((e) => DropdownMenuItem(
|
||||
value: e,
|
||||
child: Text(e.label,
|
||||
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w500))))
|
||||
.toList(),
|
||||
onChanged: (val) {
|
||||
setState(() {
|
||||
rewriteType = val!;
|
||||
});
|
||||
})),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
textField(isUpdate ? "匹配" : "名称", rewriteItem.key, keyTips,
|
||||
required: !isDelete, onSaved: (val) => rewriteItem.key = val),
|
||||
const SizedBox(height: 15),
|
||||
textField(isUpdate ? "替换" : "值", rewriteItem.value, valueTips,
|
||||
onSaved: (val) => rewriteItem.value = val),
|
||||
]))));
|
||||
}
|
||||
|
||||
Widget textField(String label, String? val, String hint, {bool required = false, FormFieldSetter<String>? onSaved}) {
|
||||
return Row(children: [
|
||||
SizedBox(width: 50, child: Text(label)),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
initialValue: val,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
validator: (val) => val?.isNotEmpty == true || !required ? null : "",
|
||||
onSaved: onSaved,
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500, fontSize: 14),
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
errorStyle: const TextStyle(height: 0, fontSize: 0),
|
||||
focusedBorder: focusedBorder(),
|
||||
isDense: true,
|
||||
border: const OutlineInputBorder()),
|
||||
))
|
||||
]);
|
||||
}
|
||||
|
||||
InputBorder focusedBorder() {
|
||||
return OutlineInputBorder(borderSide: BorderSide(color: Theme.of(context).primaryColor, width: 2));
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateList extends StatefulWidget {
|
||||
final List<RewriteItem> items;
|
||||
final RuleType ruleType;
|
||||
|
||||
const UpdateList({super.key, required this.items, required this.ruleType});
|
||||
|
||||
@override
|
||||
State<UpdateList> createState() => _UpdateListState();
|
||||
}
|
||||
|
||||
class _UpdateListState extends State<UpdateList> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
height: 320,
|
||||
width: 550,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||
color: Colors.white,
|
||||
backgroundBlendMode: BlendMode.colorBurn),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(width: 130, padding: const EdgeInsets.only(left: 10), child: const Text("类型")),
|
||||
const SizedBox(width: 50, child: Text("启用", textAlign: TextAlign.center)),
|
||||
const VerticalDivider(),
|
||||
const Expanded(child: Text("修改")),
|
||||
],
|
||||
),
|
||||
const Divider(thickness: 0.5),
|
||||
Column(children: rows(widget.items))
|
||||
])));
|
||||
}
|
||||
|
||||
int selected = -1;
|
||||
|
||||
List<Widget> rows(List<RewriteItem> list) {
|
||||
var primaryColor = Theme.of(context).primaryColor;
|
||||
|
||||
return List.generate(list.length, (index) {
|
||||
return InkWell(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
hoverColor: primaryColor.withOpacity(0.3),
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (context) => RewriteUpdateAddDialog(item: list[index], ruleType: widget.ruleType))
|
||||
.then((value) {
|
||||
if (value != null) setState(() {});
|
||||
}),
|
||||
onLongPress: () => showMenus(index),
|
||||
child: Container(
|
||||
color: selected == index
|
||||
? primaryColor
|
||||
: index.isEven
|
||||
? Colors.grey.withOpacity(0.1)
|
||||
: null,
|
||||
height: 38,
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 130, child: Text(list[index].type.label, style: const TextStyle(fontSize: 13))),
|
||||
SizedBox(
|
||||
width: 40,
|
||||
child: SwitchWidget(
|
||||
scale: 0.6,
|
||||
value: list[index].enabled,
|
||||
onChanged: (val) {
|
||||
list[index].enabled = val;
|
||||
})),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(child: Text(getText(list[index]), style: const TextStyle(fontSize: 13))),
|
||||
],
|
||||
)));
|
||||
});
|
||||
}
|
||||
|
||||
String getText(RewriteItem item) {
|
||||
bool isUpdate =
|
||||
[RewriteType.updateBody, RewriteType.updateHeader, RewriteType.updateQueryParam].contains(item.type);
|
||||
if (isUpdate) {
|
||||
return "${item.key} -> ${item.value}";
|
||||
}
|
||||
|
||||
return "${item.key}=${item.value}";
|
||||
}
|
||||
|
||||
showMenus(int index) {
|
||||
setState(() {
|
||||
selected = index;
|
||||
});
|
||||
|
||||
showModalBottomSheet(
|
||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(10))),
|
||||
context: context,
|
||||
enableDrag: true,
|
||||
builder: (ctx) {
|
||||
return Wrap(alignment: WrapAlignment.center, children: [
|
||||
BottomSheetItem(
|
||||
text: "编辑",
|
||||
onPressed: () async {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) =>
|
||||
RewriteUpdateAddDialog(item: widget.items[index], ruleType: widget.ruleType)).then((value) {
|
||||
if (value != null) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}),
|
||||
const Divider(thickness: 0.5),
|
||||
BottomSheetItem(
|
||||
text: widget.items[index].enabled ? "禁用" : "启用",
|
||||
onPressed: () => widget.items[index].enabled = !widget.items[index].enabled),
|
||||
const Divider(thickness: 0.5),
|
||||
BottomSheetItem(
|
||||
text: "删除",
|
||||
onPressed: () async {
|
||||
widget.items.removeAt(index);
|
||||
if (mounted) FlutterToastr.show('删除成功', context);
|
||||
}),
|
||||
Container(color: Theme.of(context).hoverColor, height: 8),
|
||||
TextButton(
|
||||
child: Container(
|
||||
height: 48,
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: const Text("取消", textAlign: TextAlign.center)),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
]);
|
||||
}).then((value) {
|
||||
setState(() {
|
||||
selected = -1;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user