starfish-form-custom 1.0.44 → 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,26 +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
- <EditorContent :editor="editor" class="editor-content" />
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 } from "vue";
26
- import { useEditor, EditorContent } 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
- import MenuBar from "./MenuBar.vue";
36
+ import { useWatch } from "../../utils/customHooks";
35
37
 
36
38
  export default defineComponent({
37
39
  ControlType: "RichText",
@@ -42,95 +44,37 @@ export default defineComponent({
42
44
  ...fieldProps,
43
45
  },
44
46
  components: {
45
- EditorContent,
46
- MenuBar
47
+ QuillEditor
47
48
  },
48
49
  setup(props) {
49
- // 1. 使用内部变量存储编辑器内容,避免频繁更新表单
50
- const internalContent = ref<string>(props.data[props.item.data.fieldName] || props.item.data.defaultValue || '');
50
+ useWatch(props);
51
+ const quillEditorRef = ref();
52
+ const content = ref(props.data[props.item.data.fieldName] || props.item.data.default || '');
51
53
 
52
- // 2. 完全跳过 useWatch,避免触发 change 事件
53
- // useWatch(props);
54
-
55
- const editor = useEditor({
56
- content: internalContent.value,
57
- extensions: [
58
- StarterKit.configure({
59
- codeBlock: {
60
- HTMLAttributes: {
61
- class: 'code-block',
62
- },
63
- },
64
- }),
65
- Code.configure({
66
- HTMLAttributes: {
67
- class: 'inline-code',
68
- },
69
- }),
70
- CodeBlock.configure({
71
- HTMLAttributes: {
72
- class: 'code-block',
73
- },
74
- }),
75
- Blockquote.configure({
76
- HTMLAttributes: {
77
- class: 'blockquote',
78
- },
79
- }),
80
- HorizontalRule,
81
- ],
82
- editable: !props.readonly,
83
- editorProps: {
84
- attributes: {
85
- class: 'prose focus:outline-none max-w-none',
86
- style: 'min-height: 200px; border: 1px solid #DCDFE6; padding: 8px 12px; background-color: #fff;'
87
- }
88
- },
89
- // 3. 只在失焦时更新到表单数据
90
- onBlur: ({ editor: editorInstance }) => {
91
- const html = editorInstance.getHTML();
92
- console.log('富文本失焦,更新表单数据');
93
-
94
- // 更新内部变量
95
- internalContent.value = html;
96
-
97
- // 一次性更新到表单数据
98
- props.data[props.item.data.fieldName] = html;
99
- }
100
- // 4. 移除 onUpdate 回调,避免实时更新
101
- });
102
-
103
- // 5. 监听外部数据变化(表单数据 -> 编辑器)
104
- watch(
105
- () => props.data[props.item.data.fieldName],
106
- (newValue) => {
107
- if (!editor.value || editor.value.isDestroyed) return;
108
-
109
- // 只在外部数据变化时更新编辑器
110
- if (newValue !== internalContent.value) {
111
- console.log('外部数据变化,更新编辑器');
112
- internalContent.value = newValue || '';
113
- editor.value.commands.setContent(newValue || '', false);
114
- }
115
- },
116
- { immediate: true }
54
+ // 计算只读状态
55
+ const isReadonly = computed(() =>
56
+ props.readonly || props.item.data.state === 'readonly'
117
57
  );
118
58
 
119
- // 6. 监听只读状态变化
120
- watch(() => props.readonly, (newVal) => {
121
- if (editor.value) {
122
- editor.value.setEditable(!newVal);
123
- }
124
- });
125
-
126
- onUnmounted(() => {
127
- if (editor.value) {
128
- editor.value.destroy();
59
+ // 处理内容变化
60
+ const handleContentChange = (value: string) => {
61
+ content.value = value;
62
+ // 更新父组件数据
63
+ props.data[props.item.data.fieldName] = value;
64
+ };
65
+
66
+ // 监听外部数据变化
67
+ watch(() => props.data[props.item.data.fieldName], (newValue) => {
68
+ if (newValue !== content.value) {
69
+ content.value = newValue || '';
129
70
  }
130
71
  });
131
-
72
+
132
73
  return {
133
- editor
74
+ content,
75
+ quillEditorRef,
76
+ handleContentChange,
77
+ isReadonly
134
78
  };
135
79
  },
136
80
  });
@@ -139,97 +83,38 @@ export default defineComponent({
139
83
  <style lang="scss" scoped>
140
84
  .rich-text-editor {
141
85
  min-height: 200px;
86
+ position: relative;
87
+ z-index: 1;
142
88
  }
143
89
 
90
+ // 确保编辑器层级
144
91
  :deep(.editor-content) {
145
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
+ }
146
112
  }
147
113
 
148
- :deep(.ProseMirror) {
149
- min-height: 180px;
150
- outline: none;
151
- padding: 12px;
152
- }
153
-
154
- :deep(.ProseMirror p.is-editor-empty:first-child::before) {
155
- color: #adb5bd;
156
- content: attr(data-placeholder);
157
- float: left;
158
- height: 0;
159
- pointer-events: none;
160
- }
161
-
162
- /* 行内代码样式 */
163
- :deep(.ProseMirror .inline-code) {
164
- background-color: #f3f4f6;
165
- color: #e53e3e;
166
- padding: 2px 6px;
167
- border-radius: 4px;
168
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
169
- font-size: 0.875em;
170
- border: 1px solid #e5e7eb;
171
- }
172
-
173
- /* 代码块样式 */
174
- :deep(.ProseMirror .code-block) {
175
- background-color: #1f2937;
176
- color: #f9fafb;
177
- padding: 16px;
178
- border-radius: 8px;
179
- margin: 12px 0;
180
- font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
181
- font-size: 0.875em;
182
- line-height: 1.5;
183
- position: relative;
184
- border: 1px solid #374151;
185
- }
186
-
187
- /* 引用块样式 */
188
- :deep(.ProseMirror blockquote) {
189
- border-left: 4px solid #3b82f6;
190
- background-color: #f8fafc;
191
- padding: 12px 16px;
192
- margin: 12px 0;
193
- border-radius: 0 8px 8px 0;
194
- font-style: italic;
195
- color: #4b5563;
196
- }
197
-
198
- /* 水平分割线样式 */
199
- :deep(.ProseMirror hr) {
200
- border: none;
201
- border-top: 2px solid #e5e7eb;
202
- margin: 24px 0;
203
- }
204
-
205
- /* 列表样式 */
206
- :deep(.ProseMirror ul),
207
- :deep(.ProseMirror ol) {
208
- padding-left: 24px;
209
- margin: 12px 0;
210
- }
211
- :deep(.ProseMirror ul){
212
- list-style: disc;
213
- }
214
- :deep(.ProseMirror ol){
215
- list-style: decimal;
216
- }
217
- :deep(.ProseMirror li) {
218
- margin: 4px 0;
219
- }
220
-
221
- /* 标题样式 */
222
- :deep(.ProseMirror h1) {
223
- font-size: 1.875em;
224
- font-weight: bold;
225
- margin: 24px 0 16px 0;
226
- color: #111827;
227
- }
228
-
229
- :deep(.ProseMirror h2) {
230
- font-size: 1.5em;
231
- font-weight: bold;
232
- margin: 20px 0 12px 0;
233
- color: #111827;
114
+ // 防止拖拽区域覆盖编辑器
115
+ :deep(.shape) {
116
+ .rich-text-editor {
117
+ pointer-events: auto !important;
118
+ }
234
119
  }
235
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
+ // }
@@ -1,56 +1,56 @@
1
- .starfish-keyValueItem {
2
- display: flex;
3
- justify-content: space-around;
4
- align-items: center;
5
- font-size: 12px;
6
- border: 1px solid #eee;
7
- margin-bottom: 6px;
8
- border-radius: 10px;
9
- padding: 10px 0;
10
- .keyValueSelect {
11
- width: 30px;
12
- text-align: center;
13
- }
14
- .keyValueInput {
15
- flex: 1;
16
- .inputItem {
17
- line-height: 20px;
18
- }
19
- > div {
20
- margin-bottom: 7px;
21
- span {
22
- display: block;
23
- margin-bottom: 6px;
24
- }
25
- }
26
- }
27
- .keyValueControl {
28
- width: 40px;
29
- display: flex;
30
- flex-direction: column;
31
- justify-content: space-around;
32
- align-items: center;
33
- > div {
34
- width: 18px;
35
- height: 18px;
36
- border-radius: 50%;
37
- background: #fff;
38
- display: flex;
39
- align-items: center;
40
- justify-content: center;
41
- margin: 5px 0;
42
- cursor: pointer;
43
- }
44
- .add {
45
- background: #409eff;
46
- color: white;
47
- }
48
- .remove {
49
- background: #ec4747;
50
- color: white;
51
- }
52
- i {
53
- font-size: 12px;
54
- }
55
- }
56
- }
1
+ .starfish-keyValueItem {
2
+ display: flex;
3
+ justify-content: space-around;
4
+ align-items: center;
5
+ font-size: 12px;
6
+ border: 1px solid #eee;
7
+ margin-bottom: 6px;
8
+ border-radius: 10px;
9
+ padding: 10px 0;
10
+ .keyValueSelect {
11
+ width: 30px;
12
+ text-align: center;
13
+ }
14
+ .keyValueInput {
15
+ flex: 1;
16
+ .inputItem {
17
+ line-height: 20px;
18
+ }
19
+ > div {
20
+ margin-bottom: 7px;
21
+ span {
22
+ display: block;
23
+ margin-bottom: 6px;
24
+ }
25
+ }
26
+ }
27
+ .keyValueControl {
28
+ width: 40px;
29
+ display: flex;
30
+ flex-direction: column;
31
+ justify-content: space-around;
32
+ align-items: center;
33
+ > div {
34
+ width: 18px;
35
+ height: 18px;
36
+ border-radius: 50%;
37
+ background: #fff;
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ margin: 5px 0;
42
+ cursor: pointer;
43
+ }
44
+ .add {
45
+ background: #409eff;
46
+ color: white;
47
+ }
48
+ .remove {
49
+ background: #ec4747;
50
+ color: white;
51
+ }
52
+ i {
53
+ font-size: 12px;
54
+ }
55
+ }
56
+ }
@@ -7,7 +7,6 @@ function useWatch(props: any) {
7
7
  watch(
8
8
  () => props.data[props.item.data.fieldName],
9
9
  (val, oldVal) => {
10
- console.log("watch", val, oldVal, 'props.item.data', props.item.data);
11
10
  if (props.item.data.action && props.item.data.action.onChange) {
12
11
  window.VApp.$Flex.funcExec(props.item.data.action.onChange, vm.proxy, [val, oldVal, props.data]);
13
12
  }