starfish-form-custom 1.0.32 → 1.0.33

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.
@@ -3,7 +3,7 @@ import 'element-plus/es/components/base/style/css';
3
3
  import 'element-plus/es/components/form/style/css';
4
4
  import 'element-plus/es/components/form-item/style/css';
5
5
  import { defineComponent, getCurrentInstance, ref, onMounted, openBlock, createElementBlock, createVNode, mergeProps, withCtx, Fragment, renderList, createBlock, resolveDynamicComponent, createCommentVNode, toRaw } from 'vue';
6
- import { _ as _export_sfc } from './main-a797684b.mjs';
6
+ import { _ as _export_sfc } from './main-8dad66de.mjs';
7
7
  import 'element-plus/es/components/icon/style/css';
8
8
  import 'element-plus/es/components/input/style/css';
9
9
  import 'element-plus/es/components/checkbox/style/css';
@@ -27,6 +27,7 @@ import 'element-plus/es/components/checkbox-group/style/css';
27
27
  import 'element-plus/es/components/color-picker/style/css';
28
28
  import 'element-plus/es/components/date-picker/style/css';
29
29
  import 'element-plus/es/components/radio/style/css';
30
+ import 'remixicon/fonts/remixicon.symbol.svg';
30
31
  import 'element-plus/es/components/slider/style/css';
31
32
  import 'element-plus/es/components/switch/style/css';
32
33
  import 'element-plus/es/components/time-select/style/css';
@@ -1,4 +1,4 @@
1
- export { D as Dynamicform, m as default } from './main-a797684b.mjs';
1
+ export { D as Dynamicform, m as default } from './main-8dad66de.mjs';
2
2
  import 'vue';
3
3
  import 'element-plus/es';
4
4
  import 'element-plus/es/components/base/style/css';
@@ -25,6 +25,7 @@ import 'element-plus/es/components/checkbox-group/style/css';
25
25
  import 'element-plus/es/components/color-picker/style/css';
26
26
  import 'element-plus/es/components/date-picker/style/css';
27
27
  import 'element-plus/es/components/radio/style/css';
28
+ import 'remixicon/fonts/remixicon.symbol.svg';
28
29
  import 'element-plus/es/components/slider/style/css';
29
30
  import 'element-plus/es/components/switch/style/css';
30
31
  import 'element-plus/es/components/time-select/style/css';
package/dist/style.css CHANGED
@@ -12,7 +12,50 @@
12
12
  .control > div[data-v-4ce4907e] {
13
13
  display: flex;
14
14
  align-items: center;
15
- }.starfish-editor .el-form-item__content,
15
+ }
16
+ .menu-item[data-v-398f2226] {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ width: 32px;
21
+ height: 32px;
22
+ background: white;
23
+ border: 1px solid #ddd;
24
+ border-radius: 4px;
25
+ cursor: pointer;
26
+ transition: all 0.2s ease;
27
+ }
28
+ .menu-item[data-v-398f2226]:hover {
29
+ background-color: #f0f0f0;
30
+ border-color: #999;
31
+ }
32
+ .menu-item.is-active[data-v-398f2226] {
33
+ background-color: #e0e0e0;
34
+ border-color: #666;
35
+ }
36
+ .remix[data-v-398f2226] {
37
+ width: 16px;
38
+ height: 16px;
39
+ }
40
+
41
+ .editor__header[data-v-45d3fb0a] {
42
+ display: flex;
43
+ align-items: center;
44
+ gap: 4px;
45
+ padding: 8px 12px;
46
+ border: 1px solid #DCDFE6;
47
+ border-bottom: none;
48
+ border-radius: 4px 4px 0 0;
49
+ background-color: #f8f9fa;
50
+ flex-wrap: wrap;
51
+ }
52
+ .divider[data-v-45d3fb0a] {
53
+ width: 1px;
54
+ height: 20px;
55
+ background-color: #DCDFE6;
56
+ margin: 0 4px;
57
+ }
58
+ .starfish-editor .el-form-item__content,
16
59
  .starfish-form .el-form-item__content,
