@wg-npm/survey-response 0.5.1 → 0.5.123

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.
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <FormItem
3
- :prop="`answers[${index}].answer`"
3
+ :prop="answerProp"
4
4
  :rules="{
5
5
  required: question.options.required && !question.options.disabled,
6
6
  type: 'array',
@@ -9,14 +9,15 @@
9
9
  }"
10
10
  >
11
11
  <CheckboxGroup v-model="value.answer" @on-change="selChange">
12
- <Row type="flex" justify="start" :gutter="60">
12
+ <Row :type="flexOrBlock" justify="start" :gutter="60" :style="{'display':(isMobile()?'inline-block':'flex')}">
13
13
  <Col
14
14
  v-for="choice in question.choices"
15
15
  :key="choice.id"
16
16
  :class="optionLayout"
17
- style="display: flex"
17
+ :style="{'display':(isMobile()?'inline-block':'flex')}"
18
18
  >
19
19
  <Checkbox
20
+ :style="{'display':(isMobile()?'inline-block':'flex'),'align-items':(isMobile()?'':'center')}"
20
21
  :label="choice.id"
21
22
  :key="choice.id"
22
23
  :disabled="
@@ -37,7 +38,7 @@
37
38
  choice.id === inputAnswer.choiceId &&
38
39
  value.answer.includes(choice.id)
39
40
  "
40
- :prop="`answers[${index}].inputAnswers[${inputIndex}].inputText`"
41
+ :prop="inputAnswerProp(inputIndex)"
41
42
  :rules="[
42
43
  {
43
44
  required: true,
@@ -54,10 +55,12 @@
54
55
  },
55
56
  ]"
56
57
  >
57
- <Input
58
- :disabled="question.options.readonly || choice.readonly"
59
- v-model.trim="inputAnswer.inputText"
58
+ <Span v-if="responseStatus=='ENABLED'">
59
+ {{ inputAnswer.inputText }}</Span>
60
+ <Input v-if="responseStatus=='DRAFT'"
61
+ v-model.trim="inputAnswer.inputText"
60
62
  />
63
+
61
64
  </FormItem>
62
65
  </div>
63
66
  </Col>
@@ -66,125 +69,134 @@
66
69
  </FormItem>
67
70
  </template>
68
71
  <script lang="ts">
69
- import Vue from "vue";
70
- import _ from "lodash";
71
- import OptionLayoutMixin from "../../../mixins/option-layout-mixin";
72
- import {
73
- FormItem,
74
- CheckboxGroup,
75
- Checkbox,
76
- Row,
77
- Col,
78
- Input,
79
- } from "view-design";
80
-
81
- export default Vue.extend({
82
- name: "multi-selection",
83
- inject: ["$rootComponent"],
84
- mixins: [OptionLayoutMixin],
85
- components: {
72
+ import Vue from "vue";
73
+ import _ from "lodash";
74
+ import OptionLayoutMixin from "../../../mixins/option-layout-mixin";
75
+ import QuestionMixin from "../../../mixins/question-mixin";
76
+ import {
86
77
  FormItem,
87
78
  CheckboxGroup,
88
79
  Checkbox,
89
80
  Row,
90
81
  Col,
91
- Input,
92
- },
93
- props: {
94
- question: {
95
- type: Object,
96
- required: true,
82
+ Input
83
+ } from "view-design";
84
+
85
+ export default Vue.extend({
86
+ name: "multi-selection",
87
+ inject: ["responseStatus", "$rootComponent"],
88
+ mixins: [OptionLayoutMixin, QuestionMixin],
89
+ components: {
90
+ FormItem,
91
+ CheckboxGroup,
92
+ Checkbox,
93
+ Row,
94
+ Col,
95
+ Input
97
96
  },
98
- index: Number,
99
- answer: {
100
- type: Object,
101
- required: true,
97
+ props: {
98
+ question: {
99
+ type: Object,
100
+ required: true
101
+ },
102
+ answer: {
103
+ type: Object,
104
+ required: true
105
+ }
102
106
  },
103
- },
104
- data() {
105
- return {
106
- value: this.answer,
107
- };
108
- },
109
- watch: {
110
- answer(n) {
111
- this.value = n;
107
+ data() {
108
+ return {
109
+ value: this.answer
110
+ };
112
111
  },
113
- },
114
- methods: {
115
- setCheckboxStatus() {
116
- let exclusiveChoiceId = "";
117
- const notExclusiveChoices = new Array();
118
- // @ts-ignore
119
- _.each(this.question.choices, (choice) => {
120
- if (choice.options.exclusiveEnabled) {
121
- exclusiveChoiceId = choice.id;
112
+ watch: {
113
+ answer(n) {
114
+ this.value = n;
115
+ }
116
+ },
117
+ computed: {
118
+ flexOrBlock() {
119
+ if (this.isMobile()) {
120
+ return "inline-block";
122
121
  } else {
123
- notExclusiveChoices.push(choice);
122
+ return "flex";
124
123
  }
125
- });
124
+ }
125
+ },
126
+ methods: {
127
+ inputAnswerProp(inputIndex) {
128
+ return this.isMoreSurvey
129
+ ? `moreSurveyAnswers[${this.surveyIndex}].answers[${this.index}].inputAnswers[${inputIndex}].inputText`
130
+ : `answers[${this.index}].inputAnswers[${inputIndex}].inputText`;
131
+ },
132
+ setCheckboxStatus() {
133
+ let exclusiveChoiceId = "";
134
+ const notExclusiveChoices = new Array();
135
+ // @ts-ignore
136
+ _.each(this.question.choices, (choice) => {
137
+ if (choice.options.exclusiveEnabled) {
138
+ exclusiveChoiceId = choice.id;
139
+ } else {
140
+ notExclusiveChoices.push(choice);
141
+ }
142
+ });
126
143
 
127
- // @ts-ignore
128
- _.each(notExclusiveChoices, (choice) => {
129
144
  // @ts-ignore
130
- choice.readonly = _.includes(this.value.answer, exclusiveChoiceId);
131
- });
132
- },
133
- selChange(val) {
134
- let score = 0;
135
- let minStarCount = 0;
136
- // @ts-ignore
137
- _.each(this.question.choices, (choice) => {
145
+ _.each(notExclusiveChoices, (choice) => {
146
+ // @ts-ignore
147
+ choice.readonly = _.includes(this.value.answer, exclusiveChoiceId);
148
+ });
149
+ },
150
+ selChange(val) {
151
+ let score = 0;
152
+ let minStarCount = 0;
138
153
  // @ts-ignore
139
- _.each(val, (id) => {
140
- if (choice.id == id) {
141
- if (choice.options.exclusiveEnabled) {
142
- this.value.answer = [id];
143
- score = parseInt(choice?.options?.score ?? 0);
144
- minStarCount = 1;
145
- } else {
146
- score += parseInt(choice?.options?.score ?? 0);
147
- minStarCount++;
154
+ _.each(this.question.choices, (choice) => {
155
+ // @ts-ignore
156
+ _.each(val, (id) => {
157
+ if (choice.id == id) {
158
+ if (choice.options.exclusiveEnabled) {
159
+ this.value.answer = [id];
160
+ score = parseInt(choice?.options?.score ?? 0);
161
+ minStarCount = 1;
162
+ } else {
163
+ score += parseInt(choice?.options?.score ?? 0);
164
+ minStarCount++;
165
+ }
148
166
  }
167
+ });
168
+ });
169
+
170
+ //处理输入题
171
+ _.forEach(this.value.inputAnswers, (input) => {
172
+ if (!_.includes(val, input.choiceId)) {
173
+ input.inputText = null;
149
174
  }
150
175
  });
151
- });
176
+ this.setCheckboxStatus();
152
177
 
153
- //处理输入题
154
- _.forEach(this.value.inputAnswers, (input) => {
155
- if (!_.includes(val, input.choiceId)) {
156
- input.inputText = null;
178
+ // @ts-ignore
179
+ this.answer.score = score;
180
+ if (this.question.options.starEnabled) {
181
+ this.answer.star =
182
+ minStarCount >= this.question.options.starMinCount ? 1 : 0;
157
183
  }
158
- });
159
- this.setCheckboxStatus();
160
-
161
- // @ts-ignore
162
- this.answer.score = score;
163
- if (this.question.options.starEnabled) {
164
- this.answer.star =
165
- minStarCount >= this.question.options.starMinCount ? 1 : 0;
166
184
  }
167
- },
168
- },
169
- });
185
+ }
186
+ });
170
187
  </script>
171
188
 
172
189
  <style lang="less" scoped>
173
- .ivu-row-flex-start {
174
- align-items: center;
175
- }
176
- .ivu-checkbox-group-item {
177
- display: flex;
178
- flex-direction: row;
179
- align-items: center;
180
- }
190
+ .choice-title {
191
+ margin-left: 8px;
192
+ margin-right: 8px;
193
+ }
181
194
 
182
- .choice-title {
183
- margin-left: 8px;
184
- margin-right: 8px;
185
- }
195
+ .choice-score {
196
+ margin-right: 8px;
197
+ }
186
198
 
187
- .choice-score {
188
- margin-right: 8px;
189
- }
199
+ .ivu-checkbox-wrapper {
200
+ white-space: nowrap;
201
+ }
190
202
  </style>
@@ -0,0 +1,293 @@
1
+ <template>
2
+ <div>
3
+ <div ref="editor"></div>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ import Quill from "quill";
9
+ import { EventBus } from "../../../event-bus";
10
+ import LocalMixin from "../../../mixins/locale-mixin.ts";
11
+ import { consts } from "../../../constants.ts";
12
+ import Vue from "vue";
13
+
14
+ export default Vue.extend({
15
+ name: "QuillEditor",
16
+ inject: {
17
+ showVideoScreenShotButton: {
18
+ default: false,
19
+ },
20
+ $rootComponent: {
21
+ default: {},
22
+ },
23
+ },
24
+ mixins: [LocalMixin],
25
+ props: {
26
+ value: {
27
+ type: String,
28
+ default: "",
29
+ },
30
+ placeholder: {
31
+ type: String,
32
+ default: "",
33
+ },
34
+ disabled: {
35
+ type: Boolean,
36
+ default: false,
37
+ },
38
+ maxlength: {
39
+ type: Number,
40
+ default: 5000,
41
+ },
42
+ },
43
+ mounted() {
44
+ this.initQuill();
45
+ EventBus.$on("screen-shot-urls", this.addScreenShotUrl);
46
+ this.componentId = this.generateRandomId();
47
+ },
48
+ computed: {
49
+ imageCount() {
50
+ if (this.value) {
51
+ let imgTagRegex = /<img\s[^>]*src=[\"']?([^>\"']+)[\"']?[^>]*>/g;
52
+ let matches = this.value.match(imgTagRegex);
53
+ return matches ? matches.length : 0;
54
+ }
55
+ return 0;
56
+ },
57
+ },
58
+ methods: {
59
+ addScreenShotUrl(screenData) {
60
+ if (this.componentId === screenData.messageId) {
61
+ if (this.imageCount > consts.RICH_TEXT_IMAGE_COUNT_LIMIT) {
62
+ this.$Message.error(
63
+ this.getLocaleValue(
64
+ "survey_response.common.tips.upload_image_exceed_the_limit"
65
+ )
66
+ );
67
+ return;
68
+ }
69
+ for (let url of screenData.urls) {
70
+ this.insertImageUrl(url);
71
+ }
72
+ }
73
+ },
74
+ insertImageEmbed(url) {
75
+ let range = this.quill.getSelection();
76
+ if (!range) {
77
+ range = { index: this.quill.getLength(), length: 0 };
78
+ }
79
+ this.quill.insertEmbed(range, "image", url, "user");
80
+ this.$nextTick(() => {
81
+ this.quill.update("user");
82
+ });
83
+ },
84
+ insertImageUrl(url) {
85
+ if (url) {
86
+ let range = this.quill.getSelection();
87
+ if (!range) {
88
+ range = { index: this.quill.getLength(), length: 0 };
89
+ }
90
+ const imgTag = `<img src="${url}" alt="image load fail">`;
91
+ this.quill.clipboard.dangerouslyPasteHTML(range.index, imgTag);
92
+ this.quill.setSelection(range.index + 1);
93
+ }
94
+ },
95
+ changeVideoScreenShotButtonName() {
96
+ // 查找自定义按钮并更新其文本
97
+ const toolbar = this.quill.getModule("toolbar");
98
+ const buttons = toolbar.container.querySelectorAll(
99
+ "button.ql-video-screen-shot"
100
+ );
101
+ if (buttons.length > 0) {
102
+ let button = buttons[0];
103
+ button.innerHTML = this.getLocaleValue(
104
+ "survey_response.common.tips.get_screen_shot_name"
105
+ );
106
+ button.style.padding = "2px";
107
+ button.style.fontSize = "12px";
108
+ button.style.color = "rgb(105, 140, 208)";
109
+ button.style.border = "1px solid rgb(105, 140, 208)";
110
+ button.style.borderRadius = "4px";
111
+ button.style.width = "auto";
112
+ button.style.display = "flex";
113
+ button.style.alignItems = "center";
114
+ }
115
+ },
116
+ initQuill() {
117
+ let toolbarOptions = [
118
+ [
119
+ "bold",
120
+ "italic",
121
+ "underline",
122
+ "strike",
123
+ { list: "ordered" },
124
+ { list: "bullet" },
125
+ { color: [] },
126
+ { background: [] },
127
+ { align: [] },
128
+ "clean",
129
+ ],
130
+ ];
131
+ if (this.showVideoScreenShotButton) {
132
+ toolbarOptions.push([{ "video-screen-shot": "video-screen-shot" }]);
133
+ }
134
+ this.quill = new Quill(this.$refs.editor, {
135
+ theme: "snow", // 或者 'bubble'
136
+ placeholder: this.disabled ? "" : this.placeholder,
137
+ readOnly: this.disabled,
138
+ modules: {
139
+ toolbar: this.disabled
140
+ ? false
141
+ : {
142
+ container: toolbarOptions,
143
+ handlers: {
144
+ "video-screen-shot": this.customVideoScreenShot,
145
+ },
146
+ },
147
+ },
148
+ });
149
+ this.quill.root.innerHTML = this.value;
150
+ this.quill.on("text-change", this.handleTextChange);
151
+ //quill 剪切事件自定义处理
152
+ this.quill.root.addEventListener("paste", this.handlePaste, true);
153
+ if (this.showVideoScreenShotButton && !this.disabled) {
154
+ this.changeVideoScreenShotButtonName();
155
+ }
156
+ },
157
+ handleTextChange() {
158
+ let text = this.quill.getText().trim();
159
+ if (text.length > this.maxlength) {
160
+ this.quill.deleteText(this.maxlength, text.length);
161
+ this.$Message.error(
162
+ this.getLocaleValue("survey_response.common.tips.max_length_hint")
163
+ );
164
+ }
165
+ text = this.quill.getText();
166
+ let html = this.quill.root.innerHTML;
167
+ //手动删除文字时,删空富文本留下的内容是<p><br></p>。需要将内容修改为'',外层必填校验才会生效
168
+ if (html.trim() === "<p><br></p>") {
169
+ // 设置编辑器内容为空
170
+ this.quill.root.innerHTML = "";
171
+ html = "";
172
+ }
173
+ this.$emit("input", html);
174
+ },
175
+ handlePaste(event) {
176
+ if (this.imageCount > consts.RICH_TEXT_IMAGE_COUNT_LIMIT) {
177
+ this.$Message.error(
178
+ this.getLocaleValue(
179
+ "survey_response.common.tips.upload_image_exceed_the_limit"
180
+ )
181
+ );
182
+ return;
183
+ }
184
+ let clipboard = event.clipboardData || window.clipboardData;
185
+ // IE 11 is .files other browsers are .items
186
+ if (clipboard && (clipboard.items || clipboard.files)) {
187
+ let items = clipboard.items || clipboard.files;
188
+ const IMAGE_MIME_REGEX = /^image\/(jpe?g|gif|png|svg|webp)$/i;
189
+
190
+ for (let i = 0; i < items.length; i++) {
191
+ if (IMAGE_MIME_REGEX.test(items[i].type)) {
192
+ let file = items[i].getAsFile ? items[i].getAsFile() : items[i];
193
+
194
+ if (file) {
195
+ this.quill.focus();
196
+ this.range = this.quill.getSelection();
197
+ event.preventDefault();
198
+ setTimeout(() => {
199
+ this.quill.focus();
200
+ this.range = this.quill.getSelection();
201
+ this.uploadAndInsertImage(file);
202
+ }, 0);
203
+ }
204
+ }
205
+ }
206
+ }
207
+ },
208
+ customVideoScreenShot() {
209
+ //获取视频截图
210
+ EventBus.$emit("get-screen-shot-image", this.componentId);
211
+ },
212
+ generateRandomId() {
213
+ return "id-" + Math.random().toString(36).slice(2, 11) + "-" + Date.now();
214
+ },
215
+ getLocaleValue(key) {
216
+ return this.t(key, this.$rootComponent.currentLanguage);
217
+ },
218
+ uploadAndInsertImage(file) {
219
+ const formData = new FormData();
220
+ formData.append("file", file);
221
+ let qmsAxios = this.$serverHttp;
222
+ if (qmsAxios) {
223
+ qmsAxios
224
+ .post("/api/storage/upload/file", formData)
225
+ .then((response) => {
226
+ if (response.status === 200 && response.data.data) {
227
+ let imageUrl = response.data.data.downloadUrl;
228
+ this.insertImageUrl(imageUrl);
229
+ }
230
+ })
231
+ .catch((error) => {
232
+ console.log("upload fail", error);
233
+ this.$Message.error(
234
+ this.getLocaleValue("survey_response.common.tips.upload_fail")
235
+ );
236
+ });
237
+ }
238
+ },
239
+ },
240
+ data() {
241
+ return {
242
+ quill: null,
243
+ componentId: null,
244
+ };
245
+ },
246
+ watch: {
247
+ value(newValue) {
248
+ if (newValue !== this.quill.root.innerHTML) {
249
+ this.quill.root.innerHTML = newValue;
250
+ this.$nextTick(() => {
251
+ this.quill.setSelection(newValue.length);
252
+ });
253
+ }
254
+ },
255
+ },
256
+ beforeDestroy() {
257
+ // 移除事件监听器
258
+ EventBus.$off("screen-shot-urls", this.handleScreenShotUrl);
259
+ this.quill.root.removeEventListener("paste", this.handlePaste);
260
+ },
261
+ });
262
+ </script>
263
+ <style src="quill/dist/quill.snow.css"></style>
264
+ <style src="quill/dist/quill.core.css"></style>
265
+ <style>
266
+ /* Quill的基本样式已经包含在引入的CSS文件中,如有需要,可以在这里添加额外的样式 */
267
+
268
+ .ql-snow .ql-color-picker .ql-picker-label,
269
+ .ql-snow .ql-icon-picker .ql-picker-label {
270
+ padding: 0 4px 2px 4px;
271
+ }
272
+ .ql-snow .ql-editor img {
273
+ max-width: 600px;
274
+ height: auto;
275
+ width: 100%;
276
+ }
277
+ .ql-editor {
278
+ box-sizing: border-box;
279
+ counter-reset: list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8
280
+ list-9;
281
+ line-height: 1.42;
282
+ height: 100%;
283
+ outline: 0;
284
+ overflow-y: auto;
285
+ padding: 12px 15px;
286
+ tab-size: 4;
287
+ -moz-tab-size: 4;
288
+ text-align: left;
289
+ white-space: pre-wrap;
290
+ word-wrap: break-word;
291
+ max-height: 500px;
292
+ }
293
+ </style>