record.vue 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385
  1. <template>
  2. <div class="app-container" >
  3. <el-form :inline="true" :model="deviceParams" label-width="68px">
  4. <el-form-item :label="$t('年份')" prop="year">
  5. <el-date-picker v-model="deviceParams.year" :placeholder="$t('请选择')+$t('年份')" clearable
  6. size="small"
  7. style="width: 200px"
  8. type="year"
  9. value-format="yyyy">
  10. </el-date-picker>
  11. </el-form-item>
  12. <el-form-item :label="$t('培训类型')" prop="trainingType">
  13. <el-select v-model="deviceParams.trainingType" :placeholder="$t('请选择') + $t('培训类型')" clearable
  14. size="small">
  15. <el-option
  16. v-for="dict in trainingTypeOptions"
  17. :key="dict.dictValue"
  18. :label="dict.dictLabel"
  19. :value="dict.dictValue"
  20. />
  21. </el-select>
  22. </el-form-item>
  23. <el-button icon="el-icon-search" size="mini" style="margin-left: 20px;" type="cyan" @click="handleDeviceQuery">
  24. {{ $t('搜索') }}
  25. </el-button>
  26. <el-button icon="el-icon-download" size="mini" style="margin-left: 10px;" type="success" @click="handleSnapshotData" :disabled="!devicelevelList || devicelevelList.length === 0">
  27. {{ $t('固定数据') }}
  28. </el-button>
  29. <el-button icon="el-icon-view" size="mini" style="margin-left: 10px;" type="primary" @click="handleViewSnapshot">
  30. {{ $t('查看快照') }}
  31. </el-button>
  32. <el-button icon="el-icon-download" size="mini" style="margin-left: 10px;" type="success" @click="exportToExcel" :disabled="!devicelevelList || devicelevelList.length === 0 || deviceTrainingLoad">
  33. {{ $t('导出Excel') }}
  34. </el-button>
  35. <el-button icon="el-icon-arrow-down" size="mini" style="margin-left: 10px;" type="primary" @click="increaseHeight">
  36. 增加高度
  37. </el-button>
  38. <el-row style="text-align: right">
  39. <svg-icon class="rectangleGreen" icon-class="rectangleGreen"></svg-icon>
  40. <span>{{ $t('需参加培训,已经完成培训人员') }}</span>
  41. <svg-icon class="rectangleRed" icon-class="rectanglered"></svg-icon>
  42. <span style="line-height: 40px">{{ $t('需参加培训,尚未进行培训人员') }}</span>
  43. <span style="line-height: 40px;margin-left: 22px;">╳ 需要参加培训</span>
  44. <span style="line-height: 40px;margin-left: 22px;">⚪ 不需要参加培训</span>
  45. </el-row>
  46. </el-form>
  47. <vue-draggable-resizable
  48. :draggable="dragMove"
  49. h="auto"
  50. style="background-color:white"
  51. w="auto"
  52. :w="vdr.width"
  53. :h="vdr.height"
  54. :min-width="500"
  55. :min-height="300"
  56. @activated="onVdrActivated"
  57. @deactivated="onVdrDeactivated"
  58. @dragstop="onVdrDragStop"
  59. @resizestop="onVdrResizeStop"
  60. :x="vdr.x"
  61. :y="vdr.y"
  62. >
  63. <div ref="branch" class="zoom" @wheel.prevent="handleTableWheel($event)">
  64. <el-table v-if="tableShow" ref="deviceTable" v-loading="deviceTrainingLoad"
  65. :cell-class-name="tableCellClassName"
  66. :data="devicelevelList" :span-method="colspanDeviceMethod"
  67. border
  68. :height="clientHeight"
  69. class="companyLevelTable">
  70. <el-table-column :label="$t('课程代码')" align="center" fixed width="100">
  71. <template slot-scope="scope">
  72. {{ scope.row[0] }}
  73. </template>
  74. </el-table-column>
  75. <el-table-column :label="$t('装置级') +$t('空格') + $t('培训课程')+$t('空格') + $t('名称')" align="center"
  76. fixed width="250">
  77. <template slot-scope="scope">
  78. {{ scope.row[1] }}
  79. </template>
  80. </el-table-column>
  81. <el-table-column v-for="(item, index) in deviceStaffmgrs" :key="index" :label="item.actualpost" align="center"
  82. width="150">
  83. <el-table-column :key="index" :label="item.name" align="center" width="150">
  84. <template slot-scope="scope">
  85. {{ scope.row[index + 2] }}
  86. </template>
  87. </el-table-column>
  88. </el-table-column>
  89. </el-table>
  90. </div>
  91. </vue-draggable-resizable>
  92. <!-- 用户导入对话框 -->
  93. <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
  94. <el-upload
  95. ref="upload"
  96. :limit="1"
  97. accept=".xlsx, .xls"
  98. :headers="upload.headers"
  99. :action="upload.url"
  100. :disabled="upload.isUploading"
  101. :on-progress="handleFileUploadProgress"
  102. :on-success="handleFileSuccess"
  103. :auto-upload="false"
  104. drag
  105. >
  106. <i class="el-icon-upload"></i>
  107. <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
  108. <div class="el-upload__tip text-center" slot="tip">
  109. <span>仅允许导入xls、xlsx格式文件。</span>
  110. <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
  111. </div>
  112. </el-upload>
  113. <div slot="footer" class="dialog-footer">
  114. <el-button type="primary" @click="submitFileForm">确 定</el-button>
  115. <el-button @click="upload.open = false">取 消</el-button>
  116. </div>
  117. </el-dialog>
  118. <!-- 数据快照对话框 -->
  119. <el-dialog title="创建数据快照" :visible.sync="snapshotDialog.visible" width="500px" append-to-body>
  120. <el-form ref="snapshotForm" :model="snapshotDialog.form" :rules="snapshotDialog.rules" label-width="100px">
  121. <el-form-item label="快照名称" prop="snapshotName">
  122. <el-input v-model="snapshotDialog.form.snapshotName" placeholder="请输入快照名称,如:2024年培训数据快照" />
  123. </el-form-item>
  124. <el-form-item label="快照描述" prop="description">
  125. <el-input type="textarea" v-model="snapshotDialog.form.description" placeholder="请输入快照描述,说明此快照的用途和内容" :rows="3" />
  126. </el-form-item>
  127. <el-form-item label="快照年份" prop="snapshotYear">
  128. <el-date-picker v-model="snapshotDialog.form.snapshotYear" type="year" value-format="yyyy" placeholder="选择快照年份" />
  129. </el-form-item>
  130. </el-form>
  131. <div slot="footer" class="dialog-footer">
  132. <el-button type="primary" @click="submitSnapshotData">确 定</el-button>
  133. <el-button @click="snapshotDialog.visible = false">取 消</el-button>
  134. </div>
  135. </el-dialog>
  136. </div>
  137. </template>
  138. <script>
  139. import {
  140. addTrainingrecords,
  141. delTrainingrecords,
  142. exportTrainingrecords,
  143. exportTrainingTime,
  144. getTrainingrecords,
  145. listTrainingrecords,
  146. updateTrainingrecords
  147. } from "@/api/training/trainingrecords";
  148. import {listTraining} from "@/api/training/worklicense";
  149. import {companyListParticipants, delParticipants, listParticipants} from "@/api/training/participants";
  150. import {deviceListParticipants, listDevice} from "@/api/training/device";
  151. import {devicebccListParticipants} from "@/api/training/bccdevice";
  152. import {treeselect} from "@/api/system/dept";
  153. import {selectTimeStaffmgr} from "@/api/plant/staffmgr";
  154. import {getToken} from "@/utils/auth";
  155. import Treeselect from "@riophae/vue-treeselect";
  156. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  157. import {addCommonfile, allFileList, delCommonfile, updateCommonfile} from "@/api/common/commonfile";
  158. import VueDraggableResizable from 'vue-draggable-resizable';
  159. import { createSnapshot, querySnapshot } from '@/api/training/snapshot';
  160. export default {
  161. name: "records",
  162. components: {Treeselect, VueDraggableResizable},
  163. mounted() {
  164. window.onresize = () => { //写在mounted中,onresize事件会在页面大小被调整时触发
  165. return (() => {
  166. window.screenHeight = document.body.clientHeight - 155;
  167. this.height = window.screenHeight;
  168. // 同步容器尺寸
  169. this.vdr.width = document.body.clientWidth - 80;
  170. this.vdr.height = (document.body.clientHeight - 80) * 0.8;
  171. this.$nextTick(() => {
  172. if (this.$refs.deviceTable) this.$refs.deviceTable.doLayout();
  173. });
  174. })();
  175. };
  176. },
  177. data() {
  178. return {
  179. height: document.body.clientHeight - 155,
  180. dragMove: true,
  181. transTitle: [], // transTitle 该标题为转化后的标题, 注意多一列, 因为原来的标题变成了竖着显示了, 所以多一列标题, 第一个为空即可
  182. companyStaffmgrs: [],
  183. deviceStaffmgrs: [],
  184. companylevelList: [],
  185. devicelevelList: [],
  186. devicelevelListTemp: [],
  187. // 遮罩层
  188. loading: true,
  189. // 选中数组
  190. ids: [],
  191. // 非单个禁用
  192. single: true,
  193. // 非多个禁用
  194. multiple: true,
  195. // 显示搜索条件
  196. showSearch: false,
  197. // 总条数
  198. total: 0,
  199. // 培训成绩表格数据
  200. trainingrecordsList: [],
  201. // 人员-公司级培训关系表格数据
  202. participantsList: [],
  203. // 人员-装置级培训关系表格数据
  204. devicceList: [],
  205. //上岗证培训列表
  206. worklicenseList: [],
  207. // 弹出层标题
  208. title: "",
  209. companyTitle: "",
  210. deviceTitle: "",
  211. companyTrainingTitle: "",
  212. trainingTimeTitle: "",
  213. deviceTrainingTitle: "",
  214. worklicenseTitle: "",
  215. // 部门树选项
  216. deptOptions: undefined,
  217. clientHeight: 300,
  218. // 是否显示弹出层
  219. open: false,
  220. companyOpen: false,
  221. deviceOpen: false,
  222. companyTrainingOpen: false,
  223. deviceTrainingOpen: false,
  224. worklicenseOpen: false,
  225. companyTrainingLoad: false,
  226. deviceTrainingLoad: false,
  227. tableShow: true,
  228. trainingTimeOpen: false,
  229. // 可拖拽缩放容器状态
  230. vdr: {
  231. x: 0,
  232. y: 0,
  233. width: document.body.clientWidth - 80,
  234. height: (document.body.clientHeight - 80) * 0.8,
  235. minWidth: 600,
  236. minHeight: 300
  237. },
  238. // 装置名称字典
  239. plantCodeOptions: [],
  240. // 班值字典
  241. classesOptions: [],
  242. // SUB字典
  243. subOptions: [],
  244. // 裂解字典
  245. crackingOptions: [],
  246. // 热区字典
  247. hotareaOptions: [],
  248. // 冷区字典
  249. coldareaOptions: [],
  250. // AEU/PGU字典
  251. aeupguOptions: [],
  252. // 入职字典
  253. onboardOptions: [],
  254. // TDS字典
  255. tdsOptions: [],
  256. // 上岗证复证字典
  257. postCardOptions: [],
  258. // 工艺培训字典
  259. craftOptions: [],
  260. //公司级培训字典
  261. companyOptions: [],
  262. //装置级培训字典
  263. deviceOptions: [],
  264. // 培训岗位字典
  265. positionOptions: [],
  266. //培训类型字典
  267. trainingTypeOptions: [],
  268. //点击查看的员工号
  269. employeeid: '',
  270. // 用户导入参数
  271. upload: {
  272. downloadAction: process.env.VUE_APP_BASE_API + '/common/template',
  273. //下载模板类型
  274. type: "trainingrecords",
  275. // 是否显示弹出层(用户导入)
  276. open: false,
  277. // 弹出层标题(用户导入)
  278. title: "",
  279. // 是否禁用上传
  280. isUploading: false,
  281. // 是否更新已经存在的用户数据
  282. updateSupport: 0,
  283. // 设置上传的请求头部
  284. headers: {Authorization: "Bearer " + getToken()},
  285. // 上传的地址
  286. url: process.env.VUE_APP_BASE_API + "/training/trainingrecords/importData"
  287. },
  288. // 报告附件参数
  289. doc: {
  290. file: "",
  291. // 是否显示弹出层(报告附件)
  292. open: false,
  293. // 弹出层标题(报告附件)
  294. title: this.$t('附件'),
  295. // 是否禁用上传
  296. isUploading: false,
  297. // 是否更新已经存在的用户数据
  298. updateSupport: 0,
  299. // 报告附件上传位置编号
  300. ids: 0,
  301. // 设置上传的请求头部
  302. headers: {Authorization: "Bearer " + getToken()},
  303. // 上传的地址
  304. url: process.env.VUE_APP_BASE_API + "/common/commonfile/uploadFile",
  305. commonfileList: null,
  306. queryParams: {
  307. pId: null,
  308. pType: 'traning'
  309. },
  310. pType: 'traning',
  311. pId: null,
  312. form: {}
  313. },
  314. // 查询参数
  315. queryParams: {
  316. pageNum: 1,
  317. pageSize: 20,
  318. name: null,
  319. employeeid: null,
  320. classes: null,
  321. sortDate: null,
  322. sortOrder: null,
  323. onboard: null,
  324. tds: null,
  325. postCard: null,
  326. craft: null,
  327. company: null
  328. },
  329. //人员表查询参数
  330. staffmgrQueryParams: {
  331. units: this.$constants.DEFAULT_UNITS,
  332. leftYear: this.getNowTime(),
  333. },
  334. queryCompanyParams: {
  335. staffId: null,
  336. companyId: null,
  337. },
  338. queryDeviceParams: {
  339. staffId: null,
  340. regularId: null,
  341. year: this.getNowTime(),
  342. trainingType: null
  343. },
  344. deviceParams: {
  345. units: this.$constants.DEFAULT_UNITS,
  346. staffId: null,
  347. regularId: null,
  348. year: null,
  349. trainingType: null
  350. },
  351. worklicenseParams: {
  352. employeeid: null,
  353. },
  354. companyQueryParams: {},
  355. // 表单参数
  356. form: {},
  357. pdf: {
  358. title: '',
  359. pdfUrl: '',
  360. numPages: null,
  361. open: false,
  362. pageNum: 1,
  363. pageTotalNum: 1,
  364. loadedRatio: 0,
  365. },
  366. // 表单校验
  367. rules: {
  368. plantCode: [
  369. {required: true, message: this.$t('装置名称') + this.$t('不能为空'), trigger: "change"}
  370. ],
  371. year: [
  372. {required: true, message: this.$t('年份') + this.$t('不能为空'), trigger: "blur"}
  373. ],
  374. deptId: [
  375. {required: true, message: this.$t('部门编号') + this.$t('不能为空'), trigger: "blur"}
  376. ]
  377. },
  378. // 数据快照对话框
  379. snapshotDialog: {
  380. visible: false,
  381. form: {
  382. snapshotName: '',
  383. description: '',
  384. snapshotYear: ''
  385. },
  386. rules: {
  387. snapshotName: [{ required: true, message: '请输入快照名称', trigger: 'blur' }],
  388. description: [{ required: true, message: '请输入快照描述', trigger: 'blur' }],
  389. snapshotYear: [{ required: true, message: '请选择快照年份', trigger: 'change' }]
  390. }
  391. },
  392. };
  393. },
  394. watch: {
  395. // 根据名称筛选部门树
  396. deptName(val) {
  397. this.$refs.tree.filter(val);
  398. },
  399. height(val) { //在watch中监听height,浏览器窗口大小变动时自适应高度。
  400. this.height = val;
  401. console.log(this.height, "新的高度");
  402. },
  403. },
  404. created() {
  405. //设置表格高度对应屏幕高度
  406. this.$nextTick(() => {
  407. this.clientHeight = (document.body.clientHeight - 80) * 0.8
  408. })
  409. this.getList();
  410. this.handleDevice();
  411. this.getStaffmar();
  412. this.getTreeselect();
  413. this.getDicts("PLANT_DIVIDE").then(response => {
  414. this.plantCodeOptions = response.data;
  415. });
  416. this.getDicts("TEAM_DIVIDE").then(response => {
  417. this.classesOptions = response.data;
  418. });
  419. this.getDicts("TICK_CROSS").then(response => {
  420. this.subOptions = response.data;
  421. });
  422. this.getDicts("TICK_CROSS").then(response => {
  423. this.crackingOptions = response.data;
  424. });
  425. this.getDicts("TICK_CROSS").then(response => {
  426. this.hotareaOptions = response.data;
  427. });
  428. this.getDicts("TICK_CROSS").then(response => {
  429. this.coldareaOptions = response.data;
  430. });
  431. this.getDicts("TICK_CROSS").then(response => {
  432. this.aeupguOptions = response.data;
  433. });
  434. this.getDicts("TRAINING_QUALIFY").then(response => {
  435. this.onboardOptions = response.data;
  436. });
  437. this.getDicts("TRAINING_QUALIFY").then(response => {
  438. this.tdsOptions = response.data;
  439. });
  440. this.getDicts("TRAINING_QUALIFY").then(response => {
  441. this.postCardOptions = response.data;
  442. });
  443. this.getDicts("TRAINING_QUALIFY").then(response => {
  444. this.craftOptions = response.data;
  445. });
  446. this.getDicts("TRAINING_QUALIFY").then(response => {
  447. this.companyOptions = response.data;
  448. });
  449. this.getDicts("TRAINING_QUALIFY").then(response => {
  450. this.deviceOptions = response.data;
  451. });
  452. this.getDicts("ACTUALPOST").then(response => {
  453. this.positionOptions = response.data;
  454. });
  455. this.getDicts("TRAINING_TYPE_BCC").then(response => {
  456. this.trainingTypeOptions = response.data;
  457. });
  458. },
  459. methods: {
  460. handleTableWheel(event) {
  461. let obj = this.$refs['branch']
  462. return this.tableZoom(obj, event)
  463. },
  464. tableZoom(obj, event) {
  465. // 一开始默认是100%
  466. let zoom = parseInt(obj.style.zoom, 10) || 100
  467. // 滚轮滚一下wheelDelta的值增加或减少120
  468. zoom += event.wheelDelta / 12
  469. if (zoom > 25) {
  470. obj.style.zoom = zoom + '%'
  471. }
  472. return false
  473. },
  474. // vue-draggable-resizable 相关方法
  475. onVdrActivated() {
  476. console.log('容器激活');
  477. },
  478. onVdrDeactivated() {
  479. console.log('容器失活');
  480. },
  481. onVdrDragStop(x, y) {
  482. console.log('拖拽停止:', { x, y });
  483. this.vdr.x = x;
  484. this.vdr.y = y;
  485. },
  486. // 增加容器高度
  487. increaseHeight() {
  488. console.log('增加高度前:', this.vdr.height);
  489. this.vdr.height += 200; // 每次增加200px
  490. console.log('增加高度后:', this.vdr.height);
  491. // 同时更新表格高度
  492. this.clientHeight = (this.vdr.height - 80) * 0.8;
  493. console.log('更新表格高度:', this.clientHeight);
  494. },
  495. colspanMethod({row, column, rowIndex, columnIndex}) {
  496. if (columnIndex === 0) {
  497. const _row = this.setTable(this.companylevelList).merge[rowIndex];
  498. const _col = _row > 0 ? 1 : 0;
  499. return {
  500. rowspan: _row,
  501. colspan: _col
  502. };
  503. }
  504. if (columnIndex === 1) {
  505. const _row = this.setTable(this.companylevelList).merge[rowIndex];
  506. const _col = _row > 0 ? 1 : 0;
  507. return {
  508. rowspan: _row,
  509. colspan: _col
  510. };
  511. }
  512. if (columnIndex === 2) {
  513. const _row = this.setTable(this.companylevelList).merge[rowIndex];
  514. const _col = _row > 0 ? 1 : 0;
  515. return {
  516. rowspan: _row,
  517. colspan: _col
  518. };
  519. }
  520. },
  521. colspanDeviceMethod({row, column, rowIndex, columnIndex}) {
  522. if (columnIndex === 0) {
  523. const _row = this.setTable(this.devicelevelList).merge[rowIndex];
  524. const _col = _row > 0 ? 1 : 0;
  525. return {
  526. rowspan: _row,
  527. colspan: _col
  528. };
  529. }
  530. if (columnIndex === 1) {
  531. const _row = this.setTable(this.devicelevelList).merge[rowIndex];
  532. const _col = _row > 0 ? 1 : 0;
  533. return {
  534. rowspan: _row,
  535. colspan: _col
  536. };
  537. }
  538. },
  539. setTable(tableData) {
  540. let spanArr = [],
  541. concat = 0;
  542. tableData.forEach((item, index) => {
  543. if (index === 0) {
  544. spanArr.push(1);
  545. } else {
  546. if (item[1] === tableData[index - 1][1] && item[0] === tableData[index - 1][0]) {
  547. //第一列需合并相同内容的判断条件
  548. spanArr[concat] += 1;
  549. spanArr.push(0);
  550. } else {
  551. spanArr.push(1);
  552. concat = index;
  553. }
  554. }
  555. });
  556. return {
  557. merge: spanArr
  558. };
  559. },
  560. //根据是否存在最后培训时间显示颜色提示
  561. tableCellClassName({row, column, rowIndex, columnIndex}) {
  562. // for (let i = 2; i < this.transTitle.length + 2; i++) {
  563. // if (columnIndex == i){
  564. // return this.changeColor(row[i])
  565. // }
  566. // }
  567. return this.changeColor(row[columnIndex])
  568. },
  569. changeColor(value) {
  570. if (value == null) {
  571. return 'companyNot'
  572. }
  573. if (value.trim().startsWith('20')) {
  574. return 'companyFinish'
  575. }
  576. if (value === "-") {
  577. return 'companyUrgent'
  578. }
  579. if (value === "尚未开始培训") {
  580. return 'companyNot'
  581. }
  582. },
  583. /** 查询培训成绩列表 */
  584. getList() {
  585. this.loading = true;
  586. listTrainingrecords(this.queryParams).then(response => {
  587. this.trainingrecordsList = response.rows;
  588. this.total = response.total;
  589. this.loading = false;
  590. });
  591. },
  592. //获取人员表
  593. getStaffmar() {
  594. selectTimeStaffmgr(this.staffmgrQueryParams).then(response => {
  595. const allData = response.rows
  596. const data = [];
  597. for (let i = 0; i < allData.length; i++) {
  598. data.push({
  599. name: allData[i].name,
  600. actualpost: this.selectDictLabel(this.positionOptions, allData[i].actualpost),
  601. trainingTime: allData[i].trainingTime
  602. });
  603. }
  604. this.transTitle = data;
  605. })
  606. },
  607. //获取公司级培训列表
  608. getCompanyTraining() {
  609. companyListParticipants(this.companyQueryParams).then(response => {
  610. this.companylevelList = response.companyLevelList
  611. const allData = response.staffmgrs
  612. const data = [];
  613. for (let i = 0; i < allData.length; i++) {
  614. data.push({
  615. name: allData[i].name,
  616. actualpost: this.selectDictLabel(this.positionOptions, allData[i].actualpost),
  617. });
  618. }
  619. this.companyStaffmgrs = data
  620. this.companyTrainingLoad = false
  621. this.$nextTick(() => {
  622. this.$refs.companyTable.doLayout(); // 解决表格错位
  623. });
  624. })
  625. },
  626. //获取装置级培训列表
  627. getDeviceTraining() {
  628. devicebccListParticipants(this.deviceParams).then(response => {
  629. this.devicelevelList = response.deviceLevelList
  630. const allData = response.staffmgrs
  631. const data = [];
  632. for (let i = 0; i < allData.length; i++) {
  633. data.push({
  634. name: allData[i].name,
  635. actualpost: this.selectDictLabel(this.positionOptions, allData[i].actualpost),
  636. });
  637. }
  638. this.deviceStaffmgrs = data
  639. this.deviceTrainingLoad = false
  640. this.$nextTick(() => {
  641. this.$refs.deviceTable.doLayout(); // 解决表格错位
  642. });
  643. })
  644. },
  645. /** 查询部门下拉树结构 */
  646. getTreeselect() {
  647. treeselect().then(response => {
  648. this.deptOptions = response.data;
  649. });
  650. },
  651. // 装置名称字典翻译
  652. plantCodeFormat(row, column) {
  653. return this.selectDictLabel(this.plantCodeOptions, row.plantCode);
  654. },
  655. // 班值字典翻译
  656. classesFormat(row, column) {
  657. return this.selectDictLabel(this.classesOptions, row.classes);
  658. },
  659. // SUB字典翻译
  660. subFormat(row, column) {
  661. return this.selectDictLabel(this.subOptions, row.sub);
  662. },
  663. // 裂解字典翻译
  664. crackingFormat(row, column) {
  665. return this.selectDictLabel(this.crackingOptions, row.cracking);
  666. },
  667. // 热区字典翻译
  668. hotareaFormat(row, column) {
  669. return this.selectDictLabel(this.hotareaOptions, row.hotarea);
  670. },
  671. // 冷区字典翻译
  672. coldareaFormat(row, column) {
  673. return this.selectDictLabel(this.coldareaOptions, row.coldarea);
  674. },
  675. // AEU/PGU字典翻译
  676. aeupguFormat(row, column) {
  677. return this.selectDictLabel(this.aeupguOptions, row.aeupgu);
  678. },
  679. // 入职字典翻译
  680. onboardFormat(row, column) {
  681. return this.selectDictLabel(this.onboardOptions, row.onboard);
  682. },
  683. // TDS字典翻译
  684. tdsFormat(row, column) {
  685. return this.selectDictLabel(this.tdsOptions, row.tds);
  686. },
  687. // 上岗证复证字典翻译
  688. postCardFormat(row, column) {
  689. return this.selectDictLabel(this.postCardOptions, row.postCard);
  690. },
  691. // 工艺培训字典翻译
  692. craftFormat(row, column) {
  693. return this.selectDictLabel(this.craftOptions, row.craft);
  694. },
  695. //公司级培训字典翻译
  696. companyFormat(row, column) {
  697. return this.selectDictLabel(this.companyOptions, row.company);
  698. },
  699. //装置级培训字典翻译
  700. deviceFormat(row, column) {
  701. return this.selectDictLabel(this.deviceOptions, row.device);
  702. },
  703. // 培训类型翻译
  704. trainingTypeFormat(row, column) {
  705. return this.selectDictLabel(this.trainingTypeOptions, row.trainingType);
  706. },
  707. // 取消按钮
  708. cancel() {
  709. this.open = false;
  710. this.reset();
  711. },
  712. // 表单重置
  713. reset() {
  714. this.form = {
  715. id: null,
  716. plantCode: null,
  717. name: null,
  718. employeeid: null,
  719. classes: null,
  720. companysafe: null,
  721. plantsafe: null,
  722. teamsafe: null,
  723. sub: null,
  724. cracking: null,
  725. hotarea: null,
  726. coldarea: null,
  727. aeupgu: null,
  728. upscore: null,
  729. downscore: null,
  730. delFlag: null,
  731. createrCode: null,
  732. createdate: null,
  733. updaterCode: null,
  734. updatedate: null,
  735. remarks: null,
  736. deptId: null,
  737. onboard: null,
  738. tds: null,
  739. postCard: null,
  740. craft: null,
  741. company: null,
  742. device: null
  743. };
  744. this.resetForm("form");
  745. },
  746. /** 搜索按钮操作 */
  747. handleQuery() {
  748. this.queryParams.pageNum = 1;
  749. this.getList();
  750. },
  751. //搜索按钮
  752. handleDeviceQuery() {
  753. this.deviceTrainingLoad = true;
  754. this.$nextTick(() => {
  755. this.getDeviceTraining();
  756. });
  757. },
  758. staffDeviceQuery() {
  759. this.getDevice(this.employeeid)
  760. },
  761. /** 重置按钮操作 */
  762. resetQuery() {
  763. this.resetForm("queryForm");
  764. this.handleQuery();
  765. },
  766. // 多选框选中数据
  767. handleSelectionChange(selection) {
  768. this.ids = selection.map(item => item.id)
  769. this.single = selection.length !== 1
  770. this.multiple = !selection.length
  771. },
  772. //根据分数显示颜色提示
  773. tableCellStyle({row, column, rowIndex, columnIndex}) {
  774. if (columnIndex === 4 && row.onboard === "2") {
  775. return "color: rgba(255, 26, 26, 0.98) "
  776. } else if (columnIndex === 5 && row.tds === "2") {
  777. return "color: rgba(255, 26, 26, 0.98) "
  778. } else if (columnIndex === 6 && row.postCard === "2") {
  779. return "color: rgba(255, 26, 26, 0.98) "
  780. } else if (columnIndex === 7 && row.craft === "2") {
  781. return "color: rgba(255, 26, 26, 0.98) "
  782. } else if (columnIndex === 8 && row.company === "2") {
  783. return "color: rgba(255, 26, 26, 0.98) "
  784. }
  785. },
  786. //排序
  787. sortChange(val) {
  788. if (val.prop == 'companysafe') {
  789. this.queryParams.sortDate = 'companysafe';
  790. } else if (val.prop == 'plantsafe') {
  791. this.queryParams.sortDate = 'plantsafe';
  792. } else if (val.prop == 'teamsafe') {
  793. this.queryParams.sortDate = 'teamsafe';
  794. }
  795. if (val.order === 'descending') {
  796. this.queryParams.sortOrder = 'desc'
  797. } else {
  798. this.queryParams.sortOrder = 'asc'
  799. }
  800. this.getList()
  801. },
  802. /** 获取当前年份 */
  803. getNowTime() {
  804. var now = new Date();
  805. var year = now.getFullYear(); //得到年份
  806. var defaultDate = `${year}`;
  807. defaultDate = `${year}`
  808. return defaultDate;
  809. },
  810. /** 公司级培训矩阵按钮操作 */
  811. handleCompany() {
  812. this.companyTrainingLoad = true
  813. this.companyTrainingTitle = this.$t('参加') + this.$t('空格') + this.$t('公司级') + this.$t('空格') + this.$t('培训人员') + this.$t('空格') + this.$t('名单')
  814. this.companyTrainingOpen = true
  815. // loading.close();
  816. this.tableShow = true
  817. setTimeout(() => {
  818. this.getCompanyTraining();
  819. }, 10);
  820. },
  821. /** 装置级培训矩阵按钮操作 */
  822. handleDevice() {
  823. this.deviceTrainingLoad = true
  824. this.resetForm("queryRegularForm");
  825. this.deviceTrainingTitle = this.$t('参加') + this.$t('空格') + this.$t('装置级') + this.$t('空格') + this.$t('培训人员') + this.$t('空格') + this.$t('名单')
  826. this.deviceTrainingOpen = true
  827. this.tableShow = true
  828. this.deviceParams.year = this.getNowTime();
  829. // this.$nextTick(() => {
  830. // deviceListParticipants(this.deviceParams).then(response => {
  831. // this.devicelevelListTemp = response;
  832. // this.devicelevelList = response
  833. // this.deviceTrainingLoad = false
  834. // this.$nextTick(() => {
  835. // this.$refs.deviceTable.doLayout(); // 解决表格错位
  836. // });
  837. // })
  838. // });
  839. setTimeout(() => {
  840. this.getDeviceTraining();
  841. }, 10);
  842. },
  843. /** 人员培训时长按钮操作 */
  844. handleTime() {
  845. this.trainingTimeOpen = true;
  846. this.trainingTimeTitle = this.$t('人员培训时长');
  847. },
  848. /** 新增按钮操作 */
  849. handleAdd() {
  850. this.reset();
  851. this.open = true;
  852. this.title = this.$t('新增') + this.$t('空格') + this.$t('培训成绩');
  853. },
  854. /** 修改按钮操作 */
  855. handleUpdate(row) {
  856. this.reset();
  857. const id = row.id || this.ids
  858. getTrainingrecords(id).then(response => {
  859. this.form = response.data;
  860. if (response.data.sub == 10) {
  861. this.form.sub = true
  862. }
  863. if (response.data.cracking == 10) {
  864. this.form.cracking = true
  865. }
  866. if (response.data.hotarea == 10) {
  867. this.form.hotarea = true
  868. }
  869. if (response.data.coldarea == 10) {
  870. this.form.coldarea = true
  871. }
  872. if (response.data.aeupgu == 10) {
  873. this.form.aeupgu = true
  874. }
  875. this.open = true;
  876. this.title = this.$t('修改') + this.$t('培训成绩');
  877. });
  878. },
  879. /** 提交按钮 */
  880. submitForm() {
  881. this.$refs["form"].validate(valid => {
  882. if (valid) {
  883. if (this.form.id != null) {
  884. updateTrainingrecords(this.form).then(response => {
  885. this.msgSuccess(this.$t('修改成功'));
  886. this.open = false;
  887. this.getList();
  888. });
  889. } else {
  890. addTrainingrecords(this.form).then(response => {
  891. this.msgSuccess(this.$t('新增成功'));
  892. this.open = false;
  893. this.getList();
  894. });
  895. }
  896. }
  897. });
  898. },
  899. /** 删除按钮操作 */
  900. handleDelete(row) {
  901. const ids = row.id || this.ids;
  902. this.$confirm(this.$t('是否确认删除?'), this.$t('警告'), {
  903. confirmButtonText: this.$t('确定'),
  904. cancelButtonText: this.$t('取消'),
  905. type: "warning"
  906. }).then(function () {
  907. return delTrainingrecords(ids);
  908. }).then(() => {
  909. this.getList();
  910. this.msgSuccess(this.$t('删除成功'));
  911. })
  912. },
  913. /** 公司级培训删除按钮操作 */
  914. participantsHandleDelete(row) {
  915. const ids = row.id
  916. this.$confirm(this.$t('是否确认删除?'), this.$t('警告'), {
  917. confirmButtonText: this.$t('确定'),
  918. cancelButtonText: this.$t('取消'),
  919. type: "warning"
  920. }).then(function () {
  921. return delParticipants(ids);
  922. }).then(() => {
  923. this.getParticipants(row.staffId)
  924. this.msgSuccess(this.$t('删除成功'));
  925. })
  926. },
  927. /** 导出按钮操作 */
  928. handleExport() {
  929. const queryParams = this.queryParams;
  930. this.$confirm(this.$t('是否确认导出所有培训成绩数据项?'), this.$t('警告'), {
  931. confirmButtonText: this.$t('确定'),
  932. cancelButtonText: this.$t('取消'),
  933. type: "warning"
  934. }).then(function () {
  935. return exportTrainingrecords(queryParams);
  936. }).then(response => {
  937. this.download(response.msg);
  938. })
  939. },
  940. /** 导出人员培训时长按钮操作 */
  941. exportTrainingTime() {
  942. const queryParams = this.transTitle;
  943. this.$confirm(this.$t('是否确认导出人员培训时长?'), this.$t('警告'), {
  944. confirmButtonText: this.$t('确定'),
  945. cancelButtonText: this.$t('取消'),
  946. type: "warning"
  947. }).then(function () {
  948. return exportTrainingTime();
  949. }).then(response => {
  950. this.download(response.msg);
  951. })
  952. },
  953. /** 导入按钮操作 */
  954. handleImport() {
  955. this.upload.title = this.$t('用户导入');
  956. this.upload.open = true;
  957. },
  958. /** 下载模板操作 */
  959. importTemplate() {
  960. this.$refs['downloadFileForm'].submit()
  961. },
  962. // 文件上传中处理
  963. handleFileUploadProgress(event, file, fileList) {
  964. this.upload.isUploading = true;
  965. },
  966. // 文件上传成功处理
  967. handleFileSuccess(response, file, fileList) {
  968. this.upload.open = false;
  969. this.upload.isUploading = false;
  970. this.$refs.upload.clearFiles();
  971. if (response.data[0] != null) {
  972. this.$alert(this.$t('成功导入') + response.msg + this.$t('条数据') + "," + this.$t('第') + response.data + this.$t('行数据出现错误导入失败') + "。", this.$t('导入结果'), {dangerouslyUseHTMLString: true});
  973. } else {
  974. this.$alert(this.$t('成功导入') + response.msg + this.$t('条数据'), this.$t('导入结果'), {dangerouslyUseHTMLString: true});
  975. }
  976. this.getList();
  977. },
  978. // 提交上传文件
  979. submitFileForm() {
  980. this.$refs.upload.submit();
  981. },
  982. /** 报告附件按钮操作 */
  983. handleDoc(row, fileType) {
  984. this.doc.pType = fileType
  985. this.doc.queryParams.pType = fileType
  986. this.doc.id = row.id;
  987. var titleType = '';
  988. if (fileType === 'trainingrecords-onbord') {
  989. titleType = row.name + this.$t('空格') + this.$t('的') + this.$t('入职') + this.$t('空格') + this.$t('培训')
  990. } else if (fileType === 'trainingrecords-tds') {
  991. titleType = row.name + this.$t('空格') + this.$t('的') + this.$t('TDS培训')
  992. } else if (fileType === 'trainingrecords-craft') {
  993. titleType = row.name + this.$t('空格') + this.$t('的') + this.$t('工艺培训')
  994. } else {
  995. titleType = this.$t('上岗证') + this.$t('空格') + this.$t('培训')
  996. }
  997. this.doc.title = titleType;
  998. this.doc.open = true;
  999. this.doc.queryParams.pId = row.id
  1000. this.doc.pId = row.id
  1001. this.getFileList()
  1002. this.$nextTick(() => {
  1003. this.$refs.doc.clearFiles()
  1004. })
  1005. },
  1006. getFileList() {
  1007. allFileList(this.doc.queryParams).then(response => {
  1008. response.forEach(element => {
  1009. element["isEdit"] = false
  1010. });
  1011. response.forEach(element => {
  1012. element["isAdd"] = false
  1013. });
  1014. this.doc.commonfileList = response;
  1015. });
  1016. },
  1017. //附件上传中处理
  1018. handleFileDocProgress(event, file, fileList) {
  1019. this.doc.file = file;
  1020. this.doc.isUploading = true;
  1021. },
  1022. //附件上传成功处理
  1023. handleFileDocSuccess(response, file, fileList) {
  1024. this.doc.isUploading = false;
  1025. this.$alert(response.msg, this.$t('导入结果'), {dangerouslyUseHTMLString: true});
  1026. this.getFileList()
  1027. },
  1028. // 文件下载处理
  1029. handleDownload(row) {
  1030. var name = row.fileName;
  1031. var url = row.fileUrl;
  1032. var suffix = url.substring(url.lastIndexOf("."), url.length);
  1033. const a = document.createElement('a')
  1034. a.setAttribute('download', name)
  1035. a.setAttribute('target', '_blank')
  1036. a.setAttribute('href', process.env.VUE_APP_BASE_API + url)
  1037. a.click()
  1038. },
  1039. //pdf预览
  1040. openPdf() {
  1041. window.open(this.pdf.pdfUrl);//path是文件的全路径地址
  1042. },
  1043. handleSee(row) {
  1044. this.pdf.open = true
  1045. this.pdf.title = row.fileName
  1046. this.pdf.pdfUrl = process.env.VUE_APP_BASE_API + '/pdf/web/viewer.html?file=' + process.env.VUE_APP_BASE_API + row.fileUrl
  1047. },
  1048. /** 打开上岗证复证列表 */
  1049. openWorklicense(row) {
  1050. this.worklicenseOpen = true;
  1051. this.worklicenseTitle = row.name + this.$t('的') + this.$t('上岗证复证') + this.$t('列表');
  1052. this.worklicenseParams.employeeid = row.employeeid
  1053. listTraining(this.worklicenseParams).then(response => {
  1054. this.worklicenseList = response;
  1055. });
  1056. },
  1057. /** 打开公司级培训列表 */
  1058. openCompany(row) {
  1059. this.companyOpen = true;
  1060. this.companyTitle = row.name + this.$t('空格') + this.$t('的') + this.$t('公司级') + this.$t('空格') + this.$t('培训信息');
  1061. this.$nextTick(() => {
  1062. this.getParticipants(row.employeeid)
  1063. })
  1064. },
  1065. /** 打开装置级培训列表 */
  1066. openDevice(row) {
  1067. this.deviceOpen = true;
  1068. this.resetForm("queryRegularForm");
  1069. this.deviceTitle = row.name + this.$t('空格') + this.$t('的') + this.$t('装置级') + this.$t('空格') + this.$t('培训信息');
  1070. this.employeeid = row.employeeid;
  1071. this.queryDeviceParams.year = this.getNowTime();
  1072. this.$nextTick(() => {
  1073. this.getDevice(row.employeeid)
  1074. })
  1075. },
  1076. getParticipants(employeeid) {
  1077. this.queryCompanyParams.staffId = employeeid
  1078. listParticipants(this.queryCompanyParams).then(response => {
  1079. this.participantsList = response.rows;
  1080. });
  1081. },
  1082. getDevice(employeeid) {
  1083. this.queryDeviceParams.staffId = employeeid
  1084. listDevice(this.queryDeviceParams).then(response => {
  1085. this.devicceList = response.rows;
  1086. });
  1087. },
  1088. // 取消
  1089. cancelFile(row, index) {
  1090. // 如果是新增的数据
  1091. if (row.isAdd) {
  1092. this.doc.commonfileList.splice(index, 1)
  1093. } else {
  1094. // 不是新增的数据 还原数据
  1095. for (const i in row.oldRow) {
  1096. row[i] = row.oldRow[i]
  1097. }
  1098. row.isEdit = false
  1099. }
  1100. },
  1101. edit(row) {
  1102. // 备份原始数据
  1103. row['oldRow'] = JSON.parse(JSON.stringify(row));
  1104. this.$nextTick(() => {
  1105. row.isEdit = true;
  1106. })
  1107. },
  1108. save(row) {
  1109. row.isEdit = false;
  1110. var that = this;
  1111. that.loading = true;
  1112. this.form = row;
  1113. if (row.isAdd == true) {
  1114. addCommonfile(this.form).then(response => {
  1115. this.msgSuccess(this.$t('新增成功'));
  1116. this.open = false;
  1117. this.getList();
  1118. });
  1119. } else {
  1120. updateCommonfile(this.form).then(response => {
  1121. this.msgSuccess(this.$t('修改成功'));
  1122. this.open = false;
  1123. this.getList();
  1124. });
  1125. }
  1126. },
  1127. /** 删除按钮操作 */
  1128. handleDeleteDoc(row) {
  1129. const ids = row.id || this.ids;
  1130. this.$confirm(this.$t('是否确认删除?'), this.$t('警告'), {
  1131. confirmButtonText: this.$t('确定'),
  1132. cancelButtonText: this.$t('取消'),
  1133. type: "warning"
  1134. }).then(function () {
  1135. return delCommonfile(ids);
  1136. }).then(() => {
  1137. this.getFileList()
  1138. this.msgSuccess(this.$t('删除成功'));
  1139. })
  1140. },
  1141. /** 公司级培训修改按钮操作 */
  1142. handleCompanyUpdate(row) {
  1143. this.reset();
  1144. const id = row.id || this.ids
  1145. },
  1146. closeDialog() {
  1147. this.companylevelList = []
  1148. this.devicelevelList = []
  1149. this.tableShow = false
  1150. },
  1151. /** 创建数据快照 */
  1152. handleSnapshotData() {
  1153. // 设置默认年份为当前查询的年份
  1154. this.snapshotDialog.form.snapshotYear = this.deviceParams.year || this.getNowTime();
  1155. this.snapshotDialog.visible = true;
  1156. this.$nextTick(() => {
  1157. this.$refs.snapshotForm.resetFields();
  1158. // 重新设置年份
  1159. this.snapshotDialog.form.snapshotYear = this.deviceParams.year || this.getNowTime();
  1160. });
  1161. },
  1162. /** 提交数据快照 */
  1163. submitSnapshotData() {
  1164. this.$refs.snapshotForm.validate(valid => {
  1165. if (valid) {
  1166. // 准备快照数据(忽略培训类型)
  1167. const snapshotData = {
  1168. snapshotName: this.snapshotDialog.form.snapshotName,
  1169. description: this.snapshotDialog.form.description,
  1170. snapshotYear: this.snapshotDialog.form.snapshotYear,
  1171. deviceData: this.devicelevelList,
  1172. staffData: this.deviceStaffmgrs,
  1173. queryParams: this.deviceParams
  1174. };
  1175. // 调用后端API创建快照
  1176. createSnapshot(snapshotData).then(response => {
  1177. if (response.code === 200) {
  1178. this.msgSuccess('数据快照创建成功!');
  1179. this.snapshotDialog.visible = false;
  1180. } else {
  1181. this.msgError(response.msg || '创建快照失败');
  1182. }
  1183. }).catch(error => {
  1184. this.msgError('创建快照失败:' + error.message);
  1185. });
  1186. }
  1187. });
  1188. },
  1189. /** 查看快照 */
  1190. handleViewSnapshot() {
  1191. if (!this.deviceParams.year) {
  1192. this.msgError('请选择年份');
  1193. return;
  1194. }
  1195. // 先显示表格并开启loading
  1196. this.tableShow = true;
  1197. this.deviceTrainingOpen = true;
  1198. this.deviceTrainingLoad = true;
  1199. const params = {
  1200. snapshotYear: this.deviceParams.year
  1201. };
  1202. querySnapshot(params)
  1203. .then(res => {
  1204. if (res.code !== 200 || !res.data) {
  1205. this.msgError(res.msg || '未找到对应快照');
  1206. return;
  1207. }
  1208. try {
  1209. const snapshotData = res.data.snapshotData ? JSON.parse(res.data.snapshotData) : [];
  1210. if (Array.isArray(snapshotData)) {
  1211. this.devicelevelList = snapshotData;
  1212. } else if (snapshotData && snapshotData.deviceData) {
  1213. this.devicelevelList = snapshotData.deviceData || [];
  1214. if (snapshotData.staffData && Array.isArray(snapshotData.staffData)) {
  1215. this.deviceStaffmgrs = snapshotData.staffData;
  1216. }
  1217. } else {
  1218. this.devicelevelList = [];
  1219. }
  1220. this.deviceTrainingTitle = `${this.$t('查看')} ${this.deviceParams.year} ${this.$t('装置级')} ${this.$t('培训人员')} ${this.$t('名单')}(${this.$t('快照')})`;
  1221. this.$nextTick(() => {
  1222. if (this.$refs.deviceTable) this.$refs.deviceTable.doLayout();
  1223. });
  1224. } catch (e) {
  1225. console.error(e);
  1226. this.msgError('快照数据解析失败');
  1227. }
  1228. })
  1229. .finally(() => {
  1230. this.deviceTrainingLoad = false;
  1231. });
  1232. },
  1233. /** 查询快照列表 */
  1234. querySnapshotList() {
  1235. this.loading = true;
  1236. querySnapshot(this.snapshotDialog.form).then(response => {
  1237. this.snapshotList = response.rows;
  1238. this.total = response.total;
  1239. this.loading = false;
  1240. });
  1241. },
  1242. /** 加载快照详情 */
  1243. loadSnapshotDetail(row) {
  1244. this.snapshotDialog.form.snapshotName = row.snapshotName;
  1245. this.snapshotDialog.form.description = row.description;
  1246. this.snapshotDialog.form.snapshotYear = row.snapshotYear;
  1247. this.snapshotDialog.visible = true;
  1248. this.$nextTick(() => {
  1249. this.$refs.snapshotForm.resetFields();
  1250. });
  1251. },
  1252. /** 导出Excel */
  1253. exportToExcel() {
  1254. if (!this.devicelevelList || this.devicelevelList.length === 0) {
  1255. this.msgError('没有可导出的数据');
  1256. return;
  1257. }
  1258. try {
  1259. // 构建表格HTML
  1260. let html = '<table border="1" cellspacing="0" cellpadding="5">';
  1261. // 构建表头
  1262. html += '<thead><tr>';
  1263. html += '<th style="background:#f0f0f0;">课程代码</th>';
  1264. html += '<th style="background:#f0f0f0;">培训课程名称</th>';
  1265. if (Array.isArray(this.deviceStaffmgrs)) {
  1266. this.deviceStaffmgrs.forEach(staff => {
  1267. const headerText = `${staff.name || ''}${staff.actualpost ? '(' + staff.actualpost + ')' : ''}`;
  1268. html += `<th style="background:#f0f0f0;">${this.escapeHtml(headerText)}</th>`;
  1269. });
  1270. }
  1271. html += '</tr></thead>';
  1272. // 构建数据行
  1273. html += '<tbody>';
  1274. if (Array.isArray(this.devicelevelList)) {
  1275. this.devicelevelList.forEach(row => {
  1276. html += '<tr>';
  1277. if (Array.isArray(row)) {
  1278. row.forEach(cell => {
  1279. const cellValue = cell != null ? String(cell) : '';
  1280. html += `<td>${this.escapeHtml(cellValue)}</td>`;
  1281. });
  1282. }
  1283. html += '</tr>';
  1284. });
  1285. }
  1286. html += '</tbody></table>';
  1287. // 创建Blob并下载
  1288. const blob = new Blob(['\ufeff' + html], {
  1289. type: 'application/vnd.ms-excel;charset=utf-8;'
  1290. });
  1291. const fileName = `装置级培训人员名单_${this.deviceParams.year || new Date().getFullYear()}.xls`;
  1292. // 创建下载链接
  1293. if (window.navigator.msSaveOrOpenBlob) {
  1294. // IE浏览器
  1295. window.navigator.msSaveOrOpenBlob(blob, fileName);
  1296. } else {
  1297. // 其他浏览器
  1298. const link = document.createElement('a');
  1299. link.href = URL.createObjectURL(blob);
  1300. link.download = fileName;
  1301. document.body.appendChild(link);
  1302. link.click();
  1303. document.body.removeChild(link);
  1304. URL.revokeObjectURL(link.href);
  1305. }
  1306. this.msgSuccess('导出成功');
  1307. } catch (error) {
  1308. console.error('导出Excel失败:', error);
  1309. this.msgError('导出失败,请重试');
  1310. }
  1311. },
  1312. /** HTML转义 */
  1313. escapeHtml(text) {
  1314. const map = {
  1315. '&': '&amp;',
  1316. '<': '&lt;',
  1317. '>': '&gt;',
  1318. '"': '&quot;',
  1319. "'": '&#039;'
  1320. };
  1321. return String(text).replace(/[&<>"']/g, m => map[m]);
  1322. }
  1323. }
  1324. };
  1325. </script>
  1326. <style>
  1327. .companyLevelTable {
  1328. color: black;
  1329. }
  1330. ::v-deep.el-table tbody tr:hover > td {
  1331. background-color: transparent !important;
  1332. }
  1333. .el-dialog__body {
  1334. padding-top: 0px
  1335. }
  1336. .el-table--scrollable-x .el-table__body-wrapper {
  1337. z-index: 1;
  1338. }
  1339. .rectangleRed {
  1340. width: 60px !important;
  1341. height: 30px !important;
  1342. margin-bottom: -8px;
  1343. }
  1344. .rectangleGreen {
  1345. width: 60px !important;
  1346. height: 30px !important;
  1347. margin-bottom: -8px;
  1348. }
  1349. </style>