starfish-form-custom 1.0.43 → 1.0.45

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.
@@ -12,27 +12,28 @@
12
12
  </el-tooltip>
13
13
  </div>
14
14
  <div class="control" :style="{marginLeft: labelalign != 'top'?labelWidth + 'px': ''}">
15
- <div class="rich-text-editor">
16
- <!-- 使用 MenuBar 组件 -->
17
- <MenuBar v-if="editor && !readonly" :editor="editor" />
18
- <div ref="editorContainer" class="editor-content"></div>
15
+ <div class="rich-text-editor" @click.stop>
16
+ <QuillEditor
17
+ ref="quillEditorRef"
18
+ theme="snow"
19
+ :content="content"
20
+ @update:content="handleContentChange"
21
+ toolbar="full"
22
+ :read-only="isReadonly"
23
+ class="editor-content"
24
+ />
19
25
  </div>
20
26
  </div>
21
27
  </div>
22
28
  </template>
23
29
 
24
30
  <script lang="ts">
25
- import { defineComponent, onMounted, ref, onUnmounted, watch, nextTick } from "vue";
26
- import { Editor } from '@tiptap/vue-3';
27
- import StarterKit from '@tiptap/starter-kit'
28
- import Code from '@tiptap/extension-code'
29
- import Blockquote from '@tiptap/extension-blockquote'
30
- import HorizontalRule from '@tiptap/extension-horizontal-rule'
31
- import CodeBlock from '@tiptap/extension-code-block'
31
+ import { defineComponent, ref, computed, watch } from "vue";
32
+ import { QuillEditor } from '@vueup/vue-quill'
33
+ import '@vueup/vue-quill/dist/vue-quill.snow.css';
32
34
  import { getFormConfig } from "../../utils/fieldConfig";
33
35
  import fieldProps from "../../utils/fieldProps";
34
36
  import { useWatch } from "../../utils/customHooks";
35
- import MenuBar from "./MenuBar.vue"; // 导入 MenuBar 组件
36
37
 
