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

501 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%; padding: 0px 10px;">
  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. <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="margin-left: 20px;">
  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" :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.studyFormFill.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.studyFormFill.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 { studyFormFill_jcgjqmxxList, studyFormFill_tb, studyFormFill_info, studyFormFill_jcgj, studyFormFill_qmxx, studyFormFill_exportDetail } from "@/api/business/study/studyFormFill"
  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.studyFormFill.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. studyFormFill_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: {
  267. top: 20,
  268. right: 15,
  269. bottom: 20,
  270. left: 15
  271. },
  272. })
  273. }, 200);
  274. this.saveSimpleLog({jcmc:'填报表单详情导出',jcmcEn:'Record Detail Export',name:this.form.bdmc+'('+this.form.bdbh+')',nameEn:this.form.bdmc+'('+this.form.bdbh+')'})
  275. })
  276. },
  277. //添加水印
  278. async addDynamicWatermark({ pdfContent }) {
  279. return new Promise((resolve) => {
  280. const canvas = document.createElement('canvas')
  281. const ctx = canvas.getContext('2d')
  282. // 设置canvas尺寸
  283. canvas.width = 300;
  284. canvas.height = 300;
  285. // 绘制动态水印
  286. ctx.fillStyle = `rgba(100, 100, 100, ${this.watermarkOpacity})`
  287. ctx.font = `${this.watermarkSize}px Arial`
  288. ctx.textAlign = 'center'
  289. ctx.textBaseline = 'middle'
  290. // 计算水印密度
  291. const stepX = 300
  292. const stepY = 200
  293. // 绘制倾斜水印
  294. ctx.save()
  295. ctx.translate(canvas.width / 2, canvas.height / 2)
  296. ctx.rotate(-Math.PI / 4) // 45度倾斜
  297. let time = moment().format("YYYY-MM-DD HH:mm:ss")
  298. for (let x = -canvas.width; x < canvas.width * 2; x += stepX) {
  299. for (let y = -canvas.height; y < canvas.height * 2; y += stepY) {
  300. // 动态水印内容
  301. const dynamicText = `${this.watermarkText} - ${time}`
  302. ctx.fillText(dynamicText, x, y)
  303. }
  304. }
  305. ctx.restore()
  306. // 创建水印层
  307. const watermarkLayer = document.createElement('div')
  308. watermarkLayer.className = 'watermark-layer'
  309. watermarkLayer.style.cssText = `
  310. position: absolute;
  311. top: 0;
  312. left: 0;
  313. width: 100%;
  314. height: 100%;
  315. pointer-events: none;
  316. background-image: url(${canvas.toDataURL('image/png')});
  317. background-repeat: repeat;
  318. z-index: 9999;
  319. `
  320. // 等待渲染
  321. setTimeout(() => resolve(), 100)
  322. })
  323. },
  324. //移除分页空白
  325. removePageBreak() {
  326. document.querySelectorAll('.html2pdf__page-break').forEach(el => {
  327. el.remove()
  328. })
  329. },
  330. exportExcel_bak(jcgjlx) {
  331. this.$modal.loading()
  332. studyFormFill_exportDetail(_.merge({}, this.queryParamsJcgj, { jcgjlx: jcgjlx, lang: this.$store.getters.language.split("_")[0] })).then(response => {
  333. window.open(process.env.VUE_APP_FILE_DOMAIN + response.msg)
  334. }).finally(() => {
  335. this.$modal.closeLoading()
  336. })
  337. },
  338. getJjcgjList(val) {
  339. this.$modal.loading()
  340. studyFormFill_jcgj(_.merge({}, this.queryParamsJcgj, val)).then(response => {
  341. this.jcgjList = response.rows
  342. this.jcgjTotal = response.total
  343. this.$refs.jcgjList.init(this.jcgjList)
  344. }).finally(() => {
  345. this.$modal.closeLoading()
  346. })
  347. },
  348. getQmxxList() {
  349. this.loadingQmxx = true
  350. studyFormFill_qmxx(this.queryParamsQmxx).then(response => {
  351. this.qmxxList = response.rows
  352. this.totalQmxx = response.total
  353. this.loadingQmxx = false
  354. })
  355. },
  356. cancel() {
  357. this.$emit('close')
  358. this.open = false
  359. },
  360. reset() {
  361. this.form = {
  362. id: null,
  363. studyId: null,
  364. bdbh: null,
  365. bdmc: null,
  366. bdsm: null,
  367. templateId: null,
  368. templateMc: null,
  369. bdnr: null
  370. }
  371. this.resetForm("form")
  372. },
  373. show(row) {
  374. this.reset()
  375. this.$modal.loading()
  376. this.formApprove.id = row.id
  377. this.queryParamsJcgj.formId = row.id
  378. this.queryParamsQmxx.formId = row.id
  379. studyFormFill_info({ id: row.id }).then(response => {
  380. this.form = response.data
  381. this.getQmxxList()
  382. this.getJjcgjList()
  383. this.open = true
  384. this.saveSimpleLog({ jcmc: '填报详情', jcmcEn: 'Record Detail', name: this.form.bdmc + '(' + this.form.bdbh + ')', nameEn: this.form.bdmc + '(' + this.form.bdbh + ')' })
  385. }).finally(() => {
  386. this.$modal.closeLoading()
  387. })
  388. },
  389. approve() {
  390. this.$refs["formApprove"].validate(valid => {
  391. if (valid) {
  392. this.$modal.loading()
  393. studyFormFill_tb(this.formApprove).then(response => {
  394. this.openApprove = false
  395. this.$emit('close')
  396. this.open = false
  397. }).finally(() => {
  398. this.$modal.closeLoading()
  399. })
  400. }
  401. })
  402. }
  403. }
  404. }
  405. </script>
  406. <style scoped>
  407. .content-title {
  408. width: 100%;
  409. background: #f9f9ff;
  410. font-size: 0.96rem;
  411. font-weight: bold;
  412. padding-left: 10px;
  413. height: 40px;
  414. page-break-inside: avoid;
  415. line-height: 40px;
  416. display: flex;
  417. justify-content: flex-start;
  418. text-align: left;
  419. }
  420. .line {
  421. width: 2px;
  422. float: left;
  423. height: 16px;
  424. margin-top: 12px;
  425. margin-right: 8px;
  426. border-left: #3178ff 3px solid;
  427. }
  428. .subtitle {
  429. height: 40px;
  430. line-height: 40px;
  431. color: #464647 !important;
  432. }
  433. .pdf-content {
  434. padding: 0px;
  435. font-family: Arial, sans-serif;
  436. }
  437. #watermark-overlay {
  438. position: absolute;
  439. top: -80px;
  440. left: 0;
  441. width: 100%;
  442. height: 100%;
  443. opacity: 0.6;
  444. font-size: 12px;
  445. pointer-events: none;
  446. z-index: 10;
  447. color: red;
  448. }
  449. .pdf-content h1 {
  450. color: #333;
  451. border-bottom: 2px solid #4CAF50;
  452. padding-bottom: 10px;
  453. }
  454. .upload-file-list .el-upload-list__item {
  455. margin-bottom: 0px !important;
  456. }
  457. .html2pdf__page-break {
  458. display: none !important;
  459. }
  460. .content {
  461. position: relative;
  462. z-index: 1;
  463. }
  464. .controls {
  465. margin-bottom: 20px;
  466. padding: 10px;
  467. background: #f5f5f5;
  468. }
  469. input {
  470. margin: 0 10px;
  471. padding: 8px;
  472. }
  473. </style>