17
60
  .starfish-dynamicform .el-form-item__content {
18
61
  margin-left: 0 !important;
@@ -424,7 +467,25 @@
424
467
  width: 100%;
425
468
  min-height: 60px;
426
469
  height: 100%;
427
- }.cm-gutters.cm-gutters-before {
470
+ }
471
+ .rich-text-editor[data-v-5e5c12c4] {
472
+ min-height: 200px;
473
+ }
474
+ [data-v-5e5c12c4] .editor-content {
475
+ min-height: 200px;
476
+ }
477
+ [data-v-5e5c12c4] .ProseMirror {
478
+ min-height: 180px;
479
+ outline: none;
480
+ }
481
+ [data-v-5e5c12c4] .ProseMirror p.is-editor-empty:first-child::before {
482
+ color: #adb5bd;
483
+ content: attr(data-placeholder);
484
+ float: left;
485
+ height: 0;
486
+ pointer-events: none;
487
+ }
488
+ .cm-gutters.cm-gutters-before {
428
489
  background: #133F63;
429
490
  }.el-collapse-item {
430
491
  border: 1px solid #ebeef5;
@@ -76,8 +76,8 @@ declare const _sfc_main: DefineComponent<{
76
76
  default: boolean;
77
77
  };
78
78
  }>>, {
79
- drag: boolean;
80
79
  data: Record<string, any>;
80
+ drag: boolean;
81
81
  item: Record<string, any>;
82
82
  labelalign: string;
83
83
  isForm: boolean;
@@ -0,0 +1,34 @@
1
+ import type { DefineComponent, ComponentOptionsMixin, VNodeProps, AllowedComponentProps, ComponentCustomProps, ExtractPropTypes } from '@vue/runtime-core';
2
+ import { PropType } from 'vue';
3
+ declare const _sfc_main: DefineComponent<{
4
+ editor: {
5
+ type: PropType<any>;
6
+ required: true;
7
+ };
8
+ }, {
9
+ items: ({
10
+ icon: string;
11
+ title: string;
12
+ action: () => any;
13
+ isActive: () => any;
14
+ type?: undefined;
15
+ } | {
16
+ type: string;
17
+ icon?: undefined;
18
+ title?: undefined;
19
+ action?: undefined;
20
+ isActive?: undefined;
21
+ } | {
22
+ icon: string;
23
+ title: string;
24
+ action: () => any;
25
+ isActive?: undefined;
26
+ type?: undefined;
27
+ })[];
28
+ }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record<string, any>, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
29
+ editor: {
30
+ type: PropType<any>;
31
+ required: true;
32
+ };
33
+ }>>, {}>;
34
+ export default _sfc_main;
@@ -0,0 +1,41 @@
1
+ import type { DefineComponent, ComponentOptionsMixin, VNodeProps, AllowedComponentProps, ComponentCustomProps, ExtractPropTypes } from '@vue/runtime-core';
2
+ declare const _sfc_main: DefineComponent<{
3
+ icon: {
4
+ type: StringConstructor;
5
+ required: true;
6
+ };
7
+ title: {
8
+ type: StringConstructor;
9
+ required: true;
10
+ };
11
+ action: {
12
+ type: FunctionConstructor;
13
+ required: true;
14
+ };
15
+ isActive: {
16
+ type: FunctionConstructor;
17
+ default: null;
18
+ };
19
+ }, {
20
+ remixiconUrl: string;
21
+ }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record<string, any>, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
22
+ icon: {
23
+ type: StringConstructor;
24
+ required: true;
25
+ };
26
+ title: {
27
+ type: StringConstructor;
28
+ required: true;
29
+ };
30
+ action: {
31
+ type: FunctionConstructor;
32
+ required: true;
33
+ };
34
+ isActive: {
35
+ type: FunctionConstructor;
36
+ default: null;
37
+ };
38
+ }>>, {
39
+ isActive: Function;
40
+ }>;
41
+ export default _sfc_main;
package/package.json CHANGED
@@ -1,42 +1,51 @@
1
- {
2
- "name": "starfish-form-custom",
3
- "version": "1.0.32",
4
- "main": "dist/starfish-form.mjs",
5
- "style": "dist/style.css",
6
- "module": "dist/starfish-form.mjs",
7
- "types": "dist/types/starfish-form.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./dist/starfish-form.mjs"
11
- },
12
- "./dist/style.css": {
13
- "import": "./dist/style.css"
14
- },
15
- "./*": "./*"
16
- },
17
- "scripts": {
18
- "build": "vite build",
19
- "prepublish": "npm version && npm run build"
20
- },
21
- "dependencies": {
22
- "element-plus": "^2.2.12",
23
- "jsoneditor": "^9.9.0",
24
- "vue": "^3.2.25",
25
- "@element-plus/icons-vue": "^2.0.8",
26
- "wangeditor": "^4.7.15",
27
- "vue-codemirror": "6.1.1",
28
- "codemirror": "6.0.1",
29
- "@codemirror/lang-javascript": "6.0.2"
30
- },
31
- "devDependencies": {
32
- "@vitejs/plugin-vue": "^2.3.3",
33
- "sass": "^1.35.1",
34
- "typescript": "^4.5.4",
35
- "rollup-plugin-visualizer": "^5.8.3",
36
- "unplugin-auto-import": "^0.17.5",
37
- "unplugin-vue-components": "^0.26.0",
38
- "vite": "^4.5.2",
39
- "vite-plugin-dts": "^1.2.0",
40
- "vue-tsc": "^0.34.7"
41
- }
42
- }
1
+ {
2
+ "name": "starfish-form-custom",
3
+ "version": "1.0.33",
4
+ "main": "dist/starfish-form.mjs",
5
+ "style": "dist/style.css",
6
+ "module": "dist/starfish-form.mjs",
7
+ "types": "dist/types/starfish-form.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/starfish-form.mjs"
11
+ },
12
+ "./dist/style.css": {
13
+ "import": "./dist/style.css"
14
+ },
15
+ "./*": "./*"
16
+ },
17
+ "scripts": {
18
+ "build": "vite build",
19
+ "prepublish": "npm version && npm run build"
20
+ },
21
+ "dependencies": {
22
+ "@codemirror/lang-javascript": "6.0.2",
23
+ "@element-plus/icons-vue": "^2.0.8",
24
+ "@tiptap/extension-blockquote": "^3.10.7",
25
+ "@tiptap/extension-code": "^3.10.7",
26
+ "@tiptap/extension-code-block-lowlight": "^3.10.7",
27
+ "@tiptap/extension-horizontal-rule": "^3.10.7",
28
+ "@tiptap/extension-underline": "^3.10.7",
29
+ "@tiptap/pm": "^3.10.7",
30
+ "@tiptap/starter-kit": "^3.10.7",
31
+ "@tiptap/vue-3": "^3.10.7",
32
+ "codemirror": "6.0.1",
33
+ "element-plus": "^2.2.12",
34
+ "jsoneditor": "^9.9.0",
35
+ "lowlight": "^3.3.0",
36
+ "remixicon": "^4.7.0",
37
+ "vue": "^3.2.25",
38
+ "vue-codemirror": "6.1.1"
39
+ },
40
+ "devDependencies": {
41
+ "@vitejs/plugin-vue": "^2.3.3",
42
+ "rollup-plugin-visualizer": "^5.8.3",
43
+ "sass": "^1.35.1",
44
+ "typescript": "^4.5.4",
45
+ "unplugin-auto-import": "^0.17.5",
46
+ "unplugin-vue-components": "^0.26.0",
47
+ "vite": "^4.5.2",
48
+ "vite-plugin-dts": "^1.2.0",
49
+ "vue-tsc": "^0.34.7"
50
+ }
51
+ }
@@ -0,0 +1,145 @@
1
+ <template>
2
+ <div class="editor__header">
3
+ <template v-for="(item, index) in items" :key="index">
4
+ <div v-if="item.type === 'divider'" class="divider" />
5
+ <MenuItem v-else :icon="item.icon" :title="item.title" :action="item.action" :is-active="item.isActive" />
6
+ </template>
7
+ </div>
8
+ </template>
9
+
10
+ <script lang="ts">
11
+ import { defineComponent, PropType } from 'vue'
12
+ import MenuItem from './MenuItem.vue'
13
+
14
+ export default defineComponent({
15
+ name: 'MenuBar',
16
+ components: {
17
+ MenuItem
18
+ },
19
+ props: {
20
+ editor: {
21
+ type: Object as PropType<any>,
22
+ required: true
23
+ }
24
+ },
25
+ setup(props) {
26
+ const items = [
27
+ {
28
+ icon: 'bold',
29
+ title: 'Bold',
30
+ action: () => props.editor.chain().focus().toggleBold().run(),
31
+ isActive: () => props.editor.isActive('bold'),
32
+ },
33
+ {
34
+ icon: 'italic',
35
+ title: 'Italic',
36
+ action: () => props.editor.chain().focus().toggleItalic().run(),
37
+ isActive: () => props.editor.isActive('italic'),
38
+ },
39
+ {
40
+ icon: 'strikethrough',
41
+ title: 'Strike',
42
+ action: () => props.editor.chain().focus().toggleStrike().run(),
43
+ isActive: () => props.editor.isActive('strike'),
44
+ },
45
+ {
46
+ icon: 'code-view',
47
+ title: 'Code',
48
+ action: () => props.editor.chain().focus().toggleCode().run(),
49
+ isActive: () => props.editor.isActive('code'),
50
+ },
51
+ {
52
+ type: 'divider',
53
+ },
54
+ {
55
+ icon: 'h-1',
56
+ title: 'Heading 1',
57
+ action: () => props.editor.chain().focus().toggleHeading({ level: 1 }).run(),
58
+ isActive: () => props.editor.isActive('heading', { level: 1 }),
59
+ },
60
+ {
61
+ icon: 'h-2',
62
+ title: 'Heading 2',
63
+ action: () => props.editor.chain().focus().toggleHeading({ level: 2 }).run(),
64
+ isActive: () => props.editor.isActive('heading', { level: 2 }),
65
+ },
66
+ {
67
+ icon: 'paragraph',
68
+ title: 'Paragraph',
69
+ action: () => props.editor.chain().focus().setParagraph().run(),
70
+ isActive: () => props.editor.isActive('paragraph'),
71
+ },
72
+ {
73
+ icon: 'list-unordered',
74
+ title: 'Bullet list',
75
+ action: () => props.editor.chain().focus().toggleBulletList().run(),
76
+ isActive: () => props.editor.isActive('bulletList'),
77
+ },
78
+ {
79
+ icon: 'list-ordered',
80
+ title: 'Ordered list',
81
+ action: () => props.editor.chain().focus().toggleOrderedList().run(),
82
+ isActive: () => props.editor.isActive('orderedList'),
83
+ },
84
+ {
85
+ icon: 'code-box-line',
86
+ title: 'Code block',
87
+ action: () => props.editor.chain().focus().toggleCodeBlock().run(),
88
+ isActive: () => props.editor.isActive('codeBlock'),
89
+ },
90
+ {
91
+ type: 'divider',
92
+ },
93
+ {
94
+ icon: 'double-quotes-l',
95
+ title: 'Blockquote',
96
+ action: () => props.editor.chain().focus().toggleBlockquote().run(),
97
+ isActive: () => props.editor.isActive('blockquote'),
98
+ },
99
+ {
100
+ icon: 'separator',
101
+ title: 'Horizontal rule',
102
+ action: () => props.editor.chain().focus().setHorizontalRule().run(),
103
+ },
104
+ {
105
+ type: 'divider',
106
+ },
107
+ {
108
+ icon: 'arrow-go-back-line',
109
+ title: 'Undo',
110
+ action: () => props.editor.chain().focus().undo().run(),
111
+ },
112
+ {
113
+ icon: 'arrow-go-forward-line',
114
+ title: 'Redo',
115
+ action: () => props.editor.chain().focus().redo().run(),
116
+ },
117
+ ]
118
+
119
+ return {
120
+ items
121
+ }
122
+ }
123
+ })
124
+ </script>
125
+
126
+ <style scoped>
127
+ .editor__header {
128
+ display: flex;
129
+ align-items: center;
130
+ gap: 4px;
131
+ padding: 8px 12px;
132
+ border: 1px solid #DCDFE6;
133
+ border-bottom: none;
134
+ border-radius: 4px 4px 0 0;
135
+ background-color: #f8f9fa;
136
+ flex-wrap: wrap;
137
+ }
138
+
139
+ .divider {
140
+ width: 1px;
141
+ height: 20px;
142
+ background-color: #DCDFE6;
143
+ margin: 0 4px;
144
+ }
145
+ </style>
@@ -0,0 +1,74 @@
1
+ <template>
2
+ <button
3
+ class="menu-item"
4
+ :class="{ 'is-active': isActive && isActive() }"
5
+ @click="action"
6
+ :title="title"
7
+ >
8
+ <svg class="remix">
9
+ <use :xlink:href="`${remixiconUrl}#ri-${icon}`" />
10
+ </svg>
11
+ </button>
12
+ </template>
13
+
14
+ <script lang="ts">
15
+ import { defineComponent } from 'vue'
16
+ import remixiconUrl from 'remixicon/fonts/remixicon.symbol.svg'
17
+
18
+ export default defineComponent({
19
+ name: 'MenuItem',
20
+ props: {
21
+ icon: {
22
+ type: String,
23
+ required: true
24
+ },
25
+ title: {
26
+ type: String,
27
+ required: true
28
+ },
29
+ action: {
30
+ type: Function,
31
+ required: true
32
+ },
33
+ isActive: {
34
+ type: Function,
35
+ default: null
36
+ }
37
+ },
38
+ setup() {
39
+ return {
40
+ remixiconUrl
41
+ }
42
+ }
43
+ })
44
+ </script>
45
+
46
+ <style scoped>
47
+ .menu-item {
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ width: 32px;
52
+ height: 32px;
53
+ background: white;
54
+ border: 1px solid #ddd;
55
+ border-radius: 4px;
56
+ cursor: pointer;
57
+ transition: all 0.2s ease;
58
+ }
59
+
60
+ .menu-item:hover {
61
+ background-color: #f0f0f0;
62
+ border-color: #999;
63
+ }
64
+
65
+ .menu-item.is-active {
66
+ background-color: #e0e0e0;
67
+ border-color: #666;
68
+ }
69
+
70
+ .remix {
71
+ width: 16px;
72
+ height: 16px;
73
+ }
74
+ </style>
@@ -8,53 +8,113 @@
8
8
  </el-tooltip>
9
9
  </div>
10
10
  <div class="control" :style="{marginLeft: labelalign != 'top'?labelWidth + 'px': ''}">
11
- <div ref="richText" v-if="drag" ></div>
12
- <div ref="richText" v-if="!drag" ></div>
11
+ <div class="rich-text-editor">
12
+ <!-- 使用 MenuBar 组件 -->
13
+ <MenuBar v-if="editor && !readonly" :editor="editor" />
14
+ <editor-content :editor="editor" class="editor-content" />
15
+ </div>
13
16
  </div>
14
17
  </div>
15
18
  </template>
19
+
16
20
  <script lang="ts">
17
- import { defineComponent, onMounted, ref, onUnmounted } from "vue";
18
- import E from "wangeditor";
19
- import { getFormConfig } from "../../utils/fieldConfig";
20
- import fieldProps from "../../utils/fieldProps";
21
- import { useWatch } from "../../utils/customHooks";
22
- export default defineComponent({
23
- ControlType: "RichText", // 必须与文件名匹配
24
- nameCn: "富文本",
25
- icon: "icon-textEdit",
26
- formConfig: getFormConfig("RichText", [{ fieldName: "state", component: "Radio" }]),
27
- props: {
28
- ...fieldProps,
29
- },
30
- setup(props) {
31
- const richText = ref();
32
- let editor: any = null;
33
- useWatch(props);
34
- onMounted(() => {
35
- if (props.drag) {
36
- editor = new E(richText.value);
37
- editor.config.focus = false;
38
- editor.create();
39
- } else {
40
- editor = new E(richText.value);
41
- editor.config.focus = false;
42
- editor.create();
43
- const data: any = props.data;
44
- const item: any = props.item;
45
- editor.config.onchange = function (newHtml: string) {
46
- console.log("onblur", newHtml); // 获取最新的 html 内容
47
- data[item.data.fieldName] = newHtml;
48
- };
21
+ import { defineComponent, onMounted, ref, onUnmounted, watch } from "vue";
22
+ import { useEditor, EditorContent } from '@tiptap/vue-3'
23
+ import StarterKit from '@tiptap/starter-kit'
24
+ import Code from '@tiptap/extension-code'
25
+ import Blockquote from '@tiptap/extension-blockquote'
26
+ import HorizontalRule from '@tiptap/extension-horizontal-rule'
27
+ import { getFormConfig } from "../../utils/fieldConfig";
28
+ import fieldProps from "../../utils/fieldProps";
29
+ import { useWatch } from "../../utils/customHooks";
30
+ import MenuBar from "./MenuBar.vue"; // 导入 MenuBar 组件
31
+
32
+ export default defineComponent({
33
+ ControlType: "RichText",
34
+ nameCn: "富文本",
35
+ icon: "icon-textEdit",
36
+ formConfig: getFormConfig("RichText", [{ fieldName: "state", component: "Radio" }]),
37
+ props: {
38
+ ...fieldProps,
39
+ },
40
+ components: {
41
+ EditorContent,
42
+ MenuBar
43
+ },
44
+ setup(props) {
45
+ const editor = useEditor({
46
+ content: props.data[props.item.data.fieldName] || props.item.data.defaultValue || '',
47
+ extensions: [
48
+ StarterKit,
49
+ Code,
50
+ Blockquote,
51
+ HorizontalRule,
52
+ ],
53
+ editable: !props.readonly,
54
+ editorProps: {
55
+ attributes: {
56
+ class: 'prose focus:outline-none max-w-none',
57
+ style: 'min-height: 200px; border: 1px solid #DCDFE6; border-radius: 4px; padding: 8px 12px;'
49
58
  }
50
- });
51
- onUnmounted(() => {
52
- editor.destroy();
53
- editor = null;
54
- });
55
- return {
56
- richText
57
- };
58
- },
59
- });
59
+ },
60
+ onUpdate: ({ editor }) => {
61
+ const html = editor.getHTML();
62
+ props.data[props.item.data.fieldName] = html;
63
+ },
64
+ onBlur: ({ editor }) => {
65
+ const html = editor.getHTML();
66
+ props.data[props.item.data.fieldName] = html;
67
+ }
68
+ });
69
+
70
+ useWatch(props);
71
+
72
+ // 监听数据变化
73
+ watch(() => props.item.data.defaultValue, (newVal) => {
74
+ if (editor.value && newVal) {
75
+ editor.value.commands.setContent(newVal, false);
76
+ }
77
+ });
78
+
79
+ // 监听只读状态变化
80
+ watch(() => props.readonly, (newVal) => {
81
+ if (editor.value) {
82
+ editor.value.setEditable(!newVal);
83
+ }
84
+ });
85
+
86
+ onUnmounted(() => {
87
+ if (editor.value) {
88
+ editor.value.destroy();
89
+ }
90
+ });
91
+
92
+ return {
93
+ editor
94
+ };
95
+ },
96
+ });
60
97
  </script>
98
+
99
+ <style scoped>
100
+ .rich-text-editor {
101
+ min-height: 200px;
102
+ }
103
+
104
+ :deep(.editor-content) {
105
+ min-height: 200px;
106
+ }
107
+
108
+ :deep(.ProseMirror) {
109
+ min-height: 180px;
110
+ outline: none;
111
+ }
112
+
113
+ :deep(.ProseMirror p.is-editor-empty:first-child::before) {
114
+ color: #adb5bd;
115
+ content: attr(data-placeholder);
116
+ float: left;
117
+ height: 0;
118
+ pointer-events: none;
119
+ }
120
+ </style>