EditWorkBookReport.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. <template>
  2. <!-- <teleport to=".app-container"> -->
  3. <ContentWrap :title="title" class="check-record-wrapper" v-loading="pageLoading">
  4. <!-- 作业指导书自定义附件,显示pdf预览 -->
  5. <div class="designer-inner">
  6. <SpreadViewer :initData="editData" ref="editSpreadRecordRef" @saveSuccess="saveSuccessRecord"/>
  7. </div>
  8. <div class="operation-inner">
  9. <div class="btn-list">
  10. <template v-if="isEdit">
  11. <el-button type="primary" @click="handleSubmitBefore">提交审核</el-button>
  12. <!-- <el-button type="primary" @click="handleChooseInstrument">选择仪器</el-button>-->
  13. </template>
  14. <el-button type="default" plain @click="handleCloseModal">取 消</el-button>
  15. </div>
  16. <!-- 智能纠错 -->
  17. <div class="operation-item">
  18. <div class="item-header"> 智能纠错 </div>
  19. <div class="item-content">
  20. <el-empty v-if="!checkInputList.length" description="暂无纠错内容" :image-size="120" />
  21. </div>
  22. </div>
  23. <!-- 历史版本 -->
  24. <div class="operation-item">
  25. <div class="item-header"> 历史版本 </div>
  26. <div class="item-content">
  27. <el-empty v-if="!recordVersionList.length" description="暂无版本信息" :image-size="120" />
  28. </div>
  29. </div>
  30. </div>
  31. </ContentWrap>
  32. <!-- </teleport> -->
  33. <SelectModal ref="selectModalRef" @confirm="handleConfirm" />
  34. <CustomDialog
  35. ref="customUnitDialogRef"
  36. title="选择批准人"
  37. v-model="approvalUserVisible"
  38. width="650px"
  39. :dialogAttrs="{ zIndex: 10006 }"
  40. >
  41. <el-form ref="formRef" :model="formValueData" :rules="formRules" label-width="135px">
  42. <el-form-item label="批准人" prop="workInstructionAuditId">
  43. <el-select v-model="formValueData.workInstructionAuditId" clearable placeholder="请选择">
  44. <el-option
  45. v-for="item in optionList.guidebookApproveList"
  46. :key="item.id"
  47. :label="item.nickname"
  48. :value="item.id"
  49. />
  50. </el-select>
  51. </el-form-item>
  52. </el-form>
  53. <template #footer>
  54. <el-button @click="approvalUserVisible = false">取消</el-button>
  55. <el-button type="primary" @click="handleSubmitUser">确定</el-button>
  56. </template>
  57. </CustomDialog>
  58. </template>
  59. <script lang="tsx" setup>
  60. import VuePdfEmbed from 'vue-pdf-embed'
  61. import { BoilerTaskOrderApi } from '@/api/pressure2/boilertaskorder'
  62. import {defineModel, ref} from 'vue'
  63. import SpreadDesigner from '@/components/SpreadDesigner/index.vue'
  64. import { buildFileUrl } from '@/utils'
  65. import axios from 'axios'
  66. import { uploadFile } from '@/api/common/index'
  67. import { getPressureReportTemplateInfo } from '@/api/pressure2/reportTemplate/index'
  68. import { PressureReportType } from '@/utils/constants'
  69. import SelectModal from './selectModal.vue'
  70. import { getStrDictOptions } from '@/utils/dict'
  71. import { SmartTableColumn } from '@/types/table'
  72. import { ElForm } from 'element-plus'
  73. import * as UserGroupApi from '@/api/bpm/userGroup'
  74. import * as UserApi from '@/api/system/user'
  75. import { useUserStore } from '@/store/modules/user'
  76. import { isEmpty } from 'lodash'
  77. import {
  78. getStandardTemplateInfo
  79. } from '@/api/laboratory/standard/template'
  80. import {DynamicTbValApi} from "@/api/pressure2/dynamictbval"
  81. import {DynamicTbInsApi} from "@/api/pressure2/dynamictbins"
  82. import * as GC from '@grapecity-software/spread-sheets'
  83. import {DynamicTbApi} from "@/api/pressure2/dynamictb";
  84. import {editReport} from "@/utils/reportUtil";
  85. import {DynamicTbColApi} from "@/api/pressure2/dynamictbcol";
  86. import {SpreadViewer} from "@/components/DynamicReport";
  87. import {InitParams} from "@/components/DynamicReport/SpreadInterface";
  88. const { submitOpinionNoticeApproval } = BoilerTaskOrderApi
  89. const userStore = useUserStore()
  90. const userInfo = computed(() => userStore.user)
  91. const props = defineProps({
  92. title: {
  93. type: String,
  94. default: ''
  95. },
  96. templateId: {
  97. type: String,
  98. default: ''
  99. },
  100. reportId: {
  101. type: String,
  102. default: ''
  103. },
  104. orderId: {
  105. // 任务单ID
  106. type: String,
  107. default: ''
  108. },
  109. dataJSON: {
  110. type: String,
  111. default: ''
  112. },
  113. curReportTypeInfo: {
  114. type: Object,
  115. default: () => {}
  116. },
  117. isCustomFileUrl: {
  118. type: Boolean,
  119. default: false
  120. },
  121. isEdit: {
  122. type: Boolean,
  123. default: false
  124. }
  125. })
  126. const emits = defineEmits(['success', 'cancel'])
  127. const visible = defineModel('visible', { type: Boolean, default: false })
  128. const pageLoading = ref(true)
  129. const ReportPdfRef = ref()
  130. const rowReportPdfUrl = ref('')
  131. // 编辑器对象
  132. let designer: any = null
  133. const designerRef = ref()
  134. // 关闭弹窗
  135. const handleCloseModal = () => {
  136. visible.value = false
  137. emits('cancel')
  138. }
  139. const tempDataSource = ref()
  140. //记录保存 -- 新版本
  141. const confirmSaveParams = ref()
  142. const handleConfirmSave = async()=>{
  143. const params = unref(confirmSaveParams)
  144. if(!params.id || !params.reportUrl) return
  145. const saveResult = await BoilerTaskOrderApi.submitOpinionNoticeApproval(params)
  146. if (saveResult) {
  147. //emits('confirm', params.reportUrl)
  148. emits('success', params)
  149. handleCloseModal()
  150. }
  151. }
  152. // 智能纠错
  153. const checkInputList = ref([])
  154. // 版本管理
  155. const recordVersionList = ref([])
  156. const statusDictOptions = getStrDictOptions('pressure_instrument_management_status')
  157. const selectModalRef = ref<InstanceType<typeof SelectModal>>()
  158. type ModalParamsConfig = {
  159. modalUrl: string
  160. columns: SmartTableColumn[]
  161. isSingle: boolean
  162. modalParams?: any
  163. keyWord?: string
  164. modalTitle?: string
  165. }
  166. const openModalConfig: ModalParamsConfig = {
  167. modalUrl: '/pressure/instrument-management/page',
  168. columns: [
  169. {
  170. type: 'index',
  171. label: '序号',
  172. width: 80,
  173. fieldProps: {
  174. align: 'center'
  175. }
  176. },
  177. {
  178. prop: 'status',
  179. label: '仪器状态',
  180. fieldProps: {
  181. align: 'center',
  182. minWidth: 120
  183. },
  184. render: (row: any) => {
  185. return (statusDictOptions.find((item) => item.value === row.status) || {})?.label || '-'
  186. }
  187. },
  188. {
  189. prop: 'toolboxNo',
  190. label: '工具箱编号',
  191. fieldProps: {
  192. align: 'center',
  193. minWidth: 120
  194. }
  195. },
  196. {
  197. prop: 'mgrNo',
  198. label: '管理编号',
  199. fieldProps: {
  200. align: 'center',
  201. minWidth: 120
  202. }
  203. },
  204. {
  205. prop: 'name',
  206. label: '设备名称',
  207. fieldProps: {
  208. align: 'center',
  209. minWidth: 150
  210. }
  211. },
  212. {
  213. prop: 'alias',
  214. label: '设备别名',
  215. fieldProps: {
  216. align: 'center',
  217. minWidth: 150
  218. }
  219. },
  220. {
  221. prop: 'calibrationDate',
  222. label: '校准日期',
  223. fieldProps: {
  224. align: 'center',
  225. minWidth: 120
  226. }
  227. },
  228. {
  229. prop: 'nextCalibrationDate',
  230. label: '下次校准日期',
  231. fieldProps: {
  232. align: 'center',
  233. minWidth: 120
  234. }
  235. },
  236. {
  237. prop: 'printType',
  238. label: '打印类型',
  239. fieldProps: {
  240. align: 'center',
  241. minWidth: 120
  242. }
  243. },
  244. {
  245. prop: 'mfgno',
  246. label: '出厂编号',
  247. fieldProps: {
  248. align: 'center',
  249. minWidth: 120
  250. }
  251. },
  252. {
  253. prop: 'purchaseDate',
  254. label: '购置日期',
  255. fieldProps: {
  256. align: 'center',
  257. minWidth: 120
  258. }
  259. },
  260. {
  261. prop: 'processor',
  262. label: '经手人',
  263. fieldProps: {
  264. align: 'center',
  265. minWidth: 120
  266. }
  267. },
  268. {
  269. prop: 'acceotor',
  270. label: '验收人',
  271. fieldProps: {
  272. align: 'center',
  273. minWidth: 120
  274. }
  275. },
  276. {
  277. prop: 'deptName',
  278. label: '领用部门',
  279. fieldProps: {
  280. align: 'center',
  281. minWidth: 120
  282. }
  283. }
  284. ],
  285. isSingle: true,
  286. keyWord: 'name',
  287. modalTitle: '选择仪器'
  288. }
  289. // 添加仪器关联
  290. const handleChooseInstrument = () => {
  291. selectModalRef.value?.openModal(openModalConfig)
  292. }
  293. // 将选择的仪器信息和关联的字段进行绑定,然后更新葡萄城页面显示
  294. const handleConfirm = (record: Recordable[]) => {
  295. const bindingPathSchema = designer && designer?.getData('treeNodeFromJson')
  296. console.log(designerRef.value?.getDataSource(), 'getDataSource')
  297. const prepareJson = designerRef.value?.getDataSource() || {}
  298. if (bindingPathSchema) {
  299. const currentRow = record[0] || {}
  300. const result = {}
  301. for (const key in currentRow) {
  302. if (!isEmpty(currentRow[key])) {
  303. result[key] = currentRow[key]
  304. }
  305. }
  306. const dataSource = { ...prepareJson, ...result }
  307. // console.log('dataSource', dataSource)
  308. // 设置数据源的默认值
  309. designerRef.value.newSetDataSource(bindingPathSchema, dataSource)
  310. }
  311. }
  312. // 选择审核人
  313. const formRef = ref<InstanceType<typeof ElForm>>()
  314. const formValueData = ref<Recordable>({})
  315. const formRules = reactive({
  316. workInstructionAuditId: [{ required: true, message: '请选择批准人', trigger: 'change' }]
  317. })
  318. const optionList = reactive<{
  319. guidebookApproveList: Recordable[]
  320. }>({
  321. guidebookApproveList: []
  322. })
  323. const approvalUserVisible = ref(false)
  324. // 获取审核人信息
  325. const getGuidebookApproveList = async () => {
  326. try {
  327. const { workInstructionAuditId } = await UserApi.getApprovalDetail({})
  328. formValueData.value = {
  329. workInstructionAuditId
  330. }
  331. optionList.guidebookApproveList = await UserGroupApi.getUserGroupUserList({
  332. category: 500,
  333. status: 0
  334. })
  335. approvalUserVisible.value = true
  336. } catch (error) {}
  337. }
  338. const handleSubmitUser = () => {
  339. formRef.value?.validate(async (valid) => {
  340. if (valid) {
  341. approvalUserVisible.value = false
  342. //await handleSaveNew()
  343. await handleConfirmSave()
  344. }
  345. })
  346. }
  347. const handleSubmitBefore = async () => {
  348. try {
  349. await editSpreadRecordRef.value?.handleSave()
  350. } catch (error) {
  351. console.error('保存数据失败:', error)
  352. ElMessage.error('保存数据失败,请重试')
  353. return
  354. }
  355. await getGuidebookApproveList()
  356. }
  357. const editData=ref<InitParams>(
  358. {
  359. templateId: '',
  360. refId: '',
  361. refName:'',
  362. insId:'',
  363. opType: 0, // 0:excel,1: pdf
  364. });
  365. const editSpreadRecordRef=ref();
  366. const saveSuccessRecord = async (data)=>{
  367. const dataJson = !data.dataSource ? '' : JSON.stringify(data.dataSource)
  368. confirmSaveParams.value.prepareJson = dataJson
  369. }
  370. const editPreview=()=>{
  371. editData.value.templateId = props.templateId;
  372. editData.value.refId = props.reportId;
  373. if (props?.isEdit){
  374. editData.value.opType = 0;
  375. }else{
  376. editData.value.opType = 1;
  377. }
  378. editSpreadRecordRef.value?.reloadView();
  379. confirmSaveParams.value = {
  380. id: props.reportId,
  381. reportUrl: props.reportId,
  382. auditUserIds: [formValueData.value.workInstructionAuditId]
  383. }
  384. pageLoading.value = false
  385. console.log('editDataPreview', editData.value)
  386. }
  387. onMounted(() => {
  388. editPreview()
  389. })
  390. </script>
  391. <style lang="scss" scoped>
  392. :deep(.app-container) {
  393. position: relative;
  394. }
  395. .check-record-wrapper {
  396. position: absolute;
  397. left: 0;
  398. right: 0;
  399. top: 0;
  400. bottom: 0;
  401. z-index: 2000;
  402. :deep(.el-card__body) {
  403. display: flex;
  404. justify-content: space-between;
  405. align-items: stretch;
  406. height: calc(100% - 58px);
  407. }
  408. :deep(.spread-designer-container) {
  409. height: calc(
  410. 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 64px
  411. );
  412. padding: 0;
  413. border: 1px solid var(--el-border-color);
  414. border-right-width: 0px;
  415. }
  416. .designer-inner {
  417. width: calc(100% - 440px);
  418. flex: 1;
  419. position: relative;
  420. }
  421. .operation-inner {
  422. display: flex;
  423. flex-direction: column;
  424. gap: 10px;
  425. height: 100%;
  426. flex-basis: 440px;
  427. padding: 7px 10px 20px 10px;
  428. border: 1px solid var(--el-border-color);
  429. background-color: #fff;
  430. box-sizing: border-box;
  431. overflow: hidden;
  432. .btn-list {
  433. padding: 5px 0 2px;
  434. }
  435. .version-box {
  436. padding: 8px 0 12px;
  437. }
  438. .operation-item {
  439. position: relative;
  440. max-height: calc(100% / 3);
  441. overflow: hidden;
  442. .item-header {
  443. position: sticky;
  444. left: 0;
  445. top: 0;
  446. z-index: 1000;
  447. width: 100%;
  448. height: 28px;
  449. padding-left: 20px;
  450. font-size: 16px;
  451. line-height: 28px;
  452. color: #fff;
  453. background: #fff url(@/assets/imgs/pressure/my-task-detail-operation-bg.png) no-repeat left
  454. top;
  455. background-size: 100% 28px;
  456. }
  457. .item-content {
  458. // height: calc(100% - 48px);
  459. height: auto;
  460. padding: 10px 0;
  461. font-size: 14px;
  462. overflow-y: auto;
  463. overflow-x: hidden;
  464. box-sizing: content-box;
  465. .el-empty {
  466. width: 100%;
  467. height: 150px;
  468. padding: 0;
  469. box-sizing: border-box;
  470. }
  471. }
  472. &:last-child {
  473. max-height: unset;
  474. flex-grow: 1;
  475. .item-content {
  476. height: calc(100% - 48px);
  477. }
  478. }
  479. :deep(.smart-table-inner .toolbar) {
  480. margin: 0 !important;
  481. }
  482. .error-item {
  483. display: flex;
  484. align-items: center;
  485. padding: 7px 10px;
  486. color: #41475c;
  487. border: 1px solid var(--el-border-color);
  488. border-width: 1px 0 1px 0;
  489. background-color: #f5f5fa;
  490. .el-icon {
  491. margin-right: 6px;
  492. }
  493. &:nth-child(n + 1) {
  494. margin-top: -1px;
  495. }
  496. &:nth-child(even) {
  497. background-color: #fff;
  498. }
  499. }
  500. }
  501. }
  502. }
  503. </style>