employ.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. <template>
  2. <div class="hc-page-layout-box">
  3. <div class="hc-layout-left-box menu" :style="'width:' + leftWidth + 'px;'">
  4. <div class="hc-menu-header-box">
  5. <div class="text-xl name">设备分类 - 使用记录</div>
  6. </div>
  7. <div class="hc-menu-contents-box">
  8. <el-scrollbar>
  9. <HcMenuSimple :props="menusProps" :datas="menus" :keys="menuKey" @change="menuChange"/>
  10. </el-scrollbar>
  11. </div>
  12. <!--左右拖动-->
  13. <div class="horizontal-drag-line" @mousedown="onmousedown"/>
  14. </div>
  15. <div class="hc-page-content-box">
  16. <HcCard>
  17. <template #header>
  18. <HcTooltip keys="tentative_device_employ_add">
  19. <el-button type="primary" hc-btn @click="addFormModalClick">
  20. <HcIcon name="add-circle"/>
  21. <span>新增</span>
  22. </el-button>
  23. </HcTooltip>
  24. <HcTooltip keys="tentative_device_employ_edit">
  25. <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" @click="editFormModalClick">
  26. <HcIcon name="edit"/>
  27. <span>编辑</span>
  28. </el-button>
  29. </HcTooltip>
  30. <HcTooltip keys="tentative_device_employ_del">
  31. <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" @click="delNodeModalClick">
  32. <HcIcon name="delete-bin-2"/>
  33. <span>删除</span>
  34. </el-button>
  35. </HcTooltip>
  36. <HcTooltip keys="tentative_device_employ_printer">
  37. <el-button hc-btn :disabled="tableCheckedKeys.length <= 0" :loading="printerLoading" @click="printerClick">
  38. <HcIcon name="printer"/>
  39. <span>打印</span>
  40. </el-button>
  41. </HcTooltip>
  42. <HcTooltip keys="tentative_device_employ_import">
  43. <el-button hc-btn @click="importModalClick">
  44. <HcIcon name="folder-upload"/>
  45. <span>导入</span>
  46. </el-button>
  47. </HcTooltip>
  48. </template>
  49. <template #search>
  50. <div class="w-64">
  51. <HcDatePicker :dates="betweenTime" clearable @change="betweenTimeUpdate" />
  52. </div>
  53. <div class="w-72 ml-2">
  54. <el-input v-model="searchForm.queryValue" placeholder="请输入设备名称\设备型号查询" clearable @keyup="keyUpEvent"/>
  55. </div>
  56. <div class="ml-2">
  57. <el-button type="primary" @click="searchClick">
  58. <HcIcon name="search-2"/>
  59. <span>搜索</span>
  60. </el-button>
  61. </div>
  62. </template>
  63. <HcTable ref="tableRef" :column="tableColumn" :datas="tableData" :loading="tableLoading" isCheck @selection-change="tableSelection"/>
  64. <template #action>
  65. <HcPages :pages="searchForm" @change="pageChange"/>
  66. </template>
  67. </HcCard>
  68. </div>
  69. <!--新增/编辑-->
  70. <HcDialog :show="addEditFormModal" title="新增/编辑 设备使用信息" widths="50rem" :loading="addEditFormLoading" @close="addEditFormModalClose" @save="addEditFormClick">
  71. <el-form ref="addEditFormRef" :model="addEditFormModel" :rules="addEditFormRules" label-width="auto" size="large">
  72. <div class="hc-form-item">
  73. <el-form-item label="设备分类">
  74. <el-select :disabled="deviceFormLoading" v-model="addEditFormModel.deviceClassId" block @change="deviceClassIdChange">
  75. <el-option v-for="item in typeData" :label="item.className" :value="item.id"/>
  76. </el-select>
  77. </el-form-item>
  78. <el-form-item label="检测项目">
  79. <HcSelectTree v-model="addEditFormModel.nodeId" :title="addEditFormModel.nodeName" :projectId="projectId" :contractId="contractId" :wbsId="projectInfo?.referenceWbsTemplateIdTrial" @change="HcSelectTreeChange"/>
  80. </el-form-item>
  81. </div>
  82. <div class="hc-form-item">
  83. <el-form-item label="设备编号" prop="deviceNumber">
  84. <el-select :disabled="deviceFormLoading" v-model="addEditFormModel.deviceNumber" block @change="deviceNumberChange">
  85. <el-option v-for="item in deviceFormSelectData" :label="item.deviceNumber" :value="item.deviceNumber"/>
  86. </el-select>
  87. </el-form-item>
  88. <el-form-item label="样品名称">
  89. <hcAutoComplete v-model="addEditFormModel.materialName" :datas="nodeMaterialSelectData" keys="materialName" placeholder="请选择或输入样品名称"/>
  90. </el-form-item>
  91. </div>
  92. <div class="hc-form-item">
  93. <el-form-item label="设备名称">
  94. <el-select :disabled="deviceFormLoading" v-model="addEditFormModel.deviceName" block @change="deviceNameChange">
  95. <el-option v-for="item in deviceFormSelectData" :label="item.deviceName" :value="item.deviceName"/>
  96. </el-select>
  97. </el-form-item>
  98. <el-form-item label="样品编号">
  99. <hcAutoComplete v-model="addEditFormModel.specificationNumber" :datas="nodeMaterialSelectData" keys="specificationNumber" placeholder="请选择或输入样品编号"/>
  100. </el-form-item>
  101. </div>
  102. <div class="hc-form-item">
  103. <el-form-item label="使用日期(起)" prop="startDate">
  104. <el-date-picker type="date" v-model="addEditFormModel.startDate" class="block" value-format="YYYY-MM-DD" :clearable="false"/>
  105. </el-form-item>
  106. <el-form-item label="使用日期(止)" prop="endDate">
  107. <el-date-picker type="date" v-model="addEditFormModel.endDate" class="block" value-format="YYYY-MM-DD" :clearable="false"/>
  108. </el-form-item>
  109. </div>
  110. <div class="hc-form-item">
  111. <el-form-item label="使用人员">
  112. <hcAutoComplete v-model="addEditFormModel.managerName" :datas="userListData" keys="userName" placeholder="请选择或输入使用人员名称"/>
  113. </el-form-item>
  114. <el-form-item/>
  115. </div>
  116. <el-form-item label="备注">
  117. <el-input v-model="addEditFormModel.remarks" type="textarea" placeholder="备注" :autosize="{ minRows: 3}"/>
  118. </el-form-item>
  119. </el-form>
  120. </HcDialog>
  121. <!--导入-->
  122. <HcDialog :show="importModal" title="导入设备使用数据" widths="70rem" isTable isFooterCenter :loading="importModalLoading" @save="importModalYesClick" @close="importModalClose">
  123. <div class="text-orange mb-6">
  124. <span>请先下载模板模板表格 (</span>
  125. <span class="text-link" @click="downloadImportClick">范例试验设备文件</span>
  126. <span>) ,按模板样式编辑试验容器后,在点击“选择文件”按钮选择编辑好的文件,点击确认即可导入成功!</span>
  127. </div>
  128. <HcDragUpload action="use/read-excel" @progress="uploadprogress" @finished="uploadFinished"/>
  129. <div class="hc-import-modal-table-box">
  130. <HcTable ref="tableImportRef" :column="tableColumn" :datas="tableImportData" :loading="tableImportLoading" isCheck @selection-change="tableImportSelection">
  131. <template #status="{row}">
  132. {{row.status == 1?'启用中':'已停用'}}
  133. </template>
  134. <template #isCalibration="{row}">
  135. {{row.isCalibration == 1?'是':'否'}}
  136. </template>
  137. </HcTable>
  138. </div>
  139. </HcDialog>
  140. </div>
  141. </template>
  142. <script setup>
  143. import {ref, onMounted, watch} from "vue";
  144. import {useAppStore} from "~src/store";
  145. import HcDragUpload from "./components/HcDragUpload.vue"
  146. import HcSelectTree from "./components/HcSelectTree.vue"
  147. import {getClassList, queryDeviceList, querySampleList,getSampleInfo} from "~api/tentative";
  148. import {getContractUserList} from "~api/other";
  149. import {formValidate, getArrValue,getObjValue} from "vue-utils-plus"
  150. import dataApi from "~api/tentative/device/employ";
  151. import {getRowsValue, rowsToId} from "~uti/tools";
  152. //初始变量
  153. const useAppState = useAppStore()
  154. const projectId = ref(useAppState.getProjectId);
  155. const contractId = ref(useAppState.getContractId);
  156. const projectInfo = ref(useAppState.getProjectInfo);
  157. const isCollapse = ref(useAppState.getCollapse)
  158. //监听
  159. watch(() => [
  160. useAppState.getCollapse
  161. ], ([Collapse]) => {
  162. isCollapse.value = Collapse
  163. })
  164. //渲染完成
  165. onMounted(() => {
  166. getClassListData()
  167. getTableData()
  168. getUserListData()
  169. })
  170. //获取用户列表
  171. const userListData = ref([])
  172. const getUserListData = async () => {
  173. const { data } = await getContractUserList({
  174. contractId: contractId.value
  175. })
  176. userListData.value = getArrValue(data)
  177. }
  178. //左侧菜单
  179. const menuKey = ref('0')
  180. const menuItem = ref({})
  181. const menusProps = ref({
  182. key: 'id',
  183. label: 'className'
  184. })
  185. //获取菜单数据
  186. const menus = ref([]);
  187. const typeData = ref([]);
  188. const getClassListData = async () => {
  189. const { data } = await getClassList({
  190. contractId: contractId.value
  191. })
  192. const arr = getArrValue(data)
  193. typeData.value = arr
  194. menus.value = [{id: '0', className: '全部', isNoContextMenu: true}, ...arr]
  195. }
  196. //菜单被点击
  197. const menuChange = (item) => {
  198. menuItem.value = item
  199. menuKey.value = item?.id
  200. searchForm.value.deviceClassId = item?.id
  201. searchForm.value.current = 1;
  202. getTableData()
  203. }
  204. //搜索表单
  205. const searchForm = ref({
  206. deviceClassId: '', startTime: null, endTime: null, queryValue: null,
  207. current: 1, size: 20, total: 0
  208. })
  209. //日期时间被选择
  210. const betweenTime = ref(null)
  211. const betweenTimeUpdate = ({arr}) => {
  212. betweenTime.value = arr
  213. if (arr.length > 0) {
  214. searchForm.value.startTime = arr[0]
  215. searchForm.value.endTime = arr[1]
  216. }else{
  217. searchForm.value.startTime = ''
  218. searchForm.value.endTime = ''
  219. }
  220. }
  221. //回车搜索
  222. const keyUpEvent = (e) => {
  223. if (e.key === "Enter") {
  224. searchForm.value.current = 1;
  225. getTableData()
  226. }
  227. }
  228. //搜索
  229. const searchClick = () => {
  230. searchForm.value.current = 1;
  231. getTableData()
  232. }
  233. //分页被点击
  234. const pageChange = ({current, size}) => {
  235. searchForm.value.current = current
  236. searchForm.value.size = size
  237. getTableData()
  238. }
  239. //表格数据
  240. const tableRef = ref(null)
  241. const tableColumn = ref([
  242. {key:'deviceName', name: '设备名称'},
  243. {key:'deviceClassName', name: '设备分类'},
  244. {key:'deviceNumber', name: '设备编号'},
  245. {key:'deviceModel', name: '设备型号'},
  246. {key:'factoryNumber', name: '出厂编号'},
  247. {key:'startDate', name: '使用日期(起)'},
  248. {key:'endDate', name: '使用日期(止)'},
  249. {key:'nodeName', name: '检测试验项目'},
  250. {key:'specificationNumber', name: '样品编号'},
  251. {key:'materialName', name: '样品名称'},
  252. {key:'managerName', name: '使用人'},
  253. {key:'remarks', name: '备注'},
  254. ])
  255. const tableData = ref([])
  256. //获取数据
  257. const tableLoading = ref(false)
  258. const getTableData = async () => {
  259. tableLoading.value = true
  260. const { error, code, data } = await dataApi.queryPage({
  261. projectId: projectId.value,
  262. contractId: contractId.value,
  263. ...searchForm.value,
  264. })
  265. //处理数据
  266. tableLoading.value = false
  267. if (!error && code === 200) {
  268. tableData.value = getArrValue(data['records'])
  269. searchForm.value.total = data.total || 0
  270. } else {
  271. tableData.value = []
  272. searchForm.value.total = 0
  273. }
  274. }
  275. //多选
  276. const tableCheckedKeys = ref([]);
  277. const tableSelection = (rows) => {
  278. tableCheckedKeys.value = rows
  279. }
  280. //删除
  281. const delNodeModalClick = () => {
  282. window?.$messageBox?.alert('请谨慎考虑后,确认是否需要删除?', '删除提醒', {
  283. showCancelButton: true,
  284. confirmButtonText: '确认删除',
  285. cancelButtonText: '取消',
  286. type: 'warning',
  287. callback: (action) => {
  288. if (action === 'confirm') {
  289. tableRemoveData()
  290. }
  291. }
  292. })
  293. }
  294. //批量删除
  295. const tableRemoveData = async () => {
  296. const rows = tableCheckedKeys.value
  297. if (rows.length > 0 ) {
  298. const ids = rowsToId(rows)
  299. //删除请求
  300. const { error, code } = await dataApi.removeData({
  301. contractId: contractId.value,
  302. ids: ids,
  303. })
  304. //处理数据
  305. if (!error && code === 200) {
  306. window?.$message?.success('操作成功')
  307. searchClick()
  308. }
  309. }
  310. }
  311. //打印
  312. const printerLoading = ref(false)
  313. const printerClick = async () => {
  314. const rows = tableCheckedKeys.value
  315. if (rows.length > 0 ) {
  316. printerLoading.value = true
  317. const ids = rowsToId(rows)
  318. //删除请求
  319. const { error, code, data } = await dataApi.exportPdf({
  320. projectId: projectId.value,
  321. contractId: contractId.value,
  322. ids: ids,
  323. })
  324. //处理数据
  325. printerLoading.value = false
  326. if (!error && code === 200) {
  327. window.open(data,'_blank')
  328. }
  329. }
  330. }
  331. //导入
  332. const importModal = ref(false)
  333. const importModalClick = () => {
  334. tableImportData.value = []
  335. importModal.value = true
  336. }
  337. //上传进度
  338. const uploadprogress = (res) => {
  339. importModalLoading.value = res
  340. tableImportLoading.value = res
  341. }
  342. //上传完成
  343. const uploadFinished = (data) => {
  344. tableImportData.value = getArrValue(data)
  345. }
  346. //确认导入
  347. const importModalLoading = ref(false)
  348. const importModalYesClick = () => {
  349. const keys = tableImportKeys.value
  350. if (keys.length > 0) {
  351. samplingRecord(keys)
  352. } else {
  353. window?.$message?.warning('请先勾选要导入的数据')
  354. }
  355. }
  356. //导入数据保存
  357. const samplingRecord = async (keys) => {
  358. importModalLoading.value = true
  359. const { error, code } = await dataApi.samplingRecord({
  360. projectId: projectId.value,
  361. contractId: contractId.value,
  362. list: keys
  363. })
  364. //处理数据
  365. importModalLoading.value = false
  366. if (!error && code === 200) {
  367. importModal.value = false
  368. getTableData()
  369. } else {
  370. importModal.value = false
  371. }
  372. }
  373. //关闭导入
  374. const importModalClose = () => {
  375. importModal.value = false
  376. }
  377. //表格数据
  378. const tableImportRef = ref(null)
  379. const tableImportLoading = ref(false)
  380. const tableImportData = ref([])
  381. //多选
  382. const tableImportKeys = ref([]);
  383. const tableImportSelection = (rows) => {
  384. tableImportKeys.value = rows
  385. }
  386. //新增/编辑
  387. const addEditFormModal = ref(false)
  388. const addFormModalClick = () => {
  389. addEditFormModel.value = {}
  390. addEditFormModal.value = true
  391. }
  392. //编辑
  393. const editFormModalClick = () => {
  394. const keys = tableCheckedKeys.value
  395. if (keys.length === 1) {
  396. addEditFormModel.value = keys[0]
  397. addEditFormModal.value = true
  398. } else if (keys.length > 1) {
  399. window?.$message?.warning('只能选择一条数据编辑')
  400. }
  401. }
  402. const addEditFormModalClose = () => {
  403. addEditFormModal.value = false
  404. addEditFormModel.value = {}
  405. }
  406. //新增/编辑 表单
  407. const addEditFormRef = ref(null)
  408. const addEditFormModel = ref({
  409. deviceClassId:'',
  410. nodeName:'',
  411. deviceNumber:'',
  412. materialName:'',
  413. deviceName:'',
  414. specificationNumber:'',
  415. startDate:'',
  416. endDate:'',
  417. managerName:'',
  418. remarks:'',
  419. })
  420. const addEditFormRules = {
  421. startDate: {
  422. required: false,
  423. validator: (rule, value, callback) => {
  424. const endTime = addEditFormModel.value?.endDate ?? ''
  425. if (endTime&&value > endTime) {
  426. callback(new Error('使用停止日期 不能 小于 使用起始日期'))
  427. } else {
  428. callback()
  429. }
  430. },
  431. trigger: "blur",
  432. },
  433. endDate: {
  434. required: false,
  435. validator: (rule, value, callback) => {
  436. const startTime = addEditFormModel.value?.startDate ?? ''
  437. if (value < startTime) {
  438. callback(new Error('使用停止日期 不能 小于 使用起始日期'))
  439. } else {
  440. callback()
  441. }
  442. },
  443. trigger: "blur",
  444. },
  445. deviceNumber:{
  446. required: true,
  447. trigger: 'blur',
  448. message: "请选择设备编号"
  449. }
  450. }
  451. //分类被选择
  452. const deviceFormLoading = ref(false)
  453. const deviceFormSelectData = ref([])
  454. const deviceClassIdChange = async (val) => {
  455. deviceFormLoading.value = true
  456. const {data} = await queryDeviceList({
  457. projectId: projectId.value,
  458. contractId: contractId.value,
  459. deviceClassId: val
  460. })
  461. //处理数据
  462. const records = getArrValue(data)
  463. deviceFormSelectData.value = records
  464. if (records.length > 0) {
  465. addEditFormModel.value.deviceNumber = records[0].deviceNumber
  466. addEditFormModel.value.deviceName = records[0].deviceName
  467. addEditFormModel.value.deviceInfoId = records[0].id
  468. } else {
  469. addEditFormModel.value.deviceNumber = ''
  470. addEditFormModel.value.deviceName = ''
  471. addEditFormModel.value.deviceInfoId = ''
  472. }
  473. deviceFormLoading.value = false;
  474. }
  475. //设备编号
  476. const deviceNumberChange = (val) => {
  477. addEditFormModel.value.deviceName = getRowsValue(deviceFormSelectData.value, 'deviceNumber', 'deviceName', val)
  478. addEditFormModel.value.deviceInfoId = getRowsValue(deviceFormSelectData.value, 'deviceNumber', 'id', val)
  479. }
  480. //设备名称
  481. const deviceNameChange = (val) => {
  482. addEditFormModel.value.deviceNumber = getRowsValue(deviceFormSelectData.value, 'deviceName', 'deviceNumber', val)
  483. addEditFormModel.value.deviceInfoId = getRowsValue(deviceFormSelectData.value, 'deviceName', 'id', val)
  484. }
  485. //检测项目被选择
  486. const HcSelectTreeChange = ({title, primaryKeyId}) => {
  487. addEditFormModel.value.nodeName = title
  488. addEditFormModel.value.nodeId = primaryKeyId
  489. nodeMaterialChange(primaryKeyId)
  490. }
  491. //分类被选择
  492. const nodeMaterialLoading = ref(false)
  493. const nodeMaterialSelectData = ref([])
  494. const nodeMaterialChange = async (val) => {
  495. nodeMaterialLoading.value = true
  496. // const {data} = await querySampleList({
  497. // projectId: projectId.value,
  498. // contractId: contractId.value,
  499. // nodeId: val
  500. // })
  501. const {data} = await getSampleInfo({
  502. primaryKeyId: val
  503. })
  504. //处理数据
  505. const records = getObjValue(data)
  506. nodeMaterialSelectData.value = records
  507. if (records.length > 0) {
  508. addEditFormModel.value.specificationNumber = records?.sampleNames
  509. addEditFormModel.value.materialName = records?.sampleNumbers
  510. } else {
  511. addEditFormModel.value.specificationNumber = ''
  512. addEditFormModel.value.materialName = ''
  513. }
  514. nodeMaterialLoading.value = false;
  515. }
  516. //新增/编辑 保存
  517. const addEditFormLoading = ref(false)
  518. const addEditFormClick = async () => {
  519. console.log('提交');
  520. const validate = await formValidate(addEditFormRef.value)
  521. // console.log(validate,'validate');
  522. if (validate) {
  523. addEditFormLoading.value = true
  524. const { error, code } = await dataApi.submitForm({
  525. ...addEditFormModel.value,
  526. projectId: projectId.value,
  527. contractId: contractId.value
  528. })
  529. //处理数据
  530. if (!error && code === 200) {
  531. window?.$message?.success('操作成功')
  532. addEditFormModal.value = false
  533. addEditFormLoading.value = false
  534. await getTableData()
  535. }
  536. }
  537. }
  538. //下载导入模板
  539. const downloadImportClick = () => {
  540. window.open('https://bladex-test-info.oss-cn-chengdu.aliyuncs.com//upload/20221109/aaafe6673c0338eeeba8ff537980e093.xlsx','_blank')
  541. }
  542. //左右拖动,改变树形结构宽度
  543. const leftWidth = ref(240);
  544. const onmousedown = () => {
  545. const leftNum = isCollapse.value ? 142 : 272
  546. document.onmousemove = (ve) => {
  547. let diffVal = ve.clientX - leftNum;
  548. if(diffVal >= 220 && diffVal <= 400) {
  549. leftWidth.value = diffVal;
  550. }
  551. }
  552. document.onmouseup = () => {
  553. document.onmousemove = null;
  554. document.onmouseup = null;
  555. }
  556. }
  557. </script>
  558. <style lang="scss" scoped>
  559. .hc-import-modal-table-box {
  560. position: relative;
  561. height: calc(100% - 228px);
  562. margin-top: 25px;
  563. }
  564. </style>