index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. <template>
  2. <div class="h-full flex flex-col gap-y-3">
  3. <n-page-header @back="$router.back">
  4. <template #title> 设备管理 </template>
  5. </n-page-header>
  6. <n-space justify="space-between">
  7. <div style="display: flex;align-items: center;">
  8. <n-input style="width: 300px" v-model:value="queryData.T_sn" @clear="handleClear" clearable />
  9. <n-button type="primary" style="margin-left: 10px;" @click="getDataList"> 搜索 </n-button>
  10. <div style="padding-left: 20px;font-size: 18px;">
  11. 数量 :{{total}}
  12. </div>
  13. </div>
  14. <div style="display: flex;grid-gap: 20px;">
  15. <n-button type="primary" @click="copylistData">复制</n-button>
  16. <n-button type="primary" @click="affixlistData">粘贴</n-button>
  17. <n-button type="primary" @click="batchEditing">批量修改</n-button>
  18. <n-button type="primary" @click="showAddModal">批量导入</n-button>
  19. </div>
  20. </n-space>
  21. <n-data-table remote :columns="createColumn()"
  22. v-model:checked-row-keys="checkedRowKeysListRef"
  23. :row-key="rowKeyTable"
  24. :data="data" :bordered="false" flex-height class="flex-1" />
  25. </div>
  26. <n-modal :show-icon="false" v-model:show="modal.showModal" preset="dialog" style="width: 800px;" :title="modal.title">
  27. <div style="margin-bottom: 10px;display: flex;gap:10px" v-if="modal.title!='修改编号'&&modal.title != '批量修改'">
  28. <!-- <n-button type="primary" @click="showAddModalobj" v-if="modal.title!='修改编号'&&modal.title != '批量修改'">选择添加</n-button> -->
  29. <n-upload :default-upload="false" :max="1" @change="handleChange">
  30. <n-button type="primary">
  31. <template #icon>
  32. <n-icon><CloudUploadOutline /></n-icon>
  33. </template>
  34. 上传文件</n-button>
  35. </n-upload>
  36. <n-button @click="downloadFun" type="warning">
  37. <template #icon>
  38. <n-icon><CloudDownloadOutline /></n-icon>
  39. </template>
  40. 模板下载</n-button>
  41. </div>
  42. <n-form :model="formValue" label-width="auto" show-require-mark>
  43. <template v-if="modal.title === '批量导入'">
  44. <!-- <n-form-item label="布局编号" path="T_snid">
  45. <n-input v-model:value="formValue.T_snid" type="textarea" :autosize="{
  46. minRows: 3,
  47. maxRows: 5,
  48. }" placeholder="【方式一:001,002,003】【方式二:001~002,005】" />
  49. </n-form-item> -->
  50. <n-form-item label="布局编号" path="T_snid">
  51. <div>
  52. <n-table :border="true" :single-line="true">
  53. <thead>
  54. <tr>
  55. <th>SN</th>
  56. <th>布局编号</th>
  57. <th>操作</th>
  58. </tr>
  59. </thead>
  60. <tbody>
  61. <tr v-for="item,index in checkedTable">
  62. <td><n-input v-model:value="item.T_sn" placeholder="请输入SN"/></td>
  63. <td><n-input v-model:value="item.T_id" placeholder="请输入编号"/></td>
  64. <td >
  65. <n-button @click="delectRow(index)" type="error">删除行</n-button>
  66. </td>
  67. </tr>
  68. </tbody>
  69. <n-button @click="addRows" style="margin: 10px;" type="primary">
  70. <template #icon> <n-icon><Add /></n-icon> </template> 添加一行
  71. </n-button>
  72. <n-button v-show="checkedTable.length!=0" @click="checkedTable = []" style="margin: 10px;">
  73. <template #icon> <n-icon><TrashOutline /></n-icon> </template> 全部清空
  74. </n-button>
  75. </n-table>
  76. </div>
  77. </n-form-item>
  78. </template>
  79. <template v-else-if="modal.title != '批量修改'">
  80. <n-form-item label="编号" path="T_id">
  81. <n-input v-model:value="formValue.T_id" />
  82. </n-form-item>
  83. </template>
  84. <n-form-item label="备注(默认:均匀性布点)" path="T_remark">
  85. <n-radio-group v-model:value="formValue.T_remark" style="width: 100%;">
  86. <div style="display: grid;grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));grid-gap:5px 0;">
  87. <n-radio :value="item" v-for="item,index in selectData" :key="index">
  88. {{item}}
  89. </n-radio>
  90. </div>
  91. <div style="padding-top: 20rpx;">
  92. <n-radio :value="otherVal">
  93. 其他
  94. </n-radio>
  95. <n-input v-if="!selectData.includes(formValue.T_remark)" v-model:value="otherVal"
  96. type="textarea" />
  97. </div>
  98. </n-radio-group>
  99. </n-form-item>
  100. <div style="display: flex; justify-content: flex-end">
  101. <n-button @click="modal.showModal = false" style="margin-right: 20px;">
  102. 取消
  103. </n-button>
  104. <n-button type="primary" @click="submitCallback">
  105. 提交
  106. </n-button>
  107. </div>
  108. </n-form>
  109. <n-modal :show-icon="false" v-model:show="modal.showModalobj" style="width: 900px;" preset="dialog" title="选择添加">
  110. <div style="max-height: 700px;display: flex;flex-direction: column;overflow-y: hidden;">
  111. <n-form inline label-placement="left" :model="formValues">
  112. <!-- <n-form-item label="布局编号">
  113. <n-input v-model:value="formValues.T_layout_no" placeholder="输入布局编号" />
  114. </n-form-item> -->
  115. <n-form-item label="SN">
  116. <n-input v-model:value="formValues.T_sn" placeholder="输入SN" />
  117. </n-form-item>
  118. <n-form-item>
  119. <n-button type="primary" attr-type="button" @click="searchFun">
  120. 查询
  121. </n-button>
  122. </n-form-item>
  123. <n-form-item>
  124. <!-- <n-button type="primary" :disabled="checkedRowKeysRef.length==0" attr-type="button" @click="submitis">
  125. 立即导入
  126. </n-button> -->
  127. </n-form-item>
  128. </n-form>
  129. <div style="flex: 1;overflow-y: auto;">
  130. <n-data-table :columns="createColumns()" :data="checje.chkData" :remote="true" :max-height="480" :pagination="{
  131. pageSize: formValues.page_z,
  132. page:formValues.page,
  133. pageCount:pageData.pageCount,
  134. 'show-quick-jumper':true,
  135. 'show-size-picker':true,
  136. 'display-order':[ 'size-picker', 'pages','quick-jumper'],
  137. 'page-sizes':[10, 20, 30, 40],
  138. prefix() {
  139. return `共${pageData.pageNum}条`
  140. }}"
  141. @update:page="prevFun"
  142. :on-update:page-size="pagesizeFun"
  143. v-model:default-checked-row-keys="checkedTableData"
  144. :row-key="rowKey" @update:checked-row-keys="handleCheck">
  145. </n-data-table>
  146. </div>
  147. </div>
  148. </n-modal>
  149. </n-modal>
  150. </template>
  151. <script setup>
  152. import { Add,CloudUploadOutline,CloudDownloadOutline,TrashOutline } from '@vicons/ionicons5'
  153. import { h, reactive, ref, watch } from 'vue';
  154. import { NButton, NSpace, NPopconfirm, NInput ,useNotification } from 'naive-ui';
  155. import { downloadExcelFun,getDeviceClassDownload } from "@/utils/download";
  156. import { useStore } from 'vuex';
  157. import { read, utils } from 'xlsx';
  158. import {
  159. getDeviceClassListList,
  160. addDeviceClassList,
  161. editDeviceClassList,
  162. deleteDeviceClassList,
  163. getCertificateList,
  164. getDeviceClassCopy,
  165. } from '@/api';
  166. //添加一行
  167. const addRows = async ()=>{
  168. checkedTable.value.push({
  169. T_sn:'',
  170. T_id:''
  171. })
  172. }
  173. //删除一行
  174. const delectRow = async (row)=>{
  175. checkedTable.value.splice(row,1)
  176. }
  177. //模板下载
  178. const downloadFun = async ()=>{
  179. const reslut = await getDeviceClassDownload({})
  180. console.log('下载',reslut)
  181. downloadExcelFun(reslut)
  182. }
  183. // 组件状态变化的回调
  184. const handleChange = async ({ file }) => {
  185. const dataBinary = await readFile(file.file);
  186. const workBook = read(dataBinary, {
  187. type: 'binary',
  188. cellDates: true,
  189. });
  190. const workSheet = workBook.Sheets[workBook.SheetNames[0]];
  191. let list = utils.sheet_to_json(workSheet);
  192. let arr = list.map(obj => ({
  193. T_sn: obj.SN,
  194. T_id: obj['布局编号']
  195. }));
  196. checkedTable.value = [...checkedTable.value,...arr]
  197. };
  198. /* 读取文件 */
  199. const readFile = (file) => {
  200. return new Promise((resolve) => {
  201. const reader = new FileReader();
  202. reader.readAsBinaryString(file);
  203. reader.onload = (ev) => {
  204. resolve(ev.target.result);
  205. };
  206. });
  207. };
  208. const task = window.sessionStorage.getItem('task')
  209. ? JSON.parse(window.sessionStorage.getItem('task'))
  210. : {};
  211. const checje = reactive({
  212. Selecteds: null,
  213. chkData: []
  214. })
  215. const checkedRowKeysRef = ref([]);
  216. const checkedTable = ref([])
  217. const checkedTableData = ref([])
  218. const selectData = ref([
  219. '均匀性布点','作业出入口测点','1号作业出入口测点',
  220. '2号作业出入口测点','1号风机测点','2号风机测点',
  221. '风机回风口','1号风机回风口','2号风机回风口',
  222. '照明灯测点','除湿机测点','风幕机测点',
  223. '外部环境测点','作业口外部环境测点','产品存放区域测点',
  224. '温控传感器绑定点1','温控传感器绑定点2','温湿度监测绑定点1','温湿度监测绑定点2'
  225. ])
  226. const createColumns = () => [
  227. {
  228. type: "selection",
  229. },
  230. // {
  231. // title: "布局编号",
  232. // key: "T_layout_no",
  233. // // sortOrder: false,
  234. // // sorter (rowA, rowB) {
  235. // // return rowA.age - rowB.age
  236. // // }
  237. // },
  238. {
  239. title: "SN",
  240. key: "T_sn"
  241. }
  242. ];
  243. const rowKey = (row) => row.T_sn
  244. const handleCheck = (rowKeys) => {//点击选项框勾选存值
  245. checkedRowKeysRef.value = rowKeys;
  246. const arr = [...checkedRowKeysRef.value]
  247. const arr1 = []
  248. arr.forEach(item=>{
  249. arr1.push({T_sn:item,T_id:''})
  250. })
  251. checkedTable.value = arr1;
  252. }
  253. const submitis = ()=>{
  254. console.log(checkedRowKeysRef.value)
  255. return
  256. if(checkedRowKeysRef.value.length!=0){
  257. addDeviceClassList({
  258. T_class: queryData.T_class,
  259. T_layout_no_list:checkedRowKeysRef.value.join(',')
  260. }).then(res=>{
  261. if(res.data.Code==200){
  262. getDataList();
  263. message.success('导入成功')
  264. }
  265. });
  266. }
  267. }
  268. //上下页分页
  269. const prevFun = (e) => {
  270. formValues.page = e
  271. GetficateListApi()
  272. }
  273. //每页显示条数
  274. const pagesizeFun = (e) => {
  275. formValues.page_z = e
  276. GetficateListApi()
  277. }
  278. const pageData = reactive({
  279. pageCount: 0,
  280. pageNum: 0
  281. })
  282. const formValues = reactive({
  283. T_sn: '',//SN
  284. T_task_id:JSON.parse(sessionStorage.getItem('task')).T_task_id,
  285. // T_layout_no: '',//布局编号
  286. // T_layout_no_sort: '',//排序 0默认 1升2降
  287. page: 1,
  288. page_z: 10,
  289. });
  290. // 选择导入
  291. const showAddModalobj = () => {
  292. // formValues.page = 1
  293. checkedTableData.value = []
  294. modal.showModalobj = true;
  295. checkedTable.value.forEach(item=>{
  296. checkedTableData.value.push(item.T_sn)
  297. })
  298. };
  299. //搜索
  300. const searchFun = ()=>{
  301. formValues.page = 1
  302. GetficateListApi()
  303. }
  304. //列表Api
  305. const GetficateListApi = () => {
  306. getCertificateList(formValues).then(res => {
  307. let { data: resIt } = res
  308. if (resIt.Code == 200) {
  309. resIt.Data.List!=null?checje.chkData = resIt.Data.List:checje.chkData = []
  310. pageData.pageCount = resIt.Data.Page_size
  311. pageData.pageNum = resIt.Data.Num
  312. }
  313. })
  314. }
  315. // 查询参数
  316. const queryData = reactive({
  317. T_sn: '',
  318. T_class: task.T_class,
  319. });
  320. const otherVal = ref('')
  321. watch(otherVal, (newval) => {
  322. console.log('变化',newval)
  323. formValue.T_remark = newval
  324. })
  325. let checkedRowKeysListRef = ref([])//列表勾选的选项
  326. const rowKeyTable = (row) => row.Id + '|' + row.T_id
  327. const store = useStore()
  328. const message = useMessage()
  329. /**
  330. * 复制
  331. */
  332. const copylistData = async ()=>{
  333. let initPomes = {
  334. T_copy_task_id:task.T_task_id,//复制的T_task_id
  335. T_paste_task_id:''//粘贴的T_task_id
  336. }
  337. store.commit('setcopy',initPomes)
  338. message.success('复制成功');
  339. }
  340. /**
  341. * 粘贴
  342. */
  343. const affixlistData = async ()=>{
  344. let initPomes = {...store.state.setcopyData}
  345. if(initPomes.T_copy_task_id==''){
  346. message.error('还未复制内容哦');
  347. return
  348. }
  349. initPomes.T_paste_task_id = task.T_task_id
  350. const reslut = await getDeviceClassCopy(initPomes)
  351. if(reslut.data.Code==200){
  352. message.success('粘贴成功');
  353. getDataList();
  354. }
  355. }
  356. //批量编辑
  357. const batchEditing = async ()=>{
  358. if (checkedRowKeysListRef.value.length==0) {
  359. message.error('批量修改至少选择一项');
  360. return
  361. }
  362. modal.title = '批量修改';
  363. modal.showModal = true;
  364. formValue.T_remark = '均匀性布点';
  365. }
  366. const createColumn = () => [
  367. {
  368. type: "selection",
  369. options: [
  370. {
  371. label: '反选',
  372. key: 'f2',
  373. onSelect: (pageData) => {
  374. let arrMap = [...checkedRowKeysListRef.value]
  375. let result = pageData.filter(itemA => !arrMap.find(itemB => (itemA.Id + '|' + itemA.T_id) === itemB));
  376. const valuesArray = result.map(item => item.Id + '|' + item.T_id);
  377. checkedRowKeysListRef.value = valuesArray
  378. }
  379. }
  380. ],
  381. },
  382. {
  383. title: '布局编号',
  384. key: 'T_id',
  385. },
  386. {
  387. title: '证书编号',
  388. key: 'T_Certificate_sn',
  389. },
  390. {
  391. title: '设备编号',
  392. key: 'T_sn',
  393. },
  394. {
  395. title: '证书有效期',
  396. key: 'T_failure_time',
  397. },
  398. {
  399. title: '备注',
  400. key: 'T_remark',
  401. },
  402. {
  403. title: '操作',
  404. key: 'actions',
  405. render(row) {
  406. return h(
  407. NSpace,
  408. {},
  409. {
  410. default: () =>
  411. ['修改', '删除'].map((item) => {
  412. if (item === '修改') {
  413. return h(
  414. NButton,
  415. {
  416. type: 'primary',
  417. size: 'small',
  418. onClick: () => showEditModal(row),
  419. },
  420. { default: () => item }
  421. );
  422. } else {
  423. return h(
  424. NPopconfirm,
  425. {
  426. onPositiveClick: () => deleteDeviceClass(row),
  427. },
  428. {
  429. default: () => '是否确认删除?',
  430. trigger: () =>
  431. h(
  432. NButton,
  433. {
  434. type: 'error',
  435. size: 'small',
  436. },
  437. { default: () => item }
  438. ),
  439. }
  440. );
  441. }
  442. }),
  443. }
  444. );
  445. },
  446. },
  447. ];
  448. // 设备分类-设备列表
  449. const data = ref([]);
  450. // 模态框数据源
  451. const modal = reactive({
  452. title: '',
  453. showModal: false,
  454. showModalobj: false,
  455. });
  456. watch(()=>modal.showModal,(newData)=>{
  457. if(!newData) checkedTable.value = []
  458. })
  459. // 获取表项中收集到的值的对象
  460. const formValue = reactive({
  461. T_snid: '',
  462. T_id: 0,
  463. T_remark: '',
  464. });
  465. // 输入框点击清空按钮时触发
  466. const handleClear = () => {
  467. queryData.T_sn = '';
  468. getDataList();
  469. };
  470. const notification = useNotification();
  471. //
  472. const submitCallback = async() => {
  473. if (modal.title === '批量导入') {
  474. if(checkedTable.value.length==0){
  475. message.error('缺少布局编号,请选择添加')
  476. return
  477. }else{
  478. const b = checkedTable.value.every(item => item.T_id!='')
  479. if(!b){
  480. message.error('请填写编号');
  481. }else{//可以提交
  482. console.log('提交',checkedTable.value)
  483. const arr = []
  484. checkedTable.value.forEach(item=>{
  485. arr.push(item.T_sn + ',' + item.T_id)
  486. })
  487. const code = await addDeviceClass(arr.join('|'),formValue.value);
  488. console.log('返回',code)
  489. if(code.Code==200){
  490. message.success(code.Msg);
  491. modal.showModal = false
  492. }
  493. }
  494. }
  495. }else if(modal.title == '批量修改'){
  496. let array = [...checkedRowKeysListRef.value]
  497. for (let i = 0; i < array.length; i++) {
  498. const { data: res } = await editDeviceClassList({
  499. Id: array[i].split('|')[0],
  500. T_id:array[i].split('|')[1],
  501. T_remark: formValue.T_remark,
  502. });
  503. if (i === array.length-1) {
  504. message.success('批量编辑完成');
  505. modal.showModal = false
  506. getDataList();
  507. }
  508. }
  509. } else {
  510. editDeviceClass();
  511. }
  512. };
  513. // 显示编辑
  514. const showEditModal = (row) => {
  515. console.log('显示编辑', row)
  516. if (!selectData.value.includes(row.T_remark)) {
  517. otherVal.value = row.T_remark;
  518. }
  519. modal.title = '修改编号';
  520. modal.showModal = true;
  521. formValue.Id = row.Id;
  522. formValue.T_id = `${row.T_id}`;
  523. formValue.T_remark = row.T_remark;
  524. };
  525. // 显示批量导入
  526. const showAddModal = () => {
  527. modal.title = '批量导入';
  528. modal.showModal = true;
  529. formValue.T_snid = '';
  530. formValue.T_remark = '均匀性布点';
  531. formValues.page = 1
  532. GetficateListApi()
  533. };
  534. // 删除
  535. const deleteDeviceClass = async (row) => {
  536. try {
  537. const { data: res } = await deleteDeviceClassList({
  538. Id: row.Id,
  539. });
  540. if (res.Code === 200) {
  541. message.success(res.Msg);
  542. getDataList();
  543. }
  544. } catch (e) {
  545. console.log(e);
  546. }
  547. };
  548. // 编辑编号
  549. const editDeviceClass = async () => {
  550. try {
  551. const { data: res } = await editDeviceClassList({
  552. Id: formValue.Id,
  553. T_id: formValue.T_id,
  554. T_remark: formValue.T_remark,
  555. });
  556. if (res.Code === 200) {
  557. message.success(res.Msg);
  558. modal.showModal = false
  559. getDataList();
  560. }
  561. } catch (e) {
  562. console.log(e);
  563. }
  564. };
  565. // 批量导入
  566. const addDeviceClass = async (T_sn_id_list, T_id) => {
  567. try {
  568. const { data: res } = await addDeviceClassList({
  569. T_class: queryData.T_class,
  570. T_sn_id_list,
  571. T_id,
  572. T_remark: formValue.T_remark,
  573. });
  574. return res;
  575. } catch (e) {
  576. console.log(e);
  577. } finally {
  578. getDataList();
  579. }
  580. };
  581. const total = ref(0)
  582. // 获取列表
  583. const getDataList = async () => {
  584. try {
  585. const { data: res } = await getDeviceClassListList(queryData);
  586. data.value = res.Data.List || [];
  587. total.value = res.Data.Num
  588. } catch (e) {
  589. console.log(e);
  590. }
  591. };
  592. getDataList();
  593. </script>
  594. <style scoped>
  595. table tbody {
  596. display:block;
  597. max-height:300px;
  598. overflow-y:scroll;
  599. -webkit-overflow-scrolling: touch;
  600. }
  601. table tbody::-webkit-scrollbar {
  602. display: none;
  603. }
  604. table thead, tbody tr {
  605. display:table;
  606. width:100%;
  607. table-layout:fixed;
  608. }
  609. table thead {
  610. /* width: calc( 100% - 1em ) */
  611. width: 100%
  612. }
  613. table thead th{ background:#ccc;}
  614. </style>