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

515 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" :total="jcgjTotal" @pagination="getJjcgjList" />
  98. </div>
  99. </div>
  100. </div>
  101. </div>
  102. <!-- 填报 -->
  103. <el-dialog :title="$t('page.business.study.studyFormApply.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.studyFormApply.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 { studyFormApply_jcgjqmxxList, studyFormApply_tb, studyFormApply_info, studyFormApply_jcgj, studyFormApply_qmxx, studyFormApply_exportDetail } from "@/api/business/study/studyFormApply"
  151. import { mapGetters } from 'vuex'
  152. import JcgjList from "@/views/business/comps/common/JcgjList";
  153. import JcgjExportList from "@/views/business/comps/common/JcgjExportList";
  154. import TemplateTable from '@/views/business/comps/template/TemplateTable';
  155. import moment from "moment";
  156. import VueHtml2pdf from 'vue-html2pdf'
  157. export default {
  158. name: "Xq",
  159. components: { JcgjExportList,JcgjList, TemplateTable, VueHtml2pdf },
  160. data() {
  161. return {
  162. watermarkText:'',
  163. opacity: 0.8,
  164. watermarkOpacity: 0.8,
  165. watermarkSize: 40,
  166. openApprove: false,
  167. formApprove: {
  168. id: null,
  169. qmyy: this.$t('page.business.study.studyFormApply.cjjl'),
  170. remark: '',
  171. qmrmm: '',
  172. },
  173. rulesApprove: {
  174. qmrmm: [{
  175. required: true,
  176. message: ' ',
  177. trigger: 'blur'
  178. }]
  179. },
  180. qmxxList: [],
  181. totalQmxx: 0,
  182. loadingQmxx: true,
  183. queryParamsQmxx: {
  184. formId: null,
  185. pageNum: 1,
  186. pageSize: 5
  187. },
  188. open: false,
  189. showIndex: 1,
  190. form: {},
  191. rules: {
  192. bdmc: [{
  193. required: true,
  194. message: ' ',
  195. trigger: 'blur'
  196. }],
  197. templateId: [{
  198. required: true,
  199. message: ' ',
  200. trigger: 'blur'
  201. }]
  202. },
  203. jcgjTotal: 0,
  204. jcgjList: [],
  205. queryParamsJcgj: {
  206. pageNum: 1,
  207. formId: null,
  208. pageSize: 5,
  209. },
  210. showExport: false,
  211. qmxxExportList: [],
  212. jcgjExportList: [],
  213. jcgjlxExport: 0
  214. }
  215. },
  216. computed: {
  217. ...mapGetters([
  218. 'nickName', 'name'
  219. ]),
  220. },
  221. created() {
  222. },
  223. methods: {
  224. updateWatermark() {
  225. // 创建水印背景
  226. const text = this.nickName +' '+ moment().format("YYYY-MM-DD HH:mm:ss");
  227. const canvas = document.createElement('canvas');
  228. const ctx = canvas.getContext('2d');
  229. // 设置canvas尺寸
  230. canvas.width = 300;
  231. canvas.height = 300;
  232. // 绘制水印
  233. ctx.fillStyle = `rgba(255, 0, 0, ${this.opacity})`;
  234. ctx.font = '12px Arial';
  235. ctx.textAlign = 'center';
  236. ctx.textBaseline = 'middle';
  237. // 旋转45度
  238. ctx.translate(canvas.width / 2, canvas.height / 2);
  239. ctx.rotate(-45 * Math.PI / 180);
  240. ctx.fillText(text, 0, 0);
  241. // 设置为背景
  242. const container = this.$refs.watermarkContainer;
  243. container.style.backgroundImage = `url(${canvas.toDataURL()})`;
  244. container.style.backgroundRepeat = 'repeat';
  245. },
  246. onProgress(progress) {
  247. console.log(`生成进度: ${progress}%`)
  248. this.removePageBreak()
  249. if (progress == 100) {
  250. this.showExport = false
  251. this.$modal.closeLoading()
  252. }
  253. },
  254. exportExcel(jcgjlx) {
  255. this.$modal.loading()
  256. this.jcgjlxExport = jcgjlx
  257. this.showExport = true
  258. studyFormApply_jcgjqmxxList({ jcgjlx: jcgjlx, id: this.form.id }).then(response => {
  259. this.jcgjExportList = response.data.jcgj
  260. this.qmxxExportList = response.data.qmxx
  261. this.updateWatermark()
  262. this.$refs.jcgjExportList.init(this.jcgjExportList)
  263. setTimeout(() => {
  264. this.$refs.html2Pdf.generatePdf({
  265. margin:[20,10,20,10]
  266. })
  267. }, 200);
  268. })
  269. },
  270. //添加水印
  271. async addDynamicWatermark({ pdfContent }) {
  272. return new Promise((resolve) => {
  273. const canvas = document.createElement('canvas')
  274. const ctx = canvas.getContext('2d')
  275. // 设置canvas尺寸
  276. canvas.width = 300;
  277. canvas.height = 300;
  278. // 绘制动态水印
  279. ctx.fillStyle = `rgba(100, 100, 100, ${this.watermarkOpacity})`
  280. ctx.font = `${this.watermarkSize}px Arial`
  281. ctx.textAlign = 'center'
  282. ctx.textBaseline = 'middle'
  283. // 计算水印密度
  284. const stepX = 300
  285. const stepY = 200
  286. // 绘制倾斜水印
  287. ctx.save()
  288. ctx.translate(canvas.width / 2, canvas.height / 2)
  289. ctx.rotate(-Math.PI / 4) // 45度倾斜
  290. let time=moment().format("YYYY-MM-DD HH:mm:ss")
  291. for (let x = -canvas.width; x < canvas.width * 2; x += stepX) {
  292. for (let y = -canvas.height; y < canvas.height * 2; y += stepY) {
  293. // 动态水印内容
  294. const dynamicText = `${this.watermarkText} - ${time}`
  295. ctx.fillText(dynamicText, x, y)
  296. }
  297. }
  298. ctx.restore()
  299. // 创建水印层
  300. const watermarkLayer = document.createElement('div')
  301. watermarkLayer.className = 'watermark-layer'
  302. watermarkLayer.style.cssText = `
  303. position: absolute;
  304. top: 0;
  305. left: 0;
  306. width: 100%;
  307. height: 100%;
  308. pointer-events: none;
  309. background-image: url(${canvas.toDataURL('image/png')});
  310. background-repeat: repeat;
  311. z-index: 9999;
  312. `
  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. studyFormApply_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. studyFormApply_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. studyFormApply_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. studyFormApply_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. studyFormApply_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. page-break-inside: avoid;
  407. line-height: 40px;
  408. display: flex;
  409. justify-content: flex-start;
  410. text-align: left;
  411. }
  412. .line {
  413. width: 2px;
  414. float: left;
  415. height: 16px;
  416. margin-top: 12px;
  417. margin-right: 8px;
  418. border-left: #3178ff 3px solid;
  419. }
  420. .subtitle {
  421. height: 40px;
  422. line-height: 40px;
  423. color: #464647 !important;
  424. }
  425. .pdf-content {
  426. padding: 20px;
  427. font-family: Arial, sans-serif;
  428. }
  429. #watermark-overlay {
  430. position: absolute;
  431. top: 0;
  432. left: 0;
  433. width: 100%;
  434. height: 100%;
  435. opacity: 0.6;
  436. font-size: 12px;
  437. pointer-events: none;
  438. z-index: 10;
  439. color: red;
  440. }
  441. .pdf-content h1 {
  442. color: #333;
  443. border-bottom: 2px solid #4CAF50;
  444. padding-bottom: 10px;
  445. }
  446. .pdf-content table {
  447. width: 100%;
  448. border-collapse: collapse;
  449. margin-top: 20px;
  450. }
  451. .pdf-content th,
  452. .pdf-content td {
  453. border: 1px solid #ddd;
  454. padding: 8px;
  455. text-align: left;
  456. }
  457. .pdf-content th {
  458. background-color: #f2f2f2;
  459. }
  460. .upload-file-list .el-upload-list__item {
  461. margin-bottom: 0px !important;
  462. }
  463. .el-dialog__body {
  464. padding: 10px 20px !important;
  465. }
  466. .html2pdf__page-break {
  467. display: none !important;
  468. }
  469. .content {
  470. position: relative;
  471. z-index: 1;
  472. }
  473. .controls {
  474. margin-bottom: 20px;
  475. padding: 10px;
  476. background: #f5f5f5;
  477. }
  478. input {
  479. margin: 0 10px;
  480. padding: 8px;
  481. }
  482. </style>