dialogue-question.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <template>
  2. <!-- 对话题 -->
  3. <view class="question-area" v-model="questionData">
  4. <view class="question_title">
  5. <text class="question_number">
  6. {{ questionNumberEndIsBracket(questionData.property.question_number) }}
  7. </text>
  8. <text class="question_stem" v-html="sanitizeHTML(questionData.stem)"
  9. :ref="'richText-1-1'+questionData.question_id"
  10. @longpress="previewByRichTextImg(-1,-1,questionData.question_id)"></text>
  11. </view>
  12. <view class="description"
  13. v-if="isEnable(questionData.property.is_enable_description)&&questionData.description.length > 0"
  14. v-html="sanitizeHTML(questionData.description)" :ref="'richText-2-2'+questionData.question_id"
  15. @longpress="previewByRichTextImg(-2,-2,questionData.question_id)">
  16. </view>
  17. <view class="dialogue-wrapper">
  18. <view class="option-area" v-for="(item,index) in optionList" :key="index"
  19. :style="[{'flex-direction':item.type === 'input'?'row-reverse':'row'}]">
  20. <text class="avatar" :style="{ backgroundColor: item.color }">
  21. {{ item.name }}
  22. </text>
  23. <view v-if="item.type === 'text'" class="text-area">
  24. <view class="text-box">{{item.text}}</view>
  25. </view>
  26. <view v-else-if="item.type === 'image'" class="image-area">
  27. <img :src="file_map_list[item.file_id]" @click="preview(file_map_list[item.file_id])" />
  28. </view>
  29. <view v-else-if="item.type === 'audio'" class="audio-area">
  30. <AudioPlay :file-id="item.file_id" :show-slider="true" :show-progress="false"
  31. :background-color="item.color" />
  32. </view>
  33. <view v-else-if="item.type === 'input'" class="input-student-area">
  34. <SoundRecord v-if=" isEnable(questionData.property.is_enable_voice_answer)" :wav-blob.sync="item.file_id"
  35. type="small" :disabled="answer_control[questionData.question_id].isReadOnly" />
  36. </view>
  37. </view>
  38. </view>
  39. <view class="reference" v-if="isViewReference&&answer_control[questionData.question_id].isViewRightAnswer">
  40. <text class="reference-title">参考答案</text>
  41. <text class="reference-answer">{{questionData.reference_answer}}</text>
  42. </view>
  43. <view class="reference" v-if="isViewAnalysis&&answer_control[questionData.question_id].isViewRightAnswer">
  44. <text class="reference-title">解析</text>
  45. <text class="reference-answer" v-html="sanitizeHTML(questionData.analysis)"
  46. :ref="'richText-3-3'+questionData.question_id"
  47. @longpress="previewByRichTextImg(-3,-3,questionData.question_id)">
  48. </text>
  49. </view>
  50. </view>
  51. </template>
  52. <script>
  53. import {
  54. questionData,
  55. sanitizeHTML,
  56. isEnable,
  57. answer_control,
  58. } from '@/pages/answer_question/common/data/common.js';
  59. import {
  60. GetFileURLMap
  61. } from '@/api/api.js';
  62. import SoundRecord from '@/components/sound-record/sound-record.vue';
  63. import AnswerControlMixin from '@/pages/answer_question/common/data/AnswerControlMixin.js';
  64. export default {
  65. name: "dialogue-question",
  66. mixins: [AnswerControlMixin],
  67. components: {
  68. SoundRecord,
  69. },
  70. props: {
  71. questionData: questionData
  72. },
  73. data() {
  74. return {
  75. sanitizeHTML,
  76. isEnable,
  77. answer_control,
  78. isAnswerReady: false,
  79. optionList: [],
  80. file_map_list: [],
  81. };
  82. },
  83. watch: {
  84. 'questionData.option_list': {
  85. handler(val) {
  86. let list = JSON.parse(JSON.stringify(val));
  87. let file_id_list = [];
  88. list.forEach(({
  89. type,
  90. file_id
  91. }) => {
  92. if (type === 'image' || type === 'audio') {
  93. file_id_list.push(file_id);
  94. }
  95. });
  96. GetFileURLMap({
  97. file_id_list
  98. }).then(({
  99. url_map
  100. }) => {
  101. this.file_map_list = url_map;
  102. });
  103. //遍历 list 数组
  104. const newArray = list.map(item => {
  105. // 在 role_list 数组中找到与 item.mark 相等的对象
  106. const matchedItem = this.questionData.property.role_list.find(element => element.mark === item.role);
  107. // 如果找到了匹配的对象,则将其 color 属性拷贝到 item 对象中
  108. if (matchedItem) {
  109. item.color = matchedItem.color;
  110. item.name = matchedItem.name;
  111. }
  112. return item;
  113. });
  114. this.optionList = newArray;
  115. },
  116. deep: true,
  117. immediate: true,
  118. },
  119. 'questionData.question_id': {
  120. handler(val) {
  121. this.commonComputedAnswerControl(val);
  122. this.setUserAnswer();
  123. },
  124. immediate: true,
  125. deep: true
  126. },
  127. optionList: {
  128. handler(val) {
  129. this.questionData.user_answer[this.questionData.question_id].answer_list = [];
  130. val.filter(p => p.type == "input").forEach(p => {
  131. if (p.file_id.length <= 0) return;
  132. this.questionData.user_answer[this.questionData.question_id].answer_list.push({
  133. audio_file_id: p.file_id,
  134. mark: p.mark
  135. });
  136. });
  137. },
  138. deep: true,
  139. immediate: true,
  140. },
  141. answer_control: {
  142. handler(val) {
  143. var cur = this.commonComputedAnswerControl(this.questionData.question_id);
  144. if (!cur.isJudgeAnswer) return;
  145. this.questionData.user_answer[this.questionData.question_id].answer_list.forEach(({
  146. mark,
  147. audio_file_id
  148. }) => {
  149. let findOption = this.optionList.find((item) => item.mark === mark);
  150. findOption.file_id = audio_file_id;
  151. });
  152. },
  153. deep: true,
  154. immediate: true,
  155. },
  156. },
  157. computed: {
  158. isViewReference: function() {
  159. return isEnable(this.questionData.property.is_enable_reference_answer);
  160. },
  161. isViewAnalysis: function() {
  162. return isEnable(this.questionData.property.is_enable_analysis);
  163. }
  164. },
  165. methods: {
  166. // 获取用户答案
  167. setUserAnswer: function() {
  168. var that = this;
  169. var callback = function() {
  170. var questionId = that.questionData.question_id;
  171. var answerList = that.questionData.user_answer[questionId].answer_list;
  172. that.optionList.forEach(p => {
  173. var answer = answerList.find(x => x.mark == p.mark);
  174. if (!answer) return false;
  175. p.file_id = answer.audio_file_id;
  176. })
  177. }
  178. this.$emit("getUserAnswer", this.questionData.question_id, callback);
  179. },
  180. }
  181. }
  182. </script>
  183. <style lang="scss" scoped>
  184. .question-area {
  185. .dialogue-wrapper {
  186. margin: 32rpx auto;
  187. display: flex;
  188. flex-direction: column;
  189. row-gap: 32rpx;
  190. .option-area {
  191. display: flex;
  192. column-gap: 16rpx;
  193. .avatar {
  194. display: flex;
  195. align-items: center;
  196. justify-content: center;
  197. width: 80rpx;
  198. min-width: 80rpx;
  199. height: 80rpx;
  200. min-height: 80rpx;
  201. font-size: 24rpx;
  202. color: #ffffff;
  203. border-radius: 50%;
  204. }
  205. .audio-area {
  206. width: 430rpx;
  207. /deep/ .audio-wrapper {
  208. margin: 0;
  209. }
  210. }
  211. .text-area {
  212. display: flex;
  213. flex-direction: column;
  214. align-items: flex-start;
  215. .text-box {
  216. background-color: #F0F0F0;
  217. padding: 16rpx;
  218. border-radius: 16rpx;
  219. line-height: 44rpx;
  220. font-size: 28rpx;
  221. .content-text {
  222. vertical-align: top;
  223. }
  224. /deep/ uni-textarea {
  225. display: inline-block;
  226. border-bottom: 1px solid #000000;
  227. text-align: center;
  228. vertical-align: baseline;
  229. line-height: 46rpx;
  230. font-size: 28rpx;
  231. margin: 0 8rpx;
  232. max-width: calc(100vw - 240rpx);
  233. &.right {
  234. color: $right-color;
  235. }
  236. &.wrong {
  237. color: $error-color;
  238. }
  239. }
  240. .right-answer {
  241. margin-left: -8rpx;
  242. width: auto;
  243. max-width: calc(100vw - 240rpx);
  244. height: 54rpx;
  245. line-height: 54rpx;
  246. }
  247. }
  248. /deep/ .sound-record-wrapper {
  249. margin-top: 16rpx;
  250. .sound-item-box {
  251. background-color: #F0F0F0;
  252. border-radius: 80rpx;
  253. padding: 8rpx;
  254. .progress-box {
  255. background-color: #F0F0F0;
  256. }
  257. .sound-item-microphone {
  258. background-color: $light-main-color;
  259. }
  260. }
  261. }
  262. }
  263. .image-area {
  264. max-width: 100%;
  265. max-height: 100%;
  266. img {
  267. border-radius: 16rpx;
  268. }
  269. }
  270. .input-student-area {
  271. display: flex;
  272. align-items: center;
  273. column-gap: 16rpx;
  274. /deep/ .sound-record-wrapper {
  275. margin-top: 0;
  276. .sound-item-box {
  277. background-color: #F0F0F0;
  278. border-radius: 80rpx;
  279. padding: 8rpx;
  280. .progress-box {
  281. background-color: #F0F0F0;
  282. }
  283. .sound-item-microphone {
  284. background-color: $light-main-color;
  285. }
  286. }
  287. }
  288. .avatar {
  289. display: flex;
  290. align-items: center;
  291. justify-content: center;
  292. width: 80rpx;
  293. min-width: 80rpx;
  294. height: 80rpx;
  295. min-height: 80rpx;
  296. font-size: 24rpx;
  297. color: #ffffff;
  298. border-radius: 50%;
  299. background-color: $light-main-color;
  300. }
  301. }
  302. }
  303. }
  304. .reference {
  305. margin: 32rpx 0;
  306. background-color: $uni-bg-color-grey;
  307. padding: 24rpx;
  308. font-size: 28rpx;
  309. .reference-title {
  310. display: block;
  311. line-height: 64rpx;
  312. color: #4E5969;
  313. }
  314. .reference-answer {
  315. color: #1D2129;
  316. line-height: 48rpx;
  317. }
  318. }
  319. }
  320. </style>