plm前端
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.

2800 lines
114 KiB

  1. <template>
  2. <div class="mod-config">
  3. <el-form :inline="true" label-position="top" :model="searchData">
  4. <el-form-item label="序列号">
  5. <el-input v-model="searchData.referenceNo" clearable style="width: 140px"></el-input>
  6. </el-form-item>
  7. <el-form-item label="单据状态">
  8. <el-select v-model="searchData.status" clearable style="width: 120px">
  9. <el-option label="草稿" value="草稿"></el-option>
  10. <el-option label="审批中" value="审批中"></el-option>
  11. <el-option label="已完成" value="已完成"></el-option>
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item label="节点审批人">
  15. <el-input v-model="searchData.currentApprover" clearable style="width: 120px"></el-input>
  16. </el-form-item>
  17. <el-form-item label="当前节点">
  18. <el-select v-model="searchData.nodeId" placeholder="请选择节点" clearable style="width: 140px">
  19. <el-option
  20. v-for="option in nodeOptions"
  21. :key="option.id"
  22. :label="option.name"
  23. :value="option.id">
  24. </el-option>
  25. </el-select>
  26. </el-form-item>
  27. <el-form-item :label="' '">
  28. <el-button v-if="authSearch" @click="getDataList">查询</el-button>
  29. <el-button v-if="authUpdate" @click="addOrUpdateHandle()">新增</el-button>
  30. <el-button v-if="authDelete" type="danger" @click="deleteHandle()">删除</el-button>
  31. <download-excel
  32. v-if="authSearch"
  33. :fields="exportFieldMap"
  34. :data="exportData"
  35. type="xls"
  36. :name="exportName"
  37. :header="exportHeader"
  38. :footer="exportFooter"
  39. :fetch="createExportData"
  40. :before-generate="startDownload"
  41. :before-finish="finishDownload"
  42. worksheet="导出信息"
  43. :class="['el-button', 'el-button--primary', 'el-button--medium', {'is-loading': exportLoading}]">
  44. <i v-if="exportLoading" class="el-icon-loading"></i>
  45. <span>导出</span>
  46. </download-excel>
  47. <el-button v-if="authSearch" @click="searchHandle()">搜索</el-button>
  48. </el-form-item>
  49. </el-form>
  50. <el-table
  51. ref="labTable"
  52. :height="height"
  53. :data="dataList"
  54. border
  55. :row-style="rowStyle"
  56. @row-click="changeClickRow"
  57. @current-change="currentChange"
  58. @selection-change="dataListSelectionChange"
  59. v-loading="dataListLoading"
  60. style="width: 100%;">
  61. <el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>
  62. <el-table-column prop="referenceNo" header-align="center" align="center" label="序列号" width="180"></el-table-column>
  63. <el-table-column prop="status" header-align="center" align="center" label="单据状态" width="110"></el-table-column>
  64. <el-table-column prop="nodeName" header-align="center" align="center" label="当前节点" width="140"></el-table-column>
  65. <el-table-column prop="currentApprover" header-align="center" align="center" label="当前审批人" width="150"></el-table-column>
  66. <el-table-column prop="applicant" header-align="center" align="center" label="申请人编码" width="120"></el-table-column>
  67. <el-table-column prop="applicantName" header-align="center" align="center" label="申请人名称" width="140"></el-table-column>
  68. <el-table-column prop="applicationDate" header-align="center" align="center" label="申请日期" width="120">
  69. <template slot-scope="scope">
  70. <span>{{ normalizeDateOnly(scope.row.applicationDate) }}</span>
  71. </template>
  72. </el-table-column>
  73. <el-table-column prop="applyDepartment" header-align="center" align="center" label="申请部门" width="150"></el-table-column>
  74. <el-table-column prop="requestedDate" header-align="center" align="center" label="需求日期" width="120">
  75. <template slot-scope="scope">
  76. <span>{{ normalizeDateOnly(scope.row.requestedDate) }}</span>
  77. </template>
  78. </el-table-column>
  79. <el-table-column prop="customerId" header-align="center" align="center" label="客户编码" width="120"></el-table-column>
  80. <el-table-column prop="customerName" header-align="center" align="center" label="客户名称" width="160"></el-table-column>
  81. <el-table-column prop="projectId" header-align="center" align="center" label="项目编码" width="130"></el-table-column>
  82. <el-table-column prop="projectName" header-align="center" align="center" label="项目名称" width="170"></el-table-column>
  83. <el-table-column prop="sampleName" header-align="center" align="center" label="样品名称" width="150"></el-table-column>
  84. <el-table-column prop="sampleModel" header-align="center" align="center" label="样品型号" width="150"></el-table-column>
  85. <el-table-column prop="sampleQty" header-align="center" align="center" label="样品数量" width="100"></el-table-column>
  86. <el-table-column prop="productStage" header-align="center" align="center" label="产品阶段" width="120"></el-table-column>
  87. <el-table-column prop="testType" header-align="center" align="center" label="测试类型" width="120"></el-table-column>
  88. <el-table-column prop="reportDocument" header-align="center" align="center" label="报告出具" width="120"></el-table-column>
  89. <el-table-column prop="returnSample" header-align="center" align="center" label="样品退回" width="100">
  90. <template slot-scope="scope">
  91. <span>{{ displayYesNo(scope.row.returnSample) }}</span>
  92. </template>
  93. </el-table-column>
  94. <el-table-column prop="testLab" header-align="center" align="center" label="测试实验室" width="140"></el-table-column>
  95. <el-table-column prop="testStartDate" header-align="center" align="center" label="测试开始时间" width="120">
  96. <template slot-scope="scope">
  97. <span>{{ normalizeDateOnly(scope.row.testStartDate) }}</span>
  98. </template>
  99. </el-table-column>
  100. <el-table-column prop="completedDate" header-align="center" align="center" label="测试完成日期" width="120">
  101. <template slot-scope="scope">
  102. <span>{{ normalizeDateOnly(scope.row.completedDate) }}</span>
  103. </template>
  104. </el-table-column>
  105. <el-table-column prop="testNumber" header-align="center" align="center" label="测试编码" width="140"></el-table-column>
  106. <el-table-column prop="testerName" header-align="center" align="center" label="测试人" width="180" :show-overflow-tooltip="true">
  107. <template slot-scope="scope">
  108. <span>{{ scope.row.testerName || scope.row.tester || '' }}</span>
  109. </template>
  110. </el-table-column>
  111. <el-table-column fixed="right" header-align="center" align="center" width="130" label="操作">
  112. <template slot-scope="scope">
  113. <el-link v-if="authUpdate && scope.row.status !== '已完成'" style="cursor: pointer" @click="addOrUpdateHandle(scope.row)">编辑</el-link>
  114. <el-link v-if="authUpdate && scope.row.status === '已完成'" style="cursor: pointer" @click="addOrUpdateHandle(scope.row)">详情</el-link>
  115. <el-link v-if="authIssue && scope.row.status === '草稿'" style="cursor: pointer; margin-left: 10px;" @click="issueModal(scope.row)">下达</el-link>
  116. </template>
  117. </el-table-column>
  118. </el-table>
  119. <el-pagination style="margin-top: 0px"
  120. @size-change="sizeChangeHandle"
  121. @current-change="currentChangeHandle"
  122. :current-page="pageIndex"
  123. :page-sizes="[20, 50, 100, 200, 500]"
  124. :page-size="pageSize"
  125. :total="totalPage"
  126. layout="total, sizes, prev, pager, next, jumper">
  127. </el-pagination>
  128. <el-dialog
  129. :title="modalTitle"
  130. :visible.sync="modalFlag"
  131. width="965px"
  132. top="5vh"
  133. v-drag
  134. :close-on-click-modal="false"
  135. :showClose="false">
  136. <el-tabs tab-position="left" type="border-card" v-model="activeName" style="width: 100%;height: 730px;">
  137. <el-tab-pane label="基本信息" name="basicInfo">
  138. <div style="height: 715px; overflow-y: auto;">
  139. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: -5px;">
  140. <el-form-item>
  141. <span style="cursor: pointer" v-if="!isLabFieldDisabled('applicant')" slot="label" @click="getBaseList(103, 1)"><a herf="#">申请人/Application</a></span>
  142. <span v-else slot="label">申请人/Application</span>
  143. <el-input v-model="modalData.applicant" @blur="applicantBlur(103)" :disabled="isLabFieldDisabled('applicant')" style="width: 100px"></el-input>
  144. <el-input v-model="modalData.applicantName" disabled style="width: 155px"></el-input>
  145. </el-form-item>
  146. <el-form-item label="申请日期/Apply Date">
  147. <el-date-picker v-model="modalData.applicationDate" type="date" value-format="yyyy-MM-dd" :disabled="isLabFieldDisabled('applicationDate')" style="width: 110px" :editable="false"></el-date-picker>
  148. </el-form-item>
  149. <el-form-item label="申请工厂/Apply Site">
  150. <el-select v-model="modalData.site" :disabled="isEditMode || showModalFlag" style="width: 135px" @change="handleModalSiteChange">
  151. <el-option
  152. v-for="item in siteOptions"
  153. :key="item.siteID"
  154. :label="item.siteID + '-' + item.siteName"
  155. :value="item.siteID">
  156. </el-option>
  157. </el-select>
  158. </el-form-item>
  159. <el-form-item label="申请部门/Apply Department">
  160. <el-input v-model="modalData.applyDepartment" :disabled="isLabFieldDisabled('applyDepartment')" style="width: 225px"></el-input>
  161. </el-form-item>
  162. </el-form>
  163. <el-form :inline="true" label-position="top" :model="modalData">
  164. <el-form-item>
  165. <span slot="label" v-if="!modalData.customerIdFlag && !isLabFieldDisabled('customerId')" @click="getBaseList(102, 1)"><a herf="#">客户/Customer</a></span>
  166. <span slot="label" v-else>客户/Customer</span>
  167. <el-input :disabled="modalData.customerIdFlag || isLabFieldDisabled('customerId')" readonly v-model="modalData.customerId" @blur="handleQueryCustomer" style="width: 100px"></el-input>
  168. <el-input disabled v-model="modalData.customerName" style="width: 155px"></el-input>
  169. </el-form-item>
  170. <el-form-item>
  171. <span slot="label" v-if="!modalData.projectIdFlag && !isLabFieldDisabled('projectId')" @click="chooseProjectListFlag = true"><a herf="#">项目/Project</a></span>
  172. <span slot="label" v-else>项目/Project</span>
  173. <el-input :disabled="modalData.projectIdFlag || isLabFieldDisabled('projectId')" readonly v-model="modalData.projectId" @blur="handleQueryProjectByCustomer" style="width: 100px"></el-input>
  174. <el-input disabled v-model="modalData.projectName" style="width: 155px"></el-input>
  175. </el-form-item>
  176. <el-form-item label="需求日期/Requested Date">
  177. <el-date-picker v-model="modalData.requestedDate" type="date" value-format="yyyy-MM-dd" :disabled="isLabFieldDisabled('requestedDate')" style="width: 225px" :editable="false"></el-date-picker>
  178. </el-form-item>
  179. </el-form>
  180. <el-form :inline="true" label-position="top" :model="modalData">
  181. <el-form-item label="样品名称/Sample Name">
  182. <el-input v-model="modalData.sampleName" :disabled="isLabFieldDisabled('sampleName')" style="width: 260px"></el-input>
  183. </el-form-item>
  184. <el-form-item label="样品型号/Sample Model">
  185. <el-input v-model="modalData.sampleModel" :disabled="isLabFieldDisabled('sampleModel')" style="width: 259px"></el-input>
  186. </el-form-item>
  187. <el-form-item label="样品数量/Sample QTY">
  188. <el-input v-model="modalData.sampleQty" :disabled="isLabFieldDisabled('sampleQty')" style="width: 225px"></el-input>
  189. </el-form-item>
  190. </el-form>
  191. <el-form :inline="true" label-position="top" :model="modalData">
  192. <el-form-item label="测试目的/Test Purpose">
  193. <el-input v-model="modalData.testPurpose" :disabled="isLabFieldDisabled('testPurpose')" style="width: 260px"></el-input>
  194. </el-form-item>
  195. <el-form-item label="产品阶段/Product Stage">
  196. <dict-data-select v-model="modalData.productStage" clearable :disabled="isLabFieldDisabled('productStage')" style="width: 259px" dict-type="lab_product_stage"></dict-data-select>
  197. </el-form-item>
  198. <el-form-item label="测试类型/Test Type">
  199. <el-radio-group v-model="modalData.testType" :disabled="isLabFieldDisabled('testType')" style="width: 225px">
  200. <el-radio label="内部验证" @click.native.prevent="radioClick('testType', '内部验证')">内部验证</el-radio>
  201. <el-radio label="客户要求" @click.native.prevent="radioClick('testType', '客户要求')">客户要求</el-radio>
  202. </el-radio-group>
  203. </el-form-item>
  204. </el-form>
  205. <el-form :inline="true" label-position="top" :model="modalData" style="margin-left: 2px">
  206. <el-form-item label="报告出具/Report Document">
  207. <el-radio-group v-model="modalData.reportDocument" :disabled="isLabFieldDisabled('reportDocument')" style="width: 260px">
  208. <el-radio label="内部报告" @click.native.prevent="radioClick('reportDocument', '内部报告')">内部报告</el-radio>
  209. <el-radio label="客户报告" @click.native.prevent="radioClick('reportDocument', '客户报告')">客户报告</el-radio>
  210. </el-radio-group>
  211. </el-form-item>
  212. <el-form-item label="样品退回/Return Sample">
  213. <el-radio-group v-model="modalData.returnSample" :disabled="isLabFieldDisabled('returnSample')" style="width: 259px">
  214. <el-radio label="Y" @click.native.prevent="radioClick('returnSample', 'Y')"></el-radio>
  215. <el-radio label="N" @click.native.prevent="radioClick('returnSample', 'N')"></el-radio>
  216. </el-radio-group>
  217. </el-form-item>
  218. </el-form>
  219. <el-form :inline="true" label-position="top" :model="modalData">
  220. <el-form-item label="Application Surface">
  221. <el-input v-model="modalData.applicationSurface" :disabled="isLabFieldDisabled('applicationSurface')" style="width: 533px"></el-input>
  222. </el-form-item>
  223. <el-form-item label="Test Lab">
  224. <dict-data-select v-model="modalData.testLab" clearable :disabled="isLabFieldDisabled('testLab')" style="width: 225px" dict-type="lab_test_lab" @change="handleTestLabChange"></dict-data-select>
  225. </el-form-item>
  226. </el-form>
  227. <el-form :inline="true" label-position="top" :model="modalData">
  228. <el-form-item>
  229. <span style="cursor: pointer" v-if="!isLabFieldDisabled('labApprover')" slot="label" @click="openLabApproverChooseModal"><a herf="#">实验室审批人</a></span>
  230. <span v-else slot="label">实验室审批人</span>
  231. <el-input
  232. v-model="modalData.labApproverName"
  233. readonly
  234. clearable
  235. :disabled="isLabFieldDisabled('labApprover')"
  236. style="width: 772px"
  237. @focus="openLabApproverChooseModal">
  238. <span slot="suffix" v-show="modalData.labApproverName && !isLabFieldDisabled('labApprover')" @click.stop="clearLabApproverSelection">
  239. <i class="el-icon-circle-close"></i>
  240. </span>
  241. </el-input>
  242. </el-form-item>
  243. </el-form>
  244. <el-form :inline="true" label-position="top" style="margin-top: -15px">
  245. <el-form-item label=" ">
  246. <el-button type="primary" :disabled="showModalFlag" @click="openPropertyChooseModal">测试项目及条件/Test Items & Conditions</el-button>
  247. </el-form-item>
  248. </el-form>
  249. <div class="rq" style="width: 772px;">
  250. <el-table
  251. :data="modalData.propertyItemList"
  252. border
  253. :height="260"
  254. style="width: 100%;">
  255. <el-table-column prop="itemNo" header-align="center" align="center" label="属性编码" width="100"></el-table-column>
  256. <el-table-column prop="itemDesc" header-align="center" align="left" label="属性名称" min-width="150"></el-table-column>
  257. <el-table-column header-align="center" align="center" label="属性值" min-width="250">
  258. <template slot-scope="scope">
  259. <el-select
  260. v-if="scope.row.valueChooseFlag === 'Y'"
  261. v-model="scope.row.itemValue"
  262. :disabled="showModalFlag"
  263. clearable
  264. filterable
  265. style="width: 100%;">
  266. <el-option
  267. v-for="(option, index) in (scope.row.availableValueList || [])"
  268. :key="scope.row.itemNo + '-' + index + '-' + option.availableValue"
  269. :label="option.availableValue"
  270. :value="option.availableValue">
  271. </el-option>
  272. </el-select>
  273. <el-input v-else v-model="scope.row.itemValue" :disabled="showModalFlag" clearable></el-input>
  274. </template>
  275. </el-table-column>
  276. </el-table>
  277. </div>
  278. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 0px">
  279. <el-form-item style="display: block;">
  280. <template slot="label">
  281. <span class="judge-criteria-label" @click="openTestSpecDialog">测试规范/Test spec</span>
  282. </template>
  283. <div class="judge-criteria-box">
  284. <template v-if="testSpecFileList.length > 0">
  285. <el-link
  286. v-for="item in testSpecFileList"
  287. :key="item.id"
  288. type="primary"
  289. style="margin-right: 10px"
  290. @click.stop="previewJudgeCriteriaFile(item)">
  291. {{ item.fileName }}
  292. </el-link>
  293. </template>
  294. <span v-else class="judge-criteria-placeholder">点击测试规范上传附件</span>
  295. </div>
  296. </el-form-item>
  297. </el-form>
  298. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 15px">
  299. <el-form-item style="display: block;">
  300. <template slot="label">
  301. <span class="judge-criteria-label" @click="openJudgeCriteriaDialog">判断标准/Judge Criteria</span>
  302. </template>
  303. <div class="judge-criteria-box">
  304. <template v-if="judgeCriteriaFileList.length > 0">
  305. <el-link
  306. v-for="item in judgeCriteriaFileList"
  307. :key="item.id"
  308. type="primary"
  309. style="margin-right: 10px"
  310. @click.stop="previewJudgeCriteriaFile(item)">
  311. {{ item.fileName }}
  312. </el-link>
  313. </template>
  314. <span v-else class="judge-criteria-placeholder">点击判断标准上传附件</span>
  315. </div>
  316. </el-form-item>
  317. </el-form>
  318. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 15px">
  319. <el-form-item label="注意事项/Attention" style="display: block;">
  320. <el-input type="textarea" :rows="3" v-model="modalData.attention" :disabled="isLabFieldDisabled('attention')" resize="none" style="width: 772px"></el-input>
  321. </el-form-item>
  322. </el-form>
  323. </div>
  324. </el-tab-pane>
  325. <el-tab-pane label="审批信息" name="approvalInfo">
  326. <div style="height: 715px; overflow-y: auto;">
  327. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: -5px;">
  328. <el-form-item label="备注(部门经理)/ Comments(Department Manager)" style="display: block;">
  329. <el-input type="textarea" :rows="3" v-model="modalData.departmentManagerComments" :disabled="isLabFieldDisabled('departmentManagerComments')" resize="none" style="width: 772px"></el-input>
  330. </el-form-item>
  331. </el-form>
  332. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 45px;">
  333. <el-form-item label="备注(实验室)/ Comments(Lab)" style="display: block;">
  334. <el-input type="textarea" :rows="3" v-model="modalData.labComments" :disabled="isLabFieldDisabled('labComments')" resize="none" style="width: 772px"></el-input>
  335. </el-form-item>
  336. </el-form>
  337. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 45px">
  338. <el-form-item>
  339. <span style="cursor: pointer" v-if="!isLabFieldDisabled('tester')" slot="label" @click="openTesterChooseModal"><a herf="#">测试人/Tester</a></span>
  340. <span v-else slot="label">测试人/Tester</span>
  341. <el-input
  342. v-model="modalData.testerName"
  343. readonly
  344. :disabled="isLabFieldDisabled('tester')"
  345. style="width: 772px"
  346. placeholder="请选择测试人"
  347. @focus="openTesterChooseModal">
  348. <span slot="suffix" v-show="modalData.testerName && !isLabFieldDisabled('tester')" @click.stop="clearTesterSelection">
  349. <i class="el-icon-circle-close" style="margin-left: 5px;cursor: pointer;"></i>
  350. </span>
  351. </el-input>
  352. </el-form-item>
  353. </el-form>
  354. </div>
  355. </el-tab-pane>
  356. <el-tab-pane label="测试结果" name="resultInfo">
  357. <div style="height: 715px; overflow-y: auto;">
  358. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: -5px;">
  359. <el-form-item label="测试开始时间/Test Start Date">
  360. <el-date-picker v-model="modalData.testStartDate" type="date" value-format="yyyy-MM-dd" :disabled="isLabFieldDisabled('testStartDate')" style="width: 170px" :editable="false"></el-date-picker>
  361. </el-form-item>
  362. <el-form-item label="测试完成日期/Completed Date">
  363. <el-date-picker v-model="modalData.completedDate" type="date" value-format="yyyy-MM-dd" :disabled="isLabFieldDisabled('completedDate')" style="width: 170px" :editable="false"></el-date-picker>
  364. </el-form-item>
  365. <el-form-item label="测试编码/Test Number">
  366. <el-input v-model="modalData.testNumber" :disabled="isLabFieldDisabled('testNumber')" style="width: 250px"></el-input>
  367. </el-form-item>
  368. </el-form>
  369. <el-form :inline="true" label-position="top" :model="modalData">
  370. <el-form-item label="异常情况及说明/Unnormal symptom and description" style="display: block;">
  371. <el-input type="textarea" :rows="3" v-model="modalData.unnormalSymptomAndDescription" :disabled="isLabFieldDisabled('unnormalSymptomAndDescription')" resize="none" style="width: 772px"></el-input>
  372. </el-form-item>
  373. </el-form>
  374. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 45px">
  375. <el-form-item style="display: block;">
  376. <template slot="label">
  377. <span class="judge-criteria-label" @click="openTestReportDialog">测试报告/Test Report</span>
  378. </template>
  379. <div class="judge-criteria-box">
  380. <template v-if="testReportFileList.length > 0">
  381. <el-link
  382. v-for="item in testReportFileList"
  383. :key="item.id"
  384. type="primary"
  385. style="margin-right: 10px"
  386. @click.stop="previewTestReportFile(item)">
  387. {{ item.fileName }}
  388. </el-link>
  389. </template>
  390. <span v-else class="judge-criteria-placeholder">点击测试报告上传附件</span>
  391. </div>
  392. </el-form-item>
  393. </el-form>
  394. <el-form :inline="true" label-position="top" :model="modalData" style="margin-top: 20px">
  395. <el-form-item label="备注/Comments" style="display: block;">
  396. <el-input type="textarea" :rows="3" v-model="modalData.testerComments" :disabled="isLabFieldDisabled('testerComments')" resize="none" style="width: 772px"></el-input>
  397. </el-form-item>
  398. </el-form>
  399. </div>
  400. </el-tab-pane>
  401. </el-tabs>
  402. <el-footer style="height:35px;margin-top: 15px;text-align:center">
  403. <el-button type="primary" v-if="canSaveAction()" @click="dataFormSubmit()">保存</el-button>
  404. <el-button type="primary" @click="handleModalClose">关闭</el-button>
  405. <el-button type="primary" v-if="canAgreeAction()" :loading="submitLoading" @click="agreeSubmit">同意</el-button>
  406. <el-button type="primary" v-if="canRejectAction()" :loading="submitLoading" @click="openRejectModal">驳回</el-button>
  407. </el-footer>
  408. </el-dialog>
  409. <el-dialog width="420px" title="驳回意见" :close-on-click-modal="false" :visible.sync="submitModalFlag" v-drag>
  410. <el-form :inline="true" label-position="top" style="margin-top: -15px;">
  411. <el-form-item label="意见">
  412. <el-input type="textarea" :rows="4" v-model="rejectOpinion" style="width: 380px"></el-input>
  413. </el-form-item>
  414. </el-form>
  415. <div slot="footer" class="dialog-footer">
  416. <el-button @click="submitModalFlag = false">取消</el-button>
  417. <el-button type="primary" :loading="submitLoading" @click="rejectSubmit">确定</el-button>
  418. </div>
  419. </el-dialog>
  420. <el-dialog title="属性清单" :close-on-click-modal="false" v-drag :visible.sync="propertyChooseModalFlag" width="820px">
  421. <div class="rq">
  422. <el-form :inline="true" label-position="top" :model="propertyChooseData">
  423. <el-form-item label="模板">
  424. <el-select v-model="propertyChooseData.codeNo" placeholder="请选择模板" style="width: 180px">
  425. <el-option
  426. v-for="item in propertyModelList"
  427. :key="item.codeNo"
  428. :label="item.codeDesc"
  429. :value="item.codeNo">
  430. </el-option>
  431. </el-select>
  432. </el-form-item>
  433. <el-form-item label="属性编码">
  434. <el-input v-model="propertyChooseData.itemNo" clearable style="width: 140px"></el-input>
  435. </el-form-item>
  436. <el-form-item label="属性名称">
  437. <el-input v-model="propertyChooseData.itemDesc" clearable style="width: 140px"></el-input>
  438. </el-form-item>
  439. <el-form-item :label="' '">
  440. <el-button type="primary" @click="searchPropertyItemList()">查询</el-button>
  441. </el-form-item>
  442. </el-form>
  443. <el-table
  444. :height="320"
  445. :data="propertyItemList"
  446. ref="propertyItemTable"
  447. @row-click="propertyItemClickRow"
  448. @selection-change="selectionPropertyItem"
  449. border
  450. style="width: 100%;">
  451. <el-table-column
  452. type="selection"
  453. header-align="center"
  454. align="center"
  455. width="50">
  456. </el-table-column>
  457. <el-table-column prop="itemNo" header-align="center" align="center" label="属性编码" width="180"></el-table-column>
  458. <el-table-column prop="itemDesc" header-align="center" align="left" label="属性名称" min-width="300"></el-table-column>
  459. <el-table-column prop="defaultValue" header-align="center" align="left" label="参照值" min-width="180"></el-table-column>
  460. </el-table>
  461. </div>
  462. <el-footer style="height:40px;margin-top: 20px;text-align:center">
  463. <el-button type="primary" @click="confirmPropertyItem">确认</el-button>
  464. <el-button type="primary" @click="propertyChooseModalFlag = false">关闭</el-button>
  465. </el-footer>
  466. </el-dialog>
  467. <el-dialog
  468. title="判断标准附件"
  469. :close-on-click-modal="false"
  470. :visible.sync="judgeCriteriaDialogVisible"
  471. width="900px"
  472. v-drag
  473. @close="handleJudgeCriteriaDialogClose">
  474. <oss-components
  475. ref="judgeCriteriaOss"
  476. :save-visible="authFileSave"
  477. :download-visible="authFileDownLoad"
  478. :remove-visible="authFileRemove"
  479. :preview-visible="authFilePreview"
  480. :disabled="showModalFlag || modalData.status === '已完成'"
  481. :row-click-select="true"
  482. label="序列号"
  483. :height="460"
  484. style="margin-top: 2px"
  485. :columns="judgeCriteriaFileColumnList"
  486. :order-ref1="modalData.site || ''"
  487. :order-ref2="modalData.referenceNo || ''"
  488. order-ref3="judgeCriteria">
  489. </oss-components>
  490. </el-dialog>
  491. <el-dialog
  492. title="测试规范附件"
  493. :close-on-click-modal="false"
  494. :visible.sync="testSpecDialogVisible"
  495. width="900px"
  496. v-drag
  497. @close="handleTestSpecDialogClose">
  498. <oss-components
  499. ref="testSpecOss"
  500. :save-visible="authFileSave"
  501. :download-visible="authFileDownLoad"
  502. :remove-visible="authFileRemove"
  503. :preview-visible="authFilePreview"
  504. :disabled="showModalFlag || modalData.status === '已完成'"
  505. :row-click-select="true"
  506. label="序列号"
  507. :height="460"
  508. style="margin-top: 2px"
  509. :columns="judgeCriteriaFileColumnList"
  510. :order-ref1="modalData.site || ''"
  511. :order-ref2="modalData.referenceNo || ''"
  512. order-ref3="testSpec">
  513. </oss-components>
  514. </el-dialog>
  515. <el-dialog
  516. title="测试报告附件"
  517. :close-on-click-modal="false"
  518. :visible.sync="testReportDialogVisible"
  519. width="900px"
  520. v-drag
  521. @close="handleTestReportDialogClose">
  522. <oss-components
  523. ref="testReportOss"
  524. :save-visible="authFileSave"
  525. :download-visible="authFileDownLoad"
  526. :remove-visible="authFileRemove"
  527. :preview-visible="authFilePreview"
  528. :disabled="showModalFlag || modalData.status === '已完成'"
  529. :row-click-select="true"
  530. label="序列号"
  531. :height="460"
  532. style="margin-top: 2px"
  533. :columns="judgeCriteriaFileColumnList"
  534. :order-ref1="modalData.site || ''"
  535. :order-ref2="modalData.referenceNo || ''"
  536. order-ref3="testReport">
  537. </oss-components>
  538. </el-dialog>
  539. <el-dialog title="人员选择" @open="openTesterDialog" @close="closeTesterDialog" v-drag
  540. :visible.sync="testerFlag" width="40vw" :close-on-click-modal="false">
  541. <el-form :inline="true" label-position="top" :model="testerSearchData">
  542. <el-form-item label="编码">
  543. <el-input v-model="testerSearchData.operatorId" clearable style="width: 120px"></el-input>
  544. </el-form-item>
  545. <el-form-item label="名称">
  546. <el-input v-model="testerSearchData.operatorName" clearable style="width: 120px"></el-input>
  547. </el-form-item>
  548. <el-form-item label=" ">
  549. <el-button type="primary" @click="filterTesterOptions">查询</el-button>
  550. </el-form-item>
  551. </el-form>
  552. <el-table v-if="testerFlag"
  553. :height="300"
  554. :data="testerDisplayList"
  555. @selection-change="handleTesterSelectionChange"
  556. @row-click="(row,column,event) => handleTesterTableClick(row,column,event,'testerTable')"
  557. ref="testerTable"
  558. border row-key="operatorId"
  559. style="width: 100%;">
  560. <el-table-column
  561. type="selection"
  562. header-align="center"
  563. align="center"
  564. :reserve-selection="true"
  565. width="50">
  566. </el-table-column>
  567. <el-table-column label="编码" property="operatorId" align="left" header-align="center"/>
  568. <el-table-column label="名称" property="operatorName" align="left" header-align="center"/>
  569. </el-table>
  570. <el-footer style="height:40px;margin-top: 20px;text-align:center">
  571. <el-button type="primary" @click="saveTesterChooseModal">确定</el-button>
  572. <el-button type="primary" @click="testerFlag = false">关闭</el-button>
  573. </el-footer>
  574. </el-dialog>
  575. <el-dialog title="实验室审批人选择" @open="openLabApproverDialog" @close="closeLabApproverDialog" v-drag
  576. :visible.sync="labApproverFlag" width="40vw" :close-on-click-modal="false">
  577. <el-form :inline="true" label-position="top" :model="labApproverSearchData">
  578. <el-form-item label="编码">
  579. <el-input v-model="labApproverSearchData.operatorId" clearable style="width: 120px"></el-input>
  580. </el-form-item>
  581. <el-form-item label="名称">
  582. <el-input v-model="labApproverSearchData.operatorName" clearable style="width: 120px"></el-input>
  583. </el-form-item>
  584. <el-form-item label=" ">
  585. <el-button type="primary" @click="filterLabApproverOptions">查询</el-button>
  586. </el-form-item>
  587. </el-form>
  588. <el-table v-if="labApproverFlag"
  589. :height="300"
  590. :data="labApproverDisplayList"
  591. @selection-change="handleLabApproverSelectionChange"
  592. @row-click="(row,column,event) => handleTesterTableClick(row,column,event,'labApproverTable')"
  593. ref="labApproverTable"
  594. border row-key="operatorId"
  595. style="width: 100%;">
  596. <el-table-column
  597. type="selection"
  598. header-align="center"
  599. align="center"
  600. :reserve-selection="true"
  601. width="50">
  602. </el-table-column>
  603. <el-table-column label="编码" property="operatorId" align="left" header-align="center"/>
  604. <el-table-column label="名称" property="operatorName" align="left" header-align="center"/>
  605. </el-table>
  606. <el-footer style="height:40px;margin-top: 20px;text-align:center">
  607. <el-button type="primary" @click="saveLabApproverChooseModal">确定</el-button>
  608. <el-button type="primary" @click="labApproverFlag = false">关闭</el-button>
  609. </el-footer>
  610. </el-dialog>
  611. <el-dialog title="选择-项目" :close-on-click-modal="false" @close="closeProjectInfoDialog"
  612. @open="searchProjectInfoList" :visible.sync="chooseProjectListFlag" width="35%">
  613. <el-form label-position="top" :model="searchProjectData" ref="closeProjectInfoForm">
  614. <el-row :gutter="10">
  615. <el-col :span="6">
  616. <el-form-item label="项目号">
  617. <el-input v-model="searchProjectData.projectId"></el-input>
  618. </el-form-item>
  619. </el-col>
  620. <el-col :span="6">
  621. <el-form-item label="项目名称">
  622. <el-input v-model="searchProjectData.projectName"></el-input>
  623. </el-form-item>
  624. </el-col>
  625. <el-col :span="6">
  626. <el-form-item label=" ">
  627. <el-button type="primary" @click="searchProjectInfoList">查询</el-button>
  628. </el-form-item>
  629. </el-col>
  630. </el-row>
  631. <el-table :height="223"
  632. :data="projectList"
  633. border
  634. @row-click="projectClickRow">
  635. <el-table-column label="项目号" prop="projectId"/>
  636. <el-table-column label="项目名称" prop="projectName"/>
  637. </el-table>
  638. </el-form>
  639. </el-dialog>
  640. <el-tabs v-model="activeTable" type="border-card" style="width: 100%; height: 100%;" @tab-click="tabClick" v-loading="currentDetailLoading" class="customer-tab">
  641. <el-tab-pane label="基本信息" name="basicInfo">
  642. <div :style="{height: secondHeight + 'px'}" style="margin-left: 5px;margin-top: 5px; overflow-y: auto;">
  643. <el-form :inline="true" label-position="top" :model="currentDetail">
  644. <el-form-item label="申请人">
  645. <el-input :value="currentDetail.applicant || ''" disabled style="width: 100px"></el-input>
  646. <el-input :value="currentDetail.applicantName || ''" disabled style="width: 155px"></el-input>
  647. </el-form-item>
  648. <el-form-item label="申请日期">
  649. <el-input :value="normalizeDateOnly(currentDetail.applicationDate)" disabled style="width: 110px"></el-input>
  650. </el-form-item>
  651. <el-form-item label="Site">
  652. <el-input :value="currentDetail.site || ''" disabled style="width: 135px"></el-input>
  653. </el-form-item>
  654. <el-form-item label="申请部门">
  655. <el-input :value="currentDetail.applyDepartment || ''" disabled style="width: 225px"></el-input>
  656. </el-form-item>
  657. </el-form>
  658. <el-form :inline="true" label-position="top" :model="currentDetail">
  659. <el-form-item label="客户">
  660. <el-input :value="currentDetail.customerId || ''" disabled style="width: 100px"></el-input>
  661. <el-input :value="currentDetail.customerName || ''" disabled style="width: 155px"></el-input>
  662. </el-form-item>
  663. <el-form-item label="项目">
  664. <el-input :value="currentDetail.projectId || ''" disabled style="width: 100px"></el-input>
  665. <el-input :value="currentDetail.projectName || ''" disabled style="width: 155px"></el-input>
  666. </el-form-item>
  667. <el-form-item label="需求日期">
  668. <el-input :value="normalizeDateOnly(currentDetail.requestedDate)" disabled style="width: 225px"></el-input>
  669. </el-form-item>
  670. </el-form>
  671. <el-form :inline="true" label-position="top" :model="currentDetail">
  672. <el-form-item label="样品名称">
  673. <el-input :value="currentDetail.sampleName || ''" disabled style="width: 260px"></el-input>
  674. </el-form-item>
  675. <el-form-item label="样品型号">
  676. <el-input :value="currentDetail.sampleModel || ''" disabled style="width: 259px"></el-input>
  677. </el-form-item>
  678. <el-form-item label="样品数量">
  679. <el-input :value="currentDetail.sampleQty || ''" disabled style="width: 225px"></el-input>
  680. </el-form-item>
  681. </el-form>
  682. <el-form :inline="true" label-position="top" :model="currentDetail">
  683. <el-form-item label="测试目的">
  684. <el-input :value="currentDetail.testPurpose || ''" disabled style="width: 260px"></el-input>
  685. </el-form-item>
  686. <el-form-item label="产品阶段">
  687. <el-input :value="currentDetail.productStage || ''" disabled style="width: 259px"></el-input>
  688. </el-form-item>
  689. <el-form-item label="测试类型">
  690. <el-input :value="currentDetail.testType || ''" disabled style="width: 225px"></el-input>
  691. </el-form-item>
  692. </el-form>
  693. <el-form :inline="true" label-position="top" :model="currentDetail">
  694. <el-form-item label="报告出具">
  695. <el-input :value="currentDetail.reportDocument || ''" disabled style="width: 260px"></el-input>
  696. </el-form-item>
  697. <el-form-item label="样品退回">
  698. <el-input :value="displayYesNo(currentDetail.returnSample)" disabled style="width: 259px"></el-input>
  699. </el-form-item>
  700. </el-form>
  701. <el-form :inline="true" label-position="top" :model="currentDetail">
  702. <el-form-item label="Application Surface">
  703. <el-input :value="currentDetail.applicationSurface || ''" disabled style="width: 533px"></el-input>
  704. </el-form-item>
  705. <el-form-item label="Test Lab">
  706. <el-input :value="currentDetail.testLab || ''" disabled style="width: 225px"></el-input>
  707. </el-form-item>
  708. </el-form>
  709. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 0px">
  710. <el-form-item label="实验室审批人" style="display: block;">
  711. <el-input :value="currentDetail.labApproverName || currentDetail.labApprover || ''" disabled style="width: 772px"></el-input>
  712. </el-form-item>
  713. </el-form>
  714. <div class="rq" style="width: 772px;margin-top: 0px">
  715. <el-table
  716. :data="currentDetail.propertyItemList || []"
  717. border
  718. :height="260"
  719. style="width: 100%;">
  720. <el-table-column prop="itemNo" header-align="center" align="center" label="属性编码" width="100"></el-table-column>
  721. <el-table-column prop="itemDesc" header-align="center" align="left" label="属性名称" min-width="150"></el-table-column>
  722. <el-table-column prop="itemValue" header-align="center" align="left" label="属性值" min-width="250" show-overflow-tooltip></el-table-column>
  723. </el-table>
  724. </div>
  725. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 5px">
  726. <el-form-item label="测试规范附件" style="display: block;">
  727. <div class="judge-criteria-box" style="cursor: default;">
  728. <template v-if="currentTestSpecFileList.length > 0">
  729. <el-link
  730. v-for="item in currentTestSpecFileList"
  731. :key="item.id"
  732. type="primary"
  733. style="margin-right: 10px"
  734. @click.stop="previewJudgeCriteriaFile(item)">
  735. {{ item.fileName }}
  736. </el-link>
  737. </template>
  738. <span v-else class="judge-criteria-placeholder">暂无测试规范附件</span>
  739. </div>
  740. </el-form-item>
  741. </el-form>
  742. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 18px">
  743. <el-form-item label="判断标准附件" style="display: block;">
  744. <div class="judge-criteria-box" style="cursor: default;">
  745. <template v-if="currentJudgeCriteriaFileList.length > 0">
  746. <el-link
  747. v-for="item in currentJudgeCriteriaFileList"
  748. :key="item.id"
  749. type="primary"
  750. style="margin-right: 10px"
  751. @click.stop="previewJudgeCriteriaFile(item)">
  752. {{ item.fileName }}
  753. </el-link>
  754. </template>
  755. <span v-else class="judge-criteria-placeholder">暂无判断标准附件</span>
  756. </div>
  757. </el-form-item>
  758. </el-form>
  759. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 20px">
  760. <el-form-item label="注意事项" style="display: block;">
  761. <el-input type="textarea" :rows="3" :value="currentDetail.attention || ''" disabled resize="none" style="width: 772px"></el-input>
  762. </el-form-item>
  763. </el-form>
  764. </div>
  765. </el-tab-pane>
  766. <el-tab-pane label="审批信息" name="approvalInfoDetail">
  767. <div :style="{height: secondHeight + 'px'}" style="margin-left: 5px;margin-top: 5px; overflow-y: auto;">
  768. <el-form :inline="true" label-position="top" :model="currentDetail">
  769. <el-form-item label="备注(部门经理)" style="display: block;">
  770. <el-input type="textarea" :rows="3" :value="currentDetail.departmentManagerComments || ''" disabled resize="none" style="width: 772px"></el-input>
  771. </el-form-item>
  772. </el-form>
  773. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 45px">
  774. <el-form-item label="备注(实验室)" style="display: block;">
  775. <el-input type="textarea" :rows="3" :value="currentDetail.labComments || ''" disabled resize="none" style="width: 772px"></el-input>
  776. </el-form-item>
  777. </el-form>
  778. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 45px">
  779. <el-form-item label="测试人">
  780. <el-input :value="currentDetail.testerName || currentDetail.tester || ''" disabled style="width: 772px"></el-input>
  781. </el-form-item>
  782. </el-form>
  783. </div>
  784. </el-tab-pane>
  785. <el-tab-pane label="测试结果" name="resultInfo">
  786. <div :style="{height: secondHeight + 'px'}" style="margin-left: 5px;margin-top: 5px; overflow-y: auto;">
  787. <el-form :inline="true" label-position="top" :model="currentDetail">
  788. <el-form-item label="测试开始时间">
  789. <el-input :value="normalizeDateOnly(currentDetail.testStartDate)" disabled style="width: 170px"></el-input>
  790. </el-form-item>
  791. <el-form-item label="测试完成日期">
  792. <el-input :value="normalizeDateOnly(currentDetail.completedDate)" disabled style="width: 170px"></el-input>
  793. </el-form-item>
  794. <el-form-item label="测试编码">
  795. <el-input :value="currentDetail.testNumber || ''" disabled style="width: 250px"></el-input>
  796. </el-form-item>
  797. </el-form>
  798. <el-form :inline="true" label-position="top" :model="currentDetail">
  799. <el-form-item label="异常情况及说明" style="display: block;">
  800. <el-input type="textarea" :rows="3" :value="currentDetail.unnormalSymptomAndDescription || ''" disabled resize="none" style="width: 772px"></el-input>
  801. </el-form-item>
  802. </el-form>
  803. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 45px">
  804. <el-form-item label="测试报告附件" style="display: block;">
  805. <div class="judge-criteria-box" style="cursor: default;">
  806. <template v-if="currentTestReportFileList.length > 0">
  807. <el-link
  808. v-for="item in currentTestReportFileList"
  809. :key="item.id"
  810. type="primary"
  811. style="margin-right: 10px"
  812. @click.stop="previewTestReportFile(item)">
  813. {{ item.fileName }}
  814. </el-link>
  815. </template>
  816. <span v-else class="judge-criteria-placeholder">暂无测试报告附件</span>
  817. </div>
  818. </el-form-item>
  819. </el-form>
  820. <el-form :inline="true" label-position="top" :model="currentDetail" style="margin-top: 20px">
  821. <el-form-item label="备注" style="display: block;">
  822. <el-input type="textarea" :rows="3" :value="currentDetail.testerComments || ''" disabled resize="none" style="width: 772px"></el-input>
  823. </el-form-item>
  824. </el-form>
  825. </div>
  826. </el-tab-pane>
  827. <el-tab-pane label="审批记录" name="approvalInformation">
  828. <approval-information v-model:data-list="approvalList" :height="secondHeight"></approval-information>
  829. </el-tab-pane>
  830. </el-tabs>
  831. <ChooseList ref="baseList" @getBaseData="getBaseData"></ChooseList>
  832. </div>
  833. </template>
  834. <script>
  835. import * as api from '@/api/lab/lab'
  836. import { getSiteList, searchUserListForRole } from '@/api/base/site'
  837. import { queryOss, previewOssFileById } from '@/api/oss/oss'
  838. import { checkSuperAdmin } from '@/api/changeManagement/changeManagement'
  839. import { verifyData } from '@/api/chooselist/chooselist.js'
  840. import { searchProjectInfoList } from '@/api/quotation/quotationInformation.js'
  841. import { queryCustomer } from '@/api/customer/customerInformation'
  842. import { queryProjectByCustomer } from '@/api/project/project'
  843. import ChooseList from '@/views/modules/common/Chooselist'
  844. import DictDataSelect from '../sys/dict-data-select.vue'
  845. import ApprovalInformation from '../changeManagement/approvalInformation.vue'
  846. import ossComponents from '../oss/ossComponents.vue'
  847. export default {
  848. components: {
  849. ChooseList,
  850. DictDataSelect,
  851. ApprovalInformation,
  852. ossComponents
  853. },
  854. data () {
  855. return {
  856. searchData: {
  857. site: '',
  858. referenceNo: '',
  859. status: '',
  860. currentApprover: '',
  861. nodeId: ''
  862. },
  863. exportLoading: false,
  864. exportData: [],
  865. exportFieldMap: {},
  866. exportName: '',
  867. exportHeader: ['Lab记录'],
  868. exportFooter: [],
  869. resultList: [],
  870. dataListSelections: [],
  871. dataList: [],
  872. pageIndex: 1,
  873. pageSize: 20,
  874. totalPage: 0,
  875. dataListLoading: false,
  876. activeTable: 'basicInfo',
  877. menuId: '',
  878. authSearch: false,
  879. authUpdate: false,
  880. authIssue: false,
  881. authSubmit: false,
  882. authReject: false,
  883. authDelete: false,
  884. authFileSave: false,
  885. authFileDownLoad: false,
  886. authFileRemove: false,
  887. authFilePreview: false,
  888. authDataEntry: false,
  889. superAdmin: false,
  890. createBy2: this.$store.state.user.name,
  891. height: 400,
  892. secondHeight: 200,
  893. currentRow: {},
  894. currentDetail: this.buildEmptyDetailData(),
  895. currentDetailLoading: false,
  896. currentDetailRequestId: 0,
  897. nodeOptions: [],
  898. approvalList: [],
  899. plmLabAuthorityArr: [],
  900. nodeAuthorityLoaded: false,
  901. isEditMode: false,
  902. modalTitle: 'Lab 新增',
  903. activeName: 'basicInfo',
  904. showModalFlag: false,
  905. submitModalFlag: false,
  906. rejectOpinion: '',
  907. submitLoading: false,
  908. tagNo: null,
  909. tagNo1: null,
  910. modalFlag: false,
  911. judgeCriteriaDialogVisible: false,
  912. testSpecDialogVisible: false,
  913. testReportDialogVisible: false,
  914. judgeCriteriaFileList: [],
  915. testSpecFileList: [],
  916. currentJudgeCriteriaFileList: [],
  917. currentTestSpecFileList: [],
  918. currentTestReportFileList: [],
  919. testReportFileList: [],
  920. judgeCriteriaFileColumnList: [
  921. {
  922. columnProp: 'fileName',
  923. headerAlign: 'center',
  924. align: 'center',
  925. columnLabel: '文件名称',
  926. columnHidden: false,
  927. columnImage: false,
  928. columnSortable: false,
  929. status: true,
  930. fixed: '',
  931. columnWidth: 180
  932. },
  933. {
  934. columnProp: 'fileRemark',
  935. headerAlign: 'center',
  936. align: 'center',
  937. columnLabel: '备注',
  938. columnHidden: false,
  939. columnImage: false,
  940. columnSortable: false,
  941. status: true,
  942. fixed: '',
  943. columnWidth: 220
  944. },
  945. {
  946. columnProp: 'createDate',
  947. headerAlign: 'center',
  948. align: 'center',
  949. columnLabel: '上传时间',
  950. columnHidden: false,
  951. columnImage: false,
  952. columnSortable: false,
  953. status: true,
  954. fixed: '',
  955. columnWidth: 170
  956. },
  957. {
  958. columnProp: 'createBy',
  959. headerAlign: 'center',
  960. align: 'center',
  961. columnLabel: '上传人',
  962. columnHidden: false,
  963. columnImage: false,
  964. columnSortable: false,
  965. status: true,
  966. fixed: '',
  967. columnWidth: 140
  968. }
  969. ],
  970. siteOptions: [],
  971. chooseProjectListFlag: false,
  972. searchProjectData: {
  973. site: '',
  974. projectId: undefined,
  975. projectName: undefined,
  976. customerId: undefined
  977. },
  978. projectList: [],
  979. modalData: this.buildDefaultModalData(),
  980. propertyChooseModalFlag: false,
  981. propertyModelList: [],
  982. propertyItemList: [],
  983. propertyItemSelections: [],
  984. propertyChooseData: this.buildDefaultPropertyChooseData(),
  985. testerOptionList: [],
  986. testerDisplayList: [],
  987. testerSelectionCache: [],
  988. testerFlag: false,
  989. testerSearchData: {
  990. operatorId: '',
  991. operatorName: ''
  992. },
  993. labApproverOptionList: [],
  994. labApproverDisplayList: [],
  995. labApproverSelectionCache: [],
  996. labApproverFlag: false,
  997. labApproverSearchData: {
  998. operatorId: '',
  999. operatorName: ''
  1000. }
  1001. }
  1002. },
  1003. created () {
  1004. this.menuId = this.$route.meta.menuId
  1005. this.getButtonAuthData()
  1006. this.checkSuperAdmin()
  1007. this.loadSiteOptions()
  1008. },
  1009. mounted () {
  1010. this.fetchNodeOptions()
  1011. if (this.authSearch) {
  1012. this.getDataList()
  1013. }
  1014. this.$nextTick(() => {
  1015. this.height = window.innerHeight / 2 - 30
  1016. this.secondHeight = window.innerHeight / 2 - 186
  1017. })
  1018. },
  1019. methods: {
  1020. buildDefaultPropertyChooseData () {
  1021. const currentSite = this.modalData && this.modalData.site ? this.modalData.site : this.$store.state.user.site
  1022. return {
  1023. site: currentSite,
  1024. functionType: 'LAB',
  1025. codeNo: '',
  1026. itemNo: '',
  1027. itemDesc: ''
  1028. }
  1029. },
  1030. getDefaultSiteValue () {
  1031. const loginSite = this.$store.state.user.site || ''
  1032. if (this.siteOptions.some(item => item.siteID === loginSite)) {
  1033. return loginSite
  1034. }
  1035. if (this.siteOptions.length > 0) {
  1036. return this.siteOptions[0].siteID || loginSite
  1037. }
  1038. return loginSite
  1039. },
  1040. loadSiteOptions () {
  1041. getSiteList().then(({data}) => {
  1042. if (data && data.code === 0 && Array.isArray(data.rows)) {
  1043. this.siteOptions = data.rows
  1044. } else {
  1045. this.siteOptions = []
  1046. }
  1047. }).catch(() => {
  1048. this.siteOptions = []
  1049. }).finally(() => {
  1050. if (this.siteOptions.length === 0 && this.$store.state.user.site) {
  1051. this.siteOptions = [{
  1052. siteID: this.$store.state.user.site,
  1053. siteName: this.$store.state.user.site
  1054. }]
  1055. }
  1056. })
  1057. },
  1058. buildDefaultModalData (useLoginSite = true) {
  1059. return {
  1060. site: useLoginSite ? this.$store.state.user.site : '',
  1061. referenceNo: '',
  1062. applicant: '',
  1063. applicantName: '',
  1064. applicationDate: '',
  1065. applyDepartment: '',
  1066. requestedDate: '',
  1067. customerId: '',
  1068. customerName: '',
  1069. customerIdFlag: false,
  1070. projectId: '',
  1071. projectName: '',
  1072. projectIdFlag: true,
  1073. sampleName: '',
  1074. sampleModel: '',
  1075. sampleQty: '',
  1076. testPurpose: '',
  1077. productStage: '',
  1078. testType: '',
  1079. reportDocument: '',
  1080. returnSample: '',
  1081. applicationSurface: '',
  1082. testLab: '',
  1083. labApprover: '',
  1084. labApproverName: '',
  1085. labApproverCodeList: [],
  1086. labApproverList: [],
  1087. attention: '',
  1088. testStartDate: '',
  1089. completedDate: '',
  1090. unnormalSymptomAndDescription: '',
  1091. testNumber: '',
  1092. testerComments: '',
  1093. departmentManagerComments: '',
  1094. labComments: '',
  1095. tester: '',
  1096. testerName: '',
  1097. testerCodeList: [],
  1098. testerList: [],
  1099. status: '草稿',
  1100. stepId: 10,
  1101. rejectFlag: 'N',
  1102. rejectStepId: null,
  1103. createBy2: '',
  1104. isReject: 'Y',
  1105. tpProcessControl: 'N',
  1106. csProcessControl: 'N',
  1107. propertyItemList: []
  1108. }
  1109. },
  1110. buildEmptyDetailData () {
  1111. return this.buildDefaultModalData(false)
  1112. },
  1113. fetchNodeOptions () {
  1114. if (!this.menuId) {
  1115. this.nodeOptions = []
  1116. return
  1117. }
  1118. const siteForNode = this.searchData.site || this.$store.state.user.site
  1119. const params = {
  1120. site: siteForNode,
  1121. menuId: this.menuId
  1122. }
  1123. api.getNodeList(params).then(({data}) => {
  1124. if (data && data.code === 0 && Array.isArray(data.rows)) {
  1125. this.nodeOptions = data.rows
  1126. .filter(item => item && item.nodeId)
  1127. .map(item => ({
  1128. id: item.nodeId,
  1129. name: item.nodeName
  1130. }))
  1131. } else {
  1132. this.nodeOptions = []
  1133. }
  1134. }).catch(() => {
  1135. this.nodeOptions = []
  1136. })
  1137. },
  1138. handleModalSiteChange (site) {
  1139. if (!site || this.isEditMode) {
  1140. return
  1141. }
  1142. this.testSpecDialogVisible = false
  1143. this.testReportDialogVisible = false
  1144. this.testerFlag = false
  1145. this.testerOptionList = []
  1146. this.testerDisplayList = []
  1147. this.testerSelectionCache = []
  1148. this.testerSearchData = {
  1149. operatorId: '',
  1150. operatorName: ''
  1151. }
  1152. this.resetLabApproverOptionState()
  1153. this.modalData.customerId = ''
  1154. this.modalData.customerName = ''
  1155. this.modalData.projectId = ''
  1156. this.modalData.projectName = ''
  1157. this.modalData.projectIdFlag = true
  1158. this.$set(this.modalData, 'testerCodeList', [])
  1159. this.$set(this.modalData, 'testerList', [])
  1160. this.$set(this.modalData, 'tester', '')
  1161. this.$set(this.modalData, 'testerName', '')
  1162. this.clearLabApproverFieldData()
  1163. this.$set(this.modalData, 'propertyItemList', [])
  1164. this.judgeCriteriaFileList = []
  1165. this.testSpecFileList = []
  1166. this.testReportFileList = []
  1167. this.searchProjectData.site = site
  1168. },
  1169. resetLabApproverOptionState () {
  1170. this.labApproverFlag = false
  1171. this.labApproverOptionList = []
  1172. this.labApproverDisplayList = []
  1173. this.labApproverSelectionCache = []
  1174. this.labApproverSearchData = {
  1175. operatorId: '',
  1176. operatorName: ''
  1177. }
  1178. },
  1179. clearLabApproverFieldData () {
  1180. this.$set(this.modalData, 'labApproverCodeList', [])
  1181. this.$set(this.modalData, 'labApproverList', [])
  1182. this.$set(this.modalData, 'labApprover', '')
  1183. this.$set(this.modalData, 'labApproverName', '')
  1184. },
  1185. handleTestLabChange (value) {
  1186. if (!this.modalData || this.showModalFlag || this.isLabFieldDisabled('testLab')) {
  1187. return
  1188. }
  1189. this.$set(this.modalData, 'testLab', value || '')
  1190. this.resetLabApproverOptionState()
  1191. this.clearLabApproverFieldData()
  1192. },
  1193. loadNodeAuthority (site, stepId) {
  1194. if (!site || stepId === null || stepId === undefined || !this.menuId) {
  1195. this.plmLabAuthorityArr = []
  1196. this.nodeAuthorityLoaded = true
  1197. return Promise.resolve()
  1198. }
  1199. this.nodeAuthorityLoaded = false
  1200. const params = {
  1201. site: site,
  1202. stepId: stepId,
  1203. menuId: this.menuId
  1204. }
  1205. return api.getLabNodeAuthority(params).then(({data}) => {
  1206. if (data && data.code === 0 && data.rows) {
  1207. this.plmLabAuthorityArr = data.rows.plm_lab || []
  1208. } else {
  1209. this.plmLabAuthorityArr = []
  1210. }
  1211. this.nodeAuthorityLoaded = true
  1212. }).catch(() => {
  1213. this.plmLabAuthorityArr = []
  1214. this.nodeAuthorityLoaded = true
  1215. })
  1216. },
  1217. labFieldFlag (fieldId) {
  1218. if (!this.plmLabAuthorityArr || this.plmLabAuthorityArr.length === 0) {
  1219. return 'N'
  1220. }
  1221. const target = this.plmLabAuthorityArr.find(item => item.fieldId === fieldId)
  1222. return target ? target.updateFlag : 'N'
  1223. },
  1224. isLabFieldDisabled (fieldId) {
  1225. if (this.showModalFlag) {
  1226. return true
  1227. }
  1228. if (!this.isEditMode) {
  1229. return false
  1230. }
  1231. return this.labFieldFlag(fieldId) === 'N'
  1232. },
  1233. normalizeDateOnly (value) {
  1234. if (value === null || value === undefined || value === '') {
  1235. return ''
  1236. }
  1237. if (value instanceof Date && !Number.isNaN(value.getTime())) {
  1238. const year = value.getFullYear()
  1239. const month = String(value.getMonth() + 1).padStart(2, '0')
  1240. const day = String(value.getDate()).padStart(2, '0')
  1241. return year + '-' + month + '-' + day
  1242. }
  1243. const text = String(value).trim()
  1244. if (!text) {
  1245. return ''
  1246. }
  1247. if (text.includes('T')) {
  1248. return text.split('T')[0]
  1249. }
  1250. return text.length >= 10 ? text.substring(0, 10) : text
  1251. },
  1252. getCurrentDateString () {
  1253. return this.normalizeDateOnly(new Date())
  1254. },
  1255. displayYesNo (value) {
  1256. if (value === 'Y') {
  1257. return '是'
  1258. }
  1259. if (value === 'N') {
  1260. return '否'
  1261. }
  1262. return value || ''
  1263. },
  1264. resolvePreviewContentType (fileType) {
  1265. const lower = String(fileType || '').toLowerCase()
  1266. if (!lower) {
  1267. return ''
  1268. }
  1269. if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(lower)) {
  1270. return 'image/' + lower
  1271. }
  1272. if (['mp4', 'avi', 'mov', 'wmv', 'flv'].includes(lower)) {
  1273. return 'video/' + lower
  1274. }
  1275. if (lower === 'txt') {
  1276. return 'text/plain;charset=utf-8'
  1277. }
  1278. if (lower === 'pdf') {
  1279. return 'application/pdf;charset=UTF-8'
  1280. }
  1281. if (['xls', 'xlsx', 'docx'].includes(lower)) {
  1282. return lower
  1283. }
  1284. return ''
  1285. },
  1286. refreshJudgeCriteriaOssTable () {
  1287. const ref = this.$refs.judgeCriteriaOss
  1288. if (ref && typeof ref.handleQuery === 'function') {
  1289. ref.handleQuery()
  1290. }
  1291. },
  1292. refreshTestSpecOssTable () {
  1293. const ref = this.$refs.testSpecOss
  1294. if (ref && typeof ref.handleQuery === 'function') {
  1295. ref.handleQuery()
  1296. }
  1297. },
  1298. refreshTestReportOssTable () {
  1299. const ref = this.$refs.testReportOss
  1300. if (ref && typeof ref.handleQuery === 'function') {
  1301. ref.handleQuery()
  1302. }
  1303. },
  1304. queryAttachmentFileList (site, referenceNo, orderRef3) {
  1305. if (!site || !referenceNo || !orderRef3) {
  1306. return Promise.resolve([])
  1307. }
  1308. const params = {
  1309. orderRef1: site,
  1310. orderRef2: referenceNo,
  1311. orderRef3: orderRef3
  1312. }
  1313. return queryOss(params).then(({data}) => {
  1314. if (data && data.code === 0 && Array.isArray(data.rows)) {
  1315. return data.rows
  1316. }
  1317. return []
  1318. }).catch(() => {
  1319. return []
  1320. })
  1321. },
  1322. refreshJudgeCriteriaFileList () {
  1323. return this.queryAttachmentFileList(this.modalData.site, this.modalData.referenceNo, 'judgeCriteria').then(rows => {
  1324. this.judgeCriteriaFileList = rows
  1325. })
  1326. },
  1327. refreshTestSpecFileList () {
  1328. return this.queryAttachmentFileList(this.modalData.site, this.modalData.referenceNo, 'testSpec').then(rows => {
  1329. this.testSpecFileList = rows
  1330. })
  1331. },
  1332. refreshTestReportFileList () {
  1333. return this.queryAttachmentFileList(this.modalData.site, this.modalData.referenceNo, 'testReport').then(rows => {
  1334. this.testReportFileList = rows
  1335. })
  1336. },
  1337. loadCurrentAttachmentFileLists (site, referenceNo, requestId) {
  1338. if (!site || !referenceNo) {
  1339. if (requestId === this.currentDetailRequestId) {
  1340. this.currentJudgeCriteriaFileList = []
  1341. this.currentTestSpecFileList = []
  1342. this.currentTestReportFileList = []
  1343. }
  1344. return Promise.resolve()
  1345. }
  1346. return Promise.all([
  1347. this.queryAttachmentFileList(site, referenceNo, 'judgeCriteria'),
  1348. this.queryAttachmentFileList(site, referenceNo, 'testSpec'),
  1349. this.queryAttachmentFileList(site, referenceNo, 'testReport')
  1350. ]).then(([judgeFiles, testSpecFiles, testReportFiles]) => {
  1351. if (requestId !== this.currentDetailRequestId) {
  1352. return
  1353. }
  1354. this.currentJudgeCriteriaFileList = judgeFiles
  1355. this.currentTestSpecFileList = testSpecFiles
  1356. this.currentTestReportFileList = testReportFiles
  1357. })
  1358. },
  1359. openJudgeCriteriaDialog () {
  1360. if (!this.modalData.site) {
  1361. this.$message.warning('请先选择申请工厂')
  1362. return
  1363. }
  1364. if (!this.modalData.referenceNo) {
  1365. this.$message.warning('单据编号为空,无法上传附件')
  1366. return
  1367. }
  1368. this.judgeCriteriaDialogVisible = true
  1369. this.refreshJudgeCriteriaFileList()
  1370. this.$nextTick(() => {
  1371. this.refreshJudgeCriteriaOssTable()
  1372. })
  1373. },
  1374. openTestSpecDialog () {
  1375. if (!this.modalData.site) {
  1376. this.$message.warning('请先选择申请工厂')
  1377. return
  1378. }
  1379. if (!this.modalData.referenceNo) {
  1380. this.$message.warning('单据编号为空,无法上传附件')
  1381. return
  1382. }
  1383. this.testSpecDialogVisible = true
  1384. this.refreshTestSpecFileList()
  1385. this.$nextTick(() => {
  1386. this.refreshTestSpecOssTable()
  1387. })
  1388. },
  1389. openTestReportDialog () {
  1390. if (!this.modalData.site) {
  1391. this.$message.warning('请先选择申请工厂')
  1392. return
  1393. }
  1394. if (!this.modalData.referenceNo) {
  1395. this.$message.warning('单据编号为空,无法上传附件')
  1396. return
  1397. }
  1398. this.testReportDialogVisible = true
  1399. this.refreshTestReportFileList()
  1400. this.$nextTick(() => {
  1401. this.refreshTestReportOssTable()
  1402. })
  1403. },
  1404. handleJudgeCriteriaDialogClose () {
  1405. this.refreshJudgeCriteriaFileList()
  1406. },
  1407. handleTestSpecDialogClose () {
  1408. this.refreshTestSpecFileList()
  1409. },
  1410. handleTestReportDialogClose () {
  1411. this.refreshTestReportFileList()
  1412. },
  1413. previewJudgeCriteriaFile (row) {
  1414. if (!this.authFilePreview) {
  1415. this.$message.warning('没有附件预览权限')
  1416. return
  1417. }
  1418. if (!row || !row.id) {
  1419. this.$message.warning('附件不存在,无法预览')
  1420. return
  1421. }
  1422. const type = this.resolvePreviewContentType(row.fileType)
  1423. if (!type) {
  1424. this.$message.warning('该文件格式暂不支持预览')
  1425. return
  1426. }
  1427. previewOssFileById({ id: row.id }).then(({data}) => {
  1428. const blob = new Blob([data], { type: type })
  1429. const fileURL = URL.createObjectURL(blob)
  1430. if (type === 'xls' || type === 'xlsx' || type === 'docx') {
  1431. const { href } = this.$router.resolve({
  1432. name: 'pre',
  1433. query: {
  1434. src: fileURL,
  1435. type: type
  1436. }
  1437. })
  1438. window.open(href, '_blank')
  1439. } else {
  1440. window.open(fileURL, '_blank')
  1441. }
  1442. }).catch(() => {
  1443. this.$message.error('文件预览失败')
  1444. })
  1445. },
  1446. previewTestReportFile (row) {
  1447. this.previewJudgeCriteriaFile(row)
  1448. },
  1449. resolvePropertyDefaultValue (item) {
  1450. if (item && item.defaultValue !== null && item.defaultValue !== undefined && String(item.defaultValue).trim() !== '') {
  1451. return String(item.defaultValue).trim()
  1452. }
  1453. if (item && item.valueChooseFlag === 'Y' && Array.isArray(item.availableValueList) && item.availableValueList.length > 0) {
  1454. const firstValue = item.availableValueList[0].availableValue
  1455. return firstValue === null || firstValue === undefined ? '' : String(firstValue)
  1456. }
  1457. return ''
  1458. },
  1459. normalizePropertyItem (item) {
  1460. const raw = item || {}
  1461. const availableValueList = Array.isArray(raw.availableValueList) ? raw.availableValueList.map(option => ({
  1462. itemNo: raw.itemNo || '',
  1463. availableValue: option && option.availableValue !== undefined && option.availableValue !== null ? String(option.availableValue) : ''
  1464. })).filter(option => option.availableValue !== '') : []
  1465. const defaultValue = raw.defaultValue === null || raw.defaultValue === undefined ? '' : String(raw.defaultValue)
  1466. if (defaultValue && !availableValueList.some(option => option.availableValue === defaultValue)) {
  1467. availableValueList.unshift({
  1468. itemNo: raw.itemNo || '',
  1469. availableValue: defaultValue
  1470. })
  1471. }
  1472. const itemValueSource = raw.itemValue !== undefined && raw.itemValue !== null ? raw.itemValue : raw.chooseValue
  1473. const itemValue = itemValueSource === undefined || itemValueSource === null || String(itemValueSource).trim() === ''
  1474. ? this.resolvePropertyDefaultValue({ ...raw, defaultValue, availableValueList })
  1475. : String(itemValueSource)
  1476. return {
  1477. site: raw.site || this.modalData.site || this.$store.state.user.site,
  1478. referenceNo: raw.referenceNo || this.modalData.referenceNo || '',
  1479. seqNo: raw.seqNo || 0,
  1480. itemNo: raw.itemNo || '',
  1481. itemDesc: raw.itemDesc || '',
  1482. itemValue: itemValue,
  1483. valueChooseFlag: raw.valueChooseFlag || 'N',
  1484. defaultValue: defaultValue,
  1485. availableValueList: availableValueList
  1486. }
  1487. },
  1488. loadPropertyModelList () {
  1489. const params = {
  1490. site: this.modalData.site,
  1491. functionType: 'LAB',
  1492. codeNo: '',
  1493. codeDesc: ''
  1494. }
  1495. return api.getLabPropertyModelList(params).then(({data}) => {
  1496. if (data && data.code === 0) {
  1497. this.propertyModelList = data.data || []
  1498. } else {
  1499. this.propertyModelList = []
  1500. this.$message.warning((data && data.msg) || '查询Lab属性模板失败')
  1501. }
  1502. }).catch(() => {
  1503. this.propertyModelList = []
  1504. this.$message.error('查询Lab属性模板失败')
  1505. })
  1506. },
  1507. restorePropertySelection () {
  1508. if (!this.$refs.propertyItemTable) {
  1509. return
  1510. }
  1511. this.$refs.propertyItemTable.clearSelection()
  1512. const selectedNos = new Set((this.modalData.propertyItemList || []).map(item => item.itemNo))
  1513. this.propertyItemList.forEach(item => {
  1514. if (selectedNos.has(item.itemNo)) {
  1515. this.$refs.propertyItemTable.toggleRowSelection(item, true)
  1516. }
  1517. })
  1518. },
  1519. openPropertyChooseModal () {
  1520. if (!this.modalData.site) {
  1521. this.$message.warning('请先选择申请工厂')
  1522. return
  1523. }
  1524. this.propertyChooseData = this.buildDefaultPropertyChooseData()
  1525. this.propertyChooseData.site = this.modalData.site
  1526. this.propertyItemList = []
  1527. this.propertyItemSelections = []
  1528. this.loadPropertyModelList().then(() => {
  1529. if (this.propertyModelList.length === 0) {
  1530. this.$message.warning('请先在属性模板管理维护LAB模板')
  1531. return
  1532. }
  1533. this.propertyChooseData.codeNo = this.propertyModelList[0].codeNo
  1534. this.propertyChooseModalFlag = true
  1535. this.$nextTick(() => {
  1536. this.searchPropertyItemList(true)
  1537. })
  1538. })
  1539. },
  1540. searchPropertyItemList (restoreSelection = false) {
  1541. if (!this.propertyChooseData.codeNo) {
  1542. this.propertyItemList = []
  1543. if (restoreSelection) {
  1544. this.$nextTick(() => this.restorePropertySelection())
  1545. }
  1546. this.$message.warning('请选择模板')
  1547. return
  1548. }
  1549. const params = {
  1550. site: this.modalData.site,
  1551. functionType: 'LAB',
  1552. codeNo: this.propertyChooseData.codeNo,
  1553. itemNo: this.propertyChooseData.itemNo,
  1554. itemDesc: this.propertyChooseData.itemDesc
  1555. }
  1556. api.getLabPropertyItemList(params).then(({data}) => {
  1557. if (data && data.code === 0) {
  1558. this.propertyItemList = (data.rows || []).map(item => this.normalizePropertyItem(item))
  1559. this.$nextTick(() => {
  1560. this.restorePropertySelection()
  1561. })
  1562. } else {
  1563. this.propertyItemList = []
  1564. this.$message.warning((data && data.msg) || '查询属性清单失败')
  1565. }
  1566. }).catch(() => {
  1567. this.propertyItemList = []
  1568. this.$message.error('查询属性清单失败')
  1569. })
  1570. },
  1571. propertyItemClickRow (row) {
  1572. if (!this.$refs.propertyItemTable) {
  1573. return
  1574. }
  1575. this.$refs.propertyItemTable.toggleRowSelection(row)
  1576. },
  1577. selectionPropertyItem (val) {
  1578. this.propertyItemSelections = val || []
  1579. },
  1580. confirmPropertyItem () {
  1581. if (!this.propertyItemSelections || this.propertyItemSelections.length === 0) {
  1582. this.$message.warning('请勾选属性')
  1583. return
  1584. }
  1585. const currentList = Array.isArray(this.modalData.propertyItemList) ? this.modalData.propertyItemList : []
  1586. const selectedNoSet = new Set(this.propertyItemSelections.map(item => item.itemNo))
  1587. const mergedList = []
  1588. currentList.forEach(item => {
  1589. if (selectedNoSet.has(item.itemNo)) {
  1590. mergedList.push(this.normalizePropertyItem(item))
  1591. }
  1592. })
  1593. this.propertyItemSelections.forEach(selected => {
  1594. if (!mergedList.some(item => item.itemNo === selected.itemNo)) {
  1595. const normalized = this.normalizePropertyItem(selected)
  1596. normalized.itemValue = this.resolvePropertyDefaultValue(normalized)
  1597. mergedList.push(normalized)
  1598. }
  1599. })
  1600. this.$set(this.modalData, 'propertyItemList', mergedList.map((item, index) => ({
  1601. ...this.normalizePropertyItem(item),
  1602. seqNo: index + 1
  1603. })))
  1604. this.propertyChooseModalFlag = false
  1605. },
  1606. syncPropertyItemList () {
  1607. const list = Array.isArray(this.modalData.propertyItemList) ? this.modalData.propertyItemList : []
  1608. this.$set(this.modalData, 'propertyItemList', list.map((item, index) => ({
  1609. ...this.normalizePropertyItem(item),
  1610. site: this.modalData.site,
  1611. referenceNo: this.modalData.referenceNo,
  1612. seqNo: index + 1
  1613. })))
  1614. },
  1615. getLabSubmitFieldValue (fieldId) {
  1616. if (fieldId === 'tester') {
  1617. return this.modalData.testerCodeList
  1618. }
  1619. if (fieldId === 'labApprover') {
  1620. return this.modalData.labApproverCodeList
  1621. }
  1622. return this.modalData[fieldId]
  1623. },
  1624. isSubmitFieldEmpty (value) {
  1625. if (Array.isArray(value)) {
  1626. return value.length === 0
  1627. }
  1628. if (typeof value === 'string') {
  1629. return value.trim() === ''
  1630. }
  1631. return value === null || value === undefined || value === ''
  1632. },
  1633. validateSubmitRequiredFields () {
  1634. if (this.plmLabAuthorityArr && this.plmLabAuthorityArr.length > 0) {
  1635. for (let i = 0; i < this.plmLabAuthorityArr.length; i++) {
  1636. const fieldConfig = this.plmLabAuthorityArr[i]
  1637. if (fieldConfig.required !== 'Y') {
  1638. continue
  1639. }
  1640. const value = this.getLabSubmitFieldValue(fieldConfig.fieldId)
  1641. if (this.isSubmitFieldEmpty(value)) {
  1642. this.$message.warning((fieldConfig.fieldName || fieldConfig.fieldId) + '不能为空!')
  1643. return false
  1644. }
  1645. }
  1646. }
  1647. return true
  1648. },
  1649. normalizeTesterList (value) {
  1650. const source = Array.isArray(value) ? value : String(value || '').split(';')
  1651. const uniqueList = []
  1652. const seen = new Set()
  1653. source.forEach(item => {
  1654. const code = String(item || '').trim()
  1655. if (!code || seen.has(code)) {
  1656. return
  1657. }
  1658. seen.add(code)
  1659. uniqueList.push(code)
  1660. })
  1661. return uniqueList
  1662. },
  1663. normalizeTesterOptionList (rows) {
  1664. if (!Array.isArray(rows)) {
  1665. return []
  1666. }
  1667. return rows
  1668. .map(item => {
  1669. const operatorId = String(item.operatorId || item.operator_id || item.username || item.userName || '').trim()
  1670. const operatorName = String(item.operatorName || item.operator_name || item.userDisplay || item.user_display || '').trim()
  1671. if (!operatorId || !operatorName) {
  1672. return null
  1673. }
  1674. return {
  1675. operatorId,
  1676. operatorName
  1677. }
  1678. })
  1679. .filter(item => !!item)
  1680. },
  1681. resolveTesterNameById (operatorId) {
  1682. const normalizedId = String(operatorId || '').trim()
  1683. if (!normalizedId) {
  1684. return ''
  1685. }
  1686. const target = (this.testerOptionList || []).find(item => String(item.operatorId || '').trim() === normalizedId)
  1687. return target ? String(target.operatorName || '').trim() : normalizedId
  1688. },
  1689. syncTesterField () {
  1690. const testerList = this.normalizeTesterList(this.modalData.testerCodeList || this.modalData.testerList || this.modalData.tester)
  1691. this.$set(this.modalData, 'testerCodeList', testerList)
  1692. this.$set(this.modalData, 'testerList', testerList)
  1693. this.$set(this.modalData, 'tester', testerList.join(';'))
  1694. this.$set(this.modalData, 'testerName', testerList.map(item => this.resolveTesterNameById(item)).join(';'))
  1695. },
  1696. resolveLabApproverNameById (operatorId) {
  1697. const normalizedId = String(operatorId || '').trim()
  1698. if (!normalizedId) {
  1699. return ''
  1700. }
  1701. const target = (this.labApproverOptionList || []).find(item => String(item.operatorId || '').trim() === normalizedId)
  1702. return target ? String(target.operatorName || '').trim() : normalizedId
  1703. },
  1704. syncLabApproverField () {
  1705. const labApproverList = this.normalizeTesterList(this.modalData.labApproverCodeList || this.modalData.labApproverList || this.modalData.labApprover)
  1706. this.$set(this.modalData, 'labApproverCodeList', labApproverList)
  1707. this.$set(this.modalData, 'labApproverList', labApproverList)
  1708. this.$set(this.modalData, 'labApprover', labApproverList.join(';'))
  1709. this.$set(this.modalData, 'labApproverName', labApproverList.map(item => this.resolveLabApproverNameById(item)).join(';'))
  1710. },
  1711. filterTesterOptions () {
  1712. const operatorIdKeyword = String(this.testerSearchData.operatorId || '').trim().toUpperCase()
  1713. const operatorNameKeyword = String(this.testerSearchData.operatorName || '').trim().toUpperCase()
  1714. this.testerDisplayList = (this.testerOptionList || []).filter(item => {
  1715. const operatorId = String(item.operatorId || '').toUpperCase()
  1716. const operatorName = String(item.operatorName || '').toUpperCase()
  1717. const matchOperatorId = !operatorIdKeyword || operatorId.includes(operatorIdKeyword)
  1718. const matchOperatorName = !operatorNameKeyword || operatorName.includes(operatorNameKeyword)
  1719. return matchOperatorId && matchOperatorName
  1720. })
  1721. },
  1722. loadTesterOptions () {
  1723. if (!this.modalData || !this.modalData.site) {
  1724. this.testerOptionList = []
  1725. this.testerDisplayList = []
  1726. this.testerSelectionCache = []
  1727. return Promise.resolve()
  1728. }
  1729. const params = {
  1730. site: this.modalData.site,
  1731. roleNo: 'R015',
  1732. active: 'Y'
  1733. }
  1734. return searchUserListForRole(params).then(({data}) => {
  1735. if (data && data.code === 0) {
  1736. const roleUserSet = new Set(
  1737. (Array.isArray(data.searchUserListForRole) ? data.searchUserListForRole : [])
  1738. .map(item => String(item || '').trim())
  1739. .filter(item => !!item)
  1740. )
  1741. const userList = (Array.isArray(data.userList) ? data.userList : []).filter(item => {
  1742. const username = String(item.username || item.userName || '').trim()
  1743. return !!username && roleUserSet.has(username)
  1744. })
  1745. this.testerOptionList = this.normalizeTesterOptionList(userList)
  1746. this.filterTesterOptions()
  1747. this.testerSelectionCache = []
  1748. this.syncTesterField()
  1749. } else {
  1750. this.testerOptionList = []
  1751. this.testerDisplayList = []
  1752. this.testerSelectionCache = []
  1753. }
  1754. }).catch(() => {
  1755. this.testerOptionList = []
  1756. this.testerDisplayList = []
  1757. this.testerSelectionCache = []
  1758. })
  1759. },
  1760. openTesterChooseModal () {
  1761. if (this.isLabFieldDisabled('tester')) {
  1762. return
  1763. }
  1764. if (!this.modalData || !String(this.modalData.site || '').trim()) {
  1765. this.$message.warning('请先选择工厂')
  1766. return
  1767. }
  1768. this.loadTesterOptions().then(() => {
  1769. if (!this.testerOptionList || this.testerOptionList.length === 0) {
  1770. this.$message.warning('未查询到可选测试人')
  1771. return
  1772. }
  1773. this.testerFlag = true
  1774. })
  1775. },
  1776. openTesterDialog () {
  1777. this.testerSearchData = {
  1778. operatorId: '',
  1779. operatorName: ''
  1780. }
  1781. this.filterTesterOptions()
  1782. const selectedOperatorSet = new Set(
  1783. (this.modalData.testerCodeList || [])
  1784. .map(item => String(item || '').trim())
  1785. .filter(item => !!item)
  1786. )
  1787. const selectedRows = (this.testerOptionList || []).filter(item => selectedOperatorSet.has(String(item.operatorId || '').trim()))
  1788. this.testerSelectionCache = JSON.parse(JSON.stringify(selectedRows))
  1789. this.$nextTick(() => {
  1790. if (!this.$refs.testerTable) {
  1791. return
  1792. }
  1793. this.$refs.testerTable.clearSelection()
  1794. selectedRows.forEach(item => {
  1795. this.$refs.testerTable.toggleRowSelection(item, true)
  1796. })
  1797. })
  1798. },
  1799. closeTesterDialog () {
  1800. this.testerSelectionCache = []
  1801. },
  1802. handleTesterSelectionChange (rows) {
  1803. this.testerSelectionCache = rows || []
  1804. },
  1805. handleTesterTableClick (row, column, event, tableRef) {
  1806. if (this.$refs[tableRef]) {
  1807. this.$refs[tableRef].toggleRowSelection(row)
  1808. }
  1809. },
  1810. saveTesterChooseModal () {
  1811. const selectedCodes = (this.testerSelectionCache || [])
  1812. .map(item => String(item.operatorId || '').trim())
  1813. .filter(item => !!item)
  1814. this.$set(this.modalData, 'testerCodeList', selectedCodes)
  1815. this.syncTesterField()
  1816. this.testerFlag = false
  1817. },
  1818. clearTesterSelection () {
  1819. this.$set(this.modalData, 'testerCodeList', [])
  1820. this.syncTesterField()
  1821. },
  1822. filterLabApproverOptions () {
  1823. const operatorIdKeyword = String(this.labApproverSearchData.operatorId || '').trim().toUpperCase()
  1824. const operatorNameKeyword = String(this.labApproverSearchData.operatorName || '').trim().toUpperCase()
  1825. this.labApproverDisplayList = (this.labApproverOptionList || []).filter(item => {
  1826. const operatorId = String(item.operatorId || '').toUpperCase()
  1827. const operatorName = String(item.operatorName || '').toUpperCase()
  1828. const matchOperatorId = !operatorIdKeyword || operatorId.includes(operatorIdKeyword)
  1829. const matchOperatorName = !operatorNameKeyword || operatorName.includes(operatorNameKeyword)
  1830. return matchOperatorId && matchOperatorName
  1831. })
  1832. },
  1833. loadLabApproverOptions () {
  1834. if (!this.modalData || !this.modalData.site || !this.modalData.testLab) {
  1835. this.resetLabApproverOptionState()
  1836. return Promise.resolve()
  1837. }
  1838. const params = {
  1839. site: this.modalData.site,
  1840. roleNo: this.modalData.testLab,
  1841. active: 'Y'
  1842. }
  1843. return searchUserListForRole(params).then(({data}) => {
  1844. if (data && data.code === 0) {
  1845. const roleUserSet = new Set(
  1846. (Array.isArray(data.searchUserListForRole) ? data.searchUserListForRole : [])
  1847. .map(item => String(item || '').trim())
  1848. .filter(item => !!item)
  1849. )
  1850. const userList = (Array.isArray(data.userList) ? data.userList : []).filter(item => {
  1851. const username = String(item.username || item.userName || '').trim()
  1852. return !!username && roleUserSet.has(username)
  1853. })
  1854. this.labApproverOptionList = this.normalizeTesterOptionList(userList)
  1855. this.filterLabApproverOptions()
  1856. this.labApproverSelectionCache = []
  1857. this.syncLabApproverField()
  1858. } else {
  1859. this.resetLabApproverOptionState()
  1860. }
  1861. }).catch(() => {
  1862. this.resetLabApproverOptionState()
  1863. })
  1864. },
  1865. openLabApproverChooseModal () {
  1866. if (this.isLabFieldDisabled('labApprover')) {
  1867. return
  1868. }
  1869. if (!this.modalData || !String(this.modalData.site || '').trim()) {
  1870. this.$message.warning('请先选择工厂')
  1871. return
  1872. }
  1873. if (!this.modalData.testLab) {
  1874. this.$message.warning('请先选择Test Lab')
  1875. return
  1876. }
  1877. this.loadLabApproverOptions().then(() => {
  1878. if (!this.labApproverOptionList || this.labApproverOptionList.length === 0) {
  1879. this.$message.warning('未查询到可选实验室审批人')
  1880. return
  1881. }
  1882. this.labApproverFlag = true
  1883. })
  1884. },
  1885. openLabApproverDialog () {
  1886. this.labApproverSearchData = {
  1887. operatorId: '',
  1888. operatorName: ''
  1889. }
  1890. this.filterLabApproverOptions()
  1891. const selectedOperatorSet = new Set(
  1892. (this.modalData.labApproverCodeList || [])
  1893. .map(item => String(item || '').trim())
  1894. .filter(item => !!item)
  1895. )
  1896. const selectedRows = (this.labApproverOptionList || []).filter(item => selectedOperatorSet.has(String(item.operatorId || '').trim()))
  1897. this.labApproverSelectionCache = JSON.parse(JSON.stringify(selectedRows))
  1898. this.$nextTick(() => {
  1899. if (!this.$refs.labApproverTable) {
  1900. return
  1901. }
  1902. this.$refs.labApproverTable.clearSelection()
  1903. selectedRows.forEach(item => {
  1904. this.$refs.labApproverTable.toggleRowSelection(item, true)
  1905. })
  1906. })
  1907. },
  1908. closeLabApproverDialog () {
  1909. this.labApproverSelectionCache = []
  1910. },
  1911. handleLabApproverSelectionChange (rows) {
  1912. this.labApproverSelectionCache = rows || []
  1913. },
  1914. saveLabApproverChooseModal () {
  1915. const selectedCodes = (this.labApproverSelectionCache || [])
  1916. .map(item => String(item.operatorId || '').trim())
  1917. .filter(item => !!item)
  1918. this.$set(this.modalData, 'labApproverCodeList', selectedCodes)
  1919. this.syncLabApproverField()
  1920. this.labApproverFlag = false
  1921. },
  1922. clearLabApproverSelection () {
  1923. this.$set(this.modalData, 'labApproverCodeList', [])
  1924. this.syncLabApproverField()
  1925. },
  1926. isCurrentApprover () {
  1927. return this.superAdmin || (this.modalData.createBy2 && this.modalData.createBy2.split(';').includes(this.createBy2))
  1928. },
  1929. canSaveAction () {
  1930. return !this.showModalFlag && (this.modalData.status === '草稿' || (this.modalData.status === '审批中' && this.isCurrentApprover()))
  1931. },
  1932. canAgreeAction () {
  1933. return this.authSubmit && this.isCurrentApprover() && this.modalData.status === '审批中' && this.modalData.tpProcessControl !== 'Y' && this.modalData.csProcessControl !== 'Y'
  1934. },
  1935. canRejectAction () {
  1936. return this.authReject && this.isCurrentApprover() && this.modalData.status === '审批中' && this.modalData.isReject === 'Y'
  1937. },
  1938. getButtonAuthData () {
  1939. this.authSearch = this.isAuth(this.menuId + ':search')
  1940. this.authUpdate = this.isAuth(this.menuId + ':update')
  1941. this.authIssue = this.isAuth(this.menuId + ':issue')
  1942. this.authSubmit = this.isAuth(this.menuId + ':submit')
  1943. this.authReject = this.isAuth(this.menuId + ':reject')
  1944. this.authDelete = this.isAuth(this.menuId + ':delete')
  1945. this.authFileSave = this.isAuth(this.menuId + ':fileSave')
  1946. this.authFileDownLoad = this.isAuth(this.menuId + ':fileDownLoad')
  1947. this.authFileRemove = this.isAuth(this.menuId + ':fileRemove')
  1948. this.authFilePreview = this.isAuth(this.menuId + ':filePreview')
  1949. this.authDataEntry = this.isAuth(this.menuId + ':dataEntry')
  1950. },
  1951. checkSuperAdmin () {
  1952. checkSuperAdmin().then(({data}) => {
  1953. if (data && data.code === 0) {
  1954. this.superAdmin = !!data.flag
  1955. }
  1956. })
  1957. },
  1958. rowStyle () {
  1959. return { cursor: 'pointer' }
  1960. },
  1961. dataListSelectionChange (rows) {
  1962. this.dataListSelections = rows || []
  1963. },
  1964. changeClickRow (row) {
  1965. this.handleCurrentRowChange(row)
  1966. },
  1967. currentChange (row) {
  1968. this.handleCurrentRowChange(row)
  1969. },
  1970. handleCurrentRowChange (row) {
  1971. const nextRow = row || {}
  1972. const prevSite = (this.currentRow && this.currentRow.site) || ''
  1973. const prevReferenceNo = (this.currentRow && this.currentRow.referenceNo) || ''
  1974. const nextSite = nextRow.site || ''
  1975. const nextReferenceNo = nextRow.referenceNo || ''
  1976. this.currentRow = nextRow
  1977. if (!nextSite || !nextReferenceNo) {
  1978. this.currentDetailRequestId += 1
  1979. this.currentDetailLoading = false
  1980. this.currentDetail = this.buildEmptyDetailData()
  1981. this.currentJudgeCriteriaFileList = []
  1982. this.currentTestSpecFileList = []
  1983. this.currentTestReportFileList = []
  1984. this.refreshCurrentTabTable()
  1985. return
  1986. }
  1987. if (prevSite !== nextSite || prevReferenceNo !== nextReferenceNo) {
  1988. this.loadCurrentDetail(nextRow)
  1989. }
  1990. this.refreshCurrentTabTable()
  1991. },
  1992. loadCurrentDetail (row) {
  1993. if (!row || !row.site || !row.referenceNo) {
  1994. this.currentDetailRequestId += 1
  1995. this.currentDetailLoading = false
  1996. this.currentDetail = this.buildEmptyDetailData()
  1997. this.currentJudgeCriteriaFileList = []
  1998. this.currentTestSpecFileList = []
  1999. this.currentTestReportFileList = []
  2000. return
  2001. }
  2002. const requestId = this.currentDetailRequestId + 1
  2003. this.currentDetailRequestId = requestId
  2004. this.currentDetailLoading = true
  2005. this.currentJudgeCriteriaFileList = []
  2006. this.currentTestSpecFileList = []
  2007. this.currentTestReportFileList = []
  2008. this.loadCurrentAttachmentFileLists(row.site, row.referenceNo, requestId)
  2009. api.getLabDetail(row.site, row.referenceNo).then(({data}) => {
  2010. if (requestId !== this.currentDetailRequestId) {
  2011. return
  2012. }
  2013. if (data && data.code === 0) {
  2014. const detail = data.data || {}
  2015. const mergedDetail = {
  2016. ...this.buildEmptyDetailData(),
  2017. ...row,
  2018. ...detail
  2019. }
  2020. this.fillDisplayFields(mergedDetail, row || {})
  2021. this.$set(mergedDetail, 'propertyItemList', (detail.propertyItemList || []).map(item => this.normalizePropertyItem(item)))
  2022. this.$set(mergedDetail, 'testerList', this.normalizeTesterList(mergedDetail.tester))
  2023. this.$set(mergedDetail, 'labApproverList', this.normalizeTesterList(mergedDetail.labApprover))
  2024. this.currentDetail = mergedDetail
  2025. } else {
  2026. const fallbackDetail = {
  2027. ...this.buildEmptyDetailData(),
  2028. ...row
  2029. }
  2030. this.fillDisplayFields(fallbackDetail, row || {})
  2031. this.currentDetail = fallbackDetail
  2032. }
  2033. }).catch(() => {
  2034. if (requestId !== this.currentDetailRequestId) {
  2035. return
  2036. }
  2037. const fallbackDetail = {
  2038. ...this.buildEmptyDetailData(),
  2039. ...row
  2040. }
  2041. this.fillDisplayFields(fallbackDetail, row || {})
  2042. this.currentDetail = fallbackDetail
  2043. }).finally(() => {
  2044. if (requestId === this.currentDetailRequestId) {
  2045. this.currentDetailLoading = false
  2046. }
  2047. })
  2048. },
  2049. tabClick () {
  2050. this.refreshCurrentTabTable()
  2051. },
  2052. refreshCurrentTabTable () {
  2053. if (this.activeTable === 'approvalInformation') {
  2054. this.getApprovalList()
  2055. }
  2056. },
  2057. getApprovalList () {
  2058. if (!this.currentRow || !this.currentRow.site || !this.currentRow.referenceNo) {
  2059. this.approvalList = []
  2060. return
  2061. }
  2062. const params = {
  2063. site: this.currentRow.site,
  2064. menuId: this.menuId,
  2065. documentNo: this.currentRow.referenceNo
  2066. }
  2067. api.getLabApprovalList(params).then(({data}) => {
  2068. if (data && data.code === 0) {
  2069. this.approvalList = data.rows || []
  2070. } else {
  2071. this.approvalList = []
  2072. }
  2073. }).catch(() => {
  2074. this.approvalList = []
  2075. })
  2076. },
  2077. getDataList () {
  2078. if (!this.authSearch) {
  2079. this.dataListSelections = []
  2080. this.dataList = []
  2081. this.totalPage = 0
  2082. this.currentRow = {}
  2083. this.currentDetailRequestId += 1
  2084. this.currentDetail = this.buildEmptyDetailData()
  2085. this.currentDetailLoading = false
  2086. this.currentJudgeCriteriaFileList = []
  2087. this.currentTestSpecFileList = []
  2088. this.currentTestReportFileList = []
  2089. this.approvalList = []
  2090. return
  2091. }
  2092. this.dataListSelections = []
  2093. this.dataListLoading = true
  2094. const params = {
  2095. page: this.pageIndex,
  2096. limit: this.pageSize,
  2097. site: this.searchData.site || '',
  2098. menuId: this.menuId,
  2099. referenceNo: this.searchData.referenceNo,
  2100. status: this.searchData.status,
  2101. currentApprover: this.searchData.currentApprover,
  2102. nodeId: this.searchData.nodeId
  2103. }
  2104. api.getLabList(params).then(({data}) => {
  2105. if (data && data.code === 0) {
  2106. const list = data.page.list || []
  2107. this.dataList = list.map(item => ({
  2108. applicantName: '',
  2109. customerName: '',
  2110. projectName: '',
  2111. testerName: '',
  2112. nodeId: '',
  2113. nodeName: '',
  2114. currentApprover: '',
  2115. ...item
  2116. }))
  2117. this.totalPage = data.page.totalCount
  2118. this.$nextTick(() => {
  2119. if (this.$refs.labTable && this.dataList.length > 0) {
  2120. this.$refs.labTable.setCurrentRow(this.dataList[0])
  2121. } else if (this.$refs.labTable) {
  2122. this.$refs.labTable.setCurrentRow()
  2123. }
  2124. })
  2125. } else {
  2126. this.dataListSelections = []
  2127. this.dataList = []
  2128. this.totalPage = 0
  2129. this.currentRow = {}
  2130. this.currentDetailRequestId += 1
  2131. this.currentDetail = this.buildEmptyDetailData()
  2132. this.currentDetailLoading = false
  2133. this.currentJudgeCriteriaFileList = []
  2134. this.currentTestSpecFileList = []
  2135. this.currentTestReportFileList = []
  2136. this.approvalList = []
  2137. }
  2138. this.dataListLoading = false
  2139. }).catch(() => {
  2140. this.dataListSelections = []
  2141. this.currentRow = {}
  2142. this.currentDetailRequestId += 1
  2143. this.currentDetail = this.buildEmptyDetailData()
  2144. this.currentDetailLoading = false
  2145. this.currentJudgeCriteriaFileList = []
  2146. this.currentTestSpecFileList = []
  2147. this.currentTestReportFileList = []
  2148. this.approvalList = []
  2149. this.dataListLoading = false
  2150. })
  2151. },
  2152. sizeChangeHandle (val) {
  2153. this.pageSize = val
  2154. this.pageIndex = 1
  2155. this.getDataList()
  2156. },
  2157. currentChangeHandle (val) {
  2158. this.pageIndex = val
  2159. this.getDataList()
  2160. },
  2161. searchHandle () {
  2162. if (!this.authSearch) {
  2163. return
  2164. }
  2165. this.pageIndex = 1
  2166. this.getDataList()
  2167. },
  2168. buildExportName () {
  2169. const now = new Date()
  2170. const year = String(now.getFullYear())
  2171. const month = String(now.getMonth() + 1).padStart(2, '0')
  2172. const day = String(now.getDate()).padStart(2, '0')
  2173. const hour = String(now.getHours()).padStart(2, '0')
  2174. const minute = String(now.getMinutes()).padStart(2, '0')
  2175. const second = String(now.getSeconds()).padStart(2, '0')
  2176. return 'Lab记录' + year + month + day + hour + minute + second
  2177. },
  2178. async createExportData () {
  2179. const params = {
  2180. ...this.searchData,
  2181. site: this.searchData.site || '',
  2182. menuId: this.menuId,
  2183. page: 1,
  2184. limit: -1
  2185. }
  2186. this.resultList = []
  2187. await api.getLabList(params).then(({data}) => {
  2188. if (data && data.code === 0 && data.page && Array.isArray(data.page.list)) {
  2189. this.resultList = (data.page.list || []).map(item => {
  2190. const row = { ...item }
  2191. row.applicationDate = this.normalizeDateOnly(row.applicationDate)
  2192. row.requestedDate = this.normalizeDateOnly(row.requestedDate)
  2193. row.testStartDate = this.normalizeDateOnly(row.testStartDate)
  2194. row.completedDate = this.normalizeDateOnly(row.completedDate)
  2195. row.returnSample = this.displayYesNo(row.returnSample)
  2196. row.testerName = row.testerName || row.tester || ''
  2197. return row
  2198. })
  2199. } else {
  2200. this.$message.warning((data && data.msg) || '导出数据获取失败')
  2201. }
  2202. }).catch(() => {
  2203. this.$message.error('导出数据获取失败')
  2204. })
  2205. return this.resultList
  2206. },
  2207. startDownload () {
  2208. this.exportName = this.buildExportName()
  2209. this.exportFieldMap = this.buildExportFields()
  2210. this.exportLoading = true
  2211. },
  2212. finishDownload () {
  2213. this.exportLoading = false
  2214. },
  2215. buildExportFields () {
  2216. const tableRef = this.$refs.labTable
  2217. if (!tableRef || !Array.isArray(tableRef.columns)) {
  2218. return {}
  2219. }
  2220. const fields = {}
  2221. tableRef.columns.forEach(column => {
  2222. const label = column && column.label ? String(column.label).trim() : ''
  2223. const property = column && column.property ? String(column.property).trim() : ''
  2224. if (!label || !property || label === '操作') {
  2225. return
  2226. }
  2227. fields[label] = property
  2228. })
  2229. return fields
  2230. },
  2231. radioClick (field, value) {
  2232. if (this.modalData[field] === value) {
  2233. this.modalData[field] = ''
  2234. } else {
  2235. this.modalData[field] = value
  2236. }
  2237. },
  2238. addOrUpdateHandle (row) {
  2239. if (!row && !this.authUpdate) {
  2240. this.$message.warning('没有新增权限')
  2241. return
  2242. }
  2243. if (row && !this.authUpdate && !this.authDataEntry) {
  2244. this.$message.warning('没有编辑权限')
  2245. return
  2246. }
  2247. this.modalFlag = true
  2248. this.activeName = 'basicInfo'
  2249. this.submitModalFlag = false
  2250. this.rejectOpinion = ''
  2251. this.testerFlag = false
  2252. this.testerOptionList = []
  2253. this.testerDisplayList = []
  2254. this.testerSelectionCache = []
  2255. this.testerSearchData = {
  2256. operatorId: '',
  2257. operatorName: ''
  2258. }
  2259. this.resetLabApproverOptionState()
  2260. this.plmLabAuthorityArr = []
  2261. this.nodeAuthorityLoaded = !row
  2262. this.isEditMode = !!row
  2263. this.showModalFlag = !!(row && (row.status === '已完成' || !this.authUpdate))
  2264. this.judgeCriteriaFileList = []
  2265. this.testSpecFileList = []
  2266. this.testReportFileList = []
  2267. if (row) {
  2268. this.modalTitle = 'Lab 编辑'
  2269. api.getLabDetail(row.site, row.referenceNo).then(({data}) => {
  2270. if (data && data.code === 0) {
  2271. const detail = data.data || {}
  2272. this.modalData = this.buildDefaultModalData()
  2273. this.modalData = {
  2274. ...this.modalData,
  2275. ...detail
  2276. }
  2277. this.fillDisplayFields(this.modalData, row || {})
  2278. this.$set(this.modalData, 'propertyItemList', (detail.propertyItemList || []).map(item => this.normalizePropertyItem(item)))
  2279. this.$set(this.modalData, 'testerCodeList', this.normalizeTesterList(this.modalData.tester))
  2280. this.$set(this.modalData, 'testerList', this.modalData.testerCodeList || [])
  2281. this.$set(this.modalData, 'labApproverCodeList', this.normalizeTesterList(this.modalData.labApprover))
  2282. this.$set(this.modalData, 'labApproverList', this.modalData.labApproverCodeList || [])
  2283. this.$set(this.modalData, 'customerIdFlag', true)
  2284. this.$set(this.modalData, 'projectIdFlag', true)
  2285. this.$set(this.modalData, 'createBy2', '')
  2286. this.$set(this.modalData, 'isReject', 'Y')
  2287. this.$set(this.modalData, 'tpProcessControl', 'N')
  2288. this.$set(this.modalData, 'csProcessControl', 'N')
  2289. this.$set(this.modalData, 'testerName', this.modalData.testerName || this.modalData.tester || '')
  2290. this.$set(this.modalData, 'labApproverName', this.modalData.labApproverName || this.modalData.labApprover || '')
  2291. this.loadTesterOptions()
  2292. this.loadLabApproverOptions()
  2293. this.loadModalButtonCondition()
  2294. this.loadNodeAuthority(this.modalData.site || row.site, this.modalData.stepId !== null && this.modalData.stepId !== undefined ? this.modalData.stepId : 10)
  2295. this.refreshJudgeCriteriaFileList()
  2296. this.refreshTestSpecFileList()
  2297. this.refreshTestReportFileList()
  2298. }
  2299. }).catch(() => {
  2300. this.loadNodeAuthority(row.site, row.stepId !== null && row.stepId !== undefined ? row.stepId : 10)
  2301. this.judgeCriteriaFileList = []
  2302. this.testSpecFileList = []
  2303. this.testReportFileList = []
  2304. })
  2305. } else {
  2306. this.modalTitle = 'Lab 新增'
  2307. this.showModalFlag = false
  2308. this.loadSiteOptions()
  2309. const tempReferenceNo = 'TEMP-' + new Date().getTime() + '-' + Math.floor(Math.random() * 1000)
  2310. const defaultData = this.buildDefaultModalData()
  2311. const defaultSite = this.getDefaultSiteValue()
  2312. this.modalData = {
  2313. ...defaultData,
  2314. site: defaultSite,
  2315. referenceNo: tempReferenceNo,
  2316. applicant: this.$store.state.user.name || '',
  2317. applicantName: this.$store.state.user.userDisplay || this.$store.state.user.user_display || '',
  2318. applicationDate: this.getCurrentDateString(),
  2319. customerIdFlag: false,
  2320. projectIdFlag: true,
  2321. status: '草稿',
  2322. stepId: 10,
  2323. rejectFlag: 'N'
  2324. }
  2325. this.$set(this.modalData, 'propertyItemList', [])
  2326. this.$set(this.modalData, 'testerCodeList', [])
  2327. this.$set(this.modalData, 'testerList', [])
  2328. this.$set(this.modalData, 'testerName', '')
  2329. this.$set(this.modalData, 'labApproverCodeList', [])
  2330. this.$set(this.modalData, 'labApproverList', [])
  2331. this.$set(this.modalData, 'labApproverName', '')
  2332. this.searchProjectData.site = defaultSite
  2333. this.nodeAuthorityLoaded = true
  2334. this.loadNodeAuthority(this.modalData.site, 10)
  2335. }
  2336. },
  2337. fillDisplayFields (target, source) {
  2338. this.$set(target, 'applicantName', target.applicantName || source.applicantName || '')
  2339. this.$set(target, 'customerName', target.customerName || source.customerName || '')
  2340. this.$set(target, 'projectName', target.projectName || source.projectName || '')
  2341. },
  2342. loadModalButtonCondition () {
  2343. if (!this.modalData || !this.modalData.site || !this.modalData.referenceNo) {
  2344. return
  2345. }
  2346. const params = {
  2347. site: this.modalData.site,
  2348. referenceNo: this.modalData.referenceNo
  2349. }
  2350. api.getLabButtonCondition(params).then(({data}) => {
  2351. if (data && data.code === 0 && data.data) {
  2352. this.$set(this.modalData, 'createBy2', data.data.createBy2 || '')
  2353. this.$set(this.modalData, 'isReject', data.data.isReject || 'Y')
  2354. this.$set(this.modalData, 'tpProcessControl', data.data.tpProcessControl || 'N')
  2355. this.$set(this.modalData, 'csProcessControl', data.data.csProcessControl || 'N')
  2356. }
  2357. })
  2358. },
  2359. handleModalClose () {
  2360. this.judgeCriteriaDialogVisible = false
  2361. this.testSpecDialogVisible = false
  2362. this.testReportDialogVisible = false
  2363. this.testerFlag = false
  2364. this.labApproverFlag = false
  2365. this.modalFlag = false
  2366. },
  2367. buildLabPayload (source) {
  2368. const fields = [
  2369. 'site', 'referenceNo', 'applicant', 'applicationDate', 'applyDepartment', 'requestedDate',
  2370. 'projectId', 'customerId', 'sampleName', 'sampleModel', 'sampleQty', 'testPurpose',
  2371. 'productStage', 'testType', 'reportDocument', 'returnSample', 'applicationSurface', 'testLab',
  2372. 'labApprover', 'attention', 'testStartDate', 'completedDate', 'unnormalSymptomAndDescription', 'testNumber',
  2373. 'testerComments', 'departmentManagerComments', 'labComments', 'tester', 'propertyItemList', 'status', 'stepId',
  2374. 'rejectFlag', 'rejectStepId', 'createDate', 'createBy', 'updateDate', 'updateBy'
  2375. ]
  2376. const payload = {}
  2377. fields.forEach(field => {
  2378. payload[field] = source[field]
  2379. })
  2380. return payload
  2381. },
  2382. dataFormSubmit () {
  2383. if (!this.canSaveAction()) {
  2384. this.$message.warning('当前状态不允许保存')
  2385. return
  2386. }
  2387. this.syncTesterField()
  2388. this.syncLabApproverField()
  2389. this.syncPropertyItemList()
  2390. const payload = this.buildLabPayload(this.modalData)
  2391. const submitMethod = this.modalTitle === 'Lab 新增' ? api.saveLab : api.updateLab
  2392. submitMethod(payload).then(({data}) => {
  2393. if (data && data.code === 0) {
  2394. this.$message.success('操作成功')
  2395. this.modalFlag = false
  2396. this.getDataList()
  2397. } else {
  2398. this.$message.error(data.msg || '操作失败')
  2399. }
  2400. })
  2401. },
  2402. agreeSubmit () {
  2403. if (!this.canAgreeAction()) {
  2404. this.$message.warning('当前状态不允许同意')
  2405. return
  2406. }
  2407. this.$confirm('确认同意该申请单?', '提示', {
  2408. confirmButtonText: '确定',
  2409. cancelButtonText: '取消',
  2410. type: 'warning'
  2411. }).then(() => {
  2412. this.submitLab('Y')
  2413. })
  2414. },
  2415. openRejectModal () {
  2416. if (!this.canRejectAction()) {
  2417. this.$message.warning('当前状态不允许驳回')
  2418. return
  2419. }
  2420. this.rejectOpinion = ''
  2421. this.submitModalFlag = true
  2422. },
  2423. rejectSubmit () {
  2424. if (!this.rejectOpinion || !this.rejectOpinion.trim()) {
  2425. this.$message.warning('请填写驳回意见')
  2426. return
  2427. }
  2428. this.submitLab('N')
  2429. },
  2430. submitLab (nodeConclusion) {
  2431. if (nodeConclusion === 'Y' && !this.canAgreeAction()) {
  2432. this.$message.warning('当前状态不允许同意')
  2433. return
  2434. }
  2435. if (nodeConclusion === 'N' && !this.canRejectAction()) {
  2436. this.$message.warning('当前状态不允许驳回')
  2437. return
  2438. }
  2439. if (!this.modalData.site || !this.modalData.referenceNo) {
  2440. this.$message.warning('单据主键信息缺失,无法提交审批')
  2441. return
  2442. }
  2443. this.syncTesterField()
  2444. this.syncLabApproverField()
  2445. this.syncPropertyItemList()
  2446. if (!this.nodeAuthorityLoaded) {
  2447. this.$message.warning('节点权限加载中,请稍后重试')
  2448. return
  2449. }
  2450. if (!this.validateSubmitRequiredFields()) {
  2451. return
  2452. }
  2453. this.submitLoading = true
  2454. const payload = this.buildLabPayload(this.modalData)
  2455. const params = {
  2456. ...payload,
  2457. site: this.modalData.site,
  2458. referenceNo: this.modalData.referenceNo,
  2459. nodeConclusion: nodeConclusion,
  2460. rejectOpinion: nodeConclusion === 'N' ? this.rejectOpinion : '',
  2461. menuId: this.menuId,
  2462. documentNo: this.modalData.referenceNo
  2463. }
  2464. api.submitLab(params).then(({data}) => {
  2465. if (data && data.code === 0) {
  2466. this.$message.success(nodeConclusion === 'Y' ? '同意成功' : '驳回成功')
  2467. this.submitModalFlag = false
  2468. this.modalFlag = false
  2469. this.getDataList()
  2470. } else {
  2471. this.$message.error(data.msg || '提交失败')
  2472. }
  2473. this.submitLoading = false
  2474. }).catch(() => {
  2475. this.submitLoading = false
  2476. })
  2477. },
  2478. issueModal (row) {
  2479. if (!this.authIssue) {
  2480. this.$message.warning('没有下达权限')
  2481. return
  2482. }
  2483. this.$confirm('确认下达该申请单?', '提示', {
  2484. confirmButtonText: '确定',
  2485. cancelButtonText: '取消',
  2486. type: 'warning'
  2487. }).then(() => {
  2488. const params = {
  2489. site: row.site,
  2490. referenceNo: row.referenceNo,
  2491. menuId: this.menuId
  2492. }
  2493. api.issueLab(params).then(({data}) => {
  2494. if (data && data.code === 0) {
  2495. this.$message.success('下达成功')
  2496. if (this.modalFlag && this.modalData.referenceNo === row.referenceNo) {
  2497. this.modalData.status = '审批中'
  2498. }
  2499. this.getDataList()
  2500. } else {
  2501. this.$message.error(data.msg || '下达失败')
  2502. }
  2503. })
  2504. })
  2505. },
  2506. deleteHandle () {
  2507. if (!this.authDelete) {
  2508. this.$message.warning('没有删除权限')
  2509. return
  2510. }
  2511. const selectedRows = (this.dataListSelections || []).filter(item => item && item.site && item.referenceNo)
  2512. if (!selectedRows.length) {
  2513. this.$message.warning('请先勾选要删除的单据')
  2514. return
  2515. }
  2516. const nonDraftRows = selectedRows.filter(item => item.status !== '草稿')
  2517. if (nonDraftRows.length) {
  2518. const previewText = nonDraftRows.slice(0, 3).map(item => item.referenceNo).join('、')
  2519. const suffix = nonDraftRows.length > 3 ? (' 等' + nonDraftRows.length + '条') : ''
  2520. this.$message.warning('仅草稿状态的单据允许删除,当前勾选包含非草稿单据:' + previewText + suffix)
  2521. return
  2522. }
  2523. this.$confirm('确认删除选中的' + selectedRows.length + '条Lab单据?删除后会同步删除属性和附件数据。', '提示', {
  2524. confirmButtonText: '确定',
  2525. cancelButtonText: '取消',
  2526. type: 'warning'
  2527. }).then(() => {
  2528. const deleteTasks = selectedRows.map(item => {
  2529. return api.deleteLab({
  2530. site: item.site,
  2531. referenceNo: item.referenceNo
  2532. }).then(({data}) => {
  2533. if (data && data.code === 0) {
  2534. return {
  2535. success: true,
  2536. site: item.site,
  2537. referenceNo: item.referenceNo
  2538. }
  2539. }
  2540. return {
  2541. success: false,
  2542. referenceNo: item.referenceNo,
  2543. msg: (data && data.msg) || '删除失败'
  2544. }
  2545. }).catch(() => {
  2546. return {
  2547. success: false,
  2548. referenceNo: item.referenceNo,
  2549. msg: '删除失败'
  2550. }
  2551. })
  2552. })
  2553. Promise.all(deleteTasks).then(results => {
  2554. const successRows = results.filter(item => item.success)
  2555. const failRows = results.filter(item => !item.success)
  2556. if (this.modalFlag && successRows.some(item => item.site === this.modalData.site && item.referenceNo === this.modalData.referenceNo)) {
  2557. this.modalFlag = false
  2558. }
  2559. if (!failRows.length) {
  2560. this.$message.success('删除成功,共' + successRows.length + '条')
  2561. } else if (!successRows.length) {
  2562. this.$message.error(failRows[0].msg || '删除失败')
  2563. } else {
  2564. this.$message.warning('已删除' + successRows.length + '条,失败' + failRows.length + '条')
  2565. }
  2566. this.getDataList()
  2567. })
  2568. }).catch(() => {})
  2569. },
  2570. getBaseList (val, type) {
  2571. this.tagNo = val
  2572. this.tagNo1 = type
  2573. this.$nextTick(() => {
  2574. let strVal = ''
  2575. let conSql = ''
  2576. if (val === 102) {
  2577. if (type === 1) {
  2578. strVal = this.modalData.customerId
  2579. conSql = ' order by Customer_no '
  2580. }
  2581. }
  2582. if (val === 103) {
  2583. if (type === 1) {
  2584. strVal = this.modalData.applicant
  2585. }
  2586. conSql = " and b.site = '" + this.modalData.site + "'"
  2587. }
  2588. this.$refs.baseList.init(val, strVal, conSql)
  2589. })
  2590. },
  2591. getBaseData (val) {
  2592. if (this.tagNo === 102 && this.tagNo1 === 1) {
  2593. if (val.Customer_no === this.modalData.customerId) {
  2594. return
  2595. }
  2596. if (this.modalData.projectIdFlag) {
  2597. this.modalData.projectIdFlag = false
  2598. }
  2599. this.modalData.customerId = val.Customer_no
  2600. this.modalData.customerName = val.Customer_desc
  2601. this.modalData.projectId = ''
  2602. this.modalData.projectName = ''
  2603. }
  2604. if (this.tagNo === 103 && this.tagNo1 === 1) {
  2605. this.modalData.applicant = val.username
  2606. this.modalData.applicantName = val.user_display
  2607. }
  2608. },
  2609. handleQueryCustomer () {
  2610. if (!this.modalData.customerId) {
  2611. this.modalData.customerName = ''
  2612. this.modalData.projectId = ''
  2613. this.modalData.projectName = ''
  2614. this.modalData.projectIdFlag = true
  2615. return
  2616. }
  2617. const params = {
  2618. site: this.modalData.site,
  2619. customerNo: this.modalData.customerId
  2620. }
  2621. queryCustomer(params).then(({data}) => {
  2622. if (data && data.code === 0 && data.rows && data.rows.length === 1) {
  2623. const customer = data.rows[0]
  2624. this.modalData.customerName = customer.customerDesc || customer.Customer_desc || customer.customerName || ''
  2625. this.modalData.projectIdFlag = !!this.modalData.customerIdFlag
  2626. } else {
  2627. this.modalData.customerName = ''
  2628. this.modalData.projectId = ''
  2629. this.modalData.projectName = ''
  2630. this.modalData.projectIdFlag = true
  2631. }
  2632. }).catch(() => {
  2633. this.modalData.customerName = ''
  2634. this.modalData.projectId = ''
  2635. this.modalData.projectName = ''
  2636. this.modalData.projectIdFlag = true
  2637. })
  2638. },
  2639. handleQueryProjectByCustomer () {
  2640. if (!this.modalData.projectId) {
  2641. this.modalData.projectName = ''
  2642. return
  2643. }
  2644. const params = {
  2645. site: this.modalData.site,
  2646. projectId: this.modalData.projectId
  2647. }
  2648. if (this.modalData.customerId) {
  2649. params.customerId = this.modalData.customerId
  2650. }
  2651. queryProjectByCustomer(params).then(({data}) => {
  2652. if (data && data.code === 0 && data.rows && data.rows.length === 1) {
  2653. const project = data.rows[0]
  2654. this.modalData.projectName = project.projectName || project.Project_name || ''
  2655. } else {
  2656. this.modalData.projectName = ''
  2657. }
  2658. }).catch(() => {
  2659. this.modalData.projectName = ''
  2660. })
  2661. },
  2662. projectClickRow (row) {
  2663. this.modalData.projectId = row.projectId
  2664. this.modalData.projectName = row.projectName
  2665. if (!this.modalData.customerId) {
  2666. this.modalData.customerId = row.customerId
  2667. this.modalData.customerName = row.customerName
  2668. }
  2669. this.chooseProjectListFlag = false
  2670. },
  2671. searchProjectInfoList () {
  2672. this.projectList = []
  2673. if (this.modalData.customerId && this.modalData.customerId !== '') {
  2674. this.searchProjectData.customerId = this.modalData.customerId
  2675. } else {
  2676. this.searchProjectData.customerId = undefined
  2677. }
  2678. this.searchProjectData.site = this.modalData.site
  2679. searchProjectInfoList(this.searchProjectData).then(({data}) => {
  2680. if (data && data.code === 0) {
  2681. this.projectList = data.rows || []
  2682. } else {
  2683. this.projectList = []
  2684. }
  2685. }).catch(() => {
  2686. this.$message.error('查询项目信息失败')
  2687. })
  2688. },
  2689. closeProjectInfoDialog () {
  2690. if (this.$refs.closeProjectInfoForm) {
  2691. this.$refs.closeProjectInfoForm.resetFields()
  2692. }
  2693. this.searchProjectData = {
  2694. site: this.modalData.site || this.$store.state.user.site,
  2695. projectId: undefined,
  2696. projectName: undefined,
  2697. customerId: undefined
  2698. }
  2699. },
  2700. applicantBlur (tagNo) {
  2701. if (this.modalData.applicant != null && this.modalData.applicant !== '') {
  2702. const tempData = {
  2703. tagno: tagNo,
  2704. conditionSql: " and a.username = '" + this.modalData.applicant + "'" + " and b.site = '" + this.modalData.site + "'"
  2705. }
  2706. verifyData(tempData).then(({data}) => {
  2707. if (data && data.code === 0 && data.baseListData.length > 0) {
  2708. this.modalData.applicant = data.baseListData[0].username
  2709. this.modalData.applicantName = data.baseListData[0].user_display
  2710. return
  2711. }
  2712. this.modalData.applicantName = ''
  2713. })
  2714. } else {
  2715. this.modalData.applicantName = ''
  2716. }
  2717. }
  2718. }
  2719. }
  2720. </script>
  2721. <style scoped>
  2722. /deep/ .customer-tab .el-tabs__content {
  2723. padding: 0 !important;
  2724. }
  2725. .mod-config /deep/ .el-tabs--border-card > .el-tabs__content {
  2726. padding-top: 8px;
  2727. }
  2728. .judge-criteria-label {
  2729. color: #409EFF;
  2730. text-decoration: underline;
  2731. cursor: pointer;
  2732. font-weight: 600;
  2733. }
  2734. .judge-criteria-box {
  2735. width: 772px;
  2736. min-height: 38px;
  2737. border: 1px solid #DCDFE6;
  2738. border-radius: 4px;
  2739. padding: 8px 10px;
  2740. box-sizing: border-box;
  2741. cursor: pointer;
  2742. background: #fff;
  2743. }
  2744. .judge-criteria-placeholder {
  2745. color: #C0C4CC;
  2746. }
  2747. </style>