advanced_ide.dart 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. // tools/advanced_ide.dart
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_code_editor/flutter_code_editor.dart';
  4. import 'package:flutter_js/flutter_js.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:highlight/languages/javascript.dart';
  7. import 'package:path_provider/path_provider.dart';
  8. import 'package:path/path.dart' as path;
  9. import 'package:file_picker/file_picker.dart';
  10. import 'dart:io';
  11. import 'dart:convert';
  12. import 'dart:typed_data';
  13. import 'package:flutter_highlight/themes/github.dart'; // 确保这个导入存在
  14. import 'package:flutter_highlight/themes/vs.dart'; // 添加更多主题选择
  15. import 'package:flutter_highlight/themes/darcula.dart';
  16. class AdvancedIdePage extends StatefulWidget {
  17. @override
  18. _AdvancedIdePageState createState() => _AdvancedIdePageState();
  19. }
  20. class _AdvancedIdePageState extends State<AdvancedIdePage> {
  21. late CodeController _codeController;
  22. JavascriptRuntime? _jsRuntime;
  23. String _output = '';
  24. bool _isExecuting = false;
  25. bool _isInitialized = false;
  26. Map<String, TextStyle> _currentTheme = githubTheme; // 默认主题
  27. @override
  28. void initState() {
  29. super.initState();
  30. _initializeCodeEditor();
  31. }
  32. Future<void> _initializeCodeEditor() async {
  33. try {
  34. _codeController = CodeController(
  35. text: _getExampleCode(),
  36. language: javascript,
  37. );
  38. setState(() {
  39. _isInitialized = true;
  40. });
  41. } catch (e) {
  42. print('初始化代码编辑器失败: $e');
  43. setState(() {
  44. _output = '初始化失败: $e';
  45. _isInitialized = true;
  46. });
  47. }
  48. }
  49. // 切换主题的方法
  50. void _changeTheme(String themeName) {
  51. setState(() {
  52. switch (themeName) {
  53. case 'github':
  54. _currentTheme = githubTheme;
  55. break;
  56. case 'vs':
  57. _currentTheme = vsTheme;
  58. break;
  59. case 'darcula':
  60. _currentTheme = darculaTheme;
  61. break;
  62. default:
  63. _currentTheme = githubTheme;
  64. }
  65. });
  66. }
  67. // 在 build 方法中,修改代码编辑器部分:
  68. @override
  69. Widget build(BuildContext context) {
  70. return Scaffold(
  71. appBar: AppBar(
  72. title: Text('JavaScript IDE', style: TextStyle(color: Colors.black)),
  73. backgroundColor: Colors.white,
  74. elevation: 1,
  75. actions: [
  76. // 主题切换按钮
  77. PopupMenuButton<String>(
  78. icon: Icon(Icons.palette, color: Colors.purple, size: 20),
  79. itemBuilder: (context) => [
  80. PopupMenuItem(
  81. value: 'github',
  82. child: Row(
  83. children: [
  84. Icon(Icons.brightness_5, color: Colors.blue),
  85. SizedBox(width: 8),
  86. Text('GitHub 主题'),
  87. ],
  88. ),
  89. ),
  90. PopupMenuItem(
  91. value: 'vs',
  92. child: Row(
  93. children: [
  94. Icon(Icons.brightness_5, color: Colors.grey),
  95. SizedBox(width: 8),
  96. Text('VS 主题'),
  97. ],
  98. ),
  99. ),
  100. PopupMenuItem(
  101. value: 'darcula',
  102. child: Row(
  103. children: [
  104. Icon(Icons.brightness_2, color: Colors.green),
  105. SizedBox(width: 8),
  106. Text('Darcula 主题'),
  107. ],
  108. ),
  109. ),
  110. ],
  111. onSelected: _changeTheme,
  112. tooltip: '切换主题',
  113. ),
  114. IconButton(
  115. icon: Icon(Icons.save_as, color: Colors.blue, size: 20),
  116. onPressed: _saveAs,
  117. tooltip: '另存为',
  118. ),
  119. IconButton(
  120. icon: Icon(Icons.folder_open, color: Colors.blue, size: 20),
  121. onPressed: _loadFromFile,
  122. tooltip: '从文件加载',
  123. ),
  124. IconButton(
  125. icon: Icon(Icons.play_arrow, color: Colors.green, size: 20),
  126. onPressed: _isInitialized ? _executeCode : null,
  127. tooltip: '运行代码(全新环境)',
  128. ),
  129. IconButton(
  130. icon: Icon(Icons.clear, color: Colors.red, size: 20),
  131. onPressed: _clearOutput,
  132. tooltip: '清空输出',
  133. ),
  134. IconButton(
  135. icon: Icon(Icons.refresh, color: Colors.blue, size: 20),
  136. onPressed: _resetCode,
  137. tooltip: '重置代码',
  138. ),
  139. ],
  140. ),
  141. body: !_isInitialized
  142. ? Center(
  143. child: Column(
  144. mainAxisAlignment: MainAxisAlignment.center,
  145. children: [
  146. CircularProgressIndicator(),
  147. SizedBox(height: 16),
  148. Text('正在初始化代码编辑器...'),
  149. ],
  150. ),
  151. )
  152. : Column(
  153. children: [
  154. Expanded(
  155. flex: 3,
  156. child: Container(
  157. margin: EdgeInsets.all(8),
  158. decoration: BoxDecoration(
  159. border: Border.all(color: Colors.blue[300]!),
  160. borderRadius: BorderRadius.circular(8),
  161. ),
  162. child: Column(
  163. children: [
  164. Container(
  165. padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  166. decoration: BoxDecoration(
  167. color: Colors.blue[50],
  168. borderRadius: BorderRadius.only(
  169. topLeft: Radius.circular(8),
  170. topRight: Radius.circular(8),
  171. ),
  172. ),
  173. child: Row(
  174. children: [
  175. Icon(Icons.code, size: 16, color: Colors.blue[700]!),
  176. SizedBox(width: 8),
  177. Text(
  178. 'JavaScript 代码编辑器',
  179. style: TextStyle(
  180. fontSize: 14,
  181. fontWeight: FontWeight.bold,
  182. color: Colors.blue[700]!,
  183. ),
  184. ),
  185. Spacer(),
  186. // 显示当前主题
  187. Container(
  188. padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
  189. decoration: BoxDecoration(
  190. color: Colors.white,
  191. borderRadius: BorderRadius.circular(12),
  192. border: Border.all(color: Colors.blue[300]!),
  193. ),
  194. child: Text(
  195. _getCurrentThemeName(),
  196. style: TextStyle(fontSize: 12, color: Colors.blue[700]!),
  197. ),
  198. ),
  199. SizedBox(width: 8),
  200. IconButton(
  201. icon: Icon(Icons.info_outline, size: 16),
  202. onPressed: () {
  203. showDialog(
  204. context: context,
  205. builder: (context) => AlertDialog(
  206. title: Text('使用说明'),
  207. content: Text(
  208. '• 使用本功能需要了解js基本语法\n'
  209. '• math.js的版本是12.4.3\n'
  210. '• 每次运行代码都会创建全新的JavaScript环境\n'
  211. '• 之前的变量和状态不会保留\n'
  212. '• math.js库会在每次运行时重新加载\n'
  213. '• 执行速度可能稍慢(因为要重新初始化)\n'
  214. '• 使用console.log()输出结果\n'
  215. '• 点击调色板图标可以切换语法高亮主题'
  216. ),
  217. actions: [
  218. TextButton(
  219. onPressed: () => Navigator.pop(context),
  220. child: Text('确定'),
  221. ),
  222. ],
  223. ),
  224. );
  225. },
  226. tooltip: '使用说明',
  227. ),
  228. ],
  229. ),
  230. ),
  231. Expanded(
  232. child: CodeTheme( // 添加 CodeTheme 包装
  233. data: CodeThemeData(styles: _currentTheme),
  234. child: SingleChildScrollView(
  235. child: CodeField(
  236. controller: _codeController,
  237. textStyle: TextStyle(fontFamily: 'Monospace', fontSize: 14),
  238. gutterStyle: GutterStyle(
  239. showLineNumbers: true,
  240. showErrors: true,
  241. showFoldingHandles: true,
  242. ),
  243. ),
  244. ),
  245. ),
  246. ),
  247. ],
  248. ),
  249. ),
  250. ),
  251. SizedBox(height: 8),
  252. // 输出区域
  253. // 输出区域
  254. Expanded(
  255. flex: 2,
  256. child: Container(
  257. margin: EdgeInsets.all(8),
  258. decoration: BoxDecoration(
  259. border: Border.all(color: Colors.grey[300]!),
  260. borderRadius: BorderRadius.circular(8),
  261. ),
  262. child: Column(
  263. children: [
  264. Container(
  265. padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  266. decoration: BoxDecoration(
  267. color: Colors.grey[100],
  268. borderRadius: BorderRadius.only(
  269. topLeft: Radius.circular(8),
  270. topRight: Radius.circular(8),
  271. ),
  272. ),
  273. child: Row(
  274. children: [
  275. Icon(Icons.output, size: 16, color: Colors.grey[600]!),
  276. SizedBox(width: 8),
  277. Text(
  278. '执行输出',
  279. style: TextStyle(
  280. fontSize: 14,
  281. fontWeight: FontWeight.bold,
  282. color: Colors.grey[600]!,
  283. ),
  284. ),
  285. Spacer(),
  286. if (_isExecuting)
  287. Row(
  288. children: [
  289. SizedBox(
  290. width: 16,
  291. height: 16,
  292. child: CircularProgressIndicator(strokeWidth: 2),
  293. ),
  294. SizedBox(width: 8),
  295. Text(
  296. '执行中...',
  297. style: TextStyle(
  298. fontSize: 12,
  299. color: Colors.orange,
  300. ),
  301. ),
  302. ],
  303. ),
  304. ],
  305. ),
  306. ),
  307. Expanded(
  308. child: SingleChildScrollView(
  309. padding: EdgeInsets.all(16),
  310. child: SelectableText( // 修改这里:Text -> SelectableText
  311. _output,
  312. style: TextStyle(
  313. fontFamily: 'Monospace',
  314. fontSize: 12,
  315. ),
  316. ),
  317. ),
  318. ),
  319. ],
  320. ),
  321. ),
  322. ),
  323. ],
  324. ),
  325. );
  326. }
  327. // 获取当前主题名称
  328. String _getCurrentThemeName() {
  329. if (_currentTheme == githubTheme) return 'GitHub';
  330. if (_currentTheme == vsTheme) return 'VS';
  331. if (_currentTheme == darculaTheme) return 'Darcula';
  332. return '默认';
  333. }
  334. // 其他方法保持不变...
  335. Future<void> _initializeJsRuntime() async {
  336. try {
  337. if (_jsRuntime != null) {
  338. _jsRuntime!.dispose();
  339. _jsRuntime = null;
  340. }
  341. _jsRuntime = getJavascriptRuntime();
  342. // 设置非浏览器环境的console重写
  343. _setupConsoleOverride();
  344. // 加载math.js库
  345. final String mathJsCode = await rootBundle.loadString('assets/javascripts/math.js');
  346. final JsEvalResult mathResult = _jsRuntime!.evaluate(mathJsCode);
  347. if (mathResult.isError) {
  348. print('加载math.js失败: ${mathResult.stringResult}');
  349. throw Exception('加载math.js失败: ${mathResult.stringResult}');
  350. } else {
  351. print('math.js加载成功');
  352. }
  353. } catch (e) {
  354. print('初始化JavaScript运行时失败: $e');
  355. if (_jsRuntime != null) {
  356. _jsRuntime!.dispose();
  357. _jsRuntime = null;
  358. }
  359. throw e;
  360. }
  361. }
  362. void _setupConsoleOverride() {
  363. if (_jsRuntime == null) return;
  364. const String consoleOverride = '''
  365. // 创建全局输出数组
  366. var __consoleOutput = [];
  367. // 保存原始console方法(如果存在)
  368. var originalConsole = {
  369. log: typeof console !== 'undefined' ? console.log : function() {},
  370. warn: typeof console !== 'undefined' ? console.warn : function() {},
  371. error: typeof console !== 'undefined' ? console.error : function() {},
  372. info: typeof console !== 'undefined' ? console.info : function() {}
  373. };
  374. // 重写console方法
  375. if (typeof console !== 'undefined') {
  376. console.log = function(...args) {
  377. originalConsole.log.apply(console, args);
  378. const message = args.map(arg =>
  379. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  380. ).join(' ');
  381. __consoleOutput.push('[LOG] ' + message);
  382. };
  383. console.warn = function(...args) {
  384. originalConsole.warn.apply(console, args);
  385. const message = args.map(arg =>
  386. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  387. ).join(' ');
  388. __consoleOutput.push('[WARN] ' + message);
  389. };
  390. console.error = function(...args) {
  391. originalConsole.error.apply(console, args);
  392. const message = args.map(arg =>
  393. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  394. ).join(' ');
  395. __consoleOutput.push('[ERROR] ' + message);
  396. };
  397. console.info = function(...args) {
  398. originalConsole.info.apply(console, args);
  399. const message = args.map(arg =>
  400. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  401. ).join(' ');
  402. __consoleOutput.push('[INFO] ' + message);
  403. };
  404. } else {
  405. // 如果console不存在,创建它
  406. var console = {
  407. log: function(...args) {
  408. const message = args.map(arg =>
  409. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  410. ).join(' ');
  411. __consoleOutput.push('[LOG] ' + message);
  412. },
  413. warn: function(...args) {
  414. const message = args.map(arg =>
  415. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  416. ).join(' ');
  417. __consoleOutput.push('[WARN] ' + message);
  418. },
  419. error: function(...args) {
  420. const message = args.map(arg =>
  421. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  422. ).join(' ');
  423. __consoleOutput.push('[ERROR] ' + message);
  424. },
  425. info: function(...args) {
  426. const message = args.map(arg =>
  427. typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
  428. ).join(' ');
  429. __consoleOutput.push('[INFO] ' + message);
  430. }
  431. };
  432. }
  433. // 清空输出数组的方法
  434. var __clearConsoleOutput = function() {
  435. __consoleOutput = [];
  436. };
  437. // 获取输出内容的方法(修复换行问题)
  438. var __getConsoleOutput = function() {
  439. return __consoleOutput.join('\\n'); // 关键修复:使用单个换行符
  440. };
  441. ''';
  442. _jsRuntime!.evaluate(consoleOverride);
  443. }
  444. @override
  445. void dispose() {
  446. _codeController.dispose();
  447. if (_jsRuntime != null) {
  448. _jsRuntime!.dispose();
  449. _jsRuntime = null;
  450. }
  451. super.dispose();
  452. }
  453. String _getExampleCode() {
  454. return '''// JavaScript IDE
  455. // math.js 库已自动加载,可以使用所有数学函数
  456. // 创建共享的作用域对象
  457. const scope = {};
  458. // 1. 基本算术运算
  459. console.log('1. 基本算术运算:');
  460. console.log('2 + 3 =', math.evaluate('2 + 3'));
  461. console.log('10 - 4 =', math.evaluate('10 - 4'));
  462. console.log('6 * 7 =', math.evaluate('6 * 7'));
  463. console.log('15 / 3 =', math.evaluate('15 / 3'));
  464. console.log('2^8 =', math.evaluate('2^8'));
  465. console.log('10 % 3 =', math.evaluate('10 % 3'));
  466. console.log('');
  467. // 2. 复杂表达式和运算符优先级
  468. console.log('2. 复杂表达式和运算符优先级:');
  469. console.log('2 + 3 * 4 =', math.evaluate('2 + 3 * 4'));
  470. console.log('(2 + 3) * 4 =', math.evaluate('(2 + 3) * 4'));
  471. console.log('3 + 4 * 2 / (1 - 5)^2 =', math.evaluate('3 + 4 * 2 / (1 - 5)^2'));
  472. console.log('');
  473. // 3. 数学常数
  474. console.log('3. 数学常数:');
  475. console.log('pi =', math.evaluate('pi'));
  476. console.log('e =', math.evaluate('e'));
  477. console.log('Infinity =', math.evaluate('Infinity'));
  478. console.log('');
  479. // 4. 内置函数
  480. console.log('4. 内置函数:');
  481. console.log('sqrt(25) =', math.evaluate('sqrt(25)'));
  482. console.log('sin(pi/2) =', math.evaluate('sin(pi/2)'));
  483. console.log('cos(pi) =', math.evaluate('cos(pi)'));
  484. console.log('tan(0) =', math.evaluate('tan(0)'));
  485. console.log('log(100) =', math.evaluate('log(100)'));
  486. console.log('log(100, 10) =', math.evaluate('log(100, 10)'));
  487. console.log('exp(2) =', math.evaluate('exp(2)'));
  488. console.log('abs(-5.5) =', math.evaluate('abs(-5.5)'));
  489. console.log('round(3.7) =', math.evaluate('round(3.7)'));
  490. console.log('floor(4.9) =', math.evaluate('floor(4.9)'));
  491. console.log('ceil(4.1) =', math.evaluate('ceil(4.1)'));
  492. console.log('max(1, 5, 3) =', math.evaluate('max(1, 5, 3)'));
  493. console.log('min(1, 5, 3) =', math.evaluate('min(1, 5, 3)'));
  494. console.log('');
  495. // 5. 变量和赋值
  496. console.log('5. 变量和赋值:');
  497. console.log('x = 5:', math.evaluate('x = 5', scope));
  498. console.log('y = x + 3:', math.evaluate('y = x + 3', scope));
  499. console.log('x * y =', math.evaluate('x * y', scope));
  500. console.log('');
  501. // 6. 矩阵和数组运算
  502. console.log('6. 矩阵和数组运算:');
  503. console.log('[1, 2, 3] =', math.evaluate('[1, 2, 3]'));
  504. console.log('[[1,2],[3,4]] =', math.evaluate('[[1,2],[3,4]]'));
  505. console.log('size([1,2,3,4]) =', math.evaluate('size([1,2,3,4])'));
  506. console.log('[1,2,3] + [4,5,6] =', math.evaluate('[1,2,3] + [4,5,6]'));
  507. console.log('2 * [1,2,3] =', math.evaluate('2 * [1,2,3]'));
  508. console.log('');
  509. // 7. 复数运算
  510. console.log('7. 复数运算:');
  511. console.log('2 + 3i =', math.evaluate('2 + 3i'));
  512. console.log('re(2+3i) =', math.evaluate('re(2+3i)'));
  513. console.log('im(2+3i) =', math.evaluate('im(2+3i)'));
  514. console.log('');
  515. // 8. 单位换算
  516. console.log('8. 单位换算:');
  517. console.log('5 inch to cm =', math.evaluate('5 inch to cm'));
  518. console.log('100 km/hour to m/s =', math.evaluate('100 km/hour to m/s'));
  519. console.log('');
  520. // 9. 布尔运算和比较
  521. console.log('9. 布尔运算和比较:');
  522. console.log('2 > 3 =', math.evaluate('2 > 3'));
  523. console.log('5 == 5 =', math.evaluate('5 == 5'));
  524. console.log('3 != 4 =', math.evaluate('3 != 4'));
  525. console.log('true and false =', math.evaluate('true and false'));
  526. console.log('true or false =', math.evaluate('true or false'));
  527. console.log('not true =', math.evaluate('not true'));
  528. console.log('');
  529. // 10. 条件表达式
  530. console.log('10. 条件表达式:');
  531. console.log('2 > 3 ? "yes" : "no" =', math.evaluate('2 > 3 ? "yes" : "no"'));
  532. console.log('x > 0 ? "positive" : "non-positive" =', math.evaluate('x > 0 ? "positive" : "non-positive"', scope));
  533. console.log('');
  534. // 11. 字符串操作
  535. console.log('11. 字符串操作:');
  536. console.log('concat("hello", " ", "world") =', math.evaluate('concat("hello", " ", "world")'));
  537. console.log('');
  538. // 12. 自定义函数
  539. console.log('12. 自定义函数:');
  540. math.evaluate('f(x) = x^2 + 2*x + 1', scope);
  541. console.log('f(3) =', math.evaluate('f(3)', scope));
  542. console.log('f(5) =', math.evaluate('f(5)', scope));
  543. console.log('');
  544. // 13. 统计函数
  545. console.log('13. 统计函数:');
  546. console.log('mean([1, 2, 3, 4, 5]) =', math.evaluate('mean([1, 2, 3, 4, 5])'));
  547. console.log('median([1, 2, 3, 4, 5]) =', math.evaluate('median([1, 2, 3, 4, 5])'));
  548. console.log('std([1, 2, 3, 4, 5]) =', math.evaluate('std([1, 2, 3, 4, 5])'));
  549. console.log('sum([1, 2, 3, 4, 5]) =', math.evaluate('sum([1, 2, 3, 4, 5])'));
  550. console.log('');
  551. // 14. 大数运算
  552. console.log('14. 大数运算:');
  553. console.log('123456789012345678901234567890 + 1 =', math.evaluate('123456789012345678901234567890 + 1'));
  554. console.log('2^100 =', math.evaluate('2^100'));
  555. console.log('');
  556. // 15. 链式操作示例
  557. console.log('15. 链式操作示例:');
  558. try {
  559. const result = math.chain(5)
  560. .add(3)
  561. .multiply(2)
  562. .divide(4)
  563. .done();
  564. console.log('(5 + 3) * 2 / 4 =', result);
  565. } catch (error) {
  566. console.log('链式操作出错:', error.message);
  567. }
  568. console.log('');
  569. // 16. 多表达式执行
  570. console.log('16. 多表达式执行:');
  571. const multiResult = math.evaluate(`
  572. a = 10;
  573. b = 20;
  574. c = a + b;
  575. c * 2
  576. `);
  577. console.log('多表达式结果:', multiResult);
  578. console.log('');
  579. // 17. 数值格式和精度
  580. console.log('17. 数值格式和精度:');
  581. console.log('format(1/3, 14) =', math.evaluate('format(1/3, 14)'));
  582. console.log('');
  583. // 18. 角度和弧度转换
  584. console.log('18. 角度和弧度转换:');
  585. console.log('sin(90 deg) =', math.evaluate('sin(90 deg)'));
  586. console.log('cos(180 deg) =', math.evaluate('cos(180 deg)'));
  587. console.log('');
  588. // 19. 随机数生成
  589. console.log('19. 随机数生成:');
  590. console.log('random() =', math.evaluate('random()'));
  591. console.log('random(10) =', math.evaluate('random(10)'));
  592. console.log('randomInt(10) =', math.evaluate('randomInt(10)'));
  593. console.log('random(5, 10) =', math.evaluate('random(5, 10)'));
  594. console.log('');
  595. // 20. 位运算
  596. console.log('20. 位运算:');
  597. console.log('5 & 3 =', math.evaluate('5 & 3'));
  598. console.log('5 | 3 =', math.evaluate('5 | 3'));
  599. console.log('');
  600. // 21. 阶乘和组合
  601. console.log('21. 阶乘和组合:');
  602. console.log('5! =', math.evaluate('5!'));
  603. console.log('combinations(5, 2) =', math.evaluate('combinations(5, 2)'));
  604. console.log('');
  605. // 22. 进制转换
  606. console.log('22. 进制转换:');
  607. console.log('0b1010 =', math.evaluate('0b1010'));
  608. console.log('0o777 =', math.evaluate('0o777'));
  609. console.log('0xFF =', math.evaluate('0xFF'));
  610. console.log('');
  611. // 23. 关系运算
  612. console.log('23. 关系运算:');
  613. console.log('2 < 3 =', math.evaluate('2 < 3'));
  614. console.log('2 <= 2 =', math.evaluate('2 <= 2'));
  615. console.log('3 >= 4 =', math.evaluate('3 >= 4'));
  616. console.log('');
  617. // 24. 分数运算
  618. console.log('24. 分数运算:');
  619. console.log("4/8 + 1 -1/3 = ", math.simplify('4/8 + 1 -1/3').toString());
  620. ''';
  621. }
  622. Future<void> _saveAs() async {
  623. try {
  624. final String codeText = _codeController.text;
  625. final Uint8List codeBytes = Uint8List.fromList(utf8.encode(codeText));
  626. String? outputPath = await FilePicker.platform.saveFile(
  627. dialogTitle: '另存为JavaScript文件',
  628. fileName: 'javascript_code.js',
  629. allowedExtensions: ['js'],
  630. bytes: codeBytes,
  631. );
  632. if (outputPath != null) {
  633. if (mounted) {
  634. ScaffoldMessenger.of(context).showSnackBar(
  635. SnackBar(
  636. content: Text('文件已保存'),
  637. backgroundColor: Colors.green,
  638. ),
  639. );
  640. }
  641. }
  642. } catch (e) {
  643. print('保存文件错误: $e');
  644. if (mounted) {
  645. ScaffoldMessenger.of(context).showSnackBar(
  646. SnackBar(
  647. content: Text('保存文件失败: $e'),
  648. backgroundColor: Colors.red,
  649. ),
  650. );
  651. }
  652. }
  653. }
  654. Future<void> _loadFromFile() async {
  655. try {
  656. FilePickerResult? result = await FilePicker.platform.pickFiles(
  657. type: FileType.custom,
  658. allowedExtensions: ['js'],
  659. dialogTitle: '选择JavaScript文件',
  660. );
  661. if (result != null && result.files.isNotEmpty) {
  662. final PlatformFile platformFile = result.files.single;
  663. if (platformFile.path == null && platformFile.bytes == null) {
  664. if (mounted) {
  665. ScaffoldMessenger.of(context).showSnackBar(
  666. SnackBar(
  667. content: Text('无法获取文件内容'),
  668. backgroundColor: Colors.red,
  669. ),
  670. );
  671. }
  672. return;
  673. }
  674. // 读取文件内容
  675. String contents;
  676. if (platformFile.bytes != null) {
  677. // 直接从字节数据读取
  678. contents = utf8.decode(platformFile.bytes!);
  679. } else {
  680. // 从文件路径读取
  681. final file = File(platformFile.path!);
  682. contents = await file.readAsString();
  683. }
  684. setState(() {
  685. _codeController.text = contents;
  686. _output = '文件已加载: ${platformFile.name}';
  687. });
  688. if (mounted) {
  689. ScaffoldMessenger.of(context).showSnackBar(
  690. SnackBar(
  691. content: Text('文件加载成功: ${platformFile.name}'),
  692. backgroundColor: Colors.green,
  693. ),
  694. );
  695. }
  696. }
  697. } catch (e) {
  698. print('加载文件错误: $e');
  699. if (mounted) {
  700. ScaffoldMessenger.of(context).showSnackBar(
  701. SnackBar(
  702. content: Text('加载文件失败: $e'),
  703. backgroundColor: Colors.red,
  704. ),
  705. );
  706. }
  707. }
  708. }
  709. Future<void> _executeCode() async {
  710. if (_isExecuting || !_isInitialized) return;
  711. setState(() {
  712. _isExecuting = true;
  713. _output = '正在初始化JavaScript环境...\\n';
  714. });
  715. try {
  716. await _initializeJsRuntime();
  717. final String code = _codeController.text;
  718. setState(() {
  719. _output += '执行代码中...\\n';
  720. });
  721. _jsRuntime!.evaluate('__clearConsoleOutput();');
  722. final JsEvalResult result = _jsRuntime!.evaluate(code);
  723. final JsEvalResult consoleOutputResult = _jsRuntime!.evaluate('__getConsoleOutput();');
  724. final String consoleOutput = consoleOutputResult.stringResult;
  725. setState(() {
  726. _output = '执行完成!\n';
  727. if (result.isError) {
  728. _output += '❌ 执行错误: ${result.stringResult}\n';
  729. } else {
  730. _output += '✅ 执行成功!\n';
  731. if (result.stringResult != 'null' && result.stringResult.isNotEmpty) {
  732. _output += '📊 返回值: ${result.stringResult}\n';
  733. } else {
  734. _output += '\n';
  735. }
  736. }
  737. if (consoleOutput.isNotEmpty) {
  738. _output += '--- 控制台输出 ---\n$consoleOutput';
  739. } else {
  740. _output += 'ℹ️ 没有控制台输出';
  741. }
  742. });
  743. } catch (e) {
  744. setState(() {
  745. _output = '执行出错: $e';
  746. });
  747. } finally {
  748. setState(() {
  749. _isExecuting = false;
  750. });
  751. }
  752. }
  753. void _clearOutput() {
  754. setState(() {
  755. _output = '';
  756. });
  757. }
  758. void _resetCode() {
  759. setState(() {
  760. _codeController.text = _getExampleCode();
  761. _output = '';
  762. });
  763. }
  764. void _injectMathJs() async {
  765. try {
  766. await _initializeJsRuntime();
  767. setState(() {
  768. _output = 'math.js重新加载成功! JavaScript环境已重置。';
  769. });
  770. } catch (e) {
  771. setState(() {
  772. _output = '重新加载math.js失败: $e';
  773. });
  774. }
  775. }
  776. }