37
38
  export default defineComponent({
38
39
  ControlType: "RichText",
@@ -43,225 +44,77 @@ export default defineComponent({
43
44
  ...fieldProps,
44
45
  },
45
46
  components: {
46
- MenuBar
47
+ QuillEditor
47
48
  },
48
49
  setup(props) {
49
- const editorRef = ref<Editor | null>(null);
50
- const editorContainer = ref<HTMLElement | null>(null);
50
+ useWatch(props);
51
+ const quillEditorRef = ref();
52
+ const content = ref(props.data[props.item.data.fieldName] || props.item.data.default || '');
51
53
 
52
- // 1. 不使用 useWatch(避免深度监听循环)
53
- // useWatch(props);
54
-
55
- // 2. 使用标记避免重复触发
56
- let isUpdatingFromExternal = false;
57
-
58
- const initEditor = () => {
59
- if (editorRef.value) {
60
- editorRef.value.destroy();
61
- }
62
-
63
- const initialContent = props.data[props.item.data.fieldName] || props.item.data.defaultValue || '';
64
-
65
- editorRef.value = new Editor({
66
- element: editorContainer.value!,
67
- content: initialContent,
68
- extensions: [
69
- StarterKit.configure({
70
- codeBlock: {
71
- HTMLAttributes: {
72
- class: 'code-block',
73
- },
74
- },
75
- }),
76
- Code.configure({
77
- HTMLAttributes: {
78
- class: 'inline-code',
79
- },
80
- }),
81
- CodeBlock.configure({
82
- HTMLAttributes: {
83
- class: 'code-block',
84
- },
85
- }),
86
- Blockquote.configure({
87
- HTMLAttributes: {
88
- class: 'blockquote',
89
- },
90
- }),
91
- HorizontalRule,
92
- ],
93
- editable: !props.readonly,
94
- editorProps: {
95
- attributes: {
96
- class: 'prose focus:outline-none max-w-none',
97
- style: 'min-height: 200px; border: 1px solid #DCDFE6; padding: 8px 12px; background-color: #fff;'
98
- }
99
- },
100
- onUpdate: ({ editor }) => {
101
- if (isUpdatingFromExternal) return;
102
-
103
- const html = editor.getHTML();
104
- console.log('富文本内容更新:', html.substring(0, 50) + '...');
105
-
106
- // 直接赋值到表单数据
107
- props.data[props.item.data.fieldName] = html;
108
- },
109
- onBlur: ({ editor }) => {
110
- const html = editor.getHTML();
111
- props.data[props.item.data.fieldName] = html;
112
- }
113
- });
114
- };
115
-
116
- // 3. 监听外部数据变化(从表单设置值)
117
- watch(
118
- () => props.data[props.item.data.fieldName],
119
- (newValue) => {
120
- if (!editorRef.value || editorRef.value.isDestroyed) return;
121
-
122
- const currentHtml = editorRef.value.getHTML();
123
- if (currentHtml !== newValue) {
124
- console.log('外部数据变化,更新编辑器');
125
- isUpdatingFromExternal = true;
126
- try {
127
- editorRef.value.commands.setContent(newValue || '', false);
128
- } catch (error) {
129
- console.error('更新编辑器内容失败:', error);
130
- } finally {
131
- // 短暂延迟后重置标记
132
- setTimeout(() => {
133
- isUpdatingFromExternal = false;
134
- }, 10);
135
- }
136
- }
137
- },
138
- { immediate: true }
54
+ // 计算只读状态
55
+ const isReadonly = computed(() =>
56
+ props.readonly || props.item.data.state === 'readonly'
139
57
  );
140
58
 
141
- // 4. 监听只读状态
142
- watch(() => props.readonly, (newVal) => {
143
- if (editorRef.value && !editorRef.value.isDestroyed) {
144
- editorRef.value.setEditable(!newVal);
145
- }
146
- });
147
-
148
- onMounted(() => {
149
- nextTick(() => {
150
- initEditor();
151
- });
152
- });
59
+ // 处理内容变化
60
+ const handleContentChange = (value: string) => {
61
+ content.value = value;
62
+ // 更新父组件数据
63
+ props.data[props.item.data.fieldName] = value;
64
+ };
153
65
 
154
- onUnmounted(() => {
155
- console.log("富文本销毁");
156
- if (editorRef.value) {
157
- editorRef.value.destroy();
158
- editorRef.value = null;
66
+ // 监听外部数据变化
67
+ watch(() => props.data[props.item.data.fieldName], (newValue) => {
68
+ if (newValue !== content.value) {
69
+ content.value = newValue || '';
159
70
  }
160
71
  });
161
72
 
162
73
  return {
163
- editor: editorRef,
164
- editorContainer
74
+ content,
75
+ quillEditorRef,
76
+ handleContentChange,
77
+ isReadonly
165
78
  };
166
79
  },
167
80
  });
168
81
  </script>
169
82
 
170
- <style scoped>
83
+ <style lang="scss" scoped>
171
84
  .rich-text-editor {
172
85
  min-height: 200px;
86
+ position: relative;
87
+ z-index: 1;
173
88
  }
174
89
 
90
+ // 确保编辑器层级
175
91
  :deep(.editor-content) {
176
92
  min-height: 200px;
93
+
94
+ // 确保工具栏正常显示
95
+ .ql-toolbar {
96
+ z-index: 100;
97
+ background: white;
98
+ border: 1px solid #ccc;
99
+ border-top-left-radius: 4px;
100
+ border-top-right-radius: 4px;
101
+ position: relative;
102
+ }
103
+
104
+ .ql-container {
105
+ border: 1px solid #ccc;
106
+ border-top: none;
107
+ border-bottom-left-radius: 4px;
108
+ border-bottom-right-radius: 4px;
109
+ min-height: 200px;
110
+ z-index: 99;
111
+ }
177
112
  }
178
113
 
179
- :deep(.ProseMirror) {
180
- min-height: 180px;
181
- outline: none;
182
- padding: 12px;
183
- }
184
-
185
- :deep(.ProseMirror p.is-editor-empty:first-child::before) {
186
- color: #adb5bd;
187
- content: attr(data-placeholder);
188
- float: left;
189
- height: 0;
190
- pointer-events: none;
191
- }
192
-
193
- /* 行内代码样式 */
194
- :deep(.ProseMirror .inline-code) {
195
- background-color: #f3f4f6;
196
- color: #e53e3e;
197
- padding: 2px 6px;
198
- border-radius: 4px;
199
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
200
- font-size: 0.875em;
201
- border: 1px solid #e5e7eb;
202
- }
203
-
204
- /* 代码块样式 */
205
- :deep(.ProseMirror .code-block) {
206
- background-color: #1f2937;
207
- color: #f9fafb;
208
- padding: 16px;
209
- border-radius: 8px;
210
- margin: 12px 0;
211
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
212
- font-size: 0.875em;
213
- line-height: 1.5;
214
- position: relative;
215
- border: 1px solid #374151;
216
- }
217
-
218
-
219
- /* 引用块样式 */
220
- :deep(.ProseMirror blockquote) {
221
- border-left: 4px solid #3b82f6;
222
- background-color: #f8fafc;
223
- padding: 12px 16px;
224
- margin: 12px 0;
225
- border-radius: 0 8px 8px 0;
226
- font-style: italic;
227
- color: #4b5563;
228
- }
229
-
230
- /* 水平分割线样式 */
231
- :deep(.ProseMirror hr) {
232
- border: none;
233
- border-top: 2px solid #e5e7eb;
234
- margin: 24px 0;
235
- }
236
-
237
- /* 列表样式 */
238
- :deep(.ProseMirror ul),
239
- :deep(.ProseMirror ol) {
240
- padding-left: 24px;
241
- margin: 12px 0;
242
- }
243
- :deep(.ProseMirror ul){
244
- list-style: disc;
245
- }
246
- :deep(.ProseMirror ol){
247
- list-style: decimal;
248
- }
249
- :deep(.ProseMirror li) {
250
- margin: 4px 0;
251
- }
252
-
253
- /* 标题样式 */
254
- :deep(.ProseMirror h1) {
255
- font-size: 1.875em;
256
- font-weight: bold;
257
- margin: 24px 0 16px 0;
258
- color: #111827;
259
- }
260
-
261
- :deep(.ProseMirror h2) {
262
- font-size: 1.5em;
263
- font-weight: bold;
264
- margin: 20px 0 12px 0;
265
- color: #111827;
114
+ // 防止拖拽区域覆盖编辑器
115
+ :deep(.shape) {
116
+ .rich-text-editor {
117
+ pointer-events: auto !important;
118
+ }
266
119
  }
267
120
  </style>
@@ -31,8 +31,8 @@
31
31
  <template #dropdown>
32
32
  <el-dropdown-menu>
33
33
  <el-dropdown-item command="enum">默认枚举</el-dropdown-item>
34
- <el-dropdown-item command="func">自定义函数规则</el-dropdown-item>
35
- <el-dropdown-item command="high">高级模式</el-dropdown-item>
34
+ <!-- <el-dropdown-item command="func">自定义函数规则</el-dropdown-item>
35
+ <el-dropdown-item command="high">高级模式</el-dropdown-item> -->
36
36
  </el-dropdown-menu>
37
37
  </template>
38
38
  </el-dropdown>
@@ -25,7 +25,7 @@ const validatePhone = `(rule, value, callback) => {
25
25
  if (value === "" || value == null) {
26
26
  callback(new Error("请输入"));
27
27
  } else if (!/^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$/.test(value)) {
28
- callback(new Error("请输入正确的值"));
28
+ callback(new Error("请输入正确的电话号码"));
29
29
  }
30
30
  callback();
31
31
  }`;
@@ -35,7 +35,7 @@ const validateIdCard = `(rule, value, callback) => {
35
35
  if (value === "" || value == null) {
36
36
  callback(new Error("请输入"));
37
37
  } else if (!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(value)) {
38
- callback(new Error("请输入正确的"));
38
+ callback(new Error("请输入正确的身份证号"));
39
39
  }
40
40
  callback();
41
41
  }`;
@@ -46,7 +46,7 @@ const validateEmail = `
46
46
  if (value === "" || value == null) {
47
47
  callback(new Error("请输入"));
48
48
  } else if (!/^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(value)) {
49
- callback(new Error("请输入正确的值"));
49
+ callback(new Error("请输入正确的邮箱"));
50
50
  }
51
51
  callback();
52
52
  }`;
@@ -202,7 +202,6 @@ export default defineComponent({
202
202
 
203
203
  const handleControlChange = () => {
204
204
  const allFormLists: any = props.allFormList;
205
- console.log('---handleControlChange--', allFormLists, 'props.allFormList', props.allFormList);
206
205
  allFormLists.forEach((item: any) => {
207
206
  if (item.data.showRule === "{}") {
208
207
  item.show = true;
@@ -1,115 +1,115 @@
1
- .starfish-editor,
2
- .starfish-form,
3
- .starfish-dynamicform {
4
- .el-form-item__content {
5
- margin-left: 0 !important;
6
- margin-bottom: 12px;
7
- display: block;
8
- .el-input.el-input--default.el-input--suffix,
9
- .el-select.el-select--default,
10
- .el-input-number.el-input-number--default,
11
- .el-input.el-input--default.el-date-editor.el-date-editor--date,
12
- .el-textarea.el-input--default {
13
- width: 320px;
14
- }
15
- .el-input__wrapper {
16
- width: 298px;
17
- }
18
- }
19
- .el-form-item {
20
- margin-bottom: 0 !important;
21
- }
22
- .el-form-item__error {
23
- top: 100%;
24
- left: 15px !important;
25
- }
26
- .el-form {
27
- padding-bottom: 64px;
28
- }
29
- .jsoneditor-poweredBy {
30
- display: none;
31
- }
32
- .starfish-formitem {
33
- display: flex;
34
- text-align: left;
35
- position: relative;
36
- margin: 0 12px;
37
- &.starfish-vertical {
38
- display: flex;
39
- align-items: center;
40
- .control {
41
- width: 100%;
42
- .vertical-group {
43
- display: flex;
44
- flex-direction: column;
45
- align-items: flex-start;
46
- }
47
- .input-left {
48
- .el-input__inner {
49
- text-align: left;
50
- }
51
- }
52
- .input-right {
53
- .el-input__inner {
54
- text-align: right;
55
- }
56
- }
57
- .input-center {
58
- .el-input__inner {
59
- text-align: center;
60
- }
61
- }
62
- .el-input.el-input--default.el-input--suffix,
63
- .el-select.el-select--default,
64
- .el-input-number.el-input-number--default,
65
- .el-input.el-input--default.el-date-editor.el-date-editor--date {
66
- width: 320px;
67
- }
68
- .el-input__wrapper {
69
- width: 300px;
70
- }
71
- }
72
- }
73
- &.formCover {
74
- padding-bottom: 12px;
75
- .el-input__wrapper {
76
- width: 298px;
77
- }
78
- .el-textarea.el-input--default {
79
- width: 320px;
80
- }
81
- }
82
- .label {
83
- font-size: 14px;
84
- height: 25px;
85
- line-height: 25px;
86
- padding-right: 12px;
87
- box-sizing: border-box;
88
- white-space: nowrap;
89
- &.label_right {
90
- float: left;
91
- text-align: right;
92
- position: absolute;
93
- }
94
- &.label_left {
95
- float: left;
96
- text-align: left;
97
- position: absolute;
98
- }
99
- }
100
- .item_require {
101
- color: red;
102
- }
103
- }
104
- }
105
-
106
- // #app .fullscreen {
107
- // position: fixed;
108
- // left: 0;
109
- // top: 0;
110
- // height: 100%;
111
- // width: 100%;
112
- // z-index: 1810;
113
- // transform: translate(0, 0);
114
- // -webkit-transform: translate(0, 0);
115
- // }
1
+ .starfish-editor,
2
+ .starfish-form,
3
+ .starfish-dynamicform {
4
+ .el-form-item__content {
5
+ margin-left: 0 !important;
6
+ margin-bottom: 12px;
7
+ display: block;
8
+ .el-input.el-input--default.el-input--suffix,
9
+ .el-select.el-select--default,
10
+ .el-input-number.el-input-number--default,
11
+ .el-input.el-input--default.el-date-editor.el-date-editor--date,
12
+ .el-textarea.el-input--default {
13
+ width: 320px;
14
+ }
15
+ .el-input__wrapper {
16
+ width: 298px;
17
+ }
18
+ }
19
+ .el-form-item {
20
+ margin-bottom: 0 !important;
21
+ }
22
+ .el-form-item__error {
23
+ top: 100%;
24
+ left: 120px !important;
25
+ }
26
+ .el-form {
27
+ padding-bottom: 64px;
28
+ }
29
+ .jsoneditor-poweredBy {
30
+ display: none;
31
+ }
32
+ .starfish-formitem {
33
+ display: flex;
34
+ text-align: left;
35
+ position: relative;
36
+ margin: 0 12px;
37
+ &.starfish-vertical {
38
+ display: flex;
39
+ align-items: center;
40
+ .control {
41
+ width: 100%;
42
+ .vertical-group {
43
+ display: flex;
44
+ flex-direction: column;
45
+ align-items: flex-start;
46
+ }
47
+ .input-left {
48
+ .el-input__inner {
49
+ text-align: left;
50
+ }
51
+ }
52
+ .input-right {
53
+ .el-input__inner {
54
+ text-align: right;
55
+ }
56
+ }
57
+ .input-center {
58
+ .el-input__inner {
59
+ text-align: center;
60
+ }
61
+ }
62
+ .el-input.el-input--default.el-input--suffix,
63
+ .el-select.el-select--default,
64
+ .el-input-number.el-input-number--default,
65
+ .el-input.el-input--default.el-date-editor.el-date-editor--date {
66
+ width: 320px;
67
+ }
68
+ .el-input__wrapper {
69
+ width: 300px;
70
+ }
71
+ }
72
+ }
73
+ &.formCover {
74
+ padding-bottom: 12px;
75
+ .el-input__wrapper {
76
+ width: 298px;
77
+ }
78
+ .el-textarea.el-input--default {
79
+ width: 320px;
80
+ }
81
+ }
82
+ .label {
83
+ font-size: 14px;
84
+ height: 25px;
85
+ line-height: 25px;
86
+ padding-right: 12px;
87
+ box-sizing: border-box;
88
+ white-space: nowrap;
89
+ &.label_right {
90
+ float: left;
91
+ text-align: right;
92
+ position: absolute;
93
+ }
94
+ &.label_left {
95
+ float: left;
96
+ text-align: left;
97
+ position: absolute;
98
+ }
99
+ }
100
+ .item_require {
101
+ color: red;
102
+ }
103
+ }
104
+ }
105
+
106
+ // #app .fullscreen {
107
+ // position: fixed;
108
+ // left: 0;
109
+ // top: 0;
110
+ // height: 100%;
111
+ // width: 100%;
112
+ // z-index: 1810;
113
+ // transform: translate(0, 0);
114
+ // -webkit-transform: translate(0, 0);
115
+ // }