华西海圻ELN前端工程
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

555 lines
17 KiB

  1. <template>
  2. <div>
  3. <div class="edit-container" v-if="open">
  4. <div class="edit-top">
  5. <div class="left-top">
  6. <img src="@/assets/images/back.png" @click="cancel()" />
  7. <div class="left-title"></div>
  8. </div>
  9. <div class="center-top">
  10. </div>
  11. <div class="right-top">
  12. <el-button @click="cancel()">{{ $t('form.close') }}</el-button>
  13. <el-button type="primary" v-if="form.bdzt === 5 && form.tbzt === 1" @click="openApprove = true">{{
  14. $t('page.business.study.studyFormFill.tb') }}</el-button>
  15. </div>
  16. </div>
  17. <!-- 1:流程3编辑5人员7修改9补充说明 -->
  18. <el-button type="primary" @click="exportExcel(-1)">{{ $t('page.business.study.studyFormFill.dcqbjcgj') }}
  19. </el-button>
  20. <el-button type="primary" @click="exportExcel(1)">{{ $t('page.business.study.studyFormFill.dclcjcgj') }}
  21. </el-button>
  22. <el-button type="primary" @click="exportExcel(3)">{{ $t('page.business.study.studyFormFill.dcbjjcgj') }}
  23. </el-button>
  24. <el-button type="primary" @click="exportExcel(7)">{{ $t('page.business.study.studyFormFill.dcxgjcgj') }}
  25. </el-button>
  26. <el-button type="primary" @click="exportExcel(999)">{{ $t('page.business.study.studyFormFill.dcbhsjgj') }}
  27. </el-button>
  28. <div class="edit-content">
  29. <div class="detail-content" style="width: 100%; height: 100%;">
  30. <vue-html2pdf :show-layout="true" pdf-content-width="100%" :pdf-format="form.templatePdfSize" :pdf-quality="2"
  31. :float-layout="false" pdf-orientation="landscape" :paginate-elements-by-height="0" :enable-download="true"
  32. :preview-modal="false" :filename="form.bdmc" @beforeDownload="addDynamicWatermark" ref="html2Pdf"
  33. @progress="onProgress">
  34. <section slot="pdf-content">
  35. <div class="pdf-content">
  36. <TemplateTable ref="templateTable" :sn="form.templateSn" :templateData="form" fillType="detail" />
  37. <div v-if="showExport" style="width: 100%; padding: 0px 30px ;">
  38. <div class="content-title" style="margin-bottom: 10px;">
  39. <div class="line"></div>
  40. <div class="subtitle"> {{ $t('page.business.study.studyFormFill.qmxx') }}</div>
  41. </div>
  42. <table class="datatable">
  43. <thead>
  44. <tr>
  45. <th style="width: 20%;">{{ $t('page.business.study.studyFormFill.qmr') }}</th>
  46. <th style="width: 20%;">{{ $t('page.business.study.studyFormFill.qmyy') }}</th>
  47. <th style="width: 20%;">{{ $t('page.business.study.studyFormFill.qmsj') }}</th>
  48. <th style="width: 40%;">{{ $t('page.business.study.studyFormFill.bzyy') }}</th>
  49. </tr>
  50. </thead>
  51. <tbody>
  52. <tr v-for="(item, index) in qmxxExportList" :key="index">
  53. <td>{{ item.qmrMc }}</td>
  54. <td>{{ $i18n.locale === 'zh_CN' ? item.qmyy : item.qmyyEn }}</td>
  55. <td>{{ item.createTime }}</td>
  56. <td>{{ item.remark }}</td>
  57. </tr>
  58. </tbody>
  59. </table>
  60. <div class="content-title" style="margin-top: 10px;" v-show="jcgjlxExport != 999">
  61. <div class="line"></div>
  62. <div class="subtitle"> {{ $t('page.business.study.studyFormFill.jcgj') }}</div>
  63. </div>
  64. <JcgjList ref="jcgjExportList" :readonly="true" v-show="jcgjlxExport != 999" />
  65. </div>
  66. </div>
  67. <div v-if="showExport" id="watermark-overlay" ref="watermarkContainer" :style="{
  68. '--watermark-text': `'${watermarkText}'`,
  69. '--watermark-opacity': opacity,
  70. '--watermark-size': '14px',
  71. '--watermark-color': 'red'
  72. }"></div>
  73. </section>
  74. </vue-html2pdf>
  75. <div style="padding: 0px 20px 0px 30px;margin-left: 10px;">
  76. <div class="content-title">
  77. <div class="line"></div>
  78. <div class="subtitle"> {{ $t('page.business.study.studyFormFill.qmxx') }}</div>
  79. </div>
  80. <div class="pal">
  81. <el-table :data="qmxxList" v-loading="loadingQmxx" style="width: 100%;">
  82. <el-table-column :label="$t('page.business.study.studyFormFill.qmr')" align="center" prop="qmrMc" />
  83. <el-table-column :label="$t('page.business.study.studyFormFill.qmyy')" align="center"
  84. :prop="$i18n.locale === 'zh_CN' ? 'qmyy' : 'qmyyEn'" />
  85. <el-table-column :label="$t('page.business.study.studyFormFill.qmsj')" align="center"
  86. prop="createTime" />
  87. <el-table-column :label="$t('page.business.study.studyFormFill.bzyy')" align="center" prop="remark" />
  88. </el-table>
  89. </div>
  90. <pagination v small layout="prev, pager, next" :total="totalQmxx" :page.sync="queryParamsQmxx.pageNum"
  91. :limit.sync="queryParamsQmxx.pageSize" @pagination="getQmxxList" />
  92. <div class="content-title" style="margin-top: 10px;">
  93. <div class="line"></div>
  94. <div class="subtitle"> {{ $t('page.business.study.studyFormFill.jcgj') }}</div>
  95. </div>
  96. <JcgjList ref="jcgjList" @handleQuery="getJjcgjList" :showXg="true" />
  97. <pagination v small layout="prev, pager, next" :total="jcgjTotal" @pagination="getJjcgjList" />
  98. </div>
  99. </div>
  100. </div>
  101. </div>
  102. <!-- 填报 -->
  103. <el-dialog :title="$t('page.business.study.studyFormFill.cjjl')" :visible.sync="openApprove" width="500px"
  104. append-to-body :close-on-click-modal="false">
  105. <el-form ref="formApprove" :model="formApprove" :rules="rulesApprove" label-width="120px" v-if="openApprove">
  106. <el-alert :title="$t('page.business.study.studyFormFill.ts')" :closable="false" type="success">
  107. </el-alert>
  108. <el-row>
  109. <el-col :span="24">
  110. <el-form-item :label="$t('form.qmyy')" prop="qmyy">
  111. <el-input type="text" :value="formApprove.qmyy" maxlength="50" disabled
  112. :placeholder="$t('form.placeholderInput')" />
  113. </el-form-item>
  114. </el-col>
  115. </el-row>
  116. <el-row>
  117. <el-col :span="24">
  118. <el-form-item :label="$t('form.remark')" prop="remark">
  119. <el-input type="textarea" v-model="formApprove.remark" :rows="5" maxlength="500"
  120. :placeholder="$t('form.placeholderInput')">
  121. </el-input>
  122. </el-form-item>
  123. </el-col>
  124. </el-row>
  125. <el-row>
  126. <el-col :span="24">
  127. <el-form-item :label="$t('form.signer')">
  128. <el-input type="text" v-model="nickName" maxlength="50" disabled
  129. :placeholder="$t('form.placeholderInput')" />
  130. </el-form-item>
  131. </el-col>
  132. </el-row>
  133. <el-row>
  134. <el-col :span="24">
  135. <el-form-item :label="$t('form.password')" prop="qmrmm">
  136. <el-input type="password" show-password v-model="formApprove.qmrmm" maxlength="20"
  137. :placeholder="$t('form.placeholderInput')" />
  138. </el-form-item>
  139. </el-col>
  140. </el-row>
  141. </el-form>
  142. <div slot="footer" class="dialog-footer">
  143. <el-button type="primary" @click="approve">{{ $t('form.confirm') }}</el-button>
  144. <el-button @click="openApprove = false">{{ $t('form.cancel') }}</el-button>
  145. </div>
  146. </el-dialog>
  147. </div>
  148. </template>
  149. <script>
  150. import { studyFormFill_jcgjqmxxList, studyFormFill_tb, studyFormFill_info, studyFormFill_jcgj, studyFormFill_qmxx, studyFormFill_exportDetail } from "@/api/business/study/studyFormFill"
  151. import { mapGetters } from 'vuex'
  152. import JcgjList from "@/views/business/comps/common/JcgjList";
  153. import TemplateTable from '@/views/business/comps/template/TemplateTable';
  154. import moment from "moment";
  155. import VueHtml2pdf from 'vue-html2pdf'
  156. export default {
  157. name: "Xq",
  158. components: { JcgjList, TemplateTable, VueHtml2pdf },
  159. data() {
  160. return {
  161. watermarkText:'',
  162. opacity: 0.8,
  163. watermarkOpacity: 0.8,
  164. watermarkSize: 40,
  165. openApprove: false,
  166. formApprove: {
  167. id: null,
  168. qmyy: this.$t('page.business.study.studyFormFill.cjjl'),
  169. remark: '',
  170. qmrmm: '',
  171. },
  172. rulesApprove: {
  173. qmrmm: [{
  174. required: true,
  175. message: ' ',
  176. trigger: 'blur'
  177. }]
  178. },
  179. qmxxList: [],
  180. totalQmxx: 0,
  181. loadingQmxx: true,
  182. queryParamsQmxx: {
  183. formId: null,
  184. pageNum: 1,
  185. pageSize: 5
  186. },
  187. open: false,
  188. showIndex: 1,
  189. form: {},
  190. rules: {
  191. bdmc: [{
  192. required: true,
  193. message: ' ',
  194. trigger: 'blur'
  195. }],
  196. templateId: [{
  197. required: true,
  198. message: ' ',
  199. trigger: 'blur'
  200. }]
  201. },
  202. jcgjTotal: 0,
  203. jcgjList: [],
  204. queryParamsJcgj: {
  205. pageNum: 1,
  206. formId: null,
  207. pageSize: 5,
  208. },
  209. showExport: false,
  210. qmxxExportList: [],
  211. jcgjExportList: [],
  212. jcgjlxExport: 0
  213. }
  214. },
  215. computed: {
  216. ...mapGetters([
  217. 'nickName', 'name'
  218. ]),
  219. },
  220. created() {
  221. },
  222. methods: {
  223. updateWatermark() {
  224. // 创建水印背景
  225. const text = this.nickName +' '+ moment().format("YYYY-MM-DD HH:mm:ss");
  226. const canvas = document.createElement('canvas');
  227. const ctx = canvas.getContext('2d');
  228. // 设置canvas尺寸
  229. canvas.width = 300;
  230. canvas.height = 300;
  231. // 绘制水印
  232. ctx.fillStyle = `rgba(255, 0, 0, ${this.opacity})`;
  233. ctx.font = '12px Arial';
  234. ctx.textAlign = 'center';
  235. ctx.textBaseline = 'middle';
  236. // 旋转45度
  237. ctx.translate(canvas.width / 2, canvas.height / 2);
  238. ctx.rotate(-45 * Math.PI / 180);
  239. ctx.fillText(text, 0, 0);
  240. // 设置为背景
  241. const container = this.$refs.watermarkContainer;
  242. container.style.backgroundImage = `url(${canvas.toDataURL()})`;
  243. container.style.backgroundRepeat = 'repeat';
  244. },
  245. onProgress(progress) {
  246. console.log(`生成进度: ${progress}%`)
  247. this.removePageBreak()
  248. if (progress == 100) {
  249. this.showExport = false
  250. this.$modal.closeLoading()
  251. }
  252. },
  253. exportExcel(jcgjlx) {
  254. this.$modal.loading()
  255. this.jcgjlxExport = jcgjlx
  256. this.showExport = true
  257. studyFormFill_jcgjqmxxList({ jcgjlx: jcgjlx, id: this.form.id }).then(response => {
  258. this.jcgjExportList = response.data.jcgj
  259. this.qmxxExportList = response.data.qmxx
  260. this.updateWatermark()
  261. this.$refs.jcgjExportList.init(this.jcgjExportList)
  262. setTimeout(() => {
  263. this.$refs.html2Pdf.generatePdf()
  264. }, 200);
  265. })
  266. },
  267. //添加水印
  268. async addDynamicWatermark({ pdfContent }) {
  269. return new Promise((resolve) => {
  270. const canvas = document.createElement('canvas')
  271. const ctx = canvas.getContext('2d')
  272. // 获取内容区域尺寸
  273. const rect = this.$refs.contentArea.getBoundingClientRect()
  274. canvas.width = rect.width
  275. canvas.height = rect.height
  276. // 绘制动态水印
  277. ctx.fillStyle = `rgba(100, 100, 100, ${this.watermarkOpacity})`
  278. ctx.font = `${this.watermarkSize}px Arial`
  279. ctx.textAlign = 'center'
  280. ctx.textBaseline = 'middle'
  281. // 计算水印密度
  282. const stepX = 300
  283. const stepY = 200
  284. // 绘制倾斜水印
  285. ctx.save()
  286. ctx.translate(canvas.width / 2, canvas.height / 2)
  287. ctx.rotate(-Math.PI / 4) // 45度倾斜
  288. for (let x = -canvas.width; x < canvas.width * 2; x += stepX) {
  289. for (let y = -canvas.height; y < canvas.height * 2; y += stepY) {
  290. // 动态水印内容
  291. const dynamicText = `${this.watermarkText} - ${this.getCurrentTime()}`
  292. ctx.fillText(dynamicText, x, y)
  293. }
  294. }
  295. ctx.restore()
  296. // 创建水印层
  297. const watermarkLayer = document.createElement('div')
  298. watermarkLayer.className = 'watermark-layer'
  299. watermarkLayer.style.cssText = `
  300. position: absolute;
  301. top: 0;
  302. left: 0;
  303. width: 100%;
  304. height: 100%;
  305. pointer-events: none;
  306. background-image: url(${canvas.toDataURL('image/png')});
  307. background-repeat: repeat;
  308. z-index: 9999;
  309. `
  310. // 插入水印
  311. this.$refs.contentArea.style.position = 'relative'
  312. this.$refs.contentArea.appendChild(watermarkLayer)
  313. // 等待渲染
  314. setTimeout(() => resolve(), 100)
  315. })
  316. },
  317. //移除分页空白
  318. removePageBreak() {
  319. document.querySelectorAll('.html2pdf__page-break').forEach(el => {
  320. el.remove()
  321. })
  322. },
  323. exportExcel_bak(jcgjlx) {
  324. this.$modal.loading()
  325. studyFormFill_exportDetail(_.merge({}, this.queryParamsJcgj, { jcgjlx: jcgjlx, lang: this.$store.getters.language.split("_")[0] })).then(response => {
  326. window.open(process.env.VUE_APP_FILE_DOMAIN + response.msg)
  327. }).finally(() => {
  328. this.$modal.closeLoading()
  329. })
  330. },
  331. getJjcgjList(val) {
  332. this.$modal.loading()
  333. studyFormFill_jcgj(_.merge({}, this.queryParamsJcgj, val)).then(response => {
  334. this.jcgjList = response.rows
  335. this.jcgjTotal = response.total
  336. this.$refs.jcgjList.init(this.jcgjList)
  337. }).finally(() => {
  338. this.$modal.closeLoading()
  339. })
  340. },
  341. getQmxxList() {
  342. this.loadingQmxx = true
  343. studyFormFill_qmxx(this.queryParamsQmxx).then(response => {
  344. this.qmxxList = response.rows
  345. this.totalQmxx = response.total
  346. this.loadingQmxx = false
  347. })
  348. },
  349. cancel() {
  350. this.$emit('close')
  351. this.open = false
  352. },
  353. reset() {
  354. this.form = {
  355. id: null,
  356. studyId: null,
  357. bdbh: null,
  358. bdmc: null,
  359. bdsm: null,
  360. templateId: null,
  361. templateMc: null,
  362. bdnr: null
  363. }
  364. this.resetForm("form")
  365. },
  366. show(row) {
  367. this.reset()
  368. this.$modal.loading()
  369. this.formApprove.id = row.id
  370. this.queryParamsJcgj.formId = row.id
  371. this.queryParamsQmxx.formId = row.id
  372. studyFormFill_info({ id: row.id }).then(response => {
  373. this.form = response.data
  374. this.getQmxxList()
  375. this.getJjcgjList()
  376. this.open = true
  377. }).finally(() => {
  378. this.$modal.closeLoading()
  379. })
  380. },
  381. approve() {
  382. this.$refs["formApprove"].validate(valid => {
  383. if (valid) {
  384. this.$modal.loading()
  385. studyFormFill_tb(this.formApprove).then(response => {
  386. this.openApprove = false
  387. this.$emit('close')
  388. this.open = false
  389. }).finally(() => {
  390. this.$modal.closeLoading()
  391. })
  392. }
  393. })
  394. }
  395. }
  396. }
  397. </script>
  398. <style scoped>
  399. .content-title {
  400. width: 100%;
  401. background: #f9f9ff;
  402. font-size: 0.96rem;
  403. font-weight: bold;
  404. padding-left: 10px;
  405. height: 40px;
  406. line-height: 40px;
  407. display: flex;
  408. justify-content: flex-start;
  409. text-align: left;
  410. }
  411. .line {
  412. width: 2px;
  413. float: left;
  414. height: 16px;
  415. margin-top: 12px;
  416. margin-right: 8px;
  417. border-left: #3178ff 3px solid;
  418. }
  419. .subtitle {
  420. height: 40px;
  421. line-height: 40px;
  422. color: #464647 !important;
  423. }
  424. .pdf-content {
  425. padding: 0px;
  426. font-family: Arial, sans-serif;
  427. }
  428. #watermark-overlay {
  429. position: absolute;
  430. top: 0;
  431. left: 0;
  432. width: 100%;
  433. height: 100%;
  434. opacity: 0.6;
  435. font-size: 12px;
  436. pointer-events: none;
  437. z-index: 10;
  438. color: red;
  439. }
  440. .pdf-content h1 {
  441. color: #333;
  442. border-bottom: 2px solid #4CAF50;
  443. padding-bottom: 10px;
  444. }
  445. .pdf-content table {
  446. width: 100%;
  447. border-collapse: collapse;
  448. margin-top: 20px;
  449. }
  450. .pdf-content th,
  451. .pdf-content td {
  452. border: 1px solid #ddd;
  453. padding: 8px;
  454. text-align: left;
  455. }
  456. .pdf-content th {
  457. background-color: #f2f2f2;
  458. }
  459. .datatable {
  460. border-collapse: collapse;
  461. width: 100%;
  462. page-break-inside: avoid;
  463. }
  464. .upload-file-list .el-upload-list__item {
  465. margin-bottom: 0px !important;
  466. }
  467. .datatable thead {
  468. border-left: 1px solid #d0d0d0;
  469. }
  470. .datatable th {
  471. padding: 5px 5px 4px 5px;
  472. max-width: 200px;
  473. line-height: 35px;
  474. text-align: center;
  475. color: #414753;
  476. background: #F5F7FA;
  477. border: 1px solid #d0d0d0;
  478. }
  479. .el-dialog__body {
  480. padding: 10px 20px !important;
  481. }
  482. .datatable th.operate {
  483. padding: 5px 5px 4px 5px;
  484. width: 100px;
  485. line-height: 35px;
  486. background: #F5F7FA;
  487. border: 1px solid #d0d0d0;
  488. text-align: center;
  489. }
  490. .datatable .rowAlt td,
  491. .datatable tbody tr:nth-child(2n) td {
  492. background: #F5F7FA;
  493. }
  494. .datatable td {
  495. border: solid 1px #d0d0d0;
  496. padding: 3px 5px 4px 5px;
  497. max-width: 100px;
  498. color: #414753;
  499. line-height: 35px;
  500. text-align: center;
  501. }
  502. .html2pdf__page-break {
  503. display: none !important;
  504. }
  505. .content {
  506. position: relative;
  507. z-index: 1;
  508. }
  509. .controls {
  510. margin-bottom: 20px;
  511. padding: 10px;
  512. background: #f5f5f5;
  513. }
  514. input {
  515. margin: 0 10px;
  516. padding: 8px;
  517. }
  518. </style>