mirror of
https://github.com/qinglong-app/qinglong_app.git
synced 2025-10-09 16:48:19 +08:00
新增代码文件支持行号显示
This commit is contained in:
@@ -6,3 +6,4 @@ String spTheme = "dart_mode";
|
||||
String spSecretLogined = "secret_logined";
|
||||
String spCustomColor = "customColor";
|
||||
String spLoginHistory = "loginHistory";
|
||||
String spShowLine = "spShowLine";
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import 'package:code_text_field/code_text_field.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:highlight/languages/powershell.dart';
|
||||
import 'package:qinglong_app/base/http/api.dart';
|
||||
import 'package:qinglong_app/base/http/http.dart';
|
||||
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||
import 'package:qinglong_app/base/sp_const.dart';
|
||||
import 'package:qinglong_app/base/theme.dart';
|
||||
import 'package:qinglong_app/utils/extension.dart';
|
||||
import 'package:qinglong_app/utils/sp_utils.dart';
|
||||
|
||||
class ConfigEditPage extends ConsumerStatefulWidget {
|
||||
final String content;
|
||||
@@ -21,6 +25,8 @@ class ConfigEditPage extends ConsumerStatefulWidget {
|
||||
class _ConfigEditPageState extends ConsumerState<ConfigEditPage> {
|
||||
CodeController? _codeController;
|
||||
late String result;
|
||||
late String preResult;
|
||||
List<String> operateList = [];
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -31,18 +37,23 @@ class _ConfigEditPageState extends ConsumerState<ConfigEditPage> {
|
||||
@override
|
||||
void initState() {
|
||||
result = widget.content;
|
||||
|
||||
preResult = widget.content;
|
||||
super.initState();
|
||||
generateOperateList();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
focusNode.requestFocus();
|
||||
checkClipBoard();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> notifyICloud(BuildContext context, String? title, String? content) async {
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_codeController ??= CodeController(
|
||||
text: widget.content,
|
||||
text: result,
|
||||
language: powershell,
|
||||
onChange: (value) {
|
||||
result = value;
|
||||
@@ -56,13 +67,78 @@ class _ConfigEditPageState extends ConsumerState<ConfigEditPage> {
|
||||
appBar: QlAppBar(
|
||||
canBack: true,
|
||||
backCall: () {
|
||||
Navigator.of(context).pop();
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
if (preResult == result) {
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (childContext) => CupertinoAlertDialog(
|
||||
title: const Text("温馨提示"),
|
||||
content: const Text("你编辑的内容还没用提交,确定退出吗?"),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: const Text(
|
||||
"取消",
|
||||
style: TextStyle(
|
||||
color: Color(0xff999999),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(childContext).pop();
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(
|
||||
"确定",
|
||||
style: TextStyle(
|
||||
color: ref.watch(themeProvider).primaryColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(childContext).pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
title: '编辑${widget.title}',
|
||||
actions: [
|
||||
const SizedBox(
|
||||
width: 15,
|
||||
),
|
||||
Material(
|
||||
color: Colors.transparent,
|
||||
child: PopupMenuButton<String>(
|
||||
onSelected: (String result) {
|
||||
updateValueBykey(result);
|
||||
},
|
||||
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
|
||||
...operateList
|
||||
.map(
|
||||
(e) => PopupMenuItem<String>(
|
||||
child: Text(e),
|
||||
value: e,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
],
|
||||
child: const Center(
|
||||
child: Icon(
|
||||
CupertinoIcons.arrow_up_right_diamond,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () async {
|
||||
HttpResponse<NullResponse> response = await Api.saveFile(widget.title, result);
|
||||
await notifyICloud(context, widget.title, result);
|
||||
if (response.success) {
|
||||
"提交成功".toast();
|
||||
Navigator.of(context).pop(widget.title);
|
||||
@@ -89,23 +165,176 @@ class _ConfigEditPageState extends ConsumerState<ConfigEditPage> {
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: CodeField(
|
||||
controller: _codeController!,
|
||||
expands: true,
|
||||
wrap: true,
|
||||
lineNumberStyle: const LineNumberStyle(
|
||||
width: 0,
|
||||
margin: 0,
|
||||
textStyle: TextStyle(
|
||||
color: Colors.transparent,
|
||||
fontSize: 0,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SpUtil.getBool(spShowLine, defValue: false) ? 0 : 10,
|
||||
),
|
||||
child: CodeField(
|
||||
controller: _codeController!,
|
||||
expands: true,
|
||||
background: Colors.white,
|
||||
wrap: SpUtil.getBool(spShowLine, defValue: false) ? false : true,
|
||||
hideColumn: !SpUtil.getBool(spShowLine, defValue: false),
|
||||
lineNumberStyle: LineNumberStyle(
|
||||
textStyle: TextStyle(
|
||||
color: ref.watch(themeProvider).themeColor.descColor(),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
background: ref.watch(themeProvider).themeColor.settingBgColor(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
FocusNode focusNode = FocusNode();
|
||||
|
||||
void generateOperateList() {
|
||||
operateList.clear();
|
||||
List<String> array = result.split("\n");
|
||||
for (String a in array) {
|
||||
String t = a.replaceAll(" ", "");
|
||||
if (t.trim().startsWith("export")) {
|
||||
int i = t.indexOf("export") + 6;
|
||||
int j = t.indexOf("=");
|
||||
operateList.add(t.substring(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateValueBykey(String key) async {
|
||||
String defaultValue = "";
|
||||
try {
|
||||
var clipBoard = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
|
||||
if (clipBoard != null && clipBoard.text != null) {
|
||||
String tempText = clipBoard.text!;
|
||||
|
||||
if (tempText.trim().contains("export")) {
|
||||
int i = tempText.trim().indexOf("\"");
|
||||
int j = tempText.trim().lastIndexOf("\"");
|
||||
|
||||
if (i == -1 || j == -1) {
|
||||
i = tempText.trim().indexOf("'");
|
||||
j = tempText.trim().lastIndexOf("'");
|
||||
}
|
||||
|
||||
defaultValue = tempText.trim().substring(i, j);
|
||||
} else {
|
||||
defaultValue = tempText;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
TextEditingController controller = TextEditingController(text: defaultValue.replaceAll("\"", "").replaceAll("'", ""));
|
||||
showCupertinoDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
builder: (context) => CupertinoAlertDialog(
|
||||
title: Text("编辑$key:"),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding: EdgeInsets.all(4),
|
||||
hintText: "请输入值",
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
autofocus: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: const Text(
|
||||
"取消",
|
||||
style: TextStyle(
|
||||
color: Color(0xff999999),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(
|
||||
"确定",
|
||||
style: TextStyle(
|
||||
color: ref.watch(themeProvider).primaryColor,
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
updateValueByKey(key, controller.text);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void updateValueByKey(String key, String text) {
|
||||
List<String> array = result.split("\n");
|
||||
for (String a in array) {
|
||||
String t = a.replaceAll(" ", "");
|
||||
if (t.trim().startsWith("export")) {
|
||||
int i = t.indexOf("export") + 6;
|
||||
int j = t.indexOf("=");
|
||||
|
||||
String tempResult = t.substring(i, j);
|
||||
if (tempResult == key) {
|
||||
result = result.replaceAll(a, "\nexport $key = \"$text\" \n\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_codeController = null;
|
||||
setState(() {});
|
||||
"已修改".toast();
|
||||
}
|
||||
|
||||
void checkClipBoard() async {
|
||||
try {
|
||||
String key = "";
|
||||
String value = "";
|
||||
var clipBoard = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
|
||||
if (clipBoard != null && clipBoard.text != null) {
|
||||
String tempText = clipBoard.text!;
|
||||
|
||||
if (tempText.trim().contains("export")) {
|
||||
int kI = tempText.trim().indexOf("export");
|
||||
int kJ = tempText.trim().indexOf("=");
|
||||
|
||||
key = tempText.trim().substring(kI + 6, kJ);
|
||||
|
||||
int i = tempText.trim().indexOf("\"");
|
||||
int j = tempText.trim().lastIndexOf("\"");
|
||||
|
||||
if (i == -1 || j == -1) {
|
||||
i = tempText.trim().indexOf("'");
|
||||
j = tempText.trim().lastIndexOf("'");
|
||||
}
|
||||
value = tempText.trim().substring(i, j);
|
||||
|
||||
if (key.isNotEmpty && result.contains(key) && value.isNotEmpty) {
|
||||
WidgetsBinding.instance.endOfFrame.then((value) {
|
||||
updateValueBykey(key);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:code_text_field/code_text_field.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:highlight/languages/powershell.dart';
|
||||
import 'package:qinglong_app/base/base_state_widget.dart';
|
||||
import 'package:qinglong_app/base/routes.dart';
|
||||
import 'package:qinglong_app/base/sp_const.dart';
|
||||
import 'package:qinglong_app/base/theme.dart';
|
||||
import 'package:qinglong_app/base/ui/abs_underline_tabindicator.dart';
|
||||
import 'package:qinglong_app/base/ui/empty_widget.dart';
|
||||
import 'package:qinglong_app/main.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:qinglong_app/utils/sp_utils.dart';
|
||||
|
||||
import '../../base/ui/syntax_highlighter.dart';
|
||||
import 'config_viewmodel.dart';
|
||||
@@ -19,8 +24,7 @@ class ConfigPage extends StatefulWidget {
|
||||
ConfigPageState createState() => ConfigPageState();
|
||||
}
|
||||
|
||||
class ConfigPageState extends State<ConfigPage>
|
||||
with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||
class ConfigPageState extends State<ConfigPage> with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||
int _initIndex = 0;
|
||||
BuildContext? childContext;
|
||||
|
||||
@@ -85,14 +89,8 @@ class ConfigPageState extends State<ConfigPage>
|
||||
void editMe(WidgetRef ref) {
|
||||
if (childContext == null) return;
|
||||
navigatorState.currentState?.pushNamed(Routes.routeConfigEdit, arguments: {
|
||||
"title": ref
|
||||
.read(configProvider)
|
||||
.list[DefaultTabController.of(childContext!)?.index ?? 0]
|
||||
.title,
|
||||
"content": ref.read(configProvider).content[ref
|
||||
.read(configProvider)
|
||||
.list[DefaultTabController.of(childContext!)?.index ?? 0]
|
||||
.title]
|
||||
"title": ref.read(configProvider).list[DefaultTabController.of(childContext!)?.index ?? 0].title,
|
||||
"content": ref.read(configProvider).content[ref.read(configProvider).list[DefaultTabController.of(childContext!)?.index ?? 0].title]
|
||||
}).then((value) async {
|
||||
if (value != null && (value as String).isNotEmpty) {
|
||||
await ref.read(configProvider).loadContent(value);
|
||||
@@ -105,7 +103,7 @@ class ConfigPageState extends State<ConfigPage>
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
|
||||
class CodeWidget extends StatefulWidget {
|
||||
class CodeWidget extends ConsumerStatefulWidget {
|
||||
final String content;
|
||||
|
||||
const CodeWidget({
|
||||
@@ -114,30 +112,61 @@ class CodeWidget extends StatefulWidget {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<CodeWidget> createState() => _CodeWidgetState();
|
||||
ConsumerState<CodeWidget> createState() => _CodeWidgetState();
|
||||
}
|
||||
|
||||
class _CodeWidgetState extends State<CodeWidget>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
class _CodeWidgetState extends ConsumerState<CodeWidget> with AutomaticKeepAliveClientMixin {
|
||||
CodeController? _codeController;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_codeController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
String result = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
result = widget.content;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return SelectableText.rich(
|
||||
TextSpan(
|
||||
style: GoogleFonts.droidSansMono(fontSize: 14).apply(
|
||||
fontSizeFactor: 1,
|
||||
_codeController ??= CodeController(
|
||||
text: result,
|
||||
language: powershell,
|
||||
onChange: (value) {
|
||||
result = value;
|
||||
},
|
||||
theme: ref.watch(themeProvider).themeColor.codeEditorTheme(),
|
||||
stringMap: {
|
||||
"export": const TextStyle(fontWeight: FontWeight.normal, color: Color(0xff6B2375)),
|
||||
},
|
||||
);
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SpUtil.getBool(spShowLine, defValue: false) ? 0 : 10,
|
||||
),
|
||||
children: <TextSpan>[
|
||||
DartSyntaxHighlighter(SyntaxHighlighterStyle.lightThemeStyle())
|
||||
.format(widget.content)
|
||||
],
|
||||
),
|
||||
style: DefaultTextStyle.of(context).style.apply(
|
||||
fontSizeFactor: 1,
|
||||
child: CodeField(
|
||||
controller: _codeController!,
|
||||
expands: true,
|
||||
readOnly: true,
|
||||
background: Colors.white,
|
||||
wrap: SpUtil.getBool(spShowLine, defValue: false) ? false : true,
|
||||
hideColumn: !SpUtil.getBool(spShowLine, defValue: false),
|
||||
lineNumberStyle: LineNumberStyle(
|
||||
textStyle: TextStyle(
|
||||
color: ref.watch(themeProvider).themeColor.descColor(),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
selectionWidthStyle: BoxWidthStyle.max,
|
||||
selectionHeightStyle: BoxHeightStyle.max,
|
||||
autofocus: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:qinglong_app/base/routes.dart';
|
||||
import 'package:qinglong_app/base/sp_const.dart';
|
||||
import 'package:qinglong_app/base/theme.dart';
|
||||
import 'package:qinglong_app/base/userinfo_viewmodel.dart';
|
||||
import 'package:qinglong_app/main.dart';
|
||||
import 'package:qinglong_app/utils/extension.dart';
|
||||
import 'package:qinglong_app/utils/sp_utils.dart';
|
||||
|
||||
class OtherPage extends ConsumerStatefulWidget {
|
||||
const OtherPage({Key? key}) : super(key: key);
|
||||
@@ -175,6 +177,7 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
||||
const Divider(
|
||||
indent: 15,
|
||||
),
|
||||
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (getIt<UserInfoViewModel>().useSecretLogined) {
|
||||
@@ -251,10 +254,42 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const Divider(
|
||||
indent: 15,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15,
|
||||
right: 15,
|
||||
bottom: 5,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"查看代码是否显示行号",
|
||||
style: TextStyle(
|
||||
color: ref.watch(themeProvider).themeColor.titleColor(),
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
CupertinoSwitch(
|
||||
activeColor: ref.watch(themeProvider).primaryColor,
|
||||
value: SpUtil.getBool(spShowLine, defValue: false),
|
||||
onChanged: (open) async {
|
||||
await SpUtil.putBool(spShowLine, open);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
indent: 15,
|
||||
height: 1,
|
||||
),
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
@@ -265,7 +300,7 @@ class _OtherPageState extends ConsumerState<OtherPage> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 15,
|
||||
vertical: 5,
|
||||
vertical: 10,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:code_text_field/code_text_field.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:highlight/languages/javascript.dart';
|
||||
import 'package:highlight/languages/json.dart';
|
||||
import 'package:highlight/languages/powershell.dart';
|
||||
import 'package:highlight/languages/python.dart';
|
||||
import 'package:highlight/languages/vbscript-html.dart';
|
||||
import 'package:highlight/languages/yaml.dart';
|
||||
import 'package:qinglong_app/base/http/api.dart';
|
||||
import 'package:qinglong_app/base/http/http.dart';
|
||||
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||
import 'package:qinglong_app/base/routes.dart';
|
||||
import 'package:qinglong_app/base/sp_const.dart';
|
||||
import 'package:qinglong_app/base/theme.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:qinglong_app/base/ui/lazy_load_state.dart';
|
||||
import 'package:qinglong_app/utils/extension.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
|
||||
import '../../../base/ui/syntax_highlighter.dart';
|
||||
import '../../config/config_page.dart';
|
||||
import 'package:qinglong_app/utils/sp_utils.dart';
|
||||
|
||||
/// @author NewTab
|
||||
class ScriptDetailPage extends ConsumerStatefulWidget {
|
||||
@@ -31,11 +33,62 @@ class ScriptDetailPage extends ConsumerStatefulWidget {
|
||||
_ScriptDetailPageState createState() => _ScriptDetailPageState();
|
||||
}
|
||||
|
||||
class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage>
|
||||
with LazyLoadState<ScriptDetailPage> {
|
||||
class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage> with LazyLoadState<ScriptDetailPage> {
|
||||
String? content;
|
||||
CodeController? _codeController;
|
||||
GlobalKey<CodeFieldState> codeFieldKey = GlobalKey();
|
||||
|
||||
List<Widget> actions = [];
|
||||
bool buttonshow = false;
|
||||
|
||||
void scrollToTop() {
|
||||
codeFieldKey.currentState?.getCodeScroll()?.animateTo(0, duration: const Duration(milliseconds: 200), curve: Curves.linear);
|
||||
}
|
||||
|
||||
void floatingButtonVisibility() {
|
||||
double y = codeFieldKey.currentState?.getCodeScroll()?.offset ?? 0;
|
||||
if (y > MediaQuery.of(context).size.height / 2) {
|
||||
if (buttonshow == true) return;
|
||||
setState(() {
|
||||
buttonshow = true;
|
||||
});
|
||||
} else {
|
||||
if (buttonshow == false) return;
|
||||
setState(() {
|
||||
buttonshow = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String suffix = "\n\n\n";
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_codeController?.dispose();
|
||||
_codeController = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
getLanguageType(String title) {
|
||||
if (title.endsWith(".js")) {
|
||||
return javascript;
|
||||
}
|
||||
|
||||
if (title.endsWith(".sh")) {
|
||||
return powershell;
|
||||
}
|
||||
|
||||
if (title.endsWith(".py")) {
|
||||
return python;
|
||||
}
|
||||
if (title.endsWith(".json")) {
|
||||
return json;
|
||||
}
|
||||
if (title.endsWith(".yaml")) {
|
||||
return yaml;
|
||||
}
|
||||
return vbscriptHtml;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -85,6 +138,7 @@ class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage>
|
||||
Navigator.of(context).pop();
|
||||
|
||||
showCupertinoDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
builder: (context) => CupertinoAlertDialog(
|
||||
title: const Text("确认删除"),
|
||||
@@ -110,8 +164,7 @@ class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage>
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
HttpResponse<NullResponse> result =
|
||||
await Api.delScript(widget.title, widget.path ?? "");
|
||||
HttpResponse<NullResponse> result = await Api.delScript(widget.title, widget.path ?? "");
|
||||
if (result.success) {
|
||||
"删除成功".toast();
|
||||
Navigator.of(context).pop(true);
|
||||
@@ -147,18 +200,44 @@ class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (content != null) {
|
||||
_codeController ??= CodeController(
|
||||
text: (content ?? "") + suffix,
|
||||
language: getLanguageType(widget.title),
|
||||
onChange: (value) {
|
||||
content = value + suffix;
|
||||
},
|
||||
theme: ref.watch(themeProvider).themeColor.codeEditorTheme(),
|
||||
stringMap: {
|
||||
"export": const TextStyle(fontWeight: FontWeight.normal, color: Color(0xff6B2375)),
|
||||
},
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
floatingActionButton: Visibility(
|
||||
visible: buttonshow,
|
||||
child: FloatingActionButton(
|
||||
mini: true,
|
||||
onPressed: () {
|
||||
scrollToTop();
|
||||
},
|
||||
elevation: 2,
|
||||
backgroundColor: Colors.white,
|
||||
child: const Icon(CupertinoIcons.up_arrow),
|
||||
),
|
||||
),
|
||||
appBar: QlAppBar(
|
||||
canBack: true,
|
||||
backCall: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
title: "脚本详情",
|
||||
title: widget.title,
|
||||
actions: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showCupertinoModalPopup(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (context) {
|
||||
return CupertinoActionSheet(
|
||||
title: Container(
|
||||
@@ -217,11 +296,31 @@ class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage>
|
||||
),
|
||||
body: content == null
|
||||
? const Center(
|
||||
child: CupertinoActivityIndicator(),
|
||||
)
|
||||
: ScriptCodeWidget(
|
||||
content: content ?? "",
|
||||
child: CupertinoActivityIndicator(),
|
||||
)
|
||||
: SafeArea(
|
||||
top: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SpUtil.getBool(spShowLine, defValue: false) ? 0 : 10,
|
||||
),
|
||||
child: CodeField(
|
||||
key: codeFieldKey,
|
||||
controller: _codeController!,
|
||||
expands: true,
|
||||
readOnly: true,
|
||||
wrap: SpUtil.getBool(spShowLine, defValue: false) ? false : true,
|
||||
hideColumn: !SpUtil.getBool(spShowLine, defValue: false),
|
||||
lineNumberStyle: LineNumberStyle(
|
||||
textStyle: TextStyle(
|
||||
color: ref.watch(themeProvider).themeColor.descColor(),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
background: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,71 +333,21 @@ class _ScriptDetailPageState extends ConsumerState<ScriptDetailPage>
|
||||
if (response.success) {
|
||||
content = response.bean;
|
||||
setState(() {});
|
||||
Future.delayed(
|
||||
const Duration(
|
||||
seconds: 1,
|
||||
),
|
||||
() {
|
||||
codeFieldKey.currentState?.getCodeScroll()?.addListener(floatingButtonVisibility);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
response.message?.toast();
|
||||
}
|
||||
}
|
||||
|
||||
getLanguageType(String title) {
|
||||
if (title.endsWith(".js")) {
|
||||
return "js";
|
||||
}
|
||||
|
||||
if (title.endsWith(".sh")) {
|
||||
return "sh";
|
||||
}
|
||||
|
||||
if (title.endsWith(".py")) {
|
||||
return "py";
|
||||
}
|
||||
if (title.endsWith(".json")) {
|
||||
return "json";
|
||||
}
|
||||
if (title.endsWith(".yaml")) {
|
||||
return "yaml";
|
||||
}
|
||||
return "html";
|
||||
}
|
||||
|
||||
@override
|
||||
void onLazyLoad() {
|
||||
loadData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ScriptCodeWidget extends StatefulWidget {
|
||||
final String content;
|
||||
|
||||
const ScriptCodeWidget({
|
||||
Key? key,
|
||||
required this.content,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ScriptCodeWidget> createState() => _ScriptCodeWidgetState();
|
||||
}
|
||||
|
||||
class _ScriptCodeWidgetState extends State<ScriptCodeWidget>{
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SelectableText.rich(
|
||||
TextSpan(
|
||||
style: GoogleFonts.droidSansMono(fontSize: 14).apply(
|
||||
fontSizeFactor: 1,
|
||||
),
|
||||
children: <TextSpan>[
|
||||
DartSyntaxHighlighter(SyntaxHighlighterStyle.lightThemeStyle())
|
||||
.format(widget.content)
|
||||
],
|
||||
),
|
||||
style: DefaultTextStyle.of(context).style.apply(
|
||||
fontSizeFactor: 1,
|
||||
),
|
||||
selectionWidthStyle: BoxWidthStyle.max,
|
||||
selectionHeightStyle: BoxHeightStyle.max,
|
||||
autofocus: true,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:code_text_field/code_text_field.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:highlight/languages/javascript.dart';
|
||||
@@ -10,8 +11,10 @@ import 'package:highlight/languages/yaml.dart';
|
||||
import 'package:qinglong_app/base/http/api.dart';
|
||||
import 'package:qinglong_app/base/http/http.dart';
|
||||
import 'package:qinglong_app/base/ql_app_bar.dart';
|
||||
import 'package:qinglong_app/base/sp_const.dart';
|
||||
import 'package:qinglong_app/base/theme.dart';
|
||||
import 'package:qinglong_app/utils/extension.dart';
|
||||
import 'package:qinglong_app/utils/sp_utils.dart';
|
||||
|
||||
class ScriptEditPage extends ConsumerStatefulWidget {
|
||||
final String content;
|
||||
@@ -28,6 +31,7 @@ class _ScriptEditPageState extends ConsumerState<ScriptEditPage> {
|
||||
CodeController? _codeController;
|
||||
late String result;
|
||||
FocusNode focusNode = FocusNode();
|
||||
late String preResult;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -38,7 +42,7 @@ class _ScriptEditPageState extends ConsumerState<ScriptEditPage> {
|
||||
@override
|
||||
void initState() {
|
||||
result = widget.content;
|
||||
|
||||
preResult = widget.content;
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
||||
@@ -84,7 +88,45 @@ class _ScriptEditPageState extends ConsumerState<ScriptEditPage> {
|
||||
appBar: QlAppBar(
|
||||
canBack: true,
|
||||
backCall: () {
|
||||
Navigator.of(context).pop();
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
if (preResult == result) {
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
showCupertinoDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (childContext) => CupertinoAlertDialog(
|
||||
title: const Text("温馨提示"),
|
||||
content: const Text("你编辑的内容还没用提交,确定退出吗?"),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
child: const Text(
|
||||
"取消",
|
||||
style: TextStyle(
|
||||
color: Color(0xff999999),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(childContext).pop();
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
child: Text(
|
||||
"确定",
|
||||
style: TextStyle(
|
||||
color: ref.watch(themeProvider).primaryColor,
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(childContext).pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
title: '编辑${widget.title}',
|
||||
actions: [
|
||||
@@ -117,19 +159,23 @@ class _ScriptEditPageState extends ConsumerState<ScriptEditPage> {
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
child: CodeField(
|
||||
controller: _codeController!,
|
||||
expands: true,
|
||||
wrap: true,
|
||||
lineNumberStyle: const LineNumberStyle(
|
||||
width: 0,
|
||||
margin: 0,
|
||||
textStyle: TextStyle(
|
||||
color: Colors.transparent,
|
||||
fontSize: 0,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: SpUtil.getBool(spShowLine, defValue: false) ? 0 : 10,
|
||||
),
|
||||
child: CodeField(
|
||||
controller: _codeController!,
|
||||
expands: true,
|
||||
background: Colors.white,
|
||||
wrap: SpUtil.getBool(spShowLine, defValue: false) ? false : true,
|
||||
hideColumn: !SpUtil.getBool(spShowLine, defValue: false),
|
||||
lineNumberStyle: LineNumberStyle(
|
||||
textStyle: TextStyle(
|
||||
color: ref.watch(themeProvider).themeColor.descColor(),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
background: ref.watch(themeProvider).themeColor.tabBarColor(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user