@wg-npm/survey-response 0.3.13 → 0.3.377-8.develop

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,146 +1,179 @@
1
1
  <template>
2
- <div v-if="!emptyQuestions" class="survey-response-wrapper">
3
- <Form ref="responseForm" :model="response" :disabled="readonly">
4
- <div :key="index" v-for="(question, index) in survey.questions" class="question">
5
- <question-title
6
- :question="question"
7
- :index="index"
8
- >
9
- </question-title>
10
- <question-body
11
- :question="calculateQuestion(question, index)"
12
- :answer="response.answers[index]"
13
- :index="index"
14
- :options="options"
15
- :readonly="readonly"
16
- >
17
- </question-body>
18
- </div>
19
- </Form>
20
- </div>
21
- <empty-question v-else></empty-question>
2
+ <div v-if="!emptyQuestions" class="survey-response-wrapper">
3
+ <Form ref="responseForm" :model="response" :disabled="readonly">
4
+ <div
5
+ :key="index"
6
+ v-for="(question, index) in survey.questions"
7
+ class="question"
8
+ >
9
+ <question-title :question="question" :index="index"> </question-title>
10
+ <question-body
11
+ :question="calculateQuestion(question, index)"
12
+ :answer="response.answers[index]"
13
+ :index="index"
14
+ :options="options"
15
+ :readonly="readonly"
16
+ >
17
+ </question-body>
18
+ </div>
19
+ </Form>
20
+ </div>
21
+ <empty-question v-else></empty-question>
22
22
  </template>
23
23
  <script lang="ts">
24
- import Vue from 'vue';
25
- import _ from 'lodash';
26
- import EmptyQuestion from './empty-question.vue';
27
- import QuestionTitle from './question/question-title.vue';
28
- import QuestionBody from './question/question-body.vue';
29
- import {Form} from "view-design";
30
- import {QuestionType} from "@wg-npm/survey-core";
24
+ import Vue from "vue";
25
+ import _ from "lodash";
26
+ import EmptyQuestion from "./empty-question.vue";
27
+ import QuestionTitle from "./question/question-title.vue";
28
+ import QuestionBody from "./question/question-body.vue";
29
+ import { Form } from "view-design";
30
+ import { QuestionType } from "@wg-npm/survey-core";
31
31
 
32
- export default Vue.extend({
33
- name: 'SurveyResponse',
34
- components: {
35
- QuestionTitle,
36
- QuestionBody,
37
- EmptyQuestion,
38
- Form
39
- },
40
- provide() {
41
- return {
42
- responseStar: this.response.status != this.$consts.STATUS_DRAFT ? this.response.star : null,
43
- survey: this.survey,
44
- responseStatus: this.response.status,
45
- $rootComponent: this
32
+ export default Vue.extend({
33
+ name: "SurveyResponse",
34
+ components: {
35
+ QuestionTitle,
36
+ QuestionBody,
37
+ EmptyQuestion,
38
+ Form
39
+ },
40
+ provide() {
41
+ return {
42
+ responseStar:
43
+ this.response.status != this.$consts.STATUS_DRAFT
44
+ ? this.response.star
45
+ : null,
46
+ survey: this.survey,
47
+ responseStatus: this.response.status,
48
+ $rootComponent: this
49
+ };
50
+ },
51
+ props: {
52
+ survey: {
53
+ type: Object,
54
+ required: true
55
+ },
56
+ response: {
57
+ type: Object,
58
+ required: true
59
+ },
60
+ options: {
61
+ type: Object
62
+ },
63
+ size: {
64
+ type: String,
65
+ default: "default"
66
+ }
67
+ },
68
+ data() {
69
+ return {
70
+ answers: {}
71
+ };
72
+ },
73
+ created() {
74
+ this.parseQuestionAnswers();
75
+ },
76
+ computed: {
77
+ emptyQuestions: function() {
78
+ return _.size(this.survey.questions) == 0;
79
+ },
80
+ readonly() {
81
+ return this.response.status != this.$consts.STATUS_DRAFT;
82
+ },
83
+ layout() {
84
+ const globalLayout = _.lowerCase(
85
+ _.get(this.options, "layout", undefined)
86
+ );
87
+ return globalLayout || "horizontal";
88
+ }
89
+ },
90
+ methods: {
91
+ calculateQuestion(question, index) {
92
+ let backgroundAnswers =
93
+ this.response?.variables?.backgroundResponse?.answers ?? [];
94
+ this.calculateQuestionReadonlyOption(
95
+ question,
96
+ _.isEmpty(backgroundAnswers) ? {} : backgroundAnswers[index]
97
+ );
98
+ return question;
99
+ },
100
+ calculateQuestionReadonlyOption(question, backgroundAnswer) {
101
+ if (
102
+ question.type == QuestionType.TEXT_TITLE ||
103
+ question.type == QuestionType.SINGLE_SELECTION ||
104
+ question.type == QuestionType.FILL_BLANK ||
105
+ question.type == QuestionType.SHORT_ANSWER ||
106
+ question.type == QuestionType.MULTI_SELECTION
107
+ ) {
108
+ _.set(
109
+ question.options,
110
+ "readonly",
111
+ this.haveAnswered(backgroundAnswer)
112
+ );
113
+ }
114
+ if (question.type == QuestionType.MATRIX) {
115
+ _.forEach(question.subQuestions, (subQuestion, subIndex) => {
116
+ let backgroundSubAnswer = _.get(
117
+ backgroundAnswer,
118
+ `answer[${subIndex}]`,
119
+ null
120
+ );
121
+ _.set(subQuestion, "options", {
122
+ readonly: this.haveAnswered(backgroundSubAnswer)
123
+ });
124
+ });
125
+ }
126
+ },
127
+ haveAnswered(backgroundAnswer) {
128
+ return !_.isEmpty(backgroundAnswer?.answer ?? null);
129
+ },
130
+ parseQuestionAnswers() {
131
+ _.each(this.response.answers, data => {
132
+ _.set(this.answers, data.questionId, data);
133
+ });
134
+ _.set(this.response, "answers", []);
135
+ _.each(this.survey.questions, question => {
136
+ let answer: any = _.get(this.answers, question.id);
137
+ answer = answer
138
+ ? answer
139
+ : {
140
+ answer: question.type == "MULTI_SELECTION" ? [] : null,
141
+ question_id: question.id,
142
+ question_type: question.type,
143
+ score: null,
144
+ star: null
46
145
  };
47
- },
48
- props: {
49
- survey: {
50
- type: Object,
51
- required: true
52
- },
53
- response: {
54
- type: Object,
55
- required: true
56
- },
57
- options: {
58
- type: Object
59
- },
60
- size: {
61
- type: String,
62
- default: 'default'
63
- }
64
- },
65
- data() {
66
- return {
67
- answers: {}
68
- }
69
- },
70
- created() {
71
- this.parseQuestionAnswers();
72
- },
73
- computed: {
74
- emptyQuestions: function () {
75
- return _.size(this.survey.questions) == 0;
76
- },
77
- readonly() {
78
- return this.response.status != this.$consts.STATUS_DRAFT;
79
- }
80
- },
81
- methods: {
82
- calculateQuestion(question, index) {
83
- let backgroundAnswers = this.response?.variables?.backgroundResponse?.answers ?? [];
84
- this.calculateQuestionReadonlyOption(question, _.isEmpty(backgroundAnswers) ? {} : backgroundAnswers[index]);
85
- return question;
86
- },
87
- calculateQuestionReadonlyOption(question, backgroundAnswer) {
88
- if (question.type == QuestionType.TEXT_TITLE ||
89
- question.type == QuestionType.SINGLE_SELECTION ||
90
- question.type == QuestionType.FILL_BLANK ||
91
- question.type == QuestionType.SHORT_ANSWER ||
92
- question.type == QuestionType.MULTI_SELECTION) {
93
- _.set(question.options, 'readonly', this.haveAnswered(backgroundAnswer));
94
- }
95
- if (question.type == QuestionType.MATRIX) {
96
- _.forEach(question.subQuestions, (subQuestion, subIndex) => {
97
- let backgroundSubAnswer = _.get(backgroundAnswer, `answer[${subIndex}]`, null)
98
- _.set(subQuestion, "options", {'readonly': this.haveAnswered(backgroundSubAnswer)});
99
- })
100
- }
101
- },
102
- haveAnswered(backgroundAnswer) {
103
- return !_.isEmpty(backgroundAnswer?.answer ?? null);
104
- },
105
- parseQuestionAnswers() {
106
- _.each(this.response.answers, data => {
107
- _.set(this.answers, data.questionId, data);
108
- });
109
- _.set(this.response, 'answers', []);
110
- _.each(this.survey.questions, question => {
111
- let answer: any = _.get(this.answers, question.id);
112
- answer = answer
113
- ? answer
114
- : {
115
- answer: question.type == 'MULTI_SELECTION' ? [] : null,
116
- question_id: question.id,
117
- question_type: question.type,
118
- score: null,
119
- star: null
120
- };
121
- this.response.answers.push(answer);
122
- });
123
- },
124
- validate() {
125
- let result;
126
- this.$refs["responseForm"].validate(valid => {
127
- result = valid;
128
- });
129
- return result;
130
- },
131
- getMaxScore(question) {
132
- if (question.type === 'single_selection') {
133
- // @ts-ignore
134
- var score = _.maxBy(question.choices, answer_option => answer_option.options.score || 0).options.score;
135
- if (!score) {
136
- return 0;
137
- }
138
- return score;
139
- } else {
140
- // @ts-ignore
141
- return _.sumBy(question.choices, answer_option => answer_option.options.score || 0);
142
- }
143
- }
146
+ this.response.answers.push(answer);
147
+ });
148
+ },
149
+ validate() {
150
+ let result;
151
+ this.$refs["responseForm"].validate(valid => {
152
+ result = valid;
153
+ });
154
+ return result;
155
+ },
156
+ getMaxScore(question) {
157
+ if (question.type === "single_selection") {
158
+ // @ts-ignore
159
+ var score = _.maxBy(
160
+ question.choices,
161
+ // @ts-ignore
162
+ answer_option => answer_option.options.score || 0
163
+ ).options.score;
164
+ if (!score) {
165
+ return 0;
144
166
  }
145
- });
167
+ return score;
168
+ } else {
169
+ // @ts-ignore
170
+ return _.sumBy(
171
+ question.choices,
172
+ // @ts-ignore
173
+ answer_option => answer_option.options.score || 0
174
+ );
175
+ }
176
+ }
177
+ }
178
+ });
146
179
  </script>
@@ -0,0 +1,160 @@
1
+ import Vue from "vue";
2
+
3
+ const GLOBAL_TOOLTIP_ID = "ellipsis-directive-tooltip-" + Math.random();
4
+ const singleTipBox = function (maxWidth) {
5
+ let tipBox = document.getElementById(GLOBAL_TOOLTIP_ID);
6
+ if (!tipBox) {
7
+ tipBox = document.createElement("div");
8
+ tipBox.id = GLOBAL_TOOLTIP_ID;
9
+ tipBox.style.display = "none";
10
+ tipBox.style.position = "fixed";
11
+ tipBox.style.maxWidth = maxWidth + "px";
12
+ tipBox.style.padding = "8px 12px";
13
+ tipBox.style.backgroundColor = "rgba(70,76,91,.9)";
14
+ tipBox.style.borderRadius = "4px";
15
+ tipBox.style.boxShadow = "0 1px 6px rgba(0,0,0,.2)";
16
+ tipBox.style.color = "#fff";
17
+ tipBox.style.zIndex = "1200";
18
+ tipBox.style.wordWrap = "break-word";
19
+ tipBox.style.wordBreak = "break-all";
20
+ document.body.appendChild(tipBox);
21
+
22
+ tipBox.addEventListener("mouseover", (event) => {
23
+ // @ts-ignore
24
+ tipBox.style.display = "block";
25
+ });
26
+ tipBox.addEventListener("mouseout", (event) => {
27
+ // @ts-ignore
28
+ tipBox.style.display = "none";
29
+ });
30
+ }
31
+ return tipBox;
32
+ };
33
+
34
+ const createArrow = function () {
35
+ const arrow = document.createElement("div");
36
+ arrow.style.position = "absolute";
37
+ arrow.style.width = "0";
38
+ arrow.style.height = "0";
39
+ arrow.style.borderColor = "transparent";
40
+ arrow.style.borderStyle = "solid";
41
+ return arrow;
42
+ };
43
+
44
+ const setArrowStyle = function (event: MouseEvent, arrowEle, tipBox) {
45
+ if (event.clientY > tipBox.offsetTop) {
46
+ arrowEle.style.bottom = "-5px";
47
+ arrowEle.style.top = "auto";
48
+ arrowEle.style.borderWidth = "5px 5px 0";
49
+ arrowEle.style.borderTopColor = "rgba(70,76,91,.9)";
50
+ } else {
51
+ arrowEle.style.top = "-5px";
52
+ arrowEle.style.bottom = "auto";
53
+ arrowEle.style.borderWidth = "0 5px 5px";
54
+ arrowEle.style.borderBottomColor = "rgba(70,76,91,.9)";
55
+ }
56
+
57
+ if (event.clientX > tipBox.offsetLeft + 10) {
58
+ tipBox.style.display = "flex";
59
+ tipBox.style.justifyContent = "flex-end";
60
+ } else {
61
+ tipBox.style.display = "flex";
62
+ tipBox.style.justifyContent = "flex-start";
63
+ }
64
+ };
65
+
66
+ const calcTextWidth = function (el) {
67
+ el.style.whiteSpace = "nowrap";
68
+ el.style.display = "inline-block";
69
+ const textWidth = el.offsetWidth;
70
+ el.style.display = "none";
71
+ return textWidth;
72
+ };
73
+
74
+ const calcParentWidth = function (el) {
75
+ el.style.display = "block";
76
+ return el.parentElement.offsetWidth;
77
+ };
78
+
79
+ const calcTipTop = function (el, tipBox) {
80
+ let tipTop = el.getBoundingClientRect().top + el.offsetHeight;
81
+ const tipBottom = tipTop + tipBox.offsetHeight;
82
+ if (window.innerHeight - tipBottom < 1) {
83
+ tipTop = tipTop - tipBox.offsetHeight - el.offsetHeight;
84
+ }
85
+ return tipTop + "px";
86
+ };
87
+
88
+ const calcTopLeft = function (event, tipBox) {
89
+ let tipLeft = event.clientX - 10;
90
+ const tipRight = tipLeft + tipBox.offsetWidth;
91
+ if (window.innerWidth - tipRight < 1) {
92
+ tipLeft = tipLeft - tipBox.offsetWidth;
93
+ }
94
+ return tipLeft + "px";
95
+ };
96
+
97
+ const ellipsis = function (el, defaultLine) {
98
+ const line = defaultLine || 1;
99
+ el.style.display = "-webkit-box";
100
+ el.style.webkitLineClamp = line;
101
+ el.style.webkitBoxOrient = "vertical";
102
+ el.style.textOverflow = "ellipsis";
103
+ el.style.overflow = "hidden";
104
+ };
105
+
106
+ const show = function (tipBox) {
107
+ tipBox.style.display = "block";
108
+ };
109
+
110
+ const hidden = function (tipBox) {
111
+ tipBox.style.display = "none";
112
+ };
113
+
114
+ const contentText = function (tipBox, content) {
115
+ tipBox.innerText = content;
116
+ };
117
+
118
+ const ellipsisTip = function (el: HTMLElement, binding) {
119
+ const line = (binding.value && binding.value.defaultLine) || 1;
120
+ const textWidth = calcTextWidth(el);
121
+ const parentWidth = calcParentWidth(el);
122
+ el.style.whiteSpace = "normal";
123
+ if (textWidth > parentWidth * line) {
124
+ ellipsis(el, line);
125
+ const tipBox = singleTipBox(500);
126
+ const arrowEle = createArrow();
127
+
128
+ el.addEventListener("mousemove", (event) => {
129
+ contentText(
130
+ tipBox,
131
+ (binding.value && binding.value.content) || el.innerText
132
+ );
133
+ tipBox.appendChild(arrowEle);
134
+ show(tipBox);
135
+ el.style.cursor = "pointer";
136
+ tipBox.style.top = calcTipTop(el, tipBox);
137
+ tipBox.style.left = calcTopLeft(event, tipBox);
138
+ setArrowStyle(event, arrowEle, tipBox);
139
+ });
140
+
141
+ el.addEventListener("mouseout", (event) => {
142
+ hidden(tipBox);
143
+ });
144
+ }
145
+ };
146
+
147
+ Vue.directive("ellipsis-tip", {
148
+ bind: function (el, binding, vnode) {
149
+ window.addEventListener("resize", () => ellipsisTip(el, binding));
150
+ },
151
+ inserted: function (el, binding, vnode) {
152
+ ellipsisTip(el, binding);
153
+ },
154
+ componentUpdated(el, binding) {
155
+ ellipsisTip(el, binding);
156
+ },
157
+ unbind(el, binding) {
158
+ window.removeEventListener("resize", () => ellipsisTip(el, binding));
159
+ },
160
+ });
@@ -2,20 +2,27 @@ import Vue from "vue";
2
2
  import _ from "lodash";
3
3
 
4
4
  export default Vue.extend({
5
- methods: {
6
- isMobile() {
7
- let flag = navigator.userAgent.match(
8
- /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
9
- );
10
- return flag;
11
- }
12
- },
13
- computed: {
14
- optionLayout() {
15
- if (this.isMobile) {
16
- return ""
17
- }
18
- return `question-choice-${_.lowerCase(_.get(this.question.options, "layout", "horizontal"))}`;
19
- }
5
+ methods: {
6
+ isMobile() {
7
+ const flag = navigator.userAgent.match(
8
+ /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
9
+ );
10
+ return flag;
20
11
  }
12
+ },
13
+ computed: {
14
+ optionLayout() {
15
+ const globalLayout = _.lowerCase(
16
+ _.get(this.options, "layout", undefined)
17
+ );
18
+
19
+ if (this.isMobile || "vertical" == globalLayout) {
20
+ return "";
21
+ }
22
+ const questionLayout = _.lowerCase(
23
+ _.get(this.question.options, "layout", "horizontal")
24
+ );
25
+ return `question-choice-${questionLayout}`;
26
+ }
27
+ }
21
28
  });