index.vue 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. <template>
  2. <div class="app-container">
  3. <!-- 搜索栏 -->
  4. <el-form :inline="true">
  5. <el-form-item label="会议日期">
  6. <el-date-picker
  7. size="small"
  8. v-model="meetingDate"
  9. align="right"
  10. type="date"
  11. placeholder="选择会议日期"
  12. :picker-options="pickerOptions"
  13. style="width:200px"
  14. >
  15. </el-date-picker>
  16. </el-form-item>
  17. <el-form-item label="参会导师">
  18. <el-select
  19. size="small"
  20. v-model="mentorOptionsArray"
  21. multiple
  22. placeholder="请选择导师"
  23. prop="mentorId"
  24. style="width:200px"
  25. >
  26. <el-option
  27. v-for="mentor in this.mentorOptions"
  28. :key="mentor.key"
  29. :label="mentor.value"
  30. :value="mentor.key">
  31. </el-option>
  32. </el-select>
  33. </el-form-item>
  34. <el-form-item>
  35. <el-button
  36. icon="el-icon-s-promotion"
  37. type="success"
  38. size="mini"
  39. @click="handleInvite"
  40. >
  41. 邀请
  42. </el-button>
  43. </el-form-item>
  44. </el-form>
  45. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
  46. <el-form-item label="学员" prop="successorId">
  47. <el-select
  48. size="small"
  49. v-model="queryParams.successorId"
  50. placeholder="请选择学员"
  51. style="width:200px"
  52. >
  53. <el-option
  54. v-for="successor in this.successorOptions"
  55. :key="successor.key"
  56. :label="successor.value"
  57. :value="successor.key"
  58. ></el-option>
  59. </el-select>
  60. </el-form-item>
  61. <el-form-item label="年" prop="feedbackYear">
  62. <el-date-picker
  63. v-model="queryParams.feedbackYearTemp"
  64. placeholder="请选择年"
  65. clearable
  66. type="year"
  67. size="small"
  68. @keyup.enter.native="handleQuery"
  69. style="width:200px"
  70. />
  71. </el-form-item>
  72. <el-form-item label="季度" prop="feedbackSason">
  73. <el-select
  74. v-model="queryParams.feedbackSeason"
  75. placeholder="请选择季度"
  76. clearable
  77. size="small"
  78. @keyup.enter.native="handleQuery"
  79. style="width:200px"
  80. >
  81. <el-option label="第一季度" value="1"></el-option>
  82. <el-option label="第二季度" value="2"></el-option>
  83. <el-option label="第三季度" value="3"></el-option>
  84. <el-option label="第四季度" value="4"></el-option>
  85. </el-select>
  86. </el-form-item>
  87. <el-form-item>
  88. <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  89. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  90. </el-form-item>
  91. </el-form>
  92. <!-- 标签页 -->
  93. <el-tabs type="border-card" @tab-click="handleTabClick" v-model="activeName">
  94. <el-tab-pane
  95. :key="item.name"
  96. v-for="(item) in mentorTabs"
  97. :label="item.title"
  98. :name="item.name"
  99. >
  100. <!-- 培养计划总表 -->
  101. <el-table
  102. v-loading="loading"
  103. :data="dataList"
  104. @selection-change="handleSelectionChange"
  105. ref="feedbackListSeasonalTable"
  106. border
  107. :cell-style="tableCellStyle"
  108. >
  109. <el-table-column label="培养内容" align="center" prop="plantName" width="600px"/>
  110. <el-table-column label="开始日期" align="center" prop="startDate"/>
  111. <el-table-column label="结束日期" align="center" prop="endDate"/>
  112. <el-table-column label="实际完成日期" align="center" prop="dateOfCompletion"/>
  113. <el-table-column label="学习状态" align="center" prop="studyState" :formatter="studyStateFormat"/>
  114. <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
  115. <template slot-scope="scope">
  116. <el-button
  117. size="mini"
  118. type="text"
  119. icon="el-icon-chat-dot-round"
  120. v-hasPermi="['spec:plan:edit']"
  121. @click="handleFeedback(scope.row)"
  122. >详情</el-button>
  123. </template>
  124. </el-table-column>
  125. </el-table>
  126. <!-- 汇报附件 -->
  127. <div>
  128. <h3>汇报展示PPT</h3>
  129. <el-upload
  130. ref="doc"
  131. :headers="doc.headers"
  132. class="upload-demo"
  133. :action="doc.url + '?pType=' + doc.pType + '&pId=' + doc.pId"
  134. :disabled="doc.isUploading"
  135. :on-progress="handleFileDocProgress"
  136. :on-success="handleFileDocSuccess"
  137. :auto-upload="true"
  138. multiple
  139. :limit="3">
  140. <el-button size="small" type="primary" style="margin-bottom:10px;">点击上传</el-button>
  141. </el-upload>
  142. <el-table :data="doc.commonfileList" border style="width:600px;">
  143. <el-table-column :label="$t('文件名')" align="center" prop="fileName" :show-overflow-tooltip="true">
  144. <template slot-scope="scope">
  145. <a class="link-type" @click="handleDownload(scope.row)">
  146. <span>{{ scope.row.fileName }}</span>
  147. </a>
  148. </template>
  149. </el-table-column>
  150. <el-table-column :label="$t('操作')" align="center" width="120" class-name="small-padding fixed-width">
  151. <template slot-scope="scope">
  152. <el-button
  153. v-if="scope.row.fileName.endsWith('pdf')"
  154. size="mini"
  155. type="text"
  156. icon="el-icon-view"
  157. @click="handleSee(scope.row)"
  158. >{{ $t('预览') }}</el-button>
  159. <el-button
  160. size="mini"
  161. type="text"
  162. icon="el-icon-download"
  163. @click="handleDownload(scope.row)"
  164. >{{ $t('下载') }}</el-button>
  165. <el-button
  166. size="mini"
  167. type="text"
  168. icon="el-icon-delete"
  169. @click="handleDeleteDoc(scope.row)"
  170. >{{ $t('删除') }}</el-button>
  171. </template>
  172. </el-table-column>
  173. </el-table>
  174. </div>
  175. <!-- 问卷 -->
  176. <div>
  177. <div style="display:inline-block;width:40%;">
  178. <h3 style="margin-bottom:0px;">学员表现</h3>
  179. <div class="question">
  180. 1. 学员的现场汇报精神面貌如何?
  181. </div>
  182. <div class="answer">
  183. <el-radio-group v-model="radio1" size="small">
  184. <el-radio label="1">优秀</el-radio>
  185. <el-radio label="2">良好</el-radio>
  186. <el-radio label="3">一般</el-radio>
  187. <el-radio label="4">基本合格</el-radio>
  188. </el-radio-group>
  189. </div>
  190. <div class="question">
  191. 2. 学员的现场汇报语言表达如何?
  192. </div>
  193. <div class="answer">
  194. <el-radio-group v-model="radio2" size="small">
  195. <el-radio label="1">优秀</el-radio>
  196. <el-radio label="2">良好</el-radio>
  197. <el-radio label="3">一般</el-radio>
  198. <el-radio label="4">基本合格</el-radio>
  199. </el-radio-group>
  200. </div>
  201. <div class="question">
  202. 3. 学员的现场汇报逻辑思维如何?
  203. </div>
  204. <div class="answer">
  205. <el-radio-group v-model="radio3" size="small">
  206. <el-radio label="1">优秀</el-radio>
  207. <el-radio label="2">良好</el-radio>
  208. <el-radio label="3">一般</el-radio>
  209. <el-radio label="4">基本合格</el-radio>
  210. </el-radio-group>
  211. </div>
  212. <h3 style="margin-bottom:0px;">汇报材料及内容</h3>
  213. <div class="question">
  214. 4. 汇报材料准备是否充分,汇报材料是否紧贴本季学习内容?
  215. </div>
  216. <div class="answer">
  217. <el-radio-group v-model="radio4" size="small">
  218. <el-radio label="1">充分</el-radio>
  219. <el-radio label="2">比较充分</el-radio>
  220. <el-radio label="3">一般</el-radio>
  221. <el-radio label="4">基本合格</el-radio>
  222. </el-radio-group>
  223. </div>
  224. <div class="question">
  225. 5. 汇报内容是否符合学习材料(SOP或者程序)的要求?
  226. </div>
  227. <div class="answer">
  228. <el-radio-group v-model="radio5" size="small">
  229. <el-radio label="1">完全符合</el-radio>
  230. <el-radio label="2">基本符合</el-radio>
  231. <el-radio label="3">一般</el-radio>
  232. <el-radio label="4">基本合格</el-radio>
  233. </el-radio-group>
  234. </div>
  235. <div class="question">
  236. 6. 学员对本季的学习内容、学习目标及学习结果的阐述是否具体、完整、准确?
  237. </div>
  238. <div class="answer">
  239. <el-radio-group v-model="radio6" size="small">
  240. <el-radio label="1">非常全面</el-radio>
  241. <el-radio label="2">全面</el-radio>
  242. <el-radio label="3">一般</el-radio>
  243. <el-radio label="4">基本合格</el-radio>
  244. </el-radio-group>
  245. </div>
  246. <div class="question">
  247. 7. 学员本季汇报内容较上次是否有所提升?
  248. </div>
  249. <div class="answer">
  250. <el-radio-group v-model="radio7" size="small">
  251. <el-radio label="1">进步明显</el-radio>
  252. <el-radio label="2">略有提升</el-radio>
  253. <el-radio label="3">一般</el-radio>
  254. <el-radio label="4">基本合格</el-radio>
  255. </el-radio-group>
  256. </div>
  257. </div>
  258. <div style="display:inline-block;width:40%;">
  259. <div class="question">
  260. 8. 汇报内容针对本季学习范围是否存在明显的汇报漏项或者不足?
  261. </div>
  262. <div class="answer">
  263. <el-radio-group v-model="radio8" size="small">
  264. <el-radio label="1">完全不存在</el-radio>
  265. <el-radio label="2">基本不存在</el-radio>
  266. <el-radio label="3">无明显存在</el-radio>
  267. <el-radio label="4">存在</el-radio>
  268. </el-radio-group>
  269. </div>
  270. <div class="question">
  271. 9. 学员的本季汇报内容是否结合岗位工作实际?
  272. </div>
  273. <div class="answer">
  274. <el-radio-group v-model="radio9" size="small">
  275. <el-radio label="1">紧密结合</el-radio>
  276. <el-radio label="2">结合</el-radio>
  277. <el-radio label="3">一般</el-radio>
  278. <el-radio label="4">基本合格</el-radio>
  279. </el-radio-group>
  280. </div>
  281. <div class="question">
  282. 10. 学员在实际工作中是否能够学有所用?
  283. </div>
  284. <div class="answer">
  285. <el-radio-group v-model="radio10" size="small">
  286. <el-radio label="1">灵活运用</el-radio>
  287. <el-radio label="2">基本运用</el-radio>
  288. <el-radio label="3">尝试运用</el-radio>
  289. <el-radio label="4">基本合格</el-radio>
  290. </el-radio-group>
  291. </div>
  292. <div class="question">
  293. 11. 汇报内容对目标岗位建设是否能起到启发促进作用?
  294. </div>
  295. <div class="answer">
  296. <el-radio-group v-model="radio11" size="small">
  297. <el-radio label="1">极有建设性</el-radio>
  298. <el-radio label="2">建设性</el-radio>
  299. <el-radio label="3">一般</el-radio>
  300. <el-radio label="4">基本合格</el-radio>
  301. </el-radio-group>
  302. </div>
  303. <div class="question">
  304. 12. 汇报内容对于装置及公司的人才培养计划是否能起到建设性促进作用?
  305. </div>
  306. <div class="answer">
  307. <el-radio-group v-model="radio12" size="small">
  308. <el-radio label="1">极有建设性</el-radio>
  309. <el-radio label="2">建设性</el-radio>
  310. <el-radio label="3">一般</el-radio>
  311. <el-radio label="4">基本合格</el-radio>
  312. </el-radio-group>
  313. </div>
  314. <div class="question">
  315. 13. 汇报内容是否有对装置生产、管理、建设提出合理化建议?
  316. </div>
  317. <div class="answer">
  318. <el-radio-group v-model="radio13" size="small">
  319. <el-radio label="1">有可采用的合理化建议</el-radio>
  320. <el-radio label="2">有可行性合理化建议</el-radio>
  321. <el-radio label="3">有合理化建议</el-radio>
  322. <el-radio label="4">无</el-radio>
  323. </el-radio-group>
  324. </div>
  325. <div class="question">
  326. 14. 汇报内容是否对下期培训提出新的培训需求?
  327. </div>
  328. <div class="answer">
  329. <el-radio-group v-model="radio14" size="small">
  330. <el-radio label="1">有计划外需求</el-radio>
  331. <el-radio label="2">有计划内需求</el-radio>
  332. <el-radio label="3">有需求</el-radio>
  333. <el-radio label="4">无具体需求</el-radio>
  334. </el-radio-group>
  335. </div>
  336. </div>
  337. </div>
  338. <!-- 导师反馈 -->
  339. <div>
  340. <h3 style="margin-top:0px;">导师反馈</h3>
  341. <editor :min-height="300" v-model="mentorFeedback"/>
  342. </div>
  343. <!-- 保存按钮 -->
  344. <div style="text-align:center;margin: 20px auto;">
  345. <el-button
  346. size="medium"
  347. type="success"
  348. @click="handleSave"
  349. >
  350. 保存
  351. </el-button>
  352. </div>
  353. </el-tab-pane>
  354. </el-tabs>
  355. <!-- 详情对话框 -->
  356. <el-dialog v-dialogDrag :title="feedbackDialog.title" :visible.sync="feedbackDialog.open" width="800px" append-to-body>
  357. <div style="width:700px; margin:0px auto;">
  358. <h3 style="margin-bottom:20px;">学员心得</h3>
  359. <el-table :data="tableData" border style="width: 100%">
  360. <el-table-column prop="id" label="编号" width="50"></el-table-column>
  361. <el-table-column prop="question" label="问题" width="250"></el-table-column>
  362. <el-table-column prop="answer" label="答案"></el-table-column>
  363. </el-table>
  364. <el-table :data="docFeedback.commonfileList" border>
  365. <el-table-column :label="$t('文件名')" align="center" prop="fileName" :show-overflow-tooltip="true">
  366. <template slot-scope="scope">
  367. <a class="link-type" @click="handleDownload(scope.row)">
  368. <span>{{ scope.row.fileName }}</span>
  369. </a>
  370. </template>
  371. </el-table-column>
  372. <el-table-column :label="$t('大小(Kb)')" align="center" prop="fileSize" :show-overflow-tooltip="true" width="80" />
  373. <el-table-column :label="$t('上传人')" align="center" prop="creator" :show-overflow-tooltip="true" width="120"/>
  374. <el-table-column :label="$t('操作')" align="center" width="120" class-name="small-padding fixed-width">
  375. <template slot-scope="scope">
  376. <el-button
  377. v-if="scope.row.fileName.endsWith('pdf')"
  378. size="mini"
  379. type="text"
  380. icon="el-icon-view"
  381. @click="handleSee(scope.row)"
  382. >{{ $t('预览') }}</el-button>
  383. <el-button
  384. size="mini"
  385. type="text"
  386. icon="el-icon-download"
  387. @click="handleDownload(scope.row)"
  388. >{{ $t('下载') }}</el-button>
  389. </template>
  390. </el-table-column>
  391. </el-table>
  392. <el-form v-if="detailFeedback != null">
  393. <h3 style="margin-top:30px;margin-bottom:20px;">导师反馈</h3>
  394. <el-form-item>
  395. <p v-html="detailFeedback" style="border:1px solid #DFE6EC;"/>
  396. </el-form-item>
  397. </el-form>
  398. </div>
  399. </el-dialog>
  400. </div>
  401. </template>
  402. <script>
  403. import { getAnswer, addAnswer, updateAnswer, listAnswer } from "@/api/training/spec/answer";
  404. import { allFileList, delCommonfile } from "@/api/common/commonfile";
  405. import { addFeedback, getFeedbackByParams, listInvitedSuccessor, updateFeedback, listFeedback, getFeedbackByPlanId } from "@/api/training/spec/feedback";
  406. import { listMentors } from "@/api/training/spec/successor";
  407. import { listSuccessorsByMentorId, listPlanSeasonal } from "@/api/training/spec/plan";
  408. import { getToken } from "@/utils/auth";
  409. import Treeselect from "@riophae/vue-treeselect";
  410. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  411. import Editor from '@/components/Editor';
  412. export default {
  413. name: "SeasonalFeedback",
  414. components: { Treeselect, Editor },
  415. data() {
  416. return {
  417. // 导师标签
  418. mentorTabs: [],
  419. // 详情对话框表格数据
  420. tableData: [],
  421. // 反馈id
  422. feedbackId: null,
  423. // 父级反馈id
  424. parentId: null,
  425. // 会议日期快捷选项
  426. pickerOptions: {
  427. shortcuts: [{
  428. text: '今天',
  429. onClick(picker) {
  430. picker.$emit('pick', new Date());
  431. }
  432. }, {
  433. text: '昨天',
  434. onClick(picker) {
  435. const date = new Date();
  436. date.setTime(date.getTime() - 3600 * 1000 * 24);
  437. picker.$emit('pick', date);
  438. }
  439. }, {
  440. text: '一周前',
  441. onClick(picker) {
  442. const date = new Date();
  443. date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
  444. picker.$emit('pick', date);
  445. }
  446. }, {
  447. text: '一周后',
  448. onClick(picker) {
  449. const date = new Date();
  450. date.setTime(date.getTime() + 3600 * 1000 * 24 * 7);
  451. picker.$emit('pick', date);
  452. }
  453. }]
  454. },
  455. // 会议日期
  456. meetingDate: null,
  457. // 学习状态字典
  458. studyStateOptions: [],
  459. // 汇报展示附件参数
  460. doc: { // 反馈附件
  461. file: "",
  462. // 是否显示弹出层(报告附件)
  463. open: false,
  464. // 弹出层标题(报告附件)
  465. title: "",
  466. // 是否禁用上传
  467. isUploading: false,
  468. // 是否更新已经存在的用户数据
  469. updateSupport: 0,
  470. // 报告附件上传位置编号
  471. ids: 0,
  472. // 设置上传的请求头部
  473. headers: { Authorization: "Bearer " + getToken() },
  474. // 上传的地址
  475. url: process.env.VUE_APP_BASE_API + "/common/commonfile/uploadFile",
  476. commonfileList: null,
  477. queryParams: {
  478. pId: null,
  479. pType: 'docSeasonal'
  480. },
  481. pType: 'docSeasonal',
  482. pId: null
  483. },
  484. // 培养计划总表数据
  485. dataList: [],
  486. // 首选标签页
  487. activeName: null,
  488. // 学员下拉列表
  489. successorOptions: [],
  490. // 导师下拉列表
  491. mentorOptions: [],
  492. // 导师数组
  493. mentorOptionsArray: [],
  494. // 包含导师id的查询对象
  495. queryObject: {
  496. mentorStaffId: ""
  497. },
  498. // 导师反馈内容
  499. mentorFeedback: null, // 季度反馈内容
  500. detailFeedback: null, // 计划详情反馈内容
  501. // 反馈对话框参数
  502. feedbackDialog: {
  503. id: 0,
  504. // 是否显示弹出层(报告附件)
  505. open: false,
  506. // 弹出层标题(报告附件)
  507. title: ""
  508. },
  509. // 问卷单选框
  510. radio1: '',
  511. radio2: '',
  512. radio3: '',
  513. radio4: '',
  514. radio5: '',
  515. radio6: '',
  516. radio7: '',
  517. radio8: '',
  518. radio9: '',
  519. radio10: '',
  520. radio11: '',
  521. radio12: '',
  522. radio13: '',
  523. radio14: '',
  524. // 报告附件参数
  525. doc: { // 季度汇报附件
  526. file: "",
  527. // 是否显示弹出层(报告附件)
  528. open: false,
  529. // 弹出层标题(报告附件)
  530. title: "",
  531. // 是否禁用上传
  532. isUploading: false,
  533. // 是否更新已经存在的用户数据
  534. updateSupport: 0,
  535. // 报告附件上传位置编号
  536. ids: 0,
  537. // 设置上传的请求头部
  538. headers: { Authorization: "Bearer " + getToken() },
  539. // 上传的地址
  540. url: process.env.VUE_APP_BASE_API + "/common/commonfile/uploadFile",
  541. commonfileList: null,
  542. queryParams: {
  543. pId: null,
  544. pType: 'plan'
  545. },
  546. pType: 'plan',
  547. pId: null
  548. },
  549. docFeedback: { // 反馈附件
  550. file: "",
  551. // 是否显示弹出层(报告附件)
  552. open: false,
  553. // 弹出层标题(报告附件)
  554. title: "",
  555. // 是否禁用上传
  556. isUploading: false,
  557. // 是否更新已经存在的用户数据
  558. updateSupport: 0,
  559. // 报告附件上传位置编号
  560. ids: 0,
  561. // 设置上传的请求头部
  562. headers: { Authorization: "Bearer " + getToken() },
  563. // 上传的地址
  564. url: process.env.VUE_APP_BASE_API + "/common/commonfile/uploadFile",
  565. commonfileList: null,
  566. queryParams: {
  567. pId: null,
  568. pType: 'docFeedback'
  569. },
  570. pType: 'docFeedback',
  571. pId: null
  572. },
  573. // 标签页选中项
  574. activeName: 'zhu',
  575. // 遮罩层
  576. loading: true,
  577. // 选中数组
  578. ids: [],
  579. // 非单个禁用
  580. single: true,
  581. // 非多个禁用
  582. multiple: true,
  583. // 显示搜索条件
  584. showSearch: true,
  585. // 总条数
  586. total: 0,
  587. // 专项培训反馈表格数据
  588. feedbackList: [],
  589. // 弹出层标题
  590. title: "",
  591. // 部门树选项
  592. deptOptions: undefined,
  593. clientHeight:300,
  594. // 是否显示弹出层
  595. open: false,
  596. // 用户导入参数
  597. upload: {
  598. // 是否显示弹出层(用户导入)
  599. open: false,
  600. // 弹出层标题(用户导入)
  601. title: "",
  602. // 是否禁用上传
  603. isUploading: false,
  604. // 是否更新已经存在的用户数据
  605. updateSupport: 0,
  606. // 设置上传的请求头部
  607. headers: { Authorization: "Bearer " + getToken() },
  608. // 上传的地址
  609. url: process.env.VUE_APP_BASE_API + "/spec/feedback/importData"
  610. },
  611. // 查询参数
  612. queryParams: {
  613. pageNum: 1,
  614. pageSize: 100,
  615. feedbackType: null,
  616. mentorId: null,
  617. successorId: null,
  618. successorName: null,
  619. parentId: null,
  620. planId: null,
  621. feedbackYear: null,
  622. feedbackYearTemp: null,
  623. feedbackSeason: null,
  624. feedbackMonth: null,
  625. successorFeedback: null,
  626. mentorFeedback: null,
  627. feedbackScore: null,
  628. feedbackStatus: null,
  629. meetingDate: null
  630. },
  631. // 表单参数
  632. form: {},
  633. // 表单校验
  634. rules: {
  635. }
  636. };
  637. },
  638. watch: {
  639. },
  640. created() {
  641. //设置表格高度对应屏幕高度
  642. this.$nextTick(() => {
  643. this.clientHeight = document.body.clientHeight -250
  644. })
  645. // 获取学习状态字典
  646. this.getDicts("st_study_state").then(response => {
  647. this.studyStateOptions = response.data;
  648. });
  649. // 初始化页面数据
  650. this.initPageData();
  651. },
  652. methods: {
  653. /** 标签页切换事件 */
  654. handleTabClick(tab) {
  655. // 清空问卷
  656. this.radio1 = null;
  657. this.radio2 = null;
  658. this.radio3 = null;
  659. this.radio4 = null;
  660. this.radio5 = null;
  661. this.radio6 = null;
  662. this.radio7 = null;
  663. this.radio8 = null;
  664. this.radio9 = null;
  665. this.radio10 = null;
  666. this.radio11 = null;
  667. this.radio12 = null;
  668. this.radio13 = null;
  669. this.radio14 = null;
  670. // 清空导师反馈内容
  671. this.mentorFeedback = null;
  672. // 根据标签页导师id获取反馈id
  673. getFeedbackByParams({
  674. mentorId: tab.name,
  675. successorId: this.queryParams.successorId,
  676. feedbackYear: this.queryParams.feedbackYear,
  677. feedbackSeason: this.queryParams.feedbackSeason,
  678. parentId: this.parentId
  679. }).then(response => {
  680. let data = response.data;
  681. // 设置反馈id为当前tab导师反馈id
  682. this.feedbackId = data.id;
  683. // 刷新导师反馈内容
  684. this.mentorFeedback = data.mentorFeedback;
  685. // 刷新问卷
  686. listAnswer( { feedbackId: this.feedbackId } ).then(response => {
  687. let data = response.rows;
  688. for (let i = 0; i < data.length; i++) {
  689. if (data[i].questionId == "4") { this.radio1 = data[i].answer; }
  690. if (data[i].questionId == "5") { this.radio2 = data[i].answer; }
  691. if (data[i].questionId == "6") { this.radio3 = data[i].answer; }
  692. if (data[i].questionId == "7") { this.radio4 = data[i].answer; }
  693. if (data[i].questionId == "8") { this.radio5 = data[i].answer; }
  694. if (data[i].questionId == "9") { this.radio6 = data[i].answer; }
  695. if (data[i].questionId == "10") { this.radio7 = data[i].answer; }
  696. if (data[i].questionId == "11") { this.radio8 = data[i].answer; }
  697. if (data[i].questionId == "12") { this.radio9 = data[i].answer; }
  698. if (data[i].questionId == "13") { this.radio10 = data[i].answer; }
  699. if (data[i].questionId == "14") { this.radio11 = data[i].answer; }
  700. if (data[i].questionId == "15") { this.radio12 = data[i].answer; }
  701. if (data[i].questionId == "16") { this.radio13 = data[i].answer; }
  702. if (data[i].questionId == "17") { this.radio14 = data[i].answer; }
  703. }
  704. });
  705. });
  706. },
  707. /** 初始化页面数据 */
  708. initPageData() {
  709. // 获取当前日期
  710. let date = new Date();
  711. // 搜索条件默认为当年、当季度
  712. this.queryParams.feedbackYear = date.getFullYear().toString();
  713. this.queryParams.feedbackYearTemp = date.getFullYear().toString();
  714. this.queryParams.feedbackSeason = ( ( date.getMonth() + 2 ) / 3 ).toString();
  715. // ====================获取导师下拉列表====================
  716. listMentors().then(response => {
  717. let mentorList = response.data;
  718. for (let i = 0; i < mentorList.length; i++) {
  719. let mentor = {};
  720. mentor.key = mentorList[i].mentorStaffId;
  721. mentor.value = mentorList[i].mentorStaffName;
  722. this.mentorOptions.push(mentor);
  723. }
  724. });
  725. // ====================获取当前导师的学员列表====================
  726. listSuccessorsByMentorId(this.queryObject).then(response => {
  727. let data = response.data;
  728. for (let i = 0; i < data.length; i++) {
  729. if (i == 0) {
  730. // 默认选中列表中第一个学员
  731. this.queryParams.successorId = data[i].staffId;
  732. this.getList();
  733. }
  734. this.successorOptions.push( { key: data[i].staffId, value: data[i].staffName } );
  735. }
  736. // 获取当前学员当年当季度反馈id
  737. getFeedbackByParams({
  738. successorId: this.queryParams.successorId,
  739. feedbackYear: this.queryParams.feedbackYear,
  740. feedbackSeason: this.queryParams.feedbackSeason
  741. }).then(response => {
  742. let data = response.data;
  743. this.feedbackId = data.id;
  744. this.parentId = data.id;
  745. this.meetingDate = data.meetingDate;
  746. this.mentorFeedback = data.mentorFeedback;
  747. // 初始化汇报附件
  748. this.initFileList(data.id);
  749. // ====================初始化导师标签页====================
  750. this.activeName = data.mentorId;
  751. this.mentorTabs.push({ name: data.mentorId, title: data.mentorName});
  752. listFeedback( { parentId: data.id } ).then(response => {
  753. let data = response.rows;
  754. for (let i = 0; i < data.length; i++) {
  755. this.mentorTabs.push({ name: data[i].mentorId, title: data[i].mentorName});
  756. }
  757. });
  758. // ====================获取问卷答案====================
  759. listAnswer( { feedbackId: this.feedbackId } ).then(response => {
  760. let data = response.rows;
  761. for (let i = 0; i < data.length; i++) {
  762. if (data[i].questionId == "4") { this.radio1 = data[i].answer; }
  763. if (data[i].questionId == "5") { this.radio2 = data[i].answer; }
  764. if (data[i].questionId == "6") { this.radio3 = data[i].answer; }
  765. if (data[i].questionId == "7") { this.radio4 = data[i].answer; }
  766. if (data[i].questionId == "8") { this.radio5 = data[i].answer; }
  767. if (data[i].questionId == "9") { this.radio6 = data[i].answer; }
  768. if (data[i].questionId == "10") { this.radio7 = data[i].answer; }
  769. if (data[i].questionId == "11") { this.radio8 = data[i].answer; }
  770. if (data[i].questionId == "12") { this.radio9 = data[i].answer; }
  771. if (data[i].questionId == "13") { this.radio10 = data[i].answer; }
  772. if (data[i].questionId == "14") { this.radio11 = data[i].answer; }
  773. if (data[i].questionId == "15") { this.radio12 = data[i].answer; }
  774. if (data[i].questionId == "16") { this.radio13 = data[i].answer; }
  775. if (data[i].questionId == "17") { this.radio14 = data[i].answer; }
  776. }
  777. });
  778. });
  779. });
  780. // ====================获取当前导师作为受邀导师的学员列表====================
  781. listInvitedSuccessor(null).then(response => {
  782. let data = response.data;
  783. for (let i = 0; i < data.length; i++) {
  784. this.successorOptions.push( { key: data[i].successorId, value: data[i].successorName } );
  785. }
  786. });
  787. },
  788. /** 问卷判空 */
  789. isEmpty() {
  790. let radioArray = [ this.radio1, this.radio2, this.radio3, this.radio4, this.radio5, this.radio6, this.radio7, this.radio8, this.radio9, this.radio10, this.radio11, this.radio12, this.radio13, this.radio14 ];
  791. let isEmpty = false;
  792. for (let i = 0; i < radioArray.length; i++) {
  793. if (radioArray[i] == "") {
  794. isEmpty = true;
  795. }
  796. }
  797. return isEmpty;
  798. },
  799. /** 计算导师评分 */
  800. calcFeedbackScore() {
  801. let mentorFeedbackScore = 0; // 本导师评分
  802. let invitedMentorFeedbackScoreSum = 0; // 受邀导师总分
  803. let invitedMentorFeedbackScoreAvg = 0; // 受邀导师平均分
  804. let invitedMentors = 0; // 受邀导师人数
  805. let overallScore = 0; // 季度平均分
  806. let feedbackId = null; // 本导师反馈id
  807. // 获取问题答案列表
  808. listAnswer( { feedbackId: this.feedbackId } )
  809. .then(response => {
  810. let data = response.rows;
  811. // 计算导师评分
  812. let total = 0; // 总分
  813. for (let i = 0; i < data.length; i++) {
  814. if(data[i].answer == "1") {
  815. total += 100;
  816. }
  817. }
  818. let avg = total / 14; // 平均分
  819. // 更新当前导师评分
  820. return updateFeedback( { id: this.feedbackId, feedbackScore: avg } );
  821. })
  822. .then(() => {
  823. // 获取本导师反馈
  824. return getFeedbackByParams({
  825. successorId: this.queryParams.successorId,
  826. feedbackYear: this.queryParams.feedbackYear,
  827. feedbackSeason: this.queryParams.feedbackSeason
  828. })
  829. })
  830. .then(response => {
  831. // 设置本导师反馈id
  832. feedbackId = response.data.id;
  833. // 设置导师评分
  834. mentorFeedbackScore = response.data.feedbackScore;
  835. // 获取受邀导师列表
  836. return listFeedback( { parentId: feedbackId } );
  837. })
  838. .then(response => {
  839. let data = response.rows;
  840. for (let i = 0; i < data.length; i++) {
  841. if (data[i].feedbackScore != null) {
  842. invitedMentorFeedbackScoreSum += Number(data[i].feedbackScore);
  843. invitedMentors += 1;
  844. }
  845. }
  846. // 受邀导师平均分 = 总分 / 人数
  847. invitedMentorFeedbackScoreAvg = invitedMentorFeedbackScoreSum / invitedMentors;
  848. // 季度平均分 = 本导师评分 * 60% + 受邀导师平均分 * 40%
  849. overallScore = mentorFeedbackScore * 0.6 + invitedMentorFeedbackScoreAvg * 0.4;
  850. // 更新季度平均分
  851. updateFeedback({ id: feedbackId, overallScore: overallScore });
  852. });
  853. },
  854. /** 保存反馈问题 */
  855. saveAnswer(questionId, answer) {
  856. let answerObj = {};
  857. answerObj.feedbackId = this.feedbackId;
  858. answerObj.questionId = questionId;
  859. getAnswer(answerObj).then(response => {
  860. let data = response.data;
  861. answerObj.answer = answer;
  862. if (data != null) {
  863. if (response.answer == data.answer) { // 答案一致
  864. return;
  865. } else { // 答案不一致
  866. answerObj.id = data.id;
  867. updateAnswer(answerObj).then(response => {
  868. // 计算导师评分
  869. this.calcFeedbackScore();
  870. });
  871. }
  872. } else {
  873. addAnswer(answerObj).then(response => {
  874. // 计算导师评分
  875. this.calcFeedbackScore();
  876. });
  877. }
  878. });
  879. },
  880. /** 保存导师反馈内容 */
  881. saveMentorFeedback() {
  882. let feedback = {};
  883. feedback.id = this.feedbackId;
  884. feedback.mentorFeedback = this.mentorFeedback;
  885. updateFeedback(feedback);
  886. },
  887. /** 保存按钮处理 */
  888. handleSave() {
  889. // 问卷判空
  890. if (this.isEmpty()) {
  891. this.$message.error('问卷答案不能为空')
  892. return;
  893. }
  894. let radioArray = [ this.radio1, this.radio2, this.radio3, this.radio4, this.radio5, this.radio6, this.radio7, this.radio8, this.radio9, this.radio10, this.radio11, this.radio12, this.radio13, this.radio14 ];
  895. let questionArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
  896. // 保存问卷内容
  897. for (let i = 0; i < radioArray.length; i++) {
  898. this.saveAnswer(questionArray[i]+3, radioArray[i]);
  899. }
  900. // 保存导师反馈内容
  901. this.saveMentorFeedback();
  902. this.msgSuccess("保存成功");
  903. },
  904. /** 培养计划详情处理 */
  905. handleFeedback(row) {
  906. // 加载反馈附件
  907. this.docFeedback.id = row.id;
  908. this.docFeedback.queryParams.pId = row.id
  909. this.docFeedback.pId = row.id
  910. this.getFileListFeedback();
  911. // 清空导师反馈内容
  912. this.detailFeedback = null;
  913. // 清空表格数据
  914. this.tableData = [];
  915. let feedback1 = {};
  916. feedback1.planId = row.id;
  917. feedback1.questionId = 1;
  918. let feedback2 = {};
  919. feedback2.planId = row.id;
  920. feedback2.questionId = 2;
  921. let feedback3 = {};
  922. feedback3.planId = row.id;
  923. feedback3.questionId = 3;
  924. let answerObj1 = {};
  925. let answerObj2 = {};
  926. let answerObj3 = {};
  927. // 获取问题和答案
  928. getAnswer(feedback1).then(response => {
  929. let data = response.data;
  930. if (data != null) {
  931. answerObj1 = { id: 1, question: data.question, answer: data.answer};
  932. }
  933. return getAnswer(feedback2);
  934. }).then(response => {
  935. let data = response.data;
  936. if (data != null) {
  937. answerObj2 = { id: 2, question: data.question, answer: data.answer};
  938. }
  939. return getAnswer(feedback3);
  940. }).then(response => {
  941. let data = response.data;
  942. if (data != null) {
  943. answerObj3 = { id: 3, question: data.question, answer: data.answer};
  944. this.tableData.push(answerObj1);
  945. this.tableData.push(answerObj2);
  946. this.tableData.push(answerObj3);
  947. }
  948. // 获取导师反馈内容
  949. return getFeedbackByPlanId(row.id);
  950. }).then(response => {
  951. if (response.data != null) {
  952. let data = response.data;
  953. this.detailFeedback = data.mentorFeedback;
  954. }
  955. this.feedbackDialog.title = row.staffName + row.plantName + "学习情况详情";
  956. this.feedbackDialog.planId = row.id;
  957. this.feedbackDialog.studyState = row.studyState;
  958. this.feedbackDialog.open = true;
  959. });
  960. },
  961. /** 单元格样式 */
  962. tableCellStyle(row, column, rowIndex, columnIndex) {
  963. if (row.column.label === "实际完成日期" && row.row.dateOfCompletion > row.row.endDate) {
  964. return "background: #FFEEEE"
  965. }
  966. },
  967. /** 学习状态字典翻译 */
  968. studyStateFormat(row, column) {
  969. return this.selectDictLabel(this.studyStateOptions, row.studyState);
  970. },
  971. /** 文件下载处理 */
  972. handleDownload(row) {
  973. var name = row.fileName;
  974. var url = row.fileUrl;
  975. var suffix = url.substring(url.lastIndexOf("."), url.length);
  976. const a = document.createElement('a');
  977. a.setAttribute('download', name);
  978. a.setAttribute('target', '_blank');
  979. a.setAttribute('href', process.env.VUE_APP_BASE_API + url);
  980. a.click();
  981. },
  982. openPdf() {
  983. window.open(this.pdf.pdfUrl);//path是文件的全路径地址
  984. },
  985. handleSee(row) {
  986. this.pdf.open = true;
  987. this.pdf.title = row.fileName;
  988. this.pdf.pdfUrl = process.env.VUE_APP_BASE_API +'/pdf/web/viewer.html?file=' + process.env.VUE_APP_BASE_API + row.fileUrl;
  989. },
  990. /** 汇报附件参数初始化 */
  991. initFileList(id) {
  992. this.doc.queryParams.pId = id
  993. this.doc.pId = id
  994. this.getFileList()
  995. // this.$nextTick(() => {
  996. // this.$refs.doc.clearFiles()
  997. // })
  998. },
  999. getFileList() {
  1000. allFileList(this.doc.queryParams).then(response => {
  1001. this.doc.commonfileList = response;
  1002. });
  1003. },
  1004. getFileListFeedback() {
  1005. allFileList(this.docFeedback.queryParams).then(response => {
  1006. this.docFeedback.commonfileList = response;
  1007. });
  1008. },
  1009. /** 附件上传中处理 */
  1010. handleFileDocProgress(event, file, fileList) {
  1011. this.doc.file = file;
  1012. this.doc.isUploading = true;
  1013. },
  1014. handleFileDocProgressFeedback(event, file, fileList) {
  1015. this.docFeedback.file = file;
  1016. this.docFeedback.isUploading = true;
  1017. },
  1018. /** 附件上传成功处理 */
  1019. handleFileDocSuccess(response, file, fileList) {
  1020. this.doc.isUploading = false;
  1021. this.$alert(response.msg, this.$t('导入结果'), { dangerouslyUseHTMLString: true });
  1022. this.getFileList()
  1023. },
  1024. handleFileDocSuccessFeedback(response, file, fileList) {
  1025. this.docMentorFeedback.isUploading = false;
  1026. this.$alert(response.msg, this.$t('导入结果'), { dangerouslyUseHTMLString: true });
  1027. this.getFileListFeedback()
  1028. },
  1029. /** 删除按钮操作 */
  1030. handleDeleteDoc(row) {
  1031. const ids = row.id || this.ids;
  1032. this.$confirm(this.$t('是否确认删除?'), this.$t('警告'), {
  1033. confirmButtonText: this.$t('确定'),
  1034. cancelButtonText: this.$t('取消'),
  1035. type: "warning"
  1036. }).then(function() {
  1037. return delCommonfile(ids);
  1038. }).then(() => {
  1039. this.getFileList()
  1040. this.msgSuccess(this.$t('删除成功'));
  1041. })
  1042. },
  1043. /** 邀请导师 */
  1044. handleInvite() {
  1045. let updateParams = {};
  1046. updateParams.id = this.feedbackId;
  1047. updateParams.meetingDate = this.meetingDate;
  1048. // 修改会议日期
  1049. updateFeedback(updateParams);
  1050. for (let i = 0; i < this.mentorOptionsArray.length; i++) {
  1051. let feedback = {};
  1052. feedback.parentId = this.feedbackId; // 设置父级id
  1053. feedback.feedbackYear = this.queryParams.feedbackYear;
  1054. feedback.feedbackSeason = this.queryParams.feedbackSeason;
  1055. feedback.feedbackType = 3;
  1056. feedback.mentorId = this.mentorOptionsArray[i];
  1057. feedback.successorId = this.queryParams.successorId;
  1058. getFeedbackByParams(feedback).then(response => {
  1059. let data = response.data;
  1060. if (data == null) {
  1061. // 新增受邀导师反馈记录
  1062. addFeedback(feedback);
  1063. }
  1064. });
  1065. // 发送邮件提醒受邀导师
  1066. }
  1067. // 刷新标签列表
  1068. this.msgSuccess("邀请成功,邮件已发送");
  1069. },
  1070. /** 查询专项培训反馈列表 */
  1071. getList() {
  1072. this.loading = true;
  1073. listPlanSeasonal(this.queryParams).then(response => {
  1074. this.dataList = response.rows;
  1075. this.total = response.total;
  1076. this.loading = false;
  1077. });
  1078. },
  1079. // 取消按钮
  1080. cancel() {
  1081. this.open = false;
  1082. this.reset();
  1083. },
  1084. // 表单重置
  1085. reset() {
  1086. this.form = {
  1087. id: null,
  1088. feedbackType: null,
  1089. mentorId: null,
  1090. successorId: null,
  1091. successorName: null,
  1092. parentId: null,
  1093. planId: null,
  1094. feedbackYear: null,
  1095. feedbackSeason: null,
  1096. feedbackMonth: null,
  1097. successorFeedback: null,
  1098. mentorFeedback: null,
  1099. feedbackScore: null,
  1100. feedbackStatus: 0,
  1101. meetingDate: null
  1102. };
  1103. this.resetForm("form");
  1104. },
  1105. /** 搜索按钮操作 */
  1106. handleQuery() {
  1107. this.queryParams.feedbackYear = this.queryParams.feedbackYearTemp.getFullYear();
  1108. this.queryParams.pageNum = 1;
  1109. this.getList();
  1110. // 清空问卷
  1111. this.radio1 = null;
  1112. this.radio2 = null;
  1113. this.radio3 = null;
  1114. this.radio4 = null;
  1115. this.radio5 = null;
  1116. this.radio6 = null;
  1117. this.radio7 = null;
  1118. this.radio8 = null;
  1119. this.radio9 = null;
  1120. this.radio10 = null;
  1121. this.radio11 = null;
  1122. this.radio12 = null;
  1123. this.radio13 = null;
  1124. this.radio14 = null;
  1125. // 清空导师反馈内容
  1126. this.mentorFeedback = null;
  1127. // 清空会议日期
  1128. this.meetingDate = null;
  1129. getFeedbackByParams({
  1130. successorId: this.queryParams.successorId,
  1131. feedbackYear: this.queryParams.feedbackYear,
  1132. feedbackSeason: this.queryParams.feedbackSeason,
  1133. }).then(response => {
  1134. let data = response.data;
  1135. this.feedbackId = data.id;
  1136. this.parentId = data.id;
  1137. this.meetingDate = data.meetingDate;
  1138. this.mentorFeedback = data.mentorFeedback;
  1139. // 刷新汇报附件
  1140. this.initFileList(data.id);
  1141. });
  1142. // 刷新问卷
  1143. listAnswer( { feedbackId: this.feedbackId } ).then(response => {
  1144. let data = response.rows;
  1145. for (let i = 0; i < data.length; i++) {
  1146. if (data[i].questionId == "4") { this.radio1 = data[i].answer; }
  1147. if (data[i].questionId == "5") { this.radio2 = data[i].answer; }
  1148. if (data[i].questionId == "6") { this.radio3 = data[i].answer; }
  1149. if (data[i].questionId == "7") { this.radio4 = data[i].answer; }
  1150. if (data[i].questionId == "8") { this.radio5 = data[i].answer; }
  1151. if (data[i].questionId == "9") { this.radio6 = data[i].answer; }
  1152. if (data[i].questionId == "10") { this.radio7 = data[i].answer; }
  1153. if (data[i].questionId == "11") { this.radio8 = data[i].answer; }
  1154. if (data[i].questionId == "12") { this.radio9 = data[i].answer; }
  1155. if (data[i].questionId == "13") { this.radio10 = data[i].answer; }
  1156. if (data[i].questionId == "14") { this.radio11 = data[i].answer; }
  1157. if (data[i].questionId == "15") { this.radio12 = data[i].answer; }
  1158. if (data[i].questionId == "16") { this.radio13 = data[i].answer; }
  1159. if (data[i].questionId == "17") { this.radio14 = data[i].answer; }
  1160. }
  1161. });
  1162. },
  1163. /** 重置按钮操作 */
  1164. resetQuery() {
  1165. this.resetForm("queryForm");
  1166. this.handleQuery();
  1167. },
  1168. // 多选框选中数据
  1169. handleSelectionChange(selection) {
  1170. this.ids = selection.map(item => item.id)
  1171. this.single = selection.length!==1
  1172. this.multiple = !selection.length
  1173. },
  1174. }
  1175. };
  1176. </script>
  1177. <style scoped>
  1178. .question{
  1179. margin: 20px 0px;
  1180. padding-left: 20px;
  1181. }
  1182. .answer{
  1183. padding-bottom: 10px;
  1184. padding-left: 40px;
  1185. }
  1186. h3{
  1187. margin: 20px 0px;
  1188. }
  1189. </style>