index.vue 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446
  1. <template>
  2. <div class="app-container barchart">
  3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
  4. <el-form-item label="培训内容" prop="plantName">
  5. <el-input
  6. v-model="queryParams.plantName"
  7. placeholder="请输入培训内容"
  8. clearable
  9. size="small"
  10. @keyup.enter.native="handleQuery"
  11. />
  12. </el-form-item>
  13. <el-form-item label="开始日期" prop="startDate">
  14. <el-date-picker clearable size="small" style="width: 200px"
  15. v-model="queryParams.startDate"
  16. type="date"
  17. value-format="yyyy-MM-dd"
  18. placeholder="选择开始日期">
  19. </el-date-picker>
  20. </el-form-item>
  21. <el-form-item label="结束日期" prop="endDate">
  22. <el-date-picker clearable size="small" style="width: 200px"
  23. v-model="queryParams.endDate"
  24. type="date"
  25. value-format="yyyy-MM-dd"
  26. placeholder="选择结束日期">
  27. </el-date-picker>
  28. </el-form-item>
  29. <el-form-item label="学时" prop="classHour">
  30. <el-input
  31. v-model="queryParams.classHour"
  32. placeholder="请输入学时"
  33. clearable
  34. size="small"
  35. @keyup.enter.native="handleQuery"
  36. />
  37. </el-form-item>
  38. <el-form-item :label="$t('申请状态')" prop="approveStatus">
  39. <el-select v-model="queryParams.approveStatus" :placeholder="$t('请选择') + $t('申请状态')" clearable size="small">
  40. <el-option
  41. v-for="dict in approveStatusOptions"
  42. :key="dict.dictValue"
  43. :label="dict.dictLabel"
  44. :value="dict.dictValue"
  45. />
  46. </el-select>
  47. </el-form-item>
  48. <el-form-item label="学习状态" prop="studyState">
  49. <el-select v-model="queryParams.studyState" placeholder="请选择学习状态" clearable size="small">
  50. <el-option
  51. v-for="dict in studyStateOptions"
  52. :key="dict.dictValue"
  53. :label="dict.dictLabel"
  54. :value="dict.dictValue"
  55. />
  56. </el-select>
  57. </el-form-item>
  58. <el-form-item>
  59. <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  60. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  61. </el-form-item>
  62. </el-form>
  63. <!-- 顶部工具栏 -->
  64. <el-row :gutter="10" class="mb8">
  65. <el-col :span="1.5">
  66. <el-button
  67. type="primary"
  68. icon="el-icon-plus"
  69. size="mini"
  70. @click="handleAdd"
  71. v-hasPermi="['spec:plan:add']"
  72. >新增</el-button>
  73. </el-col>
  74. <el-col :span="1.5">
  75. <el-button
  76. type="success"
  77. icon="el-icon-edit"
  78. size="mini"
  79. :disabled="single"
  80. @click="handleUpdate"
  81. v-hasPermi="['spec:plan:edit']"
  82. >修改</el-button>
  83. </el-col>
  84. <el-col :span="1.5">
  85. <el-button
  86. type="danger"
  87. icon="el-icon-delete"
  88. size="mini"
  89. :disabled="multiple"
  90. @click="handleDelete"
  91. v-hasPermi="['spec:plan:remove']"
  92. >删除</el-button>
  93. </el-col>
  94. <el-col :span="1.5">
  95. <el-button
  96. type="info"
  97. icon="el-icon-upload2"
  98. size="mini"
  99. @click="handleImport"
  100. v-hasPermi="['spec:plan:edit']"
  101. >导入</el-button>
  102. </el-col>
  103. <el-col :span="1.5">
  104. <el-button
  105. type="warning"
  106. icon="el-icon-download"
  107. size="mini"
  108. @click="handleExport"
  109. v-hasPermi="['spec:plan:export']"
  110. >导出</el-button>
  111. </el-col>
  112. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  113. </el-row>
  114. <!-- 表格 -->
  115. <el-table v-loading="loading" ref="planTable" :data="planList" @selection-change="handleSelectionChange" :height="clientHeight" border :row-style="tableRowStyle">
  116. <el-table-column type="selection" width="55" align="center" />
  117. <el-table-column label="培训员工" fixed="left"align="center" prop="staffName" :show-overflow-tooltip="true"/>
  118. <el-table-column label="培训内容" fixed="left" align="center" width="150" prop="plantName" :show-overflow-tooltip="true"/>
  119. <el-table-column label="开始日期" width="90" fixed="left" align="center" prop="startDate">
  120. <template slot-scope="scope">
  121. <span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
  122. </template>
  123. </el-table-column>
  124. <el-table-column label="结束日期" width="90" fixed="left" align="center" prop="endDate">
  125. <template slot-scope="scope">
  126. <span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
  127. </template>
  128. </el-table-column>
  129. <el-table-column label="实际完成培训日期" width="90" align="center" prop="dateOfCompletion">
  130. <template slot-scope="scope">
  131. <span>{{ parseTime(scope.row.dateOfCompletion, '{y}-{m}-{d}') }}</span>
  132. </template>
  133. </el-table-column>
  134. <el-table-column label="学时" align="center" prop="classHour" :show-overflow-tooltip="true"/>
  135. <el-table-column label="具体内容" align="center" width="200" prop="classContent" :show-overflow-tooltip="true"/>
  136. <el-table-column label="学习状态" align="center" prop="studyState" :formatter="studyStateFormat">
  137. <!-- <template slot-scope="scope">
  138. <span>{{scope.row.studyState}}</span>
  139. <el-button icon="el-icon-view" style="color:#6e96fa" v-if="scope.row.studyState == 2" @click="handleFeedback(scope.row)" circle></el-button>
  140. </template> -->
  141. </el-table-column>
  142. <el-table-column :label="$t('申请状态')" align="center" prop="approveStatus"
  143. :show-overflow-tooltip="true">
  144. <template slot-scope="scope">
  145. <el-tag
  146. :type="approveStatusType(scope.row.approveStatus)"
  147. disable-transitions>{{approveStatusFormat(scope.row)}}</el-tag>
  148. </template>
  149. </el-table-column>
  150. <el-table-column label="创建人" align="center" prop="createBy" :show-overflow-tooltip="true"/>
  151. <el-table-column label="创建时间" align="center" prop="createdate" width="130">
  152. <template slot-scope="scope">
  153. <span>{{ parseTime(scope.row.createdate, '{y}-{m}-{d} {h}:{i}') }}</span>
  154. </template>
  155. </el-table-column>
  156. <el-table-column label="修改人" align="center" prop="updateBy" :show-overflow-tooltip="true"/>
  157. <el-table-column label="修改时间" align="center" prop="updatedate" width="130">
  158. <template slot-scope="scope">
  159. <span>{{ parseTime(scope.row.updatedate, '{y}-{m}-{d} {h}:{i}') }}</span>
  160. </template>
  161. </el-table-column>
  162. <!-- 渲染时间(年|月) -->
  163. <el-table-column
  164. v-if="IsShowyear"
  165. v-for="(item, index) in showMonths"
  166. :label="item.year"
  167. prop="showMonths"
  168. align="center"
  169. :key="index"
  170. >
  171. <el-table-column
  172. v-for="(it, index1) in item.months"
  173. :label="it.str"
  174. prop="str"
  175. min-width="30"
  176. style="padding: 0; margin: 0;"
  177. align="center"
  178. :key="index1"
  179. >
  180. <template slot-scope="scope" class="progressCon">
  181. <div v-if="getCellDateWidth > 0">
  182. <div
  183. v-if="index === 0 && index1 === 0 && scope.$index % 2 === 0"
  184. class="progressUpon"
  185. :style="{width:optimizeCalcWidth(scope.row),left:optimizeCalcLeft(scope.row)}"
  186. ></div>
  187. <div
  188. v-else-if="index === 0 && index1 === 0 && scope.$index % 2 !== 0"
  189. class="progressDownon"
  190. :style="{width:optimizeCalcWidth(scope.row),left:optimizeCalcLeft(scope.row)}"
  191. ></div>
  192. </div>
  193. <div v-else>
  194. <div
  195. v-if="scope.$index % 2 === 0"
  196. :class="scope.row.beginTimeStamp <= it.endTimeStamp && scope.row.endTimeStamp >= it.beginTimeStamp ? 'progressUpon' : '' "
  197. :style="{width:scope.row.beginTimeStamp <= it.endTimeStamp && scope.row.endTimeStamp >= it.beginTimeStamp ? calcwidth(scope.row,it) : '0px',left:scope.row.beginTimeStamp <= it.endTimeStamp && scope.row.endTimeStamp >= it.beginTimeStamp ? calcLeft(scope.row,it) : '0px'}"
  198. ></div>
  199. <div v-else
  200. :class="scope.row.beginTimeStamp <= it.endTimeStamp && scope.row.endTimeStamp >= it.beginTimeStamp ? 'progressDownon' : '' "
  201. :style="{width:scope.row.beginTimeStamp <= it.endTimeStamp && scope.row.endTimeStamp >= it.beginTimeStamp ? calcwidth(scope.row,it) : '0px',left:scope.row.beginTimeStamp <= it.endTimeStamp && scope.row.endTimeStamp >= it.beginTimeStamp ? calcLeft(scope.row,it) : '0px'}"
  202. ></div>
  203. </div>
  204. </template>
  205. </el-table-column>
  206. </el-table-column>
  207. <!-- 渲染时间(月|日) -->
  208. <el-table-column
  209. v-if="!IsShowyear"
  210. v-for="(item, index) in showMonths"
  211. :label="item.year"
  212. prop="year"
  213. align="center"
  214. :key="index"
  215. >
  216. <el-table-column
  217. v-for="(it, index1) in item.months"
  218. :label="it.str"
  219. prop="str"
  220. style="padding: 0; margin: 0;"
  221. align="center"
  222. :key="index1"
  223. >
  224. <el-table-column
  225. v-for="(itm, index2) in item.days[index1].daysArr"
  226. :label="itm.day"
  227. prop="day"
  228. min-width="30"
  229. style="padding: 0; margin: 0;"
  230. align="center"
  231. :key="index2"
  232. >
  233. <template slot-scope="scope" class="progressCon">
  234. <div v-if="getCellDateWidth > 0">
  235. <div
  236. v-if="index === 0 && index1 === 0 && index2 === 0 && scope.$index % 2 === 0"
  237. class="progressUpon"
  238. :style="{width:optimizeCalcWidth(scope.row),left:optimizeCalcLeft(scope.row)}"
  239. ></div>
  240. <div
  241. v-else-if="index === 0 && index1 === 0 && index2 === 0 && scope.$index % 2 !== 0"
  242. class="progressDownon"
  243. :style="{width:optimizeCalcWidth(scope.row),left:optimizeCalcLeft(scope.row)}"
  244. ></div>
  245. </div>
  246. <div v-else>
  247. <div
  248. v-if="scope.$index % 2 === 0"
  249. :class="scope.row.beginTimeStamp <= itm.timestamp && scope.row.endTimeStamp >= itm.timestamp ? 'progressUpon' : '' "
  250. style="width:calc(100% + 2px)"
  251. ></div>
  252. <div v-else
  253. :class="scope.row.beginTimeStamp <= itm.timestamp && scope.row.endTimeStamp >= itm.timestamp ? 'progressDownon' : '' "
  254. style="width:calc(100% + 2px)"
  255. ></div>
  256. </div>
  257. </template>
  258. </el-table-column>
  259. </el-table-column>
  260. </el-table-column>
  261. <el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width" width="200">
  262. <template slot-scope="scope">
  263. <el-button
  264. size="mini"
  265. type="text"
  266. icon="el-icon-edit"
  267. @click="handleComment(scope.row)"
  268. v-hasPermi="['spec:plan:edit']"
  269. v-if="scope.row.studyState == 3"
  270. >评价</el-button>
  271. <el-button
  272. size="mini"
  273. type="text"
  274. icon="el-icon-view"
  275. v-hasPermi="['spec:plan:edit']"
  276. @click="handleDetail(scope.row)"
  277. v-if="scope.row.studyState == 2"
  278. >详情</el-button>
  279. <el-button
  280. v-if="scope.row.editFlag != 0"
  281. size="mini"
  282. type="text"
  283. icon="el-icon-edit"
  284. @click="handleUpdate(scope.row)"
  285. v-hasPermi="['spec:plan:edit']"
  286. >修改</el-button>
  287. <el-button
  288. v-if="scope.row.editFlag != 0"
  289. size="mini"
  290. type="text"
  291. icon="el-icon-delete"
  292. @click="handleDelete(scope.row)"
  293. v-hasPermi="['spec:plan:remove']"
  294. >删除</el-button>
  295. <el-button
  296. size="mini"
  297. type="text"
  298. icon="el-icon-document"
  299. @click="handleDoc(scope.row)"
  300. v-hasPermi="['spec:plan:edit']"
  301. >附件</el-button>
  302. </template>
  303. </el-table-column>
  304. </el-table>
  305. <!-- 分页 -->
  306. <pagination
  307. v-show="total>0"
  308. :total="total"
  309. :page.sync="queryParams.pageNum"
  310. :limit.sync="queryParams.pageSize"
  311. @pagination="getList"
  312. />
  313. <!-- 添加或修改培训计划对话框 -->
  314. <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  315. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  316. <el-form-item label="培训员工" prop="staffId" >
  317. <el-select v-model="form.staffId" placeholder="请选择学员" disabled>
  318. <el-option
  319. v-for="successor in successorOptions"
  320. :key="successor.key"
  321. :label="successor.value"
  322. :value="successor.key"
  323. ></el-option>
  324. </el-select>
  325. </el-form-item>
  326. <el-form-item label="培训内容" prop="plantName">
  327. <el-input v-model="form.plantName" placeholder="请输入培训内容" />
  328. </el-form-item>
  329. <el-form-item label="开始日期" prop="startDate">
  330. <el-date-picker clearable size="small" style="width: 220px"
  331. v-model="form.startDate"
  332. type="date"
  333. value-format="yyyy-MM-dd"
  334. placeholder="选择开始日期">
  335. </el-date-picker>
  336. </el-form-item>
  337. <el-form-item label="结束日期" prop="endDate">
  338. <el-date-picker clearable size="small" style="width: 220px"
  339. v-model="form.endDate"
  340. type="date"
  341. value-format="yyyy-MM-dd"
  342. placeholder="选择结束日期">
  343. </el-date-picker>
  344. </el-form-item>
  345. <el-form-item label="学时" prop="classHour">
  346. <el-input v-model="form.classHour" placeholder="请输入学时" />
  347. </el-form-item>
  348. <el-form-item label="具体内容">
  349. <el-input
  350. type="textarea"
  351. :rows="6"
  352. placeholder="请输入内容"
  353. v-model="form.classContent">
  354. </el-input>
  355. </el-form-item>
  356. <el-form-item label="学习状态" prop="studyState" v-if="this.operation=='modify'">
  357. <el-select v-model="form.studyState" placeholder="请选择学习状态">
  358. <el-option
  359. v-for="dict in studyStateOptions"
  360. :key="dict.dictValue"
  361. :label="dict.dictLabel"
  362. :value="dict.dictValue"
  363. ></el-option>
  364. </el-select>
  365. </el-form-item>
  366. </el-form>
  367. <div slot="footer" class="dialog-footer">
  368. <el-button type="primary" @click="submitForm">确 定</el-button>
  369. <el-button @click="cancel">取 消</el-button>
  370. </div>
  371. </el-dialog>
  372. <!-- 用户导入对话框 -->
  373. <el-dialog :title="upload.title" :visible.sync="upload.open" width="400px" append-to-body>
  374. <el-upload
  375. ref="upload"
  376. :limit="1"
  377. accept=".xlsx, .xls"
  378. :headers="upload.headers"
  379. :action="upload.url + '?updateSupport=' + upload.updateSupport"
  380. :disabled="upload.isUploading"
  381. :on-progress="handleFileUploadProgress"
  382. :on-success="handleFileSuccess"
  383. :auto-upload="false"
  384. drag
  385. >
  386. <i class="el-icon-upload"></i>
  387. <div class="el-upload__text">
  388. 将文件拖到此处,或
  389. <em>点击上传</em>
  390. </div>
  391. <div class="el-upload__tip" slot="tip">
  392. <el-checkbox v-model="upload.updateSupport" />是否更新已经存在的用户数据
  393. <el-link type="info" style="font-size:12px" @click="importTemplate">下载模板</el-link>
  394. </div>
  395. <div class="el-upload__tip" style="color:red" slot="tip">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
  396. </el-upload>
  397. <div slot="footer" class="dialog-footer">
  398. <el-button type="primary" @click="submitFileForm">确 定</el-button>
  399. <el-button @click="upload.open = false">取 消</el-button>
  400. </div>
  401. </el-dialog>
  402. <!-- 报告附件对话框 -->
  403. <el-dialog v-dialogDrag :title="doc.title" :visible.sync="doc.open" width="700px" append-to-body>
  404. <el-upload
  405. ref="doc"
  406. :limit="50"
  407. :headers="doc.headers"
  408. :action="doc.url + '?pType=' + doc.pType + '&pId=' + doc.pId"
  409. :disabled="doc.isUploading"
  410. :on-progress="handleFileDocProgress"
  411. :on-success="handleFileDocSuccess"
  412. :auto-upload="true"
  413. drag
  414. >
  415. <i class="el-icon-upload"></i>
  416. <div class="el-upload__text">
  417. {{ $t('将文件拖到此处,或') }}
  418. <em>{{ $t('点击上传') }}</em>
  419. </div>
  420. </el-upload>
  421. <el-table :data="doc.commonfileList" border>
  422. <el-table-column :label="$t('文件名')" align="center" prop="fileName" :show-overflow-tooltip="true">
  423. <template slot-scope="scope">
  424. <a class="link-type" @click="handleDownload(scope.row)">
  425. <span>{{ scope.row.fileName }}</span>
  426. </a>
  427. </template>
  428. </el-table-column>
  429. <el-table-column :label="$t('大小(Kb)')" align="center" prop="fileSize" :show-overflow-tooltip="true" width="80" />
  430. <el-table-column :label="$t('上传人')" align="center" prop="creator" :show-overflow-tooltip="true" width="120"/>
  431. <el-table-column :label="$t('操作')" align="center" width="120" class-name="small-padding fixed-width">
  432. <template slot-scope="scope">
  433. <el-button
  434. v-if="scope.row.fileName.endsWith('pdf')"
  435. size="mini"
  436. type="text"
  437. icon="el-icon-view"
  438. @click="handleSee(scope.row)"
  439. >{{ $t('预览') }}</el-button>
  440. <el-button
  441. size="mini"
  442. type="text"
  443. icon="el-icon-download"
  444. @click="handleDownload(scope.row)"
  445. >{{ $t('下载') }}</el-button>
  446. <el-button
  447. size="mini"
  448. type="text"
  449. icon="el-icon-delete"
  450. @click="handleDeleteDoc(scope.row)"
  451. >{{ $t('删除') }}</el-button>
  452. </template>
  453. </el-table-column>
  454. </el-table>
  455. <el-dialog v-dialogDrag :title="pdf.title" :visible.sync="pdf.open" width="1300px" append-to-body>
  456. <div style="margin-top: -60px;float: right;margin-right: 40px;">
  457. <el-button size="mini" type="text" @click="openPdf">{{$t('新页面打开PDF')}}</el-button></div>
  458. <div style="margin-top: -30px">
  459. <iframe :src="pdf.pdfUrl" frameborder="0" width="100%" height="700px"></iframe>
  460. </div>
  461. </el-dialog>
  462. <div slot="footer" class="dialog-footer">
  463. <el-button @click="doc.open = false">{{ $t('返 回') }}</el-button>
  464. </div>
  465. </el-dialog>
  466. <!-- 评价对话框 -->
  467. <el-dialog v-dialogDrag :title="comment.title" :visible.sync="comment.open" width="700px" append-to-body>
  468. <h3 style="margin-bottom:20px;text-align:center;">学员反馈</h3>
  469. <el-table :data="tableData" border style="width: 100%">
  470. <el-table-column prop="id" label="编号" width="50"></el-table-column>
  471. <el-table-column prop="question" label="问题" width="250"></el-table-column>
  472. <el-table-column prop="answer" label="答案"></el-table-column>
  473. </el-table>
  474. <h3 style="margin-top:50px;margin-bottom:20px;text-align:center;">导师反馈</h3>
  475. <el-form label-width="80px">
  476. <el-form-item label="科目成绩">
  477. <el-input v-model="commentParams.score" />
  478. </el-form-item>
  479. <el-form-item label="综合评价">
  480. <el-input v-model="commentParams.overallComment" type="textarea" rows="6"/>
  481. </el-form-item>
  482. </el-form>
  483. <div slot="footer" class="dialog-footer">
  484. <el-button @click="handleAddComment()">{{ $t('提 交') }}</el-button>
  485. <el-button @click="comment.open = false">{{ $t('返 回') }}</el-button>
  486. </div>
  487. </el-dialog>
  488. <!-- 查看培训详情对话框 -->
  489. <el-dialog v-dialogDrag :title="detail.title" :visible.sync="detail.open" width="700px" append-to-body>
  490. <h3 style="margin-bottom:20px;text-align:center;">学员反馈</h3>
  491. <el-table :data="tableData" border style="width: 100%">
  492. <el-table-column prop="id" label="编号" width="50"></el-table-column>
  493. <el-table-column prop="question" label="问题" width="250"></el-table-column>
  494. <el-table-column prop="answer" label="答案"></el-table-column>
  495. </el-table>
  496. <h3 style="margin-top:50px;margin-bottom:20px;text-align:center;">导师反馈</h3>
  497. <el-form label-width="80px">
  498. <el-form-item label="科目成绩">
  499. <el-input v-model="commentParams.score" readonly/>
  500. </el-form-item>
  501. <el-form-item label="综合评价">
  502. <el-input v-model="commentParams.overallComment" type="textarea" rows="6" readonly/>
  503. </el-form-item>
  504. </el-form>
  505. <div slot="footer" class="dialog-footer">
  506. <el-button @click="detail.open = false">{{ $t('返 回') }}</el-button>
  507. </div>
  508. </el-dialog>
  509. <plan-approve v-if="planApproveVisible" ref="planApprove" @refreshDataList="getList"></plan-approve>
  510. </div>
  511. </template>
  512. <script>
  513. import { getFeedback} from "@/api/training/spec/feedback";
  514. import { getPlan,listPlanByYearId, delPlan, addPlan, updatePlan, exportPlan, importTemplate, listSuccessorsByMentorId, listPlanByMentorId } from "@/api/training/spec/plan";
  515. import { allFileList, delCommonfile } from "@/api/common/commonfile";
  516. import { treeselect } from "@/api/system/dept";
  517. import { getToken } from "@/utils/auth";
  518. import PlanApprove from "./plan-approve"
  519. import Treeselect from "@riophae/vue-treeselect";
  520. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  521. import {getYearplan} from "@/api/training/spec/yearplan";
  522. export default {
  523. name: "MyPlan",
  524. components: { Treeselect,PlanApprove },
  525. data() {
  526. return {
  527. // 评论参数
  528. commentParams: {
  529. id: 0,
  530. score: null,
  531. overallComment: "",
  532. studyState: 2
  533. },
  534. showMonths:[],
  535. minDay:"", // 最小日期
  536. maxDay:"", // 最大日期
  537. IsShowyear:true, // 显示年|月 、 月|日
  538. // 查看培训详情参数
  539. detail: {
  540. // 是否显示弹出层(报告附件)
  541. open: false,
  542. // 弹出层标题(报告附件)
  543. title: ""
  544. },
  545. // 评价参数
  546. comment: {
  547. id: 0,
  548. // 是否显示弹出层(报告附件)
  549. open: false,
  550. // 弹出层标题(报告附件)
  551. title: ""
  552. },
  553. // 遮罩层
  554. loading: true,
  555. planApproveVisible: false,
  556. // 选中数组
  557. ids: [],
  558. dataListSelections: [],
  559. // 非单个禁用
  560. single: true,
  561. // 非多个禁用
  562. multiple: true,
  563. // 显示搜索条件
  564. showSearch: false,
  565. // 总条数
  566. total: 0,
  567. // 培训计划表格数据
  568. planList: [],
  569. // 弹出层标题
  570. title: "",
  571. // 操作
  572. operation: "",
  573. // 部门树选项
  574. deptOptions: undefined,
  575. clientHeight:300,
  576. // 是否显示弹出层
  577. open: false,
  578. // 学习状态字典
  579. studyStateOptions: [],
  580. approveStatusOptions: [],
  581. // 学员列表
  582. successorOptions: [],
  583. // 包含导师ID的查询对象
  584. queryObject: {
  585. mentorStaffId: ""
  586. },
  587. // 用户导入参数
  588. upload: {
  589. // 是否显示弹出层(用户导入)
  590. open: false,
  591. // 弹出层标题(用户导入)
  592. title: "",
  593. // 是否禁用上传
  594. isUploading: false,
  595. // 是否更新已经存在的用户数据
  596. updateSupport: 0,
  597. // 设置上传的请求头部
  598. headers: { Authorization: "Bearer " + getToken() },
  599. // 上传的地址
  600. url: process.env.VUE_APP_BASE_API + "/spec/plan/importData"
  601. },
  602. // 报告附件参数
  603. doc: {
  604. file: "",
  605. // 是否显示弹出层(报告附件)
  606. open: false,
  607. // 弹出层标题(报告附件)
  608. title: "",
  609. // 是否禁用上传
  610. isUploading: false,
  611. // 是否更新已经存在的用户数据
  612. updateSupport: 0,
  613. // 报告附件上传位置编号
  614. ids: 0,
  615. // 设置上传的请求头部
  616. headers: { Authorization: "Bearer " + getToken() },
  617. // 上传的地址
  618. url: process.env.VUE_APP_BASE_API + "/common/commonfile/uploadFile",
  619. commonfileList: null,
  620. queryParams: {
  621. pId: null,
  622. pType: 'plan'
  623. },
  624. pType: 'plan',
  625. pId: null
  626. },
  627. // 查询参数
  628. queryParams: {
  629. pageNum: 1,
  630. pageSize: 20,
  631. staffId: null,
  632. plantName: null,
  633. startDate: null,
  634. endDate: null,
  635. classHour: null,
  636. studyState: null,
  637. yearPlanId: null
  638. },
  639. // 表单参数
  640. form: {},
  641. // 表单校验
  642. rules: {
  643. },
  644. // pdf文件参数
  645. pdf : {
  646. title: '',
  647. pdfUrl: '',
  648. numPages: null,
  649. open: false,
  650. pageNum: 1,
  651. pageTotalNum: 1,
  652. loadedRatio: 0,
  653. },
  654. // 表格参数
  655. tableData: [],
  656. };
  657. },
  658. watch: {
  659. // 根据名称筛选部门树
  660. deptName(val) {
  661. this.$refs.tree.filter(val);
  662. }
  663. },
  664. computed:{
  665. // 获取最小日期-月开始时间戳
  666. minDayTimeStamp() {
  667. if(this.IsShowyear){
  668. return this.showMonths[0].months[0].beginTimeStamp
  669. }else{
  670. return this.showMonths[0].days[0].daysArr[0].timestamp
  671. }
  672. },
  673. // 获取最大日期时间戳
  674. maxDayTimeStamp() {
  675. let Length = this.showMonths.length // 年有多少个
  676. // 如果显示年|月 与 月|日 是不同的
  677. if(this.IsShowyear){
  678. let monthsLength = this.showMonths[Length - 1].months.length //月有多少个
  679. return this.showMonths[Length - 1].months[monthsLength - 1].endTimeStamp
  680. }else{
  681. let daysLength = this.showMonths[Length - 1].days.length
  682. let daysarrLength = this.showMonths[Length - 1].days[daysLength - 1].daysArr.length //天有多少个
  683. return this.showMonths[Length - 1].days[daysLength - 1].daysArr[daysarrLength - 1].timestamp + 86400000 //+86400000 是因为时间戳都是0点这样+1天的时间戳,就表示最晚一天的最后的时间
  684. }
  685. },
  686. // 最大日期与最小日期的时间戳差
  687. maxAndminDayTimeStamp() {
  688. return this.maxDayTimeStamp - this.minDayTimeStamp
  689. },
  690. // 获取日期单元格宽度 => 如果返回0则只用之前第一版的计算方法
  691. getCellDateWidth() {
  692. let num = 0 // 获取日期单元格数量
  693. if(this.IsShowyear){
  694. // 月数量
  695. for(let i = 0;i < this.showMonths.length;i++){
  696. num += this.showMonths[i].months.length
  697. }
  698. }else{
  699. // 天数量
  700. for(let i = 0;i < this.showMonths.length;i++){
  701. for(let j = 0;j < this.showMonths[i].days.length;j++){
  702. num += this.showMonths[i].days[j].daysArr.length
  703. }
  704. }
  705. }
  706. // 每个单元格最小30宽度
  707. let MaxWidth = window.screen.width
  708. let CellNum = MaxWidth / 30 > 0 ? MaxWidth / 30 : 0
  709. if(num >= CellNum){ // 页面宽 / 单元格最小宽度 = 最多放下的值
  710. return num * 30
  711. }else{
  712. // 单元格的值,就不能是最小值30了
  713. // 需要获取dom的宽度,在进行计算,但是dom还未渲染完成,因此这里就不考虑了,直接只用第一版的方法动态计算
  714. // TODO
  715. return 0
  716. }
  717. }
  718. },
  719. created() {
  720. //设置表格高度对应屏幕高度
  721. this.$nextTick(() => {
  722. this.clientHeight = document.body.clientHeight -250
  723. })
  724. const yearId = this.$route.params && this.$route.params.yearId;
  725. // 设置查询参数年度计划编号
  726. this.queryParams.yearPlanId = yearId;
  727. this.getList();
  728. this.getTreeselect();
  729. this.getDicts("st_study_state").then(response => {
  730. this.studyStateOptions = response.data;
  731. });
  732. this.getDicts("spec_training_approve_status").then(response => {
  733. this.approveStatusOptions = response.data;
  734. });
  735. this.getSuccessorOptions();
  736. },
  737. methods: {
  738. /** 表格行样式 */
  739. tableRowStyle({row, rowIndex}) {
  740. let rowBackground = {};
  741. // 实际完成培训日期超出培训结束日期
  742. if (row.endDate < row.dateOfCompletion) {
  743. // 修改变行背景色
  744. rowBackground.background = "#FFEEEE";
  745. }
  746. return rowBackground;
  747. },
  748. /** 评价提交处理 */
  749. handleAddComment() {
  750. this.commentParams.id = this.comment.id;
  751. updatePlan(this.commentParams).then(response => {
  752. this.comment.open = false;
  753. this.msgSuccess("已结束学习");
  754. this.getList();
  755. });
  756. },
  757. /** 查看培训详情处理 */
  758. handleDetail(row) {
  759. this.tableData = [];
  760. this.detail.id = row.id;
  761. this.detail.title = row.plantName + this.$t('详情');
  762. this.detail.open = true;
  763. getFeedback(row.id).then(response => {
  764. let feedbackObject = response.data;
  765. let data1 = { id: 1, question: feedbackObject.question1, answer: feedbackObject.answer1};
  766. let data2 = { id: 2, question: feedbackObject.question2, answer: feedbackObject.answer2};
  767. let data3 = { id: 3, question: feedbackObject.question3, answer: feedbackObject.answer3};
  768. this.tableData.push(data1);
  769. this.tableData.push(data2);
  770. this.tableData.push(data3);
  771. });
  772. getPlan(row.id).then(response => {
  773. this.commentParams.score = response.data.score;
  774. this.commentParams.overallComment = response.data.overallComment;
  775. });
  776. },
  777. /** 评价处理 */
  778. handleComment(row) {
  779. this.tableData = [];
  780. this.comment.id = row.id;
  781. this.comment.title = row.plantName + this.$t('评价');
  782. this.comment.open = true;
  783. getFeedback(row.id).then(response => {
  784. let feedbackObject = response.data;
  785. let data1 = { id: 1, question: feedbackObject.question1, answer: feedbackObject.answer1};
  786. let data2 = { id: 2, question: feedbackObject.question2, answer: feedbackObject.answer2};
  787. let data3 = { id: 3, question: feedbackObject.question3, answer: feedbackObject.answer3};
  788. this.tableData.push(data1);
  789. this.tableData.push(data2);
  790. this.tableData.push(data3);
  791. });
  792. },
  793. /** 文件下载处理 */
  794. handleDownload(row) {
  795. var name = row.fileName;
  796. var url = row.fileUrl;
  797. var suffix = url.substring(url.lastIndexOf("."), url.length);
  798. const a = document.createElement('a')
  799. a.setAttribute('download', name)
  800. a.setAttribute('target', '_blank')
  801. a.setAttribute('href', process.env.VUE_APP_BASE_API + url)
  802. a.click()
  803. },
  804. openPdf(){
  805. window.open(this.pdf.pdfUrl);//path是文件的全路径地址
  806. },
  807. handleSee (row){
  808. this.pdf.open =true
  809. this.pdf.title = row.fileName
  810. this.pdf.pdfUrl = process.env.VUE_APP_BASE_API +'/pdf/web/viewer.html?file=' + process.env.VUE_APP_BASE_API + row.fileUrl
  811. },
  812. /** 报告附件按钮操作 */
  813. handleDoc(row) {
  814. this.doc.id = row.id;
  815. this.doc.title = row.plantName + this.$t('附件');
  816. this.doc.open = true;
  817. this.doc.queryParams.pId = row.id
  818. this.doc.pId = row.id
  819. this.getFileList()
  820. this.$nextTick(() => {
  821. this.$refs.doc.clearFiles()
  822. })
  823. },
  824. getFileList (){
  825. allFileList(this.doc.queryParams).then(response => {
  826. this.doc.commonfileList = response;
  827. });
  828. },
  829. /** 附件上传中处理 */
  830. handleFileDocProgress(event, file, fileList) {
  831. this.doc.file = file;
  832. this.doc.isUploading = true;
  833. },
  834. /** 附件上传成功处理 */
  835. handleFileDocSuccess(response, file, fileList) {
  836. this.doc.isUploading = false;
  837. this.$alert(response.msg, this.$t('导入结果'), { dangerouslyUseHTMLString: true });
  838. this.getFileList()
  839. },
  840. /** 删除按钮操作 */
  841. handleDeleteDoc(row) {
  842. const ids = row.id || this.ids;
  843. this.$confirm(this.$t('是否确认删除?'), this.$t('警告'), {
  844. confirmButtonText: this.$t('确定'),
  845. cancelButtonText: this.$t('取消'),
  846. type: "warning"
  847. }).then(function() {
  848. return delCommonfile(ids);
  849. }).then(() => {
  850. this.getFileList()
  851. this.msgSuccess(this.$t('删除成功'));
  852. })
  853. },
  854. /** 查询学员下拉列表 */
  855. getSuccessorOptions() {
  856. listSuccessorsByMentorId(this.queryObject).then(response => {
  857. let successorList = response.data;
  858. for (let i = 0; i < successorList.length; i++) {
  859. let successor = {};
  860. successor.key = successorList[i].staffId;
  861. successor.value = successorList[i].staffName;
  862. this.successorOptions.push(successor);
  863. }
  864. });
  865. },
  866. /** 查询培训计划列表 */
  867. getList() {
  868. this.loading = true;
  869. listPlanByYearId(this.queryParams).then(response => {
  870. this.planList = response.rows;
  871. this.total = response.total;
  872. this.setrowspans() // 设置合并
  873. this.getChartTitle() // 渲染的信息
  874. this.$nextTick(() => {
  875. this.$refs.planTable.doLayout(); // 解决表格错位
  876. });
  877. this.loading = false;
  878. });
  879. },
  880. /** 查询部门下拉树结构 */
  881. getTreeselect() {
  882. treeselect().then(response => {
  883. this.deptOptions = response.data;
  884. });
  885. },
  886. /** 学习状态字典翻译 */
  887. studyStateFormat(row, column) {
  888. return this.selectDictLabel(this.studyStateOptions, row.studyState);
  889. },
  890. // 申请状态字典翻译
  891. approveStatusFormat(row, column) {
  892. return this.selectDictLabel(this.approveStatusOptions, row.approveStatus);
  893. },
  894. approveStatusType (type) {
  895. if (type == 0) {
  896. return 'danger'
  897. }else if (type == 1) {
  898. return 'success'
  899. }else if (type == 2) {
  900. return 'danger'
  901. }else {
  902. return 'info'
  903. }
  904. },
  905. /** 取消按钮 */
  906. cancel() {
  907. this.open = false;
  908. this.reset();
  909. },
  910. /** 表单重置 */
  911. reset() {
  912. this.form = {
  913. id: null,
  914. staffId: null,
  915. plantName: null,
  916. delFlag: null,
  917. createrCode: null,
  918. createdate: null,
  919. updaterCode: null,
  920. updatedate: null,
  921. deptId: null,
  922. startDate: null,
  923. endDate: null,
  924. classHour: null,
  925. classContent: null,
  926. studyState: null
  927. };
  928. this.resetForm("form");
  929. },
  930. /** 搜索按钮操作 */
  931. handleQuery() {
  932. this.queryParams.pageNum = 1;
  933. this.getList();
  934. },
  935. /** 重置按钮操作 */
  936. resetQuery() {
  937. this.resetForm("queryForm");
  938. this.handleQuery();
  939. },
  940. /** 多选框选中数据 */
  941. handleSelectionChange(selection) {
  942. this.ids = selection.map(item => item.id)
  943. this.single = selection.length!==1
  944. this.multiple = !selection.length
  945. this.dataListSelections = selection
  946. },
  947. /** 新增按钮操作 */
  948. handleAdd() {
  949. this.reset();
  950. getYearplan(this.queryParams.yearPlanId).then(response => {
  951. this.form.staffId = response.data.staffId.toString();
  952. this.open = true;
  953. this.title = "修改专项培训年度计划";
  954. this.operation = "modify";
  955. });
  956. this.open = true;
  957. this.title = "添加培训计划";
  958. this.operation = "add";
  959. },
  960. /** 修改按钮操作 */
  961. handleUpdate(row) {
  962. this.reset();
  963. const id = row.id || this.ids
  964. getPlan(id).then(response => {
  965. this.form = response.data;
  966. this.open = true;
  967. this.title = "修改培训计划";
  968. this.operation = "modify";
  969. });
  970. },
  971. /** 提交按钮 */
  972. submitForm() {
  973. this.$refs["form"].validate(valid => {
  974. if (valid) {
  975. if (this.form.id != null) {
  976. updatePlan(this.form).then(response => {
  977. this.msgSuccess("修改成功");
  978. this.open = false;
  979. this.getList();
  980. });
  981. } else {
  982. // 设置表单参数年度计划编号
  983. this.form.yearPlanId = this.queryParams.yearPlanId;
  984. addPlan(this.form).then(response => {
  985. this.msgSuccess("新增成功");
  986. this.open = false;
  987. this.getList();
  988. });
  989. }
  990. }
  991. });
  992. },
  993. /** 删除按钮操作 */
  994. handleDelete(row) {
  995. const ids = row.id || this.ids;
  996. this.$confirm('是否确认删除?', "警告", {
  997. confirmButtonText: "确定",
  998. cancelButtonText: "取消",
  999. type: "warning"
  1000. }).then(function() {
  1001. return delPlan(ids);
  1002. }).then(() => {
  1003. this.getList();
  1004. this.msgSuccess("删除成功");
  1005. })
  1006. },
  1007. /** 导出按钮操作 */
  1008. handleExport() {
  1009. const queryParams = this.queryParams;
  1010. this.$confirm('是否确认导出所有培训计划数据项?', "警告", {
  1011. confirmButtonText: "确定",
  1012. cancelButtonText: "取消",
  1013. type: "warning"
  1014. }).then(function() {
  1015. return exportPlan(queryParams);
  1016. }).then(response => {
  1017. this.download(response.msg);
  1018. })
  1019. },
  1020. /** 导入按钮操作 */
  1021. handleImport() {
  1022. this.upload.title = "用户导入";
  1023. this.upload.open = true;
  1024. },
  1025. /** 下载模板操作 */
  1026. importTemplate() {
  1027. importTemplate().then(response => {
  1028. this.download(response.msg);
  1029. });
  1030. },
  1031. /** 文件上传中处理 */
  1032. handleFileUploadProgress(event, file, fileList) {
  1033. this.upload.isUploading = true;
  1034. },
  1035. /** 文件上传成功处理 */
  1036. handleFileSuccess(response, file, fileList) {
  1037. this.upload.open = false;
  1038. this.upload.isUploading = false;
  1039. this.$refs.upload.clearFiles();
  1040. this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
  1041. this.getList();
  1042. },
  1043. /** 提交上传文件 */
  1044. submitFileForm() {
  1045. this.$refs.upload.submit();
  1046. },
  1047. addAprrove (row, type) {
  1048. var rows = row ? [row] : this.dataListSelections.map(item => {
  1049. return item
  1050. })
  1051. this.planApproveVisible = true
  1052. console.log(rows)
  1053. console.log(type)
  1054. this.$nextTick(() => {
  1055. this.$refs.planApprove.init(rows, type)
  1056. })
  1057. },
  1058. optimizeCalcWidth(val) {
  1059. let SumNum = this.maxAndminDayTimeStamp
  1060. let diffNum = this.IsShowyear ? val.endTimeStamp - val.beginTimeStamp : val.endTimeStamp + 84600000 - val.beginTimeStamp // 结束日期 - 开始日期
  1061. let rate = Math.round(diffNum / SumNum * 10000) / 100.00 //得到百分比
  1062. let widthPx = this.getCellDateWidth * rate / 100.00
  1063. return widthPx + 'px'
  1064. },
  1065. optimizeCalcLeft(val) {
  1066. let SumNum = this.maxAndminDayTimeStamp
  1067. let diffNum = val.beginTimeStamp - this.minDayTimeStamp // 开始日期 - 整个月开始日期
  1068. let rate = Math.round(diffNum / SumNum * 10000) / 100.00 //得到百分比
  1069. let leftPx = this.getCellDateWidth * rate / 100.00
  1070. return leftPx + 'px'
  1071. },
  1072. // 计算图表的Left
  1073. calcLeft(val,date) {
  1074. // 如果本月的最大时间戳都没有结束时间长,那么就可判断
  1075. if(val.endTimeStamp > date.endTimeStamp){
  1076. // 如果本月开始日期小于开始日期
  1077. if(val.beginTimeStamp > date.beginTimeStamp){
  1078. // 进行判断
  1079. let diffNum = val.beginTimeStamp - date.beginTimeStamp
  1080. let SumNum = date.endTimeStamp - date.beginTimeStamp
  1081. let rate = 100 - Math.round(diffNum / SumNum * 10000) / 100.00
  1082. // 位置判断
  1083. return rate+"%"
  1084. }
  1085. }else if(val.endTimeStamp > date.beginTimeStamp && val.endTimeStamp < date.endTimeStamp){
  1086. // 进行判断
  1087. if(val.beginTimeStamp > date.beginTimeStamp){
  1088. // 都在一个单元格中
  1089. let diffNum = val.beginTimeStamp - date.beginTimeStamp
  1090. let SumNum = date.endTimeStamp - date.beginTimeStamp
  1091. let rate = Math.round(diffNum / SumNum * 10000) / 100.00
  1092. // 位置判断
  1093. return rate+"%"
  1094. }
  1095. }
  1096. return '0px'
  1097. },
  1098. // 计算图表的Width
  1099. calcwidth(val,date) {
  1100. if(this.IsShowyear){
  1101. // 如果是 年|月
  1102. // 1. 如果本月的最大时间戳都没有结束时间长,那么就可设置100%
  1103. // 2. 如果本月的最小时间戳比结束时间长,那么设置0
  1104. // 3. 如果本月的最小时间戳没有结束时间长,那么进行判断
  1105. if(val.endTimeStamp > date.endTimeStamp){
  1106. // 如果本月开始日期小于开始日期
  1107. if(val.beginTimeStamp > date.beginTimeStamp){
  1108. // 进行判断
  1109. let diffNum = val.beginTimeStamp - date.beginTimeStamp
  1110. let SumNum = date.endTimeStamp - date.beginTimeStamp
  1111. let rate = Math.round(diffNum / SumNum * 10000) / 100.00
  1112. return 'calc('+ rate +'% + 2px)' // 结束时间超过本月最大时间戳直接+2px
  1113. }
  1114. return 'calc(100% + 2px)'
  1115. }else if(val.endTimeStamp < date.beginTimeStamp){
  1116. return '0px'
  1117. }else if(val.endTimeStamp > date.beginTimeStamp && val.endTimeStamp < date.endTimeStamp){
  1118. // 进行判断
  1119. if(val.beginTimeStamp > date.beginTimeStamp){
  1120. // 都在一个单元格中
  1121. let SumNum = date.endTimeStamp - date.beginTimeStamp // 全月多少数
  1122. let diffNum = val.endTimeStamp - val.beginTimeStamp // 过程多少数
  1123. diffNum = diffNum === 0 ? 86400000 : diffNum
  1124. let rate = Math.round(diffNum / SumNum * 10000) / 100.00
  1125. return 'calc('+ rate +'%)'
  1126. }else{
  1127. // 不在一个单元格中,则不需要判断任何信息
  1128. let diffNum = val.endTimeStamp - date.beginTimeStamp
  1129. let SumNum = date.endTimeStamp - date.beginTimeStamp
  1130. let rate = Math.round(diffNum / SumNum * 10000) / 100.00
  1131. return 'calc('+ rate +'%)'
  1132. }
  1133. }
  1134. }else{
  1135. // 如果是 月|日
  1136. // 1.如果开始时间大于 天的时间戳,则为0
  1137. // 2.如果结束时间小于 天的时间戳,则为100%
  1138. if(val.beginTimeStamp > date.timestamp){
  1139. return '0px'
  1140. }else if(val.beginTimeStamp < date.timestamp && val.endTimeStamp > date.timestamp){
  1141. return 'calc(100% + 2px)'
  1142. }
  1143. }
  1144. return '0px'
  1145. },
  1146. //获得数据相同的行数(网络复制)
  1147. objectSpanMethod({ row, column, rowIndex, columnIndex }) {
  1148. if (columnIndex === 0) {
  1149. return {
  1150. rowspan: row.rowspan,
  1151. colspan: 1
  1152. };
  1153. }
  1154. },
  1155. // 根据id去分组(网络复制)
  1156. setrowspans() {
  1157. // 先给所有的数据都加一个v.rowspan = 1
  1158. this.planList.forEach(v => {
  1159. v.rowspan = 1;
  1160. });
  1161. // 双层循环
  1162. for (let i = 0; i < this.planList.length; i++) {
  1163. // 内层循环,上面已经给所有的行都加了v.rowspan = 1
  1164. // 这里进行判断
  1165. // 如果当前行的id和下一行的id相等
  1166. // 就把当前v.rowspan + 1
  1167. // 下一行的v.rowspan - 1
  1168. for (let j = i + 1; j < this.planList.length; j++) {
  1169. //此处可根据相同字段进行合并,此处是根据的id
  1170. if (this.planList[i].id === this.planList[j].id) {
  1171. this.planList[i].rowspan++;
  1172. this.planList[j].rowspan--;
  1173. }
  1174. }
  1175. // 这里跳过已经重复的数据
  1176. i = i + this.planList[i].rowspan - 1;
  1177. }
  1178. },
  1179. // 渲染表格头,首先是年月,如2018年11月
  1180. getChartTitle(startDate, endDate){
  1181. var chartTable = this.planList;
  1182. // 准备日期,为了防止多次赋值属性,导致计算属性重新计算,决定只赋值一次
  1183. let maxDayParams = "",minDayParams = ""
  1184. for(var i = 0; i < chartTable.length; i++){
  1185. var StartDate = chartTable[i].startDate;
  1186. var EndDate = chartTable[i].endDate;
  1187. var mainObj = chartTable[i];
  1188. // 计划日期
  1189. maxDayParams === "" ? maxDayParams = EndDate : ''
  1190. minDayParams === "" ? minDayParams = StartDate : ""
  1191. maxDayParams = this.compareDate(EndDate,maxDayParams,true)
  1192. minDayParams = this.compareDate(StartDate,minDayParams,false)
  1193. var days = this.GetNumberOfDays(StartDate,EndDate) // 计算工期
  1194. this.planList[i].days = days + 1 // 加一是因为没算结算当天
  1195. // 记录时间戳
  1196. this.planList[i].beginTimeStamp = this.getTimeStamp(StartDate)
  1197. this.planList[i].endTimeStamp = this.getTimeStamp(EndDate)
  1198. }
  1199. this.minDay = minDayParams
  1200. this.maxDay = maxDayParams
  1201. console.log("小:"+this.minDay)
  1202. console.log("大:"+this.maxDay)
  1203. this.getYearArr()
  1204. console.log(this.showMonths)
  1205. },
  1206. // 获取需要的格式的年月日信息
  1207. getYearArr(){
  1208. // 如果有一个日期相差超过366天,按照年,不超过则按天
  1209. var days = this.GetNumberOfDays(this.minDay,this.maxDay)
  1210. if(days >= 365){
  1211. //需要记录月份
  1212. this.IsShowyear = true
  1213. }else{
  1214. //需要记录天数
  1215. this.IsShowyear = false
  1216. }
  1217. // 获取需要格式的年月信息
  1218. let yearArr = []; // 存年
  1219. let minYear = new Date(this.minDay).getFullYear();
  1220. let maxYear = new Date(this.maxDay).getFullYear();
  1221. // 保存年
  1222. if(minYear === maxYear){
  1223. yearArr.push({
  1224. year: minYear+"",
  1225. months: [], // 放月的数组
  1226. days: []
  1227. })
  1228. }else{
  1229. for(let i = minYear;i <= maxYear;i++){
  1230. yearArr.push({
  1231. year: i+"",
  1232. months: [], // 放月的数组
  1233. days: []
  1234. })
  1235. }
  1236. }
  1237. // 保存月
  1238. for(let i = 0;i < yearArr.length;i++){
  1239. let minMonth = 1
  1240. if(i === 0){
  1241. minMonth = new Date(this.minDay).getMonth() + 1;
  1242. }
  1243. // 1.eg:2016年9月 至 2019年01月
  1244. // 2.如果只有一个年,那么月份就是最小月到最大月
  1245. if(yearArr.length > 1){
  1246. // 如果是最后一年,那么就要判断是否到最后一个月
  1247. if(yearArr.length - 1 === i){
  1248. let maxMonth = new Date(this.maxDay).getMonth() + 1;
  1249. let _maxDay = new Date(this.maxDay).getDate() // 最大日期 天数
  1250. let j = 1
  1251. while(j <= maxMonth){
  1252. let monthsDays = this.getLastDay(yearArr[i].year, j); // 获取月份一共有多少天
  1253. let timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-" + monthsDays)
  1254. let timestampstr1 = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-01")
  1255. yearArr[i].months.push({str:j+"月",num:j,endTimeStamp:timestampstr,beginTimeStamp:timestampstr1});// 记录最大最小的时间戳
  1256. // 如果显示月|天 才用记录天
  1257. if(!this.IsShowyear){
  1258. yearArr[i].days[yearArr[i].days.length] = {year: "",month: "",daysArr: {}}
  1259. yearArr[i].days[yearArr[i].days.length - 1].year = yearArr[i].year // 赋值年
  1260. yearArr[i].days[yearArr[i].days.length - 1].month = j; // 赋值月
  1261. yearArr[i].days[yearArr[i].days.length - 1].daysArr = []
  1262. // 如果结束月,不是到本月最后一天,而是截止到最
  1263. if(j === maxMonth){
  1264. monthsDays = _maxDay
  1265. }
  1266. for(let k = 1; k <= monthsDays; k++){
  1267. timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-" + (k > 9 ? k : '0'+k))
  1268. yearArr[i].days[yearArr[i].days.length - 1].daysArr.push({day: k+"",timestamp:timestampstr});// 赋值天
  1269. }
  1270. }
  1271. j++
  1272. }
  1273. }else{
  1274. if(i === 0){
  1275. let j = minMonth
  1276. let _minDay = new Date(this.minDay).getDate() // 最小日期 天数
  1277. while(j <= 12){
  1278. let monthsDays = this.getLastDay(yearArr[i].year, j); // 获取月份一共有多少天
  1279. let timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-" + monthsDays)
  1280. let timestampstr1 = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-01")
  1281. yearArr[i].months.push({str:j+"月",num:j,endTimeStamp:timestampstr,beginTimeStamp:timestampstr1});// 记录最大最小的时间戳
  1282. // 如果显示月|天 才用记录天
  1283. if(!this.IsShowyear){
  1284. yearArr[i].days[yearArr[i].days.length] = {year: "",month: "",daysArr: {}}
  1285. yearArr[i].days[yearArr[i].days.length - 1].year = yearArr[i].year // 赋值月
  1286. yearArr[i].days[yearArr[i].days.length - 1].month = j // 赋值月
  1287. yearArr[i].days[yearArr[i].days.length - 1].daysArr = []
  1288. for(let k = 1; k <= monthsDays; k++){
  1289. // 如果开始月,就不从1日开始,而是从最开始的日期那天开始算起
  1290. if(k === 1 && j === minMonth){
  1291. k = _minDay
  1292. }
  1293. timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-" + (k > 9 ? k : '0'+k))
  1294. yearArr[i].days[yearArr[i].days.length - 1].daysArr.push({day: k+"",timestamp:timestampstr});// 赋值天
  1295. }
  1296. }
  1297. j++
  1298. }
  1299. }else{
  1300. let j = 1
  1301. while(j <= 12){
  1302. let monthsDays = this.getLastDay(yearArr[i].year, j); // 获取月份一共有多少天
  1303. let timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-" + monthsDays)
  1304. let timestampstr1 = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-01")
  1305. yearArr[i].months.push({str:j+"月",num:j,endTimeStamp:timestampstr,beginTimeStamp:timestampstr1});// 记录最大最小的时间戳
  1306. // 如果显示月|天 才用记录天
  1307. if(!this.IsShowyear){
  1308. yearArr[i].days[yearArr[i].days.length] = {year: "",month: "",daysArr: {}}
  1309. yearArr[i].days[yearArr[i].days.length - 1].year = yearArr[i].year // 赋值年
  1310. yearArr[i].days[yearArr[i].days.length - 1].month = j // 赋值月
  1311. yearArr[i].days[yearArr[i].days.length - 1].daysArr = []
  1312. for(let k = 1; k <= monthsDays; k++){
  1313. timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (j > 9 ? j : '0'+j) + "-" + (k > 9 ? k : '0'+k))
  1314. yearArr[i].days[yearArr[i].days.length - 1].daysArr.push({day: k+"",timestamp:timestampstr});// 赋值天
  1315. }
  1316. }
  1317. j++
  1318. }
  1319. }
  1320. }
  1321. }else{
  1322. // 如果只有一年那么没有必要判断是否记录天
  1323. let maxMonth = new Date(this.maxDay).getMonth() + 1;
  1324. let _minMonth = minMonth
  1325. let _minDay = new Date(this.minDay).getDate() // 最小日期 天数
  1326. let _maxDay = new Date(this.maxDay).getDate() // 最大日期 天数
  1327. while(_minMonth <= maxMonth){
  1328. let monthsDays = this.getLastDay(yearArr[i].year, _minMonth); // 获取月份一共有多少天
  1329. let timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (_minMonth > 9 ? _minMonth : '0'+_minMonth) + "-" + monthsDays)
  1330. let timestampstr1 = this.getTimeStamp(yearArr[i].year +"-"+ (_minMonth > 9 ? _minMonth : '0'+_minMonth) + "-01")
  1331. yearArr[i].months.push({str:_minMonth+"月",num:_minMonth,endTimeStamp:timestampstr,beginTimeStamp:timestampstr1});// 记录最大最小的时间戳
  1332. yearArr[i].days[yearArr[i].days.length] = {year: "",month: "",daysArr: {}}
  1333. yearArr[i].days[yearArr[i].days.length - 1].year = yearArr[i].year // 赋值月
  1334. yearArr[i].days[yearArr[i].days.length - 1].month = _minMonth // 赋值月
  1335. yearArr[i].days[yearArr[i].days.length - 1].daysArr = []
  1336. // 如果结束月,不是到本月最后一天,而是截止到最
  1337. if(_minMonth === maxMonth){
  1338. monthsDays = _maxDay
  1339. }
  1340. for(let k = 1; k <= monthsDays; k++){
  1341. // 如果开始月,就不从1日开始,而是从最开始的日期那天开始算起
  1342. if(k === 1 && _minMonth === minMonth){
  1343. k = _minDay
  1344. }
  1345. timestampstr = this.getTimeStamp(yearArr[i].year +"-"+ (_minMonth > 9 ? _minMonth : '0'+_minMonth) + "-" + (k > 9 ? k : '0'+k))
  1346. yearArr[i].days[yearArr[i].days.length - 1].daysArr.push({day: k+"",timestamp:timestampstr});// 赋值天
  1347. }
  1348. _minMonth++
  1349. }
  1350. }
  1351. }
  1352. this.showMonths = yearArr
  1353. },
  1354. // 获取月的最后一天
  1355. getLastDay(myyear, mymonth){
  1356. var new_date = new Date(myyear, mymonth, 0);
  1357. return new_date.getDate();
  1358. },
  1359. // 获得天数
  1360. GetNumberOfDays(date1,date2){
  1361. //date1:开始日期,date2结束日期
  1362. var a1 = Date.parse(new Date(date1));
  1363. var a2 = Date.parse(new Date(date2));
  1364. var day = parseInt((a2-a1)/ 86400000);//核心:时间戳相减,然后除以天数
  1365. return day
  1366. },
  1367. // 时间比较 true 比大
  1368. compareDate(dateTime1,dateTime2,condition = true)
  1369. {
  1370. var formatDate1 = new Date(dateTime1)
  1371. var formatDate2 = new Date(dateTime2)
  1372. if(formatDate1 >= formatDate2)
  1373. {
  1374. return condition ? dateTime1 : dateTime2
  1375. }
  1376. else
  1377. {
  1378. return condition ? dateTime2 : dateTime1
  1379. }
  1380. },
  1381. // 获取时间戳
  1382. getTimeStamp(val){
  1383. return new Date(val).getTime()
  1384. },
  1385. }
  1386. };
  1387. </script>
  1388. <style>
  1389. /* 单元格padding */
  1390. .barchart .el-table--border th:first-child .cell, .el-table--border td:first-child .cell{
  1391. padding: 0px;
  1392. }
  1393. .barchart .el-table th > .cell{
  1394. padding: 0px;
  1395. }
  1396. /* 单元格高度 */
  1397. .barchart .el-table--medium th,.barchart .el-table--medium td {
  1398. padding: 0px 0px !important;
  1399. height: 25px !important;
  1400. }
  1401. .barchart .progressCon{
  1402. padding: 0;
  1403. margin: 0;
  1404. position: relative;
  1405. }
  1406. .barchart .progressUpon{
  1407. background: rgb(38, 84, 124);
  1408. height: 1em;
  1409. /* width: calc(100% + 2px); // +2是因为边框线为2px*/
  1410. z-index: 2;
  1411. position: absolute;
  1412. top: 25%;
  1413. left:0px
  1414. }
  1415. .barchart .progressDownon{
  1416. background: rgb(255,209,102);
  1417. height: 1em;
  1418. /* width: calc(100% + 2px); // +2是因为边框线为2px*/
  1419. z-index: 2;
  1420. position: absolute;
  1421. top: 25%;
  1422. left:0px
  1423. }
  1424. </style>