@houaoran/designer 1.0.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 (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/components.es.js +11424 -0
  4. package/dist/components.umd.js +878 -0
  5. package/dist/index.es.js +39113 -0
  6. package/dist/index.umd.js +1187 -0
  7. package/package.json +96 -0
  8. package/src/components/DragBox.vue +49 -0
  9. package/src/components/DragTool.vue +235 -0
  10. package/src/components/EventConfig.vue +557 -0
  11. package/src/components/FcDesigner.vue +2569 -0
  12. package/src/components/FcTitle.vue +69 -0
  13. package/src/components/FetchConfig.vue +415 -0
  14. package/src/components/FieldInput.vue +371 -0
  15. package/src/components/FnConfig.vue +315 -0
  16. package/src/components/FnEditor.vue +327 -0
  17. package/src/components/FnInput.vue +103 -0
  18. package/src/components/FormLabel.vue +47 -0
  19. package/src/components/HtmlEditor.vue +125 -0
  20. package/src/components/JsonPreview.vue +146 -0
  21. package/src/components/OptionsTextInput.vue +151 -0
  22. package/src/components/PropsInput.vue +72 -0
  23. package/src/components/Required.vue +75 -0
  24. package/src/components/Row.vue +26 -0
  25. package/src/components/SignaturePad.vue +176 -0
  26. package/src/components/Struct.vue +153 -0
  27. package/src/components/StructEditor.vue +121 -0
  28. package/src/components/StructTree.vue +209 -0
  29. package/src/components/TableOptions.vue +164 -0
  30. package/src/components/TreeOptions.vue +167 -0
  31. package/src/components/TypeSelect.vue +144 -0
  32. package/src/components/Validate.vue +302 -0
  33. package/src/components/ValueInput.vue +89 -0
  34. package/src/components/Warning.vue +46 -0
  35. package/src/components/ai/AiPanel.vue +1122 -0
  36. package/src/components/ai/MarkdownRenderer.vue +548 -0
  37. package/src/components/language/LanguageConfig.vue +174 -0
  38. package/src/components/language/LanguageInput.vue +191 -0
  39. package/src/components/style/BackgroundInput.vue +315 -0
  40. package/src/components/style/BorderInput.vue +242 -0
  41. package/src/components/style/BoxSizeInput.vue +166 -0
  42. package/src/components/style/BoxSpaceInput.vue +269 -0
  43. package/src/components/style/ColorInput.vue +90 -0
  44. package/src/components/style/ConfigItem.vue +118 -0
  45. package/src/components/style/FontInput.vue +197 -0
  46. package/src/components/style/PositionInput.vue +146 -0
  47. package/src/components/style/RadiusInput.vue +164 -0
  48. package/src/components/style/ShadowContent.vue +335 -0
  49. package/src/components/style/ShadowInput.vue +91 -0
  50. package/src/components/style/SizeInput.vue +118 -0
  51. package/src/components/style/StyleConfig.vue +307 -0
  52. package/src/components/table/Table.vue +252 -0
  53. package/src/components/table/TableView.vue +1058 -0
  54. package/src/components/tableForm/TableForm.vue +471 -0
  55. package/src/components/tableForm/TableFormColumnView.vue +103 -0
  56. package/src/components/tableForm/TableFormView.vue +46 -0
  57. package/src/components/tree/FcTree.vue +713 -0
  58. package/src/components/tree/FcTreeNode.vue +216 -0
  59. package/src/config/base/field.js +43 -0
  60. package/src/config/base/form.js +132 -0
  61. package/src/config/base/style.js +26 -0
  62. package/src/config/base/validate.js +15 -0
  63. package/src/config/index.js +70 -0
  64. package/src/config/menu.js +24 -0
  65. package/src/config/rule/alert.js +45 -0
  66. package/src/config/rule/button.js +49 -0
  67. package/src/config/rule/card.js +40 -0
  68. package/src/config/rule/cascader.js +121 -0
  69. package/src/config/rule/checkbox.js +68 -0
  70. package/src/config/rule/col.js +86 -0
  71. package/src/config/rule/collapse.js +30 -0
  72. package/src/config/rule/collapseItem.js +36 -0
  73. package/src/config/rule/color.js +53 -0
  74. package/src/config/rule/date.js +66 -0
  75. package/src/config/rule/dateRange.js +60 -0
  76. package/src/config/rule/divider.js +31 -0
  77. package/src/config/rule/editor.js +31 -0
  78. package/src/config/rule/group.js +86 -0
  79. package/src/config/rule/html.js +43 -0
  80. package/src/config/rule/image.js +32 -0
  81. package/src/config/rule/input.js +62 -0
  82. package/src/config/rule/number.js +49 -0
  83. package/src/config/rule/password.js +52 -0
  84. package/src/config/rule/radio.js +43 -0
  85. package/src/config/rule/rate.js +44 -0
  86. package/src/config/rule/row.js +46 -0
  87. package/src/config/rule/select.js +70 -0
  88. package/src/config/rule/signaturePad.js +59 -0
  89. package/src/config/rule/slider.js +53 -0
  90. package/src/config/rule/space.js +44 -0
  91. package/src/config/rule/subForm.js +47 -0
  92. package/src/config/rule/switch.js +46 -0
  93. package/src/config/rule/tabPane.js +29 -0
  94. package/src/config/rule/table.js +37 -0
  95. package/src/config/rule/tableForm.js +115 -0
  96. package/src/config/rule/tableFormColumn.js +55 -0
  97. package/src/config/rule/tabs.js +38 -0
  98. package/src/config/rule/tag.js +69 -0
  99. package/src/config/rule/text.js +41 -0
  100. package/src/config/rule/textarea.js +63 -0
  101. package/src/config/rule/time.js +58 -0
  102. package/src/config/rule/timeRange.js +49 -0
  103. package/src/config/rule/title.js +37 -0
  104. package/src/config/rule/transfer.js +59 -0
  105. package/src/config/rule/tree.js +70 -0
  106. package/src/config/rule/treeSelect.js +77 -0
  107. package/src/config/rule/upload.js +107 -0
  108. package/src/form/index.js +19 -0
  109. package/src/index.js +173 -0
  110. package/src/locale/en.js +981 -0
  111. package/src/locale/zh-cn.js +983 -0
  112. package/src/style/fonts/fc-icons.woff +0 -0
  113. package/src/style/icon.css +1052 -0
  114. package/src/style/index.css +836 -0
  115. package/src/utils/form.js +9 -0
  116. package/src/utils/highlight/highlight.min.js +307 -0
  117. package/src/utils/highlight/javascript.min.js +80 -0
  118. package/src/utils/highlight/style.css +1 -0
  119. package/src/utils/highlight/xml.min.js +29 -0
  120. package/src/utils/hintStubs.js +120 -0
  121. package/src/utils/index.js +544 -0
  122. package/src/utils/jsonDiff.js +173 -0
  123. package/src/utils/locale.js +23 -0
  124. package/src/utils/message.js +19 -0
  125. package/src/utils/template.js +105 -0
  126. package/types/index.d.ts +575 -0
@@ -0,0 +1,146 @@
1
+ <template>
2
+ <el-container class="_fc-json-preview">
3
+ <el-header height="40px" class="_fc-l-tabs">
4
+ <div class="_fc-l-tab"
5
+ :class="{active: active==='rule'}"
6
+ @click="active='rule'"> {{ t('designer.json') }}
7
+ </div>
8
+ <div class="_fc-l-tab"
9
+ :class="{active: active==='options'}"
10
+ @click="active='options'"> {{ t('designer.form') }}
11
+ </div>
12
+ <div class="_fc-json-preview-down">
13
+ <el-tooltip effect="dark" :content="t('props.export')" placement="top" :hide-after="0">
14
+ <i class="fc-icon icon-download" @click="downloadJson"></i>
15
+ </el-tooltip>
16
+ </div>
17
+ </el-header>
18
+ <el-main style="padding: 8px;">
19
+ <StructEditor ref="editor" v-model="value" @blur="handleBlur" @focus="handleFocus" format
20
+ style="height:100%;"></StructEditor>
21
+ </el-main>
22
+ </el-container>
23
+ </template>
24
+
25
+ <script>
26
+ import {defineComponent} from 'vue';
27
+ import StructEditor from './StructEditor.vue';
28
+ import {designerForm} from '../utils/form';
29
+
30
+ export default defineComponent({
31
+ name: 'JsonPreview',
32
+ components: {StructEditor},
33
+ inject: ['designer'],
34
+ data() {
35
+ return {
36
+ active: 'rule',
37
+ value: this.designer.setupState.getRule(),
38
+ oldValue: '',
39
+ }
40
+ },
41
+ watch: {
42
+ active() {
43
+ this.updateValue();
44
+ }
45
+ },
46
+ computed: {
47
+ change() {
48
+ if (this.active === 'rule') {
49
+ return this.designer.setupState.children;
50
+ } else {
51
+ return this.designer.setupState.formOptions;
52
+ }
53
+ },
54
+ t() {
55
+ return this.designer.setupState.t;
56
+ },
57
+ },
58
+ methods: {
59
+ updateValue() {
60
+ if (this.active === 'rule') {
61
+ this.value = this.designer.setupState.getRule();
62
+ } else {
63
+ this.value = this.designer.setupState.getOptions();
64
+ }
65
+ },
66
+ handleFocus() {
67
+ this.oldValue = designerForm.toJson(this.value);
68
+ },
69
+ handleBlur() {
70
+ let str;
71
+ if (this.$refs.editor.save() && (str = designerForm.toJson(this.value)) !== this.oldValue) {
72
+ if (this.active === 'rule') {
73
+ this.designer.setupState.setRule(this.value ? str : []);
74
+ } else {
75
+ this.designer.setupState.setOptions(this.value || {});
76
+ }
77
+ }
78
+ },
79
+ downloadJson() {
80
+ const data = {
81
+ rule: this.designer.setupState.getRule(),
82
+ options: this.designer.setupState.getOptions(),
83
+ };
84
+ const str = designerForm.toJson(data);
85
+ const blob = new Blob([str], {type: 'application/json'});
86
+ const url = URL.createObjectURL(blob);
87
+ const link = document.createElement('a');
88
+ link.href = url;
89
+ link.download = `fc-json-${Date.now()}.json`;
90
+ link.click();
91
+ setTimeout(() => {
92
+ URL.revokeObjectURL(url);
93
+ }, 0);
94
+ }
95
+ },
96
+ mounted() {
97
+ this.$watch(() => this.change, () => {
98
+ this.updateValue();
99
+ }, {deep: true});
100
+ }
101
+ });
102
+ </script>
103
+
104
+ <style>
105
+ ._fc-json-preview {
106
+ display: flex;
107
+ width: 100%;
108
+ color: #262626;
109
+ }
110
+
111
+ ._fc-json-preview-down {
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ height: 100%;
116
+ position: absolute;
117
+ right: 8px;
118
+ top: 0;
119
+ }
120
+
121
+ ._fc-json-preview .icon-download {
122
+ width: 24px;
123
+ height: 24px;
124
+ border: none;
125
+ background: transparent;
126
+ border-radius: 3px;
127
+ color: #666666;
128
+ cursor: pointer;
129
+ display: flex;
130
+ align-items: center;
131
+ justify-content: center;
132
+ transition: all 0.15s ease;
133
+ opacity: 0.8;
134
+ }
135
+
136
+ ._fc-json-preview .icon-download:hover {
137
+ background: #f5f5f5;
138
+ color: #262626;
139
+ opacity: 1;
140
+ }
141
+
142
+ ._fc-json-preview .CodeMirror {
143
+ height: 100%;
144
+ font-size: 12px;
145
+ }
146
+ </style>
@@ -0,0 +1,151 @@
1
+ <template>
2
+ <Struct
3
+ v-model="structValue"
4
+ text
5
+ :title="t('struct.editText')"
6
+ :defaultValue="''"
7
+ :placeholder="placeholder"
8
+ :validate="validateStruct"
9
+ ></Struct>
10
+ </template>
11
+
12
+ <script>
13
+ import { defineComponent } from 'vue';
14
+ import Struct from './Struct.vue';
15
+
16
+ export default defineComponent({
17
+ name: 'OptionsTextInput',
18
+ emits: ['update:modelValue'],
19
+ components: { Struct },
20
+ inject: ['designer'],
21
+ props: {
22
+ modelValue: {
23
+ type: Array,
24
+ default: () => [],
25
+ },
26
+ field: {
27
+ type: Object,
28
+ default: () => ({
29
+ label: 'label',
30
+ value: 'value',
31
+ children: 'children',
32
+ }),
33
+ },
34
+ parseSpace: {
35
+ type: Boolean,
36
+ default: false,
37
+ },
38
+ placeholder: {
39
+ type: String,
40
+ default: '',
41
+ },
42
+ },
43
+ computed: {
44
+ t() {
45
+ return this.designer.setupState.t;
46
+ },
47
+ labelKey() {
48
+ return this.field?.label || 'label';
49
+ },
50
+ valueKey() {
51
+ return this.field?.value || 'value';
52
+ },
53
+ childrenKey() {
54
+ return this.field?.children || 'children';
55
+ },
56
+ },
57
+ data() {
58
+ return {
59
+ structValue: '',
60
+ syncingFromModel: false,
61
+ };
62
+ },
63
+ watch: {
64
+ modelValue: {
65
+ handler(val) {
66
+ this.syncingFromModel = true;
67
+ this.structValue = this.optionsToText(Array.isArray(val) ? val : []);
68
+ this.$nextTick(() => {
69
+ this.syncingFromModel = false;
70
+ });
71
+ },
72
+ immediate: true,
73
+ deep: true,
74
+ },
75
+ structValue(val) {
76
+ if (this.syncingFromModel) {
77
+ return;
78
+ }
79
+ this.$emit('update:modelValue', this.textToOptions(val));
80
+ },
81
+ parseSpace() {
82
+ this.syncingFromModel = true;
83
+ this.structValue = this.optionsToText(Array.isArray(this.modelValue) ? this.modelValue : []);
84
+ this.$nextTick(() => {
85
+ this.syncingFromModel = false;
86
+ });
87
+ },
88
+ },
89
+ methods: {
90
+ validateStruct(val) {
91
+ return typeof val === 'string';
92
+ },
93
+ optionsToText(options, depth = 0) {
94
+ if (!Array.isArray(options) || !options.length) {
95
+ return '';
96
+ }
97
+ const lines = [];
98
+ options.forEach(item => {
99
+ const text = item?.[this.labelKey] ?? item?.[this.valueKey] ?? '';
100
+ if (text !== '') {
101
+ const level = this.parseSpace ? depth : 0;
102
+ lines.push(`${' '.repeat(level)}${text}`);
103
+ }
104
+ if (Array.isArray(item?.[this.childrenKey]) && item[this.childrenKey].length) {
105
+ const childText = this.optionsToText(item[this.childrenKey], depth + 1);
106
+ if (childText) {
107
+ lines.push(childText);
108
+ }
109
+ }
110
+ });
111
+ return lines.join('\n');
112
+ },
113
+ textToOptions(text) {
114
+ const lines = String(text || '')
115
+ .replace(/\t/g, ' ')
116
+ .split(/\r?\n/)
117
+ .filter(line => line.trim() !== '');
118
+ const root = [];
119
+ const stack = [];
120
+
121
+ lines.forEach(line => {
122
+ const indent = (line.match(/^ */) || [''])[0].length;
123
+ const depth = this.parseSpace ? Math.min(indent, stack.length) : 0;
124
+ const content = line.trim();
125
+ const node = {};
126
+ node[this.labelKey] = content;
127
+ node[this.valueKey] = content;
128
+
129
+ while (stack.length > depth) {
130
+ stack.pop();
131
+ }
132
+
133
+ if (depth === 0 || !stack[depth - 1]) {
134
+ root.push(node);
135
+ } else {
136
+ const parent = stack[depth - 1];
137
+ if (!Array.isArray(parent[this.childrenKey])) {
138
+ parent[this.childrenKey] = [];
139
+ }
140
+ parent[this.childrenKey].push(node);
141
+ }
142
+
143
+ stack[depth] = node;
144
+ stack.length = depth + 1;
145
+ });
146
+
147
+ return root;
148
+ },
149
+ },
150
+ });
151
+ </script>
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <Struct class="_fd-props-input" :modelValue="props" @update:modelValue="onInput" :title="t('designer.customProps')">
3
+ <i class="fc-icon icon-edit"></i>
4
+ </Struct>
5
+ </template>
6
+
7
+ <script>
8
+ import {defineComponent} from 'vue';
9
+ import Struct from './Struct.vue';
10
+ import extend from '@form-create/utils/lib/extend';
11
+
12
+ export default defineComponent({
13
+ name: 'PropsInput',
14
+ components: {Struct},
15
+ inject: ['designer'],
16
+ data() {
17
+ return {}
18
+ },
19
+ computed: {
20
+ t() {
21
+ return this.designer.setupState.t;
22
+ },
23
+ activeRule() {
24
+ return this.designer.setupState.activeRule;
25
+ },
26
+ props() {
27
+ const propsKeys = this.activeRule._fc_store?.props_keys || [];
28
+ const props = {};
29
+ propsKeys.forEach(k => {
30
+ if (this.activeRule.props && this.activeRule.props[k] != null) {
31
+ props[k] = this.activeRule.props[k];
32
+ }
33
+ });
34
+ return props;
35
+ },
36
+ },
37
+ methods: {
38
+ onInput(props) {
39
+ if (!this.activeRule.props) {
40
+ this.activeRule.props = {};
41
+ }
42
+ if (!this.activeRule._fc_store) {
43
+ this.activeRule._fc_store = {};
44
+ }
45
+ Object.keys(this.props).forEach(k => {
46
+ if ((props || {})[k] == null) {
47
+ delete this.activeRule.props[k];
48
+ }
49
+ });
50
+ extend(this.activeRule.props, props || {});
51
+ const keys = Object.keys(props || {});
52
+ if (keys.length) {
53
+ this.activeRule._fc_store.props_keys = keys;
54
+ } else {
55
+ delete this.activeRule._fc_store.props_keys;
56
+ }
57
+ }
58
+ }
59
+
60
+ });
61
+ </script>
62
+
63
+ <style>
64
+ ._fd-props-input {
65
+ display: inline-block;
66
+ width: 16px;
67
+ }
68
+
69
+ ._fd-props-input .fc-icon {
70
+ cursor: pointer;
71
+ }
72
+ </style>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <div class="_fd-required">
3
+ <el-switch v-model="required"></el-switch>
4
+ <LanguageInput v-model="requiredMsg" v-if="required"
5
+ :placeholder="t('validate.requiredPlaceholder')"></LanguageInput>
6
+ </div>
7
+ </template>
8
+
9
+ <script>
10
+ import is from '@form-create/utils/lib/type';
11
+ import {defineComponent} from 'vue';
12
+ import LanguageInput from './language/LanguageInput.vue';
13
+
14
+ export default defineComponent({
15
+ name: 'Required',
16
+ components: {LanguageInput},
17
+ emits: ['update:modelValue'],
18
+ props: {
19
+ modelValue: {}
20
+ },
21
+ inject: ['designer'],
22
+ watch: {
23
+ required() {
24
+ this.update();
25
+ },
26
+ requiredMsg() {
27
+ this.update();
28
+ },
29
+ modelValue(n) {
30
+ const flag = is.String(n);
31
+ this.required = n === undefined ? false : (flag ? true : !!n);
32
+ this.requiredMsg = flag ? n : '';
33
+ },
34
+ },
35
+ computed: {
36
+ t() {
37
+ return this.designer.setupState.t;
38
+ },
39
+ },
40
+ data() {
41
+ const flag = is.String(this.modelValue);
42
+ return {
43
+ required: this.modelValue === undefined ? false : (flag ? true : !!this.modelValue),
44
+ requiredMsg: flag ? this.modelValue : ''
45
+ };
46
+ },
47
+ methods: {
48
+ update() {
49
+ let val;
50
+ if (this.required === false) {
51
+ val = false;
52
+ } else {
53
+ val = this.requiredMsg || true;
54
+ }
55
+ this.$emit('update:modelValue', val);
56
+ },
57
+ }
58
+ });
59
+ </script>
60
+
61
+ <style>
62
+ ._fd-required {
63
+ display: flex;
64
+ align-items: center;
65
+ width: 100%;
66
+ }
67
+
68
+ ._fd-required .el-input {
69
+ margin-left: 15px;
70
+ }
71
+
72
+ ._fd-required .el-switch {
73
+ height: 28px;
74
+ }
75
+ </style>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <div class="_fd-row el-row" :class="{'_fc-child-empty' : !$slots.default}" v-bind="$attrs">
3
+ <slot name="default"></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ import {defineComponent} from 'vue';
9
+
10
+ export default defineComponent({
11
+ name: 'fcRow',
12
+ mounted() {
13
+ }
14
+
15
+ });
16
+ </script>
17
+
18
+ <style>
19
+ ._fd-row {
20
+ width: 100%;
21
+ box-sizing: border-box;
22
+ display: flex;
23
+ flex-wrap: wrap;
24
+ position: relative;
25
+ }
26
+ </style>
@@ -0,0 +1,176 @@
1
+ <template>
2
+ <div class="_fc-signature" :class="{disabled: disabled}">
3
+ <template v-if="modelValue">
4
+ <div class="_fc-signature-preview">
5
+ <i class="fc-icon icon-delete2" v-if="!disabled" @click="remove"></i>
6
+ <img :src="modelValue" alt="signature">
7
+ </div>
8
+ </template>
9
+ <template v-else>
10
+ <div class="_fc-signature-btn" @click="open"><i class="fc-icon icon-edit2"></i> {{ tipText }}</div>
11
+ </template>
12
+ <el-dialog class="_fc-signature-dialog" :title="padTitleText"
13
+ v-model="visible"
14
+ destroy-on-close
15
+ :close-on-click-modal="false"
16
+ append-to-body width="640px">
17
+ <canvas class="_fc-signature-pad" ref="pad" width="600px" height="270px"></canvas>
18
+ <template #footer>
19
+ <div>
20
+ <el-button size="default" @click="clear()">{{ formCreateInject.t('reset') || '重置' }}</el-button>
21
+ <el-button type="primary" :disabled="isEmpty" @click="submit" size="default">
22
+ {{ formCreateInject.t('ok') || '确定' }}
23
+ </el-button>
24
+ </div>
25
+ </template>
26
+ </el-dialog>
27
+ </div>
28
+ </template>
29
+
30
+ <script>
31
+ import {defineComponent, markRaw} from 'vue';
32
+ import SignaturePad from 'signature_pad';
33
+
34
+ export default defineComponent({
35
+ name: 'SignaturePad',
36
+ emits: ['update:modelValue', 'change', 'remove'],
37
+ data() {
38
+ return {
39
+ visible: false,
40
+ isEmpty: true,
41
+ signaturePad: null,
42
+ };
43
+ },
44
+ props: {
45
+ modelValue: String,
46
+ penColor: String,
47
+ disabled: Boolean,
48
+ tip: String,
49
+ padTitle: String,
50
+ formCreateInject: Object,
51
+ beforeRemove: Function,
52
+ },
53
+ computed: {
54
+ tipText() {
55
+ return this.tip || this.formCreateInject?.t('signaturePadTip') || '点击添加手写签名';
56
+ },
57
+ padTitleText() {
58
+ return this.padTitle || this.formCreateInject?.t('signaturePadTitle') || '请在虚线框内书写';
59
+ },
60
+ },
61
+ watch: {
62
+ visible(val) {
63
+ if (val) {
64
+ this.isEmpty = true;
65
+ this.$nextTick(() => {
66
+ this.signaturePad = markRaw(new SignaturePad(this.$refs.pad, {
67
+ penColor: this.penColor,
68
+ }));
69
+ this.signaturePad.addEventListener('endStroke', () => {
70
+ this.isEmpty = this.signaturePad.isEmpty();
71
+ });
72
+ });
73
+ } else {
74
+ this.signaturePad.off();
75
+ this.signaturePad = null;
76
+ }
77
+ }
78
+ },
79
+
80
+ methods: {
81
+ open() {
82
+ if (!this.disabled) {
83
+ this.visible = true;
84
+ }
85
+ },
86
+ clear() {
87
+ this.signaturePad.clear();
88
+ this.isEmpty = true;
89
+ },
90
+ submit() {
91
+ const res = this.signaturePad.toDataURL();
92
+ this.updateValue(res);
93
+ this.visible = false;
94
+ },
95
+ updateValue(val) {
96
+ this.$emit('update:modelValue', val);
97
+ this.$emit('change', val);
98
+ },
99
+ async remove() {
100
+ if (this.disabled) {
101
+ return;
102
+ }
103
+ // 检查是否有删除前置回调
104
+ if (this.beforeRemove) {
105
+ const result = await this.beforeRemove();
106
+ // 如果回调返回 false,则停止删除
107
+ if (result === false) {
108
+ return;
109
+ }
110
+ }
111
+ this.updateValue('');
112
+ this.$emit('remove');
113
+ },
114
+ },
115
+ });
116
+ </script>
117
+
118
+ <style>
119
+
120
+ ._fc-signature {
121
+ width: 100%;
122
+ }
123
+
124
+ ._fc-signature-btn, ._fc-signature-preview {
125
+ width: 100%;
126
+ min-width: 160px;
127
+ height: 88px;
128
+ line-height: 88px;
129
+ font-size: 14px;
130
+ color: rgb(201, 204, 216);
131
+ border-radius: 4px;
132
+ border: 1px dashed rgb(212, 215, 224);
133
+ text-align: center;
134
+ background: rgb(255, 255, 255);
135
+ position: relative;
136
+ box-sizing: border-box;
137
+ }
138
+
139
+ ._fc-signature-btn {
140
+ cursor: pointer;
141
+ }
142
+
143
+ ._fc-signature.disabled ._fc-signature-btn {
144
+ cursor: not-allowed;
145
+ }
146
+
147
+ ._fc-signature-preview > img {
148
+ display: inline-block;
149
+ height: 88px;
150
+ }
151
+
152
+ ._fc-signature-preview .icon-delete2 {
153
+ position: absolute;
154
+ top: 9px;
155
+ right: 9px;
156
+ display: inline-block;
157
+ line-height: 14px;
158
+ font-size: 14px;
159
+ cursor: pointer;
160
+ }
161
+
162
+ ._fc-signature-btn i {
163
+ font-size: 14px;
164
+ }
165
+
166
+ ._fc-signature-dialog .el-dialog__body {
167
+ text-align: center;
168
+ }
169
+
170
+ ._fc-signature-pad {
171
+ border-radius: 4px;
172
+ border: 1px dashed #D4D7E0;
173
+ background-image: linear-gradient(#FFFFFF 14px, transparent 0), linear-gradient(90deg, #FFFFFF 14px, #D4D7E0 0);
174
+ background-size: 15px 15px;
175
+ }
176
+ </style>