@skyfox2000/webui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.eslintrc.js +23 -0
  2. package/.prettierrc +11 -0
  3. package/.vscode/settings.json +25 -0
  4. package/README.md +104 -0
  5. package/env.d.ts +11 -0
  6. package/index.html +19 -0
  7. package/lib/AceEditor.d.ts +4 -0
  8. package/lib/BasicLayout.d.ts +4 -0
  9. package/lib/Error403.d.ts +4 -0
  10. package/lib/Error404.d.ts +4 -0
  11. package/lib/ExcelForm.d.ts +4 -0
  12. package/lib/UploadForm.d.ts +4 -0
  13. package/lib/assets/modules/basicLayout-YP_-EySb.js +726 -0
  14. package/lib/assets/modules/error403-Bi0E2twj.js +33 -0
  15. package/lib/assets/modules/error404-BF7vasR_.js +33 -0
  16. package/lib/assets/modules/excelForm-Dzndz-SG.js +109 -0
  17. package/lib/assets/modules/excelForm-WJVQmaDT.js +317 -0
  18. package/lib/assets/modules/index-FzWSvscZ.js +107 -0
  19. package/lib/assets/modules/index-ekkaExvB.js +49 -0
  20. package/lib/assets/modules/uploadForm-BahGnrAq.js +415 -0
  21. package/lib/assets/modules/uploadForm-DEnOjhwc.js +308 -0
  22. package/lib/components/common/button/index.vue.d.ts +42 -0
  23. package/lib/components/common/button/index.vue.d.ts.map +1 -0
  24. package/lib/components/common/icon/appicon.vue.d.ts +12 -0
  25. package/lib/components/common/icon/appicon.vue.d.ts.map +1 -0
  26. package/lib/components/common/icon/fullscreen.vue.d.ts +4 -0
  27. package/lib/components/common/icon/fullscreen.vue.d.ts.map +1 -0
  28. package/lib/components/common/icon/helper.vue.d.ts +23 -0
  29. package/lib/components/common/icon/helper.vue.d.ts.map +1 -0
  30. package/lib/components/common/icon/index.vue.d.ts +244 -0
  31. package/lib/components/common/icon/index.vue.d.ts.map +1 -0
  32. package/lib/components/common/icon/layoutIcon.vue.d.ts +44 -0
  33. package/lib/components/common/icon/layoutIcon.vue.d.ts.map +1 -0
  34. package/lib/components/common/icon/projectIcon.vue.d.ts +60 -0
  35. package/lib/components/common/icon/projectIcon.vue.d.ts.map +1 -0
  36. package/lib/components/common/icon/toolIcon.vue.d.ts +44 -0
  37. package/lib/components/common/icon/toolIcon.vue.d.ts.map +1 -0
  38. package/lib/components/common/index.d.ts +19 -0
  39. package/lib/components/common/index.d.ts.map +1 -0
  40. package/lib/components/common/tooltip/index.vue.d.ts +22 -0
  41. package/lib/components/common/tooltip/index.vue.d.ts.map +1 -0
  42. package/lib/components/content/dialog/excelForm.vue.d.ts +31 -0
  43. package/lib/components/content/dialog/excelForm.vue.d.ts.map +1 -0
  44. package/lib/components/content/dialog/index.vue.d.ts +35 -0
  45. package/lib/components/content/dialog/index.vue.d.ts.map +1 -0
  46. package/lib/components/content/dialog/uploadForm.vue.d.ts +25 -0
  47. package/lib/components/content/dialog/uploadForm.vue.d.ts.map +1 -0
  48. package/lib/components/content/drawer/index.vue.d.ts +27 -0
  49. package/lib/components/content/drawer/index.vue.d.ts.map +1 -0
  50. package/lib/components/content/form/formItem.vue.d.ts +26 -0
  51. package/lib/components/content/form/formItem.vue.d.ts.map +1 -0
  52. package/lib/components/content/form/index.vue.d.ts +26 -0
  53. package/lib/components/content/form/index.vue.d.ts.map +1 -0
  54. package/lib/components/content/index.d.ts +27 -0
  55. package/lib/components/content/index.d.ts.map +1 -0
  56. package/lib/components/content/search/index.vue.d.ts +30 -0
  57. package/lib/components/content/search/index.vue.d.ts.map +1 -0
  58. package/lib/components/content/search/searchItem.vue.d.ts +24 -0
  59. package/lib/components/content/search/searchItem.vue.d.ts.map +1 -0
  60. package/lib/components/content/table/index.vue.d.ts +37 -0
  61. package/lib/components/content/table/index.vue.d.ts.map +1 -0
  62. package/lib/components/content/table/tableOperate.vue.d.ts +19 -0
  63. package/lib/components/content/table/tableOperate.vue.d.ts.map +1 -0
  64. package/lib/components/content/toolbar/icontool.vue.d.ts +8 -0
  65. package/lib/components/content/toolbar/icontool.vue.d.ts.map +1 -0
  66. package/lib/components/content/toolbar/index.vue.d.ts +19 -0
  67. package/lib/components/content/toolbar/index.vue.d.ts.map +1 -0
  68. package/lib/components/content/tree/index.vue.d.ts +47 -0
  69. package/lib/components/content/tree/index.vue.d.ts.map +1 -0
  70. package/lib/components/error/error403.vue.d.ts +4 -0
  71. package/lib/components/error/error403.vue.d.ts.map +1 -0
  72. package/lib/components/error/error404.vue.d.ts +4 -0
  73. package/lib/components/error/error404.vue.d.ts.map +1 -0
  74. package/lib/components/form/aceEditor/aceConfig.d.ts +9 -0
  75. package/lib/components/form/aceEditor/aceConfig.d.ts.map +1 -0
  76. package/lib/components/form/aceEditor/index.vue.d.ts +13 -0
  77. package/lib/components/form/aceEditor/index.vue.d.ts.map +1 -0
  78. package/lib/components/form/autoComplete/index.vue.d.ts +140 -0
  79. package/lib/components/form/autoComplete/index.vue.d.ts.map +1 -0
  80. package/lib/components/form/cascader/index.vue.d.ts +110 -0
  81. package/lib/components/form/cascader/index.vue.d.ts.map +1 -0
  82. package/lib/components/form/checkbox/index.vue.d.ts +129 -0
  83. package/lib/components/form/checkbox/index.vue.d.ts.map +1 -0
  84. package/lib/components/form/datePicker/index.vue.d.ts +7 -0
  85. package/lib/components/form/datePicker/index.vue.d.ts.map +1 -0
  86. package/lib/components/form/index.d.ts +41 -0
  87. package/lib/components/form/index.d.ts.map +1 -0
  88. package/lib/components/form/input/index.vue.d.ts +27 -0
  89. package/lib/components/form/input/index.vue.d.ts.map +1 -0
  90. package/lib/components/form/input/inputIcon.vue.d.ts +11 -0
  91. package/lib/components/form/input/inputIcon.vue.d.ts.map +1 -0
  92. package/lib/components/form/input/inputNumber.vue.d.ts +4 -0
  93. package/lib/components/form/input/inputNumber.vue.d.ts.map +1 -0
  94. package/lib/components/form/input/inputPassword.vue.d.ts +4 -0
  95. package/lib/components/form/input/inputPassword.vue.d.ts.map +1 -0
  96. package/lib/components/form/propEditor/index.vue.d.ts +13 -0
  97. package/lib/components/form/propEditor/index.vue.d.ts.map +1 -0
  98. package/lib/components/form/radio/index.vue.d.ts +134 -0
  99. package/lib/components/form/radio/index.vue.d.ts.map +1 -0
  100. package/lib/components/form/radio/radioStatus.vue.d.ts +32 -0
  101. package/lib/components/form/radio/radioStatus.vue.d.ts.map +1 -0
  102. package/lib/components/form/rangePicker/index.vue.d.ts +17 -0
  103. package/lib/components/form/rangePicker/index.vue.d.ts.map +1 -0
  104. package/lib/components/form/select/index.vue.d.ts +143 -0
  105. package/lib/components/form/select/index.vue.d.ts.map +1 -0
  106. package/lib/components/form/switch/index.vue.d.ts +44 -0
  107. package/lib/components/form/switch/index.vue.d.ts.map +1 -0
  108. package/lib/components/form/textarea/index.vue.d.ts +4 -0
  109. package/lib/components/form/textarea/index.vue.d.ts.map +1 -0
  110. package/lib/components/form/transfer/index.vue.d.ts +39 -0
  111. package/lib/components/form/transfer/index.vue.d.ts.map +1 -0
  112. package/lib/components/form/transfer/transferTable.vue.d.ts +39 -0
  113. package/lib/components/form/transfer/transferTable.vue.d.ts.map +1 -0
  114. package/lib/components/form/treeSelect/index.vue.d.ts +39 -0
  115. package/lib/components/form/treeSelect/index.vue.d.ts.map +1 -0
  116. package/lib/components/form/upload/uploadList.vue.d.ts +477 -0
  117. package/lib/components/form/upload/uploadList.vue.d.ts.map +1 -0
  118. package/lib/components/index.d.ts +9 -0
  119. package/lib/components/index.d.ts.map +1 -0
  120. package/lib/components/layout/breadcrumb/index.vue.d.ts +4 -0
  121. package/lib/components/layout/breadcrumb/index.vue.d.ts.map +1 -0
  122. package/lib/components/layout/content/index.vue.d.ts +23 -0
  123. package/lib/components/layout/content/index.vue.d.ts.map +1 -0
  124. package/lib/components/layout/datetime/index.vue.d.ts +4 -0
  125. package/lib/components/layout/datetime/index.vue.d.ts.map +1 -0
  126. package/lib/components/layout/header/headerExits.vue.d.ts +4 -0
  127. package/lib/components/layout/header/headerExits.vue.d.ts.map +1 -0
  128. package/lib/components/layout/header/index.vue.d.ts +4 -0
  129. package/lib/components/layout/header/index.vue.d.ts.map +1 -0
  130. package/lib/components/layout/index.d.ts +17 -0
  131. package/lib/components/layout/index.d.ts.map +1 -0
  132. package/lib/components/layout/menu/index.vue.d.ts +7 -0
  133. package/lib/components/layout/menu/index.vue.d.ts.map +1 -0
  134. package/lib/components/layout/menu/menuTabs.vue.d.ts +4 -0
  135. package/lib/components/layout/menu/menuTabs.vue.d.ts.map +1 -0
  136. package/lib/components/layout/page/basicLayout.vue.d.ts +7 -0
  137. package/lib/components/layout/page/basicLayout.vue.d.ts.map +1 -0
  138. package/lib/es/AceEditor/index.js +168 -0
  139. package/lib/es/BasicLayout/index.js +4 -0
  140. package/lib/es/Error403/index.js +4 -0
  141. package/lib/es/Error404/index.js +4 -0
  142. package/lib/es/ExcelForm/index.js +5 -0
  143. package/lib/es/UploadForm/index.js +5 -0
  144. package/lib/index.d.ts +2 -0
  145. package/lib/webui.css +1 -0
  146. package/lib/webui.es.js +3349 -0
  147. package/package.json +66 -0
  148. package/plugins/vite-plugin-auto-generate-vue.ts +105 -0
  149. package/postcss.config.ts +6 -0
  150. package/src/assets/global.css +9 -0
  151. package/src/components/common/button/index.vue +126 -0
  152. package/src/components/common/icon/appicon.vue +28 -0
  153. package/src/components/common/icon/fullscreen.vue +13 -0
  154. package/src/components/common/icon/helper.vue +30 -0
  155. package/src/components/common/icon/index.vue +426 -0
  156. package/src/components/common/icon/layoutIcon.vue +33 -0
  157. package/src/components/common/icon/projectIcon.vue +41 -0
  158. package/src/components/common/icon/toolIcon.vue +33 -0
  159. package/src/components/common/index.ts +19 -0
  160. package/src/components/common/tooltip/index.vue +25 -0
  161. package/src/components/content/dialog/excelForm.vue +479 -0
  162. package/src/components/content/dialog/index.vue +149 -0
  163. package/src/components/content/dialog/uploadForm.vue +228 -0
  164. package/src/components/content/drawer/index.vue +93 -0
  165. package/src/components/content/form/formItem.vue +76 -0
  166. package/src/components/content/form/index.vue +48 -0
  167. package/src/components/content/index.ts +32 -0
  168. package/src/components/content/search/index.vue +135 -0
  169. package/src/components/content/search/searchItem.vue +52 -0
  170. package/src/components/content/table/index.vue +215 -0
  171. package/src/components/content/table/tableOperate.vue +131 -0
  172. package/src/components/content/toolbar/icontool.vue +151 -0
  173. package/src/components/content/toolbar/index.vue +107 -0
  174. package/src/components/content/tree/index.vue +140 -0
  175. package/src/components/error/error403.vue +14 -0
  176. package/src/components/error/error404.vue +14 -0
  177. package/src/components/form/aceEditor/aceConfig.ts +90 -0
  178. package/src/components/form/aceEditor/index.vue +175 -0
  179. package/src/components/form/autoComplete/index.vue +171 -0
  180. package/src/components/form/cascader/index.vue +110 -0
  181. package/src/components/form/checkbox/index.vue +108 -0
  182. package/src/components/form/datePicker/index.vue +29 -0
  183. package/src/components/form/index.ts +54 -0
  184. package/src/components/form/input/index.vue +70 -0
  185. package/src/components/form/input/inputIcon.vue +39 -0
  186. package/src/components/form/input/inputNumber.vue +23 -0
  187. package/src/components/form/input/inputPassword.vue +22 -0
  188. package/src/components/form/propEditor/index.vue +81 -0
  189. package/src/components/form/radio/index.vue +132 -0
  190. package/src/components/form/radio/radioStatus.vue +42 -0
  191. package/src/components/form/rangePicker/index.vue +64 -0
  192. package/src/components/form/select/index.vue +186 -0
  193. package/src/components/form/switch/index.vue +58 -0
  194. package/src/components/form/textarea/index.vue +23 -0
  195. package/src/components/form/transfer/index.vue +95 -0
  196. package/src/components/form/transfer/transferTable.vue +124 -0
  197. package/src/components/form/treeSelect/index.vue +108 -0
  198. package/src/components/form/upload/uploadList.vue +235 -0
  199. package/src/components/index.ts +97 -0
  200. package/src/components/layout/breadcrumb/index.vue +38 -0
  201. package/src/components/layout/content/index.vue +28 -0
  202. package/src/components/layout/datetime/index.vue +16 -0
  203. package/src/components/layout/header/headerExits.vue +28 -0
  204. package/src/components/layout/header/index.vue +43 -0
  205. package/src/components/layout/index.ts +16 -0
  206. package/src/components/layout/menu/index.vue +64 -0
  207. package/src/components/layout/menu/menuTabs.vue +56 -0
  208. package/src/components/layout/page/basicLayout.vue +67 -0
  209. package/src/vite-env.d.ts +8 -0
  210. package/tailwind.config.ts +11 -0
  211. package/tsconfig.json +53 -0
  212. package/vite.config.ts +117 -0
  213. package//344/273/243/347/240/201/350/247/204/350/214/203/345/217/212/351/243/216/346/240/274/346/214/207/345/215/227.md +116 -0
@@ -0,0 +1,140 @@
1
+ <script lang="ts" setup>
2
+ import { onMounted, ref, useAttrs, watch } from 'vue';
3
+ import { Tree } from 'ant-design-vue';
4
+ import { ToolIcon } from '../../common';
5
+ import { TreeControl, TreeNode, queryTree } from '@skyfox2000/webbase';
6
+ import { TreeDataNode } from 'ant-design-vue/es/vc-tree-select/interface';
7
+ import { EventDataNode } from 'ant-design-vue/es/vc-tree/interface';
8
+ import { fieldMapping } from '@skyfox2000/fapi';
9
+
10
+ /**
11
+ * 组件属性定义
12
+ */
13
+ const props = defineProps<{
14
+ /**
15
+ * 页面主数据
16
+ */
17
+ treeCtrl: TreeControl;
18
+ /**
19
+ * 选中的节点
20
+ */
21
+ selectedKeys?: (string | number)[];
22
+ /**
23
+ * 展开的节点
24
+ */
25
+ expandedKeys?: (string | number)[];
26
+ }>();
27
+
28
+ const attrs = useAttrs();
29
+
30
+ // 树数据
31
+ const treeCtrl = props.treeCtrl;
32
+ // 树节点数据
33
+ const treeData = ref<TreeNode[]>([]);
34
+
35
+ // 监听树数据变化
36
+ watch(
37
+ () => treeCtrl.data.value,
38
+ (newVal) => {
39
+ if (newVal) {
40
+ treeData.value = treeCtrl.fieldMap ? fieldMapping(treeCtrl.fieldMap, newVal) : newVal;
41
+ handleSelect(props.selectedKeys ?? []);
42
+ }
43
+ },
44
+ );
45
+
46
+ const emit = defineEmits<{
47
+ (
48
+ e: 'select',
49
+ selectedKeys: (string | number)[],
50
+ info?: {
51
+ event: 'select';
52
+ node: EventDataNode;
53
+ selected: boolean;
54
+ selectedNodes: TreeDataNode[];
55
+ },
56
+ ): void;
57
+ (e: 'update:selectedKeys', value: (string | number)[]): void;
58
+ }>();
59
+
60
+ const selectedKeys = ref<(string | number)[]>([]);
61
+ watch(
62
+ () => props.selectedKeys,
63
+ (newVal) => {
64
+ if (newVal) {
65
+ selectedKeys.value = newVal;
66
+ }
67
+ },
68
+ { immediate: true },
69
+ );
70
+ const expandedKeys = ref<(string | number)[]>(['-']);
71
+ watch(
72
+ () => props.expandedKeys,
73
+ (newVal) => {
74
+ if (newVal) {
75
+ expandedKeys.value = newVal;
76
+ }
77
+ },
78
+ { immediate: true },
79
+ );
80
+
81
+ // 值变化事件
82
+ const handleSelect = (
83
+ keys: (string | number)[],
84
+ info?: {
85
+ event: 'select';
86
+ node: EventDataNode;
87
+ selected: boolean;
88
+ selectedNodes: TreeDataNode[];
89
+ },
90
+ ) => {
91
+ if (keys.length === 0) {
92
+ keys.push(...selectedKeys.value);
93
+ }
94
+ if (keys.length > 0) {
95
+ treeCtrl.node.value = info?.node.dataRef as unknown as TreeNode;
96
+ }
97
+ selectedKeys.value = keys;
98
+ emit('update:selectedKeys', keys);
99
+ emit('select', keys, info);
100
+ };
101
+
102
+ // 组件挂载后自动加载树数据
103
+ onMounted(() => {
104
+ if (!treeCtrl.fieldMap) {
105
+ treeCtrl.fieldMap = {
106
+ label: 'Name',
107
+ key: 'Id',
108
+ value: 'Id',
109
+ icon: 'Icon',
110
+ };
111
+ }
112
+ if (treeCtrl.data.value) {
113
+ treeData.value = fieldMapping(treeCtrl.fieldMap, treeCtrl.data.value);
114
+ } else if (treeCtrl.autoload) {
115
+ queryTree(treeCtrl);
116
+ }
117
+ });
118
+ </script>
119
+
120
+ <template>
121
+ <Tree
122
+ class="w-full"
123
+ :show-line="true"
124
+ :tree-data="treeData"
125
+ v-model:expanded-keys="expandedKeys"
126
+ :selected-keys="selectedKeys"
127
+ :loading="treeCtrl.isTreeLoading"
128
+ @select="handleSelect"
129
+ v-bind="attrs"
130
+ >
131
+ <template #title="{ key, label, icon }">
132
+ <slot name="title" :label="label" :key="key" :icon="icon">
133
+ <div class="flex items-center gap-1">
134
+ <ToolIcon :icon="icon" />
135
+ <span class="text-nowrap">{{ label }}</span>
136
+ </div>
137
+ </slot>
138
+ </template>
139
+ </Tree>
140
+ </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ import { Result, Button } from 'ant-design-vue';
3
+ import { AppRouter } from '@skyfox2000/webbase';
4
+ const onClicked = () => {
5
+ AppRouter.back();
6
+ };
7
+ </script>
8
+ <template>
9
+ <Result status="403" title="403" sub-title="您没有权限访问当前页面!">
10
+ <template #extra>
11
+ <Button type="primary" @click="onClicked">点击返回</Button>
12
+ </template>
13
+ </Result>
14
+ </template>
@@ -0,0 +1,14 @@
1
+ <script setup lang="ts">
2
+ import { Result, Button } from 'ant-design-vue';
3
+ import { AppRouter } from '@skyfox2000/webbase';
4
+ const onClicked = () => {
5
+ AppRouter.back();
6
+ };
7
+ </script>
8
+ <template>
9
+ <Result status="404" title="404" sub-title="页面不存在或者没有权限访问页面!">
10
+ <template #extra>
11
+ <Button type="primary" @click="onClicked">点击返回</Button>
12
+ </template>
13
+ </Result>
14
+ </template>
@@ -0,0 +1,90 @@
1
+ // aceConfig.js
2
+ // 移除直接导入ace-builds,改为在需要时动态加载
3
+ // import ace from 'ace-builds';
4
+ // import 'ace-builds/src-min-noconflict/ext-language_tools.js';
5
+ // 假设我们会有一个全局的ace变量
6
+ declare global {
7
+ interface Window {
8
+ ace: any;
9
+ }
10
+ }
11
+
12
+ // 确保ace编辑器已经加载
13
+ export const ensureAceLoaded = async (baseUrl: string): Promise<any> => {
14
+ if (window.ace) {
15
+ return window.ace;
16
+ }
17
+
18
+ // 动态加载ace主脚本
19
+ return new Promise((resolve, reject) => {
20
+ const script = document.createElement('script');
21
+ script.src = `${baseUrl}ace/ace.js`;
22
+ script.async = true;
23
+ script.onload = () => {
24
+ // 设置ace基础路径
25
+ window.ace.config.set('basePath', `${baseUrl}ace/`);
26
+
27
+ // 加载语言工具扩展
28
+ const extScript = document.createElement('script');
29
+ extScript.src = `${baseUrl}ace/ext-language_tools.js`;
30
+ extScript.async = true;
31
+ extScript.onload = () => {
32
+ resolve(window.ace);
33
+ };
34
+ extScript.onerror = () => {
35
+ reject(new Error('Failed to load language tools extension'));
36
+ };
37
+ document.head.appendChild(extScript);
38
+ };
39
+ script.onerror = () => {
40
+ reject(new Error('Failed to load Ace editor'));
41
+ };
42
+ document.head.appendChild(script);
43
+ });
44
+ };
45
+
46
+ export const loadWorker = async (baseUrl: string, lang: string) => {
47
+ const ace = await ensureAceLoaded(baseUrl);
48
+
49
+ // 动态生成模式和工人文件的路径
50
+ const modePath = `${baseUrl}ace/mode-${lang}.js`;
51
+ const workerPath = `${baseUrl}ace/worker-${lang}.js`;
52
+
53
+ // 加载语言模式
54
+ if (!document.querySelector(`script[src="${modePath}"]`)) {
55
+ const modeScript = document.createElement('script');
56
+ modeScript.src = modePath;
57
+ modeScript.async = true;
58
+ document.head.appendChild(modeScript);
59
+ // 等待脚本加载完成
60
+ await new Promise((resolve) => {
61
+ modeScript.onload = resolve;
62
+ modeScript.onerror = resolve; // 即使出错也继续
63
+ });
64
+ }
65
+
66
+ ace.config.setModuleUrl(`ace/mode/${lang}`, modePath);
67
+ ace.config.setModuleUrl(`ace/mode/${lang}_worker`, workerPath);
68
+ };
69
+
70
+ export const loadTheme = async (baseUrl: string, theme: string) => {
71
+ const ace = await ensureAceLoaded(baseUrl);
72
+
73
+ // 动态生成主题文件的路径
74
+ const themePath = `${baseUrl}ace/theme-${theme}.js`;
75
+
76
+ // 加载主题
77
+ if (!document.querySelector(`script[src="${themePath}"]`)) {
78
+ const themeScript = document.createElement('script');
79
+ themeScript.src = themePath;
80
+ themeScript.async = true;
81
+ document.head.appendChild(themeScript);
82
+ // 等待脚本加载完成
83
+ await new Promise((resolve) => {
84
+ themeScript.onload = resolve;
85
+ themeScript.onerror = resolve; // 即使出错也继续
86
+ });
87
+ }
88
+
89
+ ace.config.setModuleUrl(`ace/theme/${theme}`, themePath);
90
+ };
@@ -0,0 +1,175 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, ref, watch, defineAsyncComponent } from 'vue';
3
+ import { ToolIcon, Dialog } from '../../index';
4
+ import { loadTheme, loadWorker, ensureAceLoaded } from './aceConfig';
5
+
6
+ const props = defineProps<{
7
+ value?: any;
8
+ baseUrl: string;
9
+ lang?: 'json' | 'javascript' | 'yaml';
10
+ }>();
11
+
12
+ // 获取当前应用的基础路径
13
+ const baseUrl = props.baseUrl.endsWith('/') ? props.baseUrl : props.baseUrl + '/';
14
+
15
+
16
+ // 动态导入VAceEditor组件
17
+ const VAceEditor = defineAsyncComponent(() => {
18
+ // 确保Ace已加载
19
+ return ensureAceLoaded(baseUrl).then(() => {
20
+ return import('vue3-ace-editor').then((mod) => {
21
+ return mod.VAceEditor;
22
+ });
23
+ });
24
+ });
25
+
26
+ /**
27
+ * 使用的开发语言
28
+ */
29
+ const language = props.lang ?? 'json';
30
+ /**
31
+ * 样式主题
32
+ */
33
+ const theme = 'github';
34
+
35
+ /**
36
+ * 编辑器内容
37
+ */
38
+ const content = ref(''); // 初始代码内容
39
+ const height = props.value ? 'h-[240px]' : 'h-[100px]';
40
+ type AceOptions = Partial<{
41
+ useWorker: boolean;
42
+ enableBasicAutocompletion: boolean;
43
+ enableLiveAutocompletion: boolean;
44
+ enableSnippets: boolean;
45
+ showPrintMargin: boolean;
46
+ highlightActiveLine: boolean;
47
+ highlightSelectedWord: boolean;
48
+ tabSize: number;
49
+ fontSize: number;
50
+ wrap: boolean;
51
+ readOnly: boolean;
52
+ }>;
53
+
54
+ const options = {
55
+ useWorker: true,
56
+ showPrintMargin: false,
57
+ highlightActiveLine: true,
58
+ highlightSelectedWord: true,
59
+ tabSize: 3,
60
+ fontSize: 10,
61
+ wrap: true,
62
+ readOnly: false,
63
+ };
64
+
65
+ const aceOptions = ref<AceOptions>({
66
+ ...options
67
+ });
68
+
69
+ const emit = defineEmits(['update:value']);
70
+
71
+ // 用于格式化JSON字符串以便展示
72
+ const handleBlur = () => {
73
+ if (language === 'json') {
74
+ const res = format();
75
+ if (res) emit('update:value', JSON.parse(content.value));
76
+ } else {
77
+ emit('update:value', content.value);
78
+ }
79
+ };
80
+
81
+ // 格式化 JSON 内容
82
+ const format = (): boolean => {
83
+ if (content.value) {
84
+ const value = content.value;
85
+ let formattedValue = value;
86
+
87
+ // 正则表达式匹配属性名(可能没有引号或使用单引号)
88
+ const regex = /(\w+)(?=\s*:)/g;
89
+
90
+ // 使用 replace 方法将属性名替换为双引号包围的格式
91
+ formattedValue = formattedValue.replace(regex, `"$1"`);
92
+
93
+ // 正则表达式匹配单引号包围的字符串值
94
+ const singleQuoteRegex = /'([^']*)'/g;
95
+
96
+ // 使用 replace 方法将单引号包围的字符串值替换为双引号包围的格式
97
+ formattedValue = formattedValue.replace(singleQuoteRegex, `"$1"`);
98
+
99
+ try {
100
+ // 重新解析 JSON 字符串,并格式化
101
+ const parsed = JSON.parse(formattedValue);
102
+ const formattedJSON = JSON.stringify(parsed, null, 3);
103
+ content.value = formattedJSON;
104
+ return true;
105
+ } catch (_) { }
106
+ }
107
+ return false;
108
+ };
109
+
110
+ const updateContent = () => {
111
+ if (props.value && typeof props.value === 'object') {
112
+ content.value = JSON.stringify(props.value, null, 3);
113
+ } else content.value = props.value ?? '';
114
+ };
115
+
116
+ // 监听 props.value 的变化
117
+ watch(
118
+ () => props.value,
119
+ (newValue, oldValue) => {
120
+ // 当 props.value 发生变化时,更新 content
121
+ if (newValue !== oldValue) {
122
+ updateContent();
123
+ }
124
+ },
125
+ );
126
+
127
+ const isFullScreen = ref(false);
128
+ const editorLoaded = ref(false);
129
+
130
+ const toggleFullScreen = () => {
131
+ isFullScreen.value = !isFullScreen.value;
132
+ };
133
+
134
+ onMounted(async () => {
135
+ try {
136
+ // 先加载Ace核心
137
+ await ensureAceLoaded(baseUrl);
138
+ // 再加载主题和worker
139
+ await Promise.all([loadTheme(baseUrl, theme), loadWorker(baseUrl, language)]);
140
+ editorLoaded.value = true;
141
+ updateContent();
142
+ aceOptions.value = {
143
+ ...options,
144
+ // TODO 会提示拼写错误
145
+ // enableBasicAutocompletion: true,
146
+ // enableLiveAutocompletion: true,
147
+ // enableSnippets: true,
148
+ };
149
+ } catch (error) {
150
+ console.error('Failed to load Ace editor:', error);
151
+ }
152
+ });
153
+ </script>
154
+ <template>
155
+ <div class="w-full overflow-hidden rounded-md border-1 border-solid border-gray-300">
156
+ <div class="w-full h-[30px] bg-[#ccc] relative flex items-center justify-between px-2">
157
+ <div>{{ language.toUpperCase() }}</div>
158
+ <ToolIcon icon="icon-fullscreen" clickable class="w-4 h-4 text-[12px]" @click="toggleFullScreen" />
159
+ </div>
160
+ <div class="flex-1 overflow-hidden">
161
+ <VAceEditor v-if="editorLoaded" :class="[height]" v-model:value="content" :lang="language" :theme="theme"
162
+ :options="aceOptions" @blur="handleBlur" @keyup.enter.native.stop @keydown.enter.native.stop />
163
+ <div v-else :class="[height, 'flex justify-center items-center']">加载编辑器中...</div>
164
+ </div>
165
+ </div>
166
+ <Dialog title="代码编辑器" v-model:open="isFullScreen" :width="680" :footer="null">
167
+ <VAceEditor v-if="isFullScreen && editorLoaded" class="h-[500px] w-[99.3%] border-1 border-solid border-gray-200"
168
+ v-model:value="content" :lang="language" :theme="theme" :options="aceOptions" @blur="handleBlur"
169
+ @keyup.enter.native.stop @keydown.enter.native.stop />
170
+ <div v-else-if="isFullScreen"
171
+ class="h-[500px] w-[99.3%] flex justify-center items-center border-1 border-solid border-gray-200">
172
+ 加载编辑器中...
173
+ </div>
174
+ </Dialog>
175
+ </template>
@@ -0,0 +1,171 @@
1
+ <script setup lang="ts">
2
+ import { ref, onMounted, onUnmounted, useAttrs, watch } from 'vue';
3
+ import { AutoComplete } from 'ant-design-vue';
4
+ import {
5
+ useInputFactory,
6
+ OptionCommProps,
7
+ OptionItemProps,
8
+ onOptionChanged,
9
+ loadOption,
10
+ unloadOption,
11
+ SelectValue,
12
+ getSelectedLabels,
13
+ formValidate,
14
+ outFormDataFields,
15
+ } from '@skyfox2000/webbase';
16
+ import { ReqParams, IUrlInfo } from '@skyfox2000/fapi';
17
+
18
+ const props = defineProps({
19
+ ...OptionCommProps,
20
+ autoload: {
21
+ type: Boolean,
22
+ default: false,
23
+ },
24
+ value: {
25
+ type: [String, Number, null, Array],
26
+ default: undefined,
27
+ },
28
+ /**
29
+ * 查询字段
30
+ * - 模糊查询
31
+ */
32
+ searchField: {
33
+ type: String,
34
+ },
35
+ /**
36
+ * 自定义查询参数
37
+ * - 查询参数
38
+ * - query参数
39
+ */
40
+ onsearch: {
41
+ type: Function,
42
+ },
43
+ /**
44
+ * 修改输入数据则自动清空关联数据
45
+ */
46
+ autoClean: {
47
+ type: Boolean,
48
+ default: true,
49
+ },
50
+ });
51
+ // 关闭自动继承属性到根元素
52
+ defineOptions({
53
+ inheritAttrs: false,
54
+ });
55
+ const attrs = useAttrs(); // 手动获取 $attrs
56
+
57
+ const url = ref<IUrlInfo>({
58
+ ...props.url,
59
+ url: props.url?.url || '',
60
+ fieldMap: props.fieldMap || props.url?.fieldMap,
61
+ params: props.params || props.url?.params,
62
+ loadingText: false,
63
+ });
64
+
65
+ /// 避免类型错误
66
+ const innerValue = ref<SelectValue | undefined>(undefined);
67
+
68
+ watch(
69
+ () => props.value,
70
+ () => {
71
+ innerValue.value = props.value as unknown as SelectValue;
72
+ },
73
+ { immediate: true },
74
+ );
75
+
76
+ watch(
77
+ () => innerValue.value,
78
+ (newVal) => {
79
+ // 如果配置了 formData 和 outFields,默认清空数据
80
+ if (props.autoClean && props.formData && props.outFields && newVal !== props.value) {
81
+ outFormDataFields(props.formData, props.outFields, null);
82
+ }
83
+ },
84
+ );
85
+
86
+ const inputFactory = useInputFactory();
87
+ const { editorCtrl, errInfo, labelText } = inputFactory;
88
+
89
+ const emit = defineEmits(['update:value', 'select', 'change', 'update:labels']);
90
+ inputFactory.inputEmit = emit;
91
+
92
+ /**
93
+ * 实际的选择项
94
+ */
95
+ const selectOptions = ref<OptionItemProps[]>([]);
96
+
97
+ const onSearch = (value: string) => {
98
+ selectOptions.value = [];
99
+ if (value === '') return;
100
+ let search_value = value.trim();
101
+ let query: ReqParams = {
102
+ ...url.value.params,
103
+ Query: {
104
+ ...url.value.params?.Query,
105
+ },
106
+ };
107
+ if (props.searchField) {
108
+ query.Query![props.searchField] = {
109
+ $like: '%' + search_value + '%',
110
+ };
111
+ }
112
+ if (props.onsearch) {
113
+ props.onsearch(search_value, query);
114
+ }
115
+
116
+ loadOption(true, props, selectOptions, inputFactory, url.value, query);
117
+ };
118
+
119
+ const onSelected = (value: any) => {
120
+ const selectedOptions = onOptionChanged(props, value as SelectValue, selectOptions, inputFactory);
121
+
122
+ /// 选择后才更新数据
123
+ emit('update:value', value);
124
+ emit('select', value);
125
+
126
+ const labels: string[] = getSelectedLabels(selectedOptions);
127
+ emit('update:labels', labels);
128
+
129
+ if (errInfo?.value.errClass && editorCtrl) {
130
+ /// 重新开始验证
131
+ formValidate(editorCtrl);
132
+ }
133
+ };
134
+
135
+ onMounted(() => {
136
+ if (url.value && !url.value.fieldMap) {
137
+ url.value.fieldMap = {
138
+ title: 'Name',
139
+ label: 'Name',
140
+ value: 'Name',
141
+ key: 'Id',
142
+ };
143
+ }
144
+ });
145
+
146
+ onUnmounted(() => {
147
+ unloadOption(props, inputFactory);
148
+ });
149
+ </script>
150
+
151
+ <template>
152
+ <div>
153
+ <AutoComplete
154
+ v-model:value="innerValue"
155
+ :class="[errInfo?.errClass, 'error w-full']"
156
+ :options="selectOptions"
157
+ @search="onSearch"
158
+ @select="onSelected"
159
+ :placeholder="props.url && !props.url.loading ? '请输入并选择' + labelText : ''"
160
+ v-bind="attrs"
161
+ >
162
+ </AutoComplete>
163
+ </div>
164
+ </template>
165
+
166
+ <style scoped>
167
+ .error :deep(.ant-select-selector) {
168
+ border-color: #ff4d4f80;
169
+ box-shadow: 0 0 3px 0 #ff4d4f;
170
+ }
171
+ </style>