华西海圻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.

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