accept-form.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <template>
  2. <div>
  3. <div class="tool-btn" style="width: 800px; margin: 0 auto; text-align: right;">
  4. <el-button size="small" type="primary" icon="el-icon-printer" v-print="printConfig" @click="isPrintPage = true">打印</el-button>
  5. </div>
  6. <div id="acceptDoc" class="doc-page" v-loading="loading">
  7. <div :class="['doc-header', { printCls: isPrintPage }]">
  8. <h3 class="page-title" style="height: 60px; line-height: 60px; margin: 0;">{{ teantData.tenantName }}</h3>
  9. <h3 class="page-title" style="margin: 5px 0 16px;">研发项目验收资料</h3>
  10. <div class="project-basic-info">
  11. <div class="info-row">{{ `研发项目名称:${ selProject.xmmc || '' }` }}</div>
  12. <div class="info-row">{{ `项目负责人:${ selProject.xmfzrxm || '' }` }}</div>
  13. <div class="info-row">{{ `项目申请单位:${ selProject.xmsqdw || '' }` }}</div>
  14. <div class="info-row">{{ `项目执行期:${ getXmDateRange || '' }` }}</div>
  15. <div class="info-row">
  16. <span>填报时间:</span>
  17. <span>
  18. <el-date-picker v-show="!isPrintPage" v-model="formData.tbsj" value-format="yyyy-MM-dd" />
  19. <span v-show="isPrintPage">{{ formData.tbsj }}</span>
  20. </span>
  21. </div>
  22. </div>
  23. </div>
  24. <!-- 打印强制分页 -->
  25. <div style="page-break-after: always;"></div>
  26. <h3 class="page-title" style="margin: 24px 0 16px;">研发项目验收申请表</h3>
  27. <table style="margin-top: 20px;">
  28. <tbody>
  29. <tr>
  30. <td class="doc-label">研发项目名称</td>
  31. <td class="doc-input">
  32. <project-select
  33. v-show="!isPrintPage"
  34. v-model="currProjectId"
  35. @change="handleProjectChange"
  36. ></project-select>
  37. <span v-show="isPrintPage">{{ selProject.xmmc }}</span>
  38. </td>
  39. <td class="doc-label">项目申请单位</td>
  40. <td class="doc-input">
  41. {{ selProject.xmsqdw }}
  42. </td>
  43. </tr>
  44. <tr>
  45. <td class="doc-label">项目负责人</td>
  46. <td class="doc-input">{{ selProject.xmfzrxm }} </td>
  47. <td class="doc-label">研究类型</td>
  48. <td class="doc-input">{{ selProject.yjlx }}</td>
  49. </tr>
  50. <tr>
  51. <td class="doc-label">研发开始时间</td>
  52. <td class="doc-input">{{ selProject.xmkssj }} </td>
  53. <td class="doc-label">研发结束时间</td>
  54. <td class="doc-input">{{ selProject.xmjssj }}</td>
  55. </tr>
  56. <tr>
  57. <td class="doc-label">项目来源</td>
  58. <td class="doc-input">{{ selProject.xmly }} </td>
  59. <td class="doc-label">项目开展形式</td>
  60. <td class="doc-input">{{ selProject.xmkzxs }}</td>
  61. </tr>
  62. <tr>
  63. <td class="doc-label">预期成果形式</td>
  64. <td class="doc-input">
  65. <div style="padding: 12px;">
  66. <div v-for="(item, index) of selProject.yqcgxs.split(',')" :key="index">{{ item }}</div>
  67. </div>
  68. </td>
  69. <td class="doc-label">项目技术经济目标</td>
  70. <td class="doc-input">
  71. <div style="padding: 12px;">
  72. <div v-for="(item, index) of selProject.xmjsjjmb.split(',')" :key="index">{{ item }}</div>
  73. </div>
  74. </td>
  75. </tr>
  76. </tbody>
  77. </table>
  78. <avue-form v-show="!isPrintPage" v-model="formData" :option="option" style="margin-top: 30px; padding: 0;">
  79. <template slot="ysbgLabel">
  80. <label>验收报告</label>
  81. <div style="position: absolute; top: 0; left: 220px; z-index: 1;">
  82. <file-download-btn templateKey="研发项目管理-项目结题-验收报告"></file-download-btn>
  83. </div>
  84. </template>
  85. <template slot="jsbgLabel">
  86. <label>技术报告</label>
  87. <div style="position: absolute; top: 0; left: 220px; z-index: 1;">
  88. <file-download-btn templateKey="研发项目管理-项目结题-技术报告"></file-download-btn>
  89. </div>
  90. </template>
  91. <template slot="cgbgLabel">
  92. <label>成果报告</label>
  93. <div style="position: absolute; top: 0; left: 220px; z-index: 1;">
  94. <file-download-btn templateKey="研发项目管理-项目结题-成果报告"></file-download-btn>
  95. </div>
  96. </template>
  97. <template slot="xybgLabel">
  98. <label>效益报告</label>
  99. <div style="position: absolute; top: 0; left: 220px; z-index: 1;">
  100. <file-download-btn templateKey="研发项目管理-项目结题-效益报告"></file-download-btn>
  101. </div>
  102. </template>
  103. <template slot="yspwLabel">
  104. <label>验收批文</label>
  105. <div style="position: absolute; top: 0; left: 220px; z-index: 1;">
  106. <file-download-btn templateKey="研发项目管理-项目结题-验收批文"></file-download-btn>
  107. </div>
  108. </template>
  109. </avue-form>
  110. <div v-show="isPrintPage" class="file-list" style="margin-top: 30px;">
  111. <div class="file-list-item" style="display: flex; margin-bottom: 18px;">
  112. <div class="name">验收报告:</div>
  113. <div class="file-wrap">
  114. <div v-for="(item, idx) of formData.ysbg" :key="idx">{{ item.label }}</div>
  115. </div>
  116. </div>
  117. <div class="file-list-item" style="display: flex; margin-bottom: 18px;">
  118. <div class="name">技术报告:</div>
  119. <div class="file-wrap">
  120. <div v-for="(item, idx) of formData.jsbg" :key="idx">{{ item.label }}</div>
  121. </div>
  122. </div>
  123. <div class="file-list-item" style="display: flex; margin-bottom: 18px;">
  124. <div class="name">成果报告:</div>
  125. <div class="file-wrap">
  126. <div v-for="(item, idx) of formData.cgbg" :key="idx">{{ item.label }}</div>
  127. </div>
  128. </div>
  129. <div class="file-list-item" style="display: flex; margin-bottom: 18px;">
  130. <div class="name">效益报告:</div>
  131. <div class="file-wrap">
  132. <div v-for="(item, idx) of formData.xybg" :key="idx">{{ item.label }}</div>
  133. </div>
  134. </div>
  135. <div class="file-list-item" style="display: flex; margin-bottom: 18px;">
  136. <div class="name">验收批文:</div>
  137. <div class="file-wrap">
  138. <div v-for="(item, idx) of formData.yspw" :key="idx">{{ item.label }}</div>
  139. </div>
  140. </div>
  141. </div>
  142. <p v-show="!isPrintPage" class="tip">注:可一次上传多个相关文件(文档、表格、图片等)。 支持格式:DOC, PDF, Excel, PNG, JPG, PPT 等。 请确保文件内容清晰有效。</p>
  143. <div v-show="!isPrintPage" class="btn-wrap" style="text-align: center; margin-top: 30px;">
  144. <template v-if="!projectIsEnd">
  145. <el-button type="primary" style="width: 160px;" @click="handleSaveBtn(0)">保 存</el-button>
  146. <el-button type="primary" style="width: 160px;" @click="handleSaveBtn(2)">确定结题</el-button>
  147. </template>
  148. <el-button v-else type="primary" style="width: 160px;" @click="handleSaveBtn(1)">取消结题</el-button>
  149. </div>
  150. </div>
  151. </div>
  152. </template>
  153. <script>
  154. import projectSelect from "@/components/project-select";
  155. import secondUnitCascader from "@/components/second-unit-cascader";
  156. import fileDownloadBtn from "@/components/file-download-btn";
  157. import { save, getDetail } from "@/api/projectManage/accept";
  158. import moment from "moment";
  159. import { mapGetters } from "vuex";
  160. import { getDetail as getTenantDetail } from '@/api/system/tenant';
  161. import { downloadFileByUrl } from "@/util/util";
  162. export default {
  163. components: {
  164. projectSelect,
  165. secondUnitCascader,
  166. fileDownloadBtn
  167. },
  168. props: {
  169. projectId: [String, Number]
  170. },
  171. data() {
  172. return {
  173. formData: {},
  174. loading: false,
  175. projectList: [],
  176. selProject: {
  177. yqcgxs: '',
  178. xmjsjjmb: '',
  179. },
  180. currProjectId: '',
  181. teantData: {},
  182. isPrintPage: false,
  183. printConfig: {
  184. el: '#acceptDoc', //被打印部分的id
  185. closeCallback: () => {
  186. this.isPrintPage = false;
  187. }
  188. },
  189. // 项目是否已结束
  190. projectIsEnd: false,
  191. };
  192. },
  193. computed: {
  194. ...mapGetters(["userInfo"]),
  195. getXmDateRange() {
  196. if (this.selProject.xmkssj && this.selProject.xmjssj) {
  197. let xmkssj = moment(this.selProject.xmkssj).format('yyyy年MM月DD日');
  198. let xmjssj = moment(this.selProject.xmjssj).format('yyyy年MM月DD日')
  199. return `${xmkssj}至${xmjssj}`;
  200. }
  201. return `年 月 日至 年 月 日`;
  202. },
  203. option() {
  204. return {
  205. menuBtn: false,
  206. labelWidth: 75,
  207. column: [
  208. {
  209. label: "验收报告",
  210. prop: "ysbg",
  211. type: "upload",
  212. multiple: true,
  213. span: 24,
  214. dataType: "object",
  215. propsHttp: {
  216. url: "link",
  217. name: "originalName",
  218. res: "data",
  219. },
  220. action: '/api/kd-resource/oss/endpoint/put-file',
  221. uploadPreview: (file, column, done) => {
  222. downloadFileByUrl(file.url, file.name);
  223. return;
  224. },
  225. disabled: !this.currProjectId
  226. },
  227. {
  228. label: "技术报告",
  229. prop: "jsbg",
  230. type: "upload",
  231. multiple: true,
  232. span: 24,
  233. dataType: "object",
  234. propsHttp: {
  235. url: "link",
  236. name: "originalName",
  237. res: "data",
  238. },
  239. action: '/api/kd-resource/oss/endpoint/put-file',
  240. uploadPreview: (file, column, done) => {
  241. downloadFileByUrl(file.url, file.name);
  242. return;
  243. },
  244. disabled: !this.currProjectId
  245. },
  246. {
  247. label: "成果报告",
  248. prop: "cgbg",
  249. type: "upload",
  250. multiple: true,
  251. span: 24,
  252. dataType: "object",
  253. propsHttp: {
  254. url: "link",
  255. name: "originalName",
  256. res: "data",
  257. },
  258. action: '/api/kd-resource/oss/endpoint/put-file',
  259. uploadPreview: (file, column, done) => {
  260. downloadFileByUrl(file.url, file.name);
  261. return;
  262. },
  263. disabled: !this.currProjectId
  264. },
  265. {
  266. label: "效益报告",
  267. prop: "xybg",
  268. type: "upload",
  269. multiple: true,
  270. span: 24,
  271. dataType: "object",
  272. propsHttp: {
  273. url: "link",
  274. name: "originalName",
  275. res: "data",
  276. },
  277. action: '/api/kd-resource/oss/endpoint/put-file',
  278. uploadPreview: (file, column, done) => {
  279. downloadFileByUrl(file.url, file.name);
  280. return;
  281. },
  282. disabled: !this.currProjectId
  283. },
  284. {
  285. label: "验收批文",
  286. prop: "yspw",
  287. type: "upload",
  288. multiple: true,
  289. span: 24,
  290. dataType: "object",
  291. action: '/api/kd-resource/oss/endpoint/put-file',
  292. uploadPreview: (file, column, done) => {
  293. downloadFileByUrl(file.url, file.name);
  294. return;
  295. },
  296. propsHttp: {
  297. url: "link",
  298. name: "originalName",
  299. res: "data",
  300. },
  301. disabled: !this.currProjectId
  302. },
  303. ],
  304. }
  305. }
  306. },
  307. watch: {
  308. currProjectId() {
  309. this.getImplementDetail();
  310. }
  311. },
  312. mounted() {
  313. this.getTenantDetailFunc(this.userInfo.tenant_id);
  314. this.currProjectId = this.projectId || '';
  315. },
  316. methods: {
  317. // 获取租户详情信息
  318. getTenantDetailFunc(tenantId) {
  319. getTenantDetail(tenantId).then(({ data }) => {
  320. if (data.code == 200) {
  321. this.teantData = data.data;
  322. }
  323. });
  324. },
  325. getImplementDetail() {
  326. this.loading = true;
  327. getDetail({ xmId: this.currProjectId, yearAndMonth: this.yearAndMonth }).then(({ data }) => {
  328. this.loading = false;
  329. if (data.code == 200 && data.data && Object.keys(data.data).length) {
  330. this.formData = {
  331. id: data.data.id,
  332. xmId: data.data.xmId,
  333. tbsj: data.data.tbsj,
  334. ysbg: data.data.ysbg ? JSON.parse(data.data.ysbg) : [],
  335. jsbg: data.data.jsbg ? JSON.parse(data.data.jsbg) : [],
  336. cgbg: data.data.cgbg ? JSON.parse(data.data.cgbg) : [],
  337. xybg: data.data.xybg ? JSON.parse(data.data.xybg) : [],
  338. yspw: data.data.yspw ? JSON.parse(data.data.yspw) : [],
  339. };
  340. } else {
  341. this.formData = {
  342. id: undefined,
  343. tbsj: '',
  344. ysbg: [],
  345. jsbg: [],
  346. cgbg: [],
  347. xybg: [],
  348. yspw: [],
  349. };
  350. }
  351. }).catch(() => {
  352. this.loading = false;
  353. });
  354. },
  355. handleProjectChange(data) {
  356. this.selProject = data;
  357. this.projectIsEnd = data.status == 2 ? true : false;
  358. },
  359. validForm() {
  360. let errorText = '';
  361. if (!this.formData.tbsj) {
  362. errorText = '请选择填报时间';
  363. } else if (!this.currProjectId) {
  364. errorText = '请选择填报的项目';
  365. }
  366. return errorText;
  367. },
  368. /**
  369. * 提交表单
  370. * @param status 0(暂存)、2(确定结题)、1(取消结题)
  371. */
  372. handleSaveBtn(operateType) {
  373. let errorText = this.validForm();
  374. if (errorText) {
  375. this.$message.error(errorText+'!');
  376. return;
  377. }
  378. const params = { ...this.formData, xmId: this.currProjectId, status: operateType };
  379. params.ysbg = JSON.stringify(params.ysbg);
  380. params.jsbg = JSON.stringify(params.jsbg);
  381. params.cgbg = JSON.stringify(params.cgbg);
  382. params.xybg = JSON.stringify(params.xybg);
  383. params.yspw = JSON.stringify(params.yspw);
  384. this.saveData(params, operateType);
  385. },
  386. saveData(params, operateType) {
  387. save(params).then(({ data }) => {
  388. if (data.code == 200) {
  389. let msgObj = {
  390. 0: '保存成功',
  391. 1: '项目取消结题成功',
  392. 2: '项目结题成功'
  393. };
  394. if (operateType == 2) {
  395. this.projectIsEnd = true;
  396. } else {
  397. this.projectIsEnd = false;
  398. }
  399. this.$message.success(`${msgObj[operateType]}!`);
  400. }
  401. });
  402. }
  403. },
  404. };
  405. </script>
  406. <style lang="scss" scoped>
  407. .page-title {
  408. text-align: center;
  409. font-size: 26px;
  410. }
  411. .project-basic-info .info-row {
  412. line-height: 26px;
  413. color: #333;
  414. }
  415. .doc-header.printCls {
  416. padding-top: 50mm;
  417. .project-basic-info {
  418. padding-top: 20mm;
  419. padding-left: 30mm;
  420. font-size: 20px;
  421. font-weight: 600;
  422. .info-row {
  423. line-height: 50px;
  424. }
  425. }
  426. }
  427. .doc-page {
  428. width: 760px;
  429. margin: 0 auto 30px;
  430. }
  431. table {
  432. width: 100%;
  433. border: 1px solid #333;
  434. }
  435. .doc-label {
  436. text-align: center;
  437. padding: 6px;
  438. color: #777;
  439. font-size: 14px;
  440. width: 15%;
  441. }
  442. .doc-input {
  443. text-align: center;
  444. color: #333;
  445. width: 35%;
  446. height: 40px;
  447. }
  448. .el-input__inner {
  449. border: none;
  450. }
  451. tr td {
  452. border: 1px solid #333;
  453. font-size: 14px;
  454. }
  455. .tip {
  456. position: relative;
  457. top: 0;
  458. color: red;
  459. padding-left: 75px;
  460. margin-top: 0;
  461. font-size: 12px;
  462. }
  463. </style>