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.

3273 lines
131 KiB

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