index.vue 76 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665
  1. <template>
  2. <div class="main-container">
  3. <ibps-container
  4. class="page"
  5. >
  6. <template>
  7. <ibps-crud
  8. key="istree"
  9. ref="crud"
  10. :data="listData"
  11. :toolbars="listConfig.toolbars"
  12. :search-form="listConfig.searchForm"
  13. :pk-key="pkKey"
  14. :columns="listConfig.columns"
  15. :loading="loading"
  16. :pagination="pagination"
  17. :display-field="tableTitle"
  18. :index-row="false"
  19. @sort-change="handleSortChange"
  20. @action-event="handleAction"
  21. @pagination-change="handlePaginationChange"
  22. >
  23. <template
  24. slot="posSlot"
  25. slot-scope="{row}"
  26. >
  27. <ibps-user-selector
  28. v-model="row.bianZhiBuMen"
  29. type="position"
  30. readonly-text="text"
  31. :multiple="true"
  32. :disabled="true"
  33. />
  34. </template>
  35. <template
  36. slot="userSlot"
  37. slot-scope="{row}"
  38. >
  39. <ibps-user-selector
  40. v-model="row.guanLiRen"
  41. type="user"
  42. readonly-text="text"
  43. :multiple="true"
  44. :disabled="true"
  45. />
  46. </template>
  47. <template
  48. slot="deviceSlot"
  49. slot-scope="{row}"
  50. >
  51. <ibps-custom-dialog
  52. v-model="row.weiHuFangShi"
  53. size="mini"
  54. template-key="sbbqdhk"
  55. multiple
  56. :disabled="true"
  57. type="dialog"
  58. class="custom-dialog"
  59. placeholder="请选择"
  60. icon="el-icon-search"
  61. />
  62. </template>
  63. <template
  64. slot="deviceStateSlot"
  65. slot-scope="{row}"
  66. >
  67. <span>{{ stateList[row.sheBeiZhuangTa] || row.sheBeiZhuangTa }}</span>
  68. </template>
  69. <template
  70. slot="placeSlot"
  71. slot-scope="{row}"
  72. >
  73. <ibps-custom-dialog
  74. v-model="row.cunFangWeiZhi"
  75. size="mini"
  76. template-key="fjxzkdd"
  77. multiple
  78. :disabled="true"
  79. type="dialog"
  80. class="custom-dialog"
  81. placeholder="请选择"
  82. icon="el-icon-search"
  83. />
  84. </template>
  85. <template
  86. slot="customButton"
  87. slot-scope="{row}"
  88. >
  89. <el-button type="text" icon="el-icon-edit-outline" @click="goEdit(row)">修改</el-button>
  90. <!-- <el-button type="text" icon="el-icon-view" @click="goLook(row)">查阅</el-button> -->
  91. <el-button type="text" icon="ibps-icon-table" @click="goLookForm(row)">表单</el-button>
  92. </template>
  93. <template
  94. slot="expandSlot"
  95. slot-scope="{row}"
  96. >
  97. <el-row :gutter="20" style="height:145px;padding:0 20px 0 0" type="flex" align="middle">
  98. <el-col :span="5" :push="2">
  99. <el-row>
  100. <el-col :span="3">
  101. <el-image
  102. class="icon-image"
  103. :src="images[0]"
  104. fit="contain"
  105. />
  106. </el-col>
  107. <el-col :span="21">
  108. <div class="title">验收信息</div>
  109. <div class="ctx">
  110. <div class="item">接收日期:{{ row.jieShouRiQi || '/' }}</div>
  111. <div class="item">验收日期:{{ row.yanShouRiQi || '/' }}</div>
  112. <div class="item">核查日期:{{ row.biXuSheShi || '/' }}</div>
  113. </div>
  114. </el-col>
  115. </el-row>
  116. </el-col>
  117. <el-col :span="5" :push="2">
  118. <el-row>
  119. <el-col :span="3">
  120. <el-image
  121. class="icon-image"
  122. :src="images[1]"
  123. fit="contain"
  124. />
  125. </el-col>
  126. <el-col :span="21">
  127. <div class="title">建档信息</div>
  128. <div class="ctx">
  129. <div class="item">
  130. <div class="cusitem">
  131. <span class="span">建档人:</span>
  132. <ibps-user-selector
  133. :value="row.bianZhiRen"
  134. type="user"
  135. readonly-text="text"
  136. :multiple="true"
  137. :disabled="true"
  138. size="mini"
  139. style="width:100px"
  140. />
  141. </div>
  142. </div>
  143. <div class="item">
  144. <div class="cusitem">
  145. <span class="span">建档部门:</span>
  146. <ibps-user-selector
  147. :value="row.bianZhiBuMen"
  148. type="position"
  149. readonly-text="text"
  150. :disabled="true"
  151. :multiple="false"
  152. size="mini"
  153. style="width:100px"
  154. />
  155. </div>
  156. </div>
  157. <div class="item">建档时间:{{ row.bianZhiShiJian || '/' }}</div>
  158. </div>
  159. </el-col>
  160. </el-row>
  161. </el-col>
  162. <el-col :span="5" :push="2">
  163. <el-row>
  164. <el-col :span="3">
  165. <el-image
  166. class="icon-image"
  167. :src="images[2]"
  168. fit="contain"
  169. />
  170. </el-col>
  171. <el-col :span="21">
  172. <div class="title">维护信息</div>
  173. <div class="ctx">
  174. <div class="item">是否维护:{{ row.shiFouWeiHu || '/' }}</div>
  175. <div class="item">是否24H开机:{{ row.jianKongYiJu || '/' }}</div>
  176. <div class="item">是否限用:{{ row.xiaoZhunWuCha || '/' }}</div>
  177. </div>
  178. </el-col>
  179. </el-row>
  180. </el-col>
  181. <el-col :span="5" :push="2">
  182. <el-row>
  183. <el-col :span="3">
  184. <el-image
  185. class="icon-image"
  186. :src="images[3]"
  187. fit="contain"
  188. />
  189. </el-col>
  190. <el-col :span="21">
  191. <div class="title">校准信息</div>
  192. <div class="ctx">
  193. <div class="item">是否校准:{{ row.shiFouXiaoZhun || '/' }}</div>
  194. <div class="item">校准周期:{{ row.xiaoZhunZQ?`${row.xiaoZhunZQ}月` : '/' }}</div>
  195. <div class="item">最近校准时间:{{ row.yiXiaoRiQi || '/' }}</div>
  196. </div>
  197. </el-col>
  198. </el-row>
  199. </el-col>
  200. <el-col :span="4" :push="1">
  201. <el-row>
  202. <el-col :span="24">
  203. <el-image
  204. class="device-image"
  205. :src="ImageUrl(row)"
  206. fit="fill"
  207. :preview-src-list="ImageAllUrl(row)"
  208. :title="`[${row.sheBeiMingCheng}]设备首图,点击查看更多`"
  209. >
  210. <div slot="error" class="image-slot">
  211. <el-empty class="device-image" description="暂无图片" :image-size="70" />
  212. </div>
  213. </el-image>
  214. </el-col>
  215. </el-row>
  216. </el-col>
  217. </el-row>
  218. </template>
  219. <!-- 搜索条件 -->
  220. <template slot="pos">
  221. <ibps-user-selector
  222. v-model="search.pos"
  223. type="position"
  224. readonly-text="text"
  225. :multiple="true"
  226. size="mini"
  227. :filter="filter"
  228. filtrate
  229. />
  230. </template>
  231. <template slot="time">
  232. <el-date-picker
  233. v-model="search.time"
  234. size="mini"
  235. type="daterange"
  236. :picker-options="pickerOptions"
  237. range-separator="至"
  238. start-placeholder="开始日期"
  239. end-placeholder="结束日期"
  240. align="right"
  241. value-format="yyyy-MM-dd"
  242. />
  243. </template>
  244. <template slot="nowNumber">
  245. <el-input v-model="search.nowNumber" size="mini" />
  246. </template>
  247. <template slot="preNumber">
  248. <el-input v-model="search.preNumber" size="mini" />
  249. </template>
  250. <template slot="deviceName">
  251. <el-input v-model="search.deviceName" size="mini" />
  252. </template>
  253. <template slot="deviceType">
  254. <el-select v-model="search.deviceType" placeholder="请选择" size="mini" :clearable="true">
  255. <el-option
  256. v-for="item in ['检验系统','通用设备','软件','信息系统']"
  257. :key="item"
  258. :label="item"
  259. :value="item"
  260. />
  261. </el-select>
  262. </template>
  263. <template slot="deviceStatus">
  264. <el-select v-model="search.deviceStatus" placeholder="请选择" size="mini" :clearable="true">
  265. <el-option
  266. v-for="(v,k) in stateList"
  267. :key="k"
  268. :label="v"
  269. :value="k"
  270. />
  271. </el-select>
  272. </template>
  273. <template slot="place">
  274. <el-input v-model="search.place" size="mini" />
  275. </template>
  276. <template slot="managePeople">
  277. <ibps-user-selector
  278. v-model="search.managePeople"
  279. type="user"
  280. readonly-text="text"
  281. :multiple="true"
  282. size="mini"
  283. :filter="filter"
  284. filtrate
  285. />
  286. </template>
  287. <template slot="deviceClass">
  288. <ibps-custom-dialog
  289. v-model="search.deviceClass"
  290. size="mini"
  291. template-key="sbbqdhk"
  292. multiple
  293. :disabled="false"
  294. type="dialog"
  295. class="custom-dialog"
  296. placeholder="请选择"
  297. icon="el-icon-search"
  298. />
  299. </template>
  300. </ibps-crud>
  301. </template>
  302. </ibps-container>
  303. <DeviceDialog v-if="deviceDialogShow" :params="params" :state-list="stateList" @close="close" />
  304. <input id="" ref="file1" type="file" name="" accept=".xlsx,.xls" @change="handleUploadChange1">
  305. <input id="" ref="file2" type="file" name="" accept=".xlsx,.xls" @change="handleUploadChange2">
  306. <custom-dialog
  307. :visible="customDialogVisible"
  308. :value="[]"
  309. template-key="sbfzpz"
  310. :dynamic-params="{}"
  311. @close="(visible) => (customDialogVisible = visible)"
  312. />
  313. <bpmn-formrender
  314. :visible="npmDialogFormVisible"
  315. def-id="1120718364969271296"
  316. @close="visible => npmDialogFormVisible = visible"
  317. />
  318. <DeviceTag :scan-visible="printVisible" :obj="printObj" :state-list="stateList" @scanOff="scanOff" />
  319. <el-dialog
  320. :close-on-click-modal="false"
  321. :close-on-press-escape="false"
  322. :top="'3vh'"
  323. :width="'90%'"
  324. class="js-custom-dialog"
  325. append-to-body
  326. :fullscreen="false"
  327. :visible.sync="iframeVisible"
  328. >
  329. <iframe :src="srcUrl" :height="'100%'" :width="'100%'" frameborder="0" scrolling="no" />
  330. </el-dialog>
  331. </div>
  332. </template>
  333. <script>
  334. import { getSetting } from '@/utils/query'
  335. import image01 from '@/assets/images/device/01.png'
  336. import image02 from '@/assets/images/device/02.png'
  337. import image03 from '@/assets/images/device/03.png'
  338. import image04 from '@/assets/images/device/04.png'
  339. import { getImage } from '@/api/platform/file/attachment'
  340. import xlsx from 'xlsx'
  341. import fs from 'file-saver'
  342. import DataTemplateFormrenderDialog from '@/business/platform/data/templaterender/form/dialog.vue'
  343. import ActionUtils from '@/utils/action'
  344. import FixHeight from '@/mixins/height'
  345. import ibpsUserSelector from '@/business/platform/org/selector'
  346. import DeviceDialog from './deviceDialog.vue'
  347. import { queryequipmentCard, removeEquipmentCard, getequipmentCard, saveEquipmentCard } from '@/api/platform/device/device'
  348. import CustomDialog from '@/business/platform/data/templaterender/custom-dialog/dialog'
  349. import dayjs from 'dayjs'
  350. import DeviceTag from '@/views/system/jbdScan/goods/deviceTag.vue'
  351. export default {
  352. components: {
  353. DeviceTag,
  354. DataTemplateFormrenderDialog,
  355. DeviceDialog,
  356. ibpsUserSelector,
  357. CustomDialog,
  358. IbpsCustomDialog: () => import('@/business/platform/data/templaterender/custom-dialog')
  359. },
  360. mixins: [FixHeight],
  361. data () {
  362. const { userId, level = {}, position } = this.$store.getters || {}
  363. return {
  364. filter: [{
  365. descVal: '1',
  366. includeSub: true,
  367. old: 'position',
  368. partyId: this.$store.getters.userInfo.employee.positions,
  369. partyName: '',
  370. scriptContent: '',
  371. type: 'user',
  372. userType: 'position'
  373. }],
  374. images: [image01, image02, image03, image04],
  375. ImportDeviceType: '',
  376. iframeVisible: false,
  377. srcUrl: '',
  378. printObj: [],
  379. printVisible: false,
  380. DialogVisible: true,
  381. npmDialogFormVisible: false,
  382. customDialogVisible: false,
  383. pickerOptions: {
  384. shortcuts: [{
  385. text: '最近一周',
  386. onClick (picker) {
  387. const end = new Date()
  388. const start = new Date()
  389. start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
  390. picker.$emit('pick', [start, end])
  391. }
  392. }, {
  393. text: '最近一个月',
  394. onClick (picker) {
  395. const end = new Date()
  396. const start = new Date()
  397. start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
  398. picker.$emit('pick', [start, end])
  399. }
  400. }, {
  401. text: '最近三个月',
  402. onClick (picker) {
  403. const end = new Date()
  404. const start = new Date()
  405. start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
  406. picker.$emit('pick', [start, end])
  407. }
  408. }]
  409. },
  410. params: {},
  411. deviceDialogShow: false,
  412. position: position,
  413. level: level.second || level.first,
  414. userId: userId,
  415. search: {
  416. pos: '',
  417. time: [],
  418. nowNumber: '',
  419. preNumber: '',
  420. deviceName: '',
  421. deviceType: '',
  422. deviceStatus: '',
  423. place: '',
  424. managePeople: '',
  425. deviceClass: ''
  426. },
  427. loading: false,
  428. pkKey: 'id', // 主键 如果主键不是pk需要传主键
  429. pkValue: '',
  430. templateKey: '',
  431. visible: false,
  432. categoryKey: '',
  433. tableTitle: '设备基本信息列表', // 标题
  434. listData: [],
  435. selectListData: [], // 全部数据
  436. bianlistData: {
  437. dataResult: [],
  438. pageResult: {
  439. limit: 0,
  440. page: 0,
  441. totalCount: 0,
  442. totalPages: 0
  443. }
  444. },
  445. listConfig: {
  446. // 工具栏
  447. toolbars: [
  448. { key: 'search' },
  449. { key: 'customAdd', label: '设备建档', icon: 'ibps-icon-plus', type: 'success' },
  450. { key: 'customBpm', label: '设备台账', icon: 'ibps-icon-file-text', type: 'info' },
  451. { key: 'customPrint', label: '打印标签', icon: 'ibps-icon-cog', type: 'warning' },
  452. { key: 'customExport', label: '导出数据', icon: 'ibps-icon-sign-in', type: 'primary' },
  453. { key: 'customImport', label: '导入数据', icon: 'ibps-icon-sign-in', type: 'primary' },
  454. { key: 'customSetting', label: '设置分组配置', icon: 'ibps-icon-cogs', type: 'info' },
  455. { key: 'customRemove', label: '删除', icon: 'ibps-icon-close', type: 'danger' }
  456. ],
  457. // 查询条件
  458. searchForm: {
  459. forms: [
  460. { prop: '', label: '部门', fieldType: 'slot', slotName: 'pos' }, // user 插槽
  461. { prop: '', label: '建档时间', fieldType: 'slot', slotName: 'time' },
  462. { prop: '', label: '设备编号', fieldType: 'slot', slotName: 'nowNumber' },
  463. { prop: '', label: '原设备编号', fieldType: 'slot', slotName: 'preNumber' },
  464. { prop: '', label: '设备名称', fieldType: 'slot', slotName: 'deviceName' },
  465. { prop: '', label: '设备类型', fieldType: 'slot', slotName: 'deviceType' },
  466. { prop: '', label: '设备状态', fieldType: 'slot', slotName: 'deviceStatus' },
  467. { prop: '', label: '放置地点', fieldType: 'slot', slotName: 'place' },
  468. { prop: '', label: '管理人', fieldType: 'slot', slotName: 'managePeople' },
  469. { prop: '', label: '设备分组', fieldType: 'slot', slotName: 'deviceClass' }
  470. ]
  471. },
  472. // 表格字段配置
  473. columns: [
  474. { type: 'expand', slotName: 'expandSlot' },
  475. { prop: 'bianZhiBuMen', label: '部门', slotName: 'posSlot', sortable: true },
  476. { prop: 'bianZhiShiJian', label: '建档时间', sortable: true },
  477. { prop: 'sheBeiShiBieH', label: '设备编号', sortable: true },
  478. { prop: 'yuanSheBeiBian', label: '原设备编号', sortable: true },
  479. { prop: 'sheBeiMingCheng', label: '设备名称', sortable: true },
  480. { prop: 'sheBeiLeiXing', label: '设备类型', sortable: true },
  481. { prop: 'guiGeXingHao', label: '规格型号', sortable: true },
  482. { prop: 'sheBeiZhuangTa', label: '设备状态', sortable: true, slotName: 'deviceStateSlot' },
  483. { prop: 'guanLiRen', label: '保管人', slotName: 'userSlot', sortable: true },
  484. { prop: 'weiHuFangShi', label: '设备分组', slotName: 'deviceSlot', sortable: true },
  485. { prop: 'cunFangWeiZhi', label: '放置地点', slotName: 'placeSlot', sortable: true },
  486. { prop: '', label: '操作', width: 130, slotName: 'customButton' }
  487. ]
  488. },
  489. pagination: {
  490. limit: 20, page: 1
  491. },
  492. sorts: [{ field: 'BIAN_ZHI_SHI_JIAN', order: 'desc' }],
  493. sqlWhere: {},
  494. searchWhere: {},
  495. deviceColumns: {
  496. bianZhiBuMen: '部门',
  497. sheBeiMingCheng: '设备名称',
  498. // sheBeiShiBieH: '设备编号(导入无需填写)',
  499. yuanSheBeiBian: '原设备编号(必填,且不可重复)',
  500. sheBeiZhuangTa: '设备状态(合格/停用/限用)',
  501. sheBeiLeiXing: '设备类型(检验系统/通用设备/软件/信息系统)',
  502. shiFouWeiHu: '是否维护(是/否)',
  503. shiFouXiaoZhun: '是否校准(是/否)',
  504. weiHuFangShi: '设备分组',
  505. guiGeXingHao: '规格型号',
  506. cunFangDiDian: '存放地点(格式:房间号+空格+房间名)',
  507. guanLiRen: '保管人',
  508. ziChanBianHao: '资产编号',
  509. gongYingShang: '供应商',
  510. lianXiFangShi: '联系方式',
  511. changShang: '厂商',
  512. jiShenXuHao: '机身序号',
  513. zhuCeZhengHao: '注册证号',
  514. chuChangRiQi: '出厂日期',
  515. yanShouRiQi: '验收日期',
  516. jieShouRiQi: '接收日期',
  517. qiYongRiQi: '投入日期',
  518. yiXiaoRiQi: '已校日期',
  519. xiaoZhunZQ: '检定/校准周期(以月为单位)',
  520. xiaoZhunYouXia: '校准有效期至',
  521. shiYongKeShi: '检定/校准单位',
  522. ceLiangGongZuo: '测量/工作范围',
  523. huanJingYaoQiu: '环境要求',
  524. dianYuanYaoQiu: '电源要求',
  525. jieShouZhuangTai: '接收时状态(新设备/二手或翻新设备)',
  526. jianDingXiao: '检定/校准参数',
  527. zuiDaYunCha: 'U/精确度/最大允差',
  528. zhengShuBianHa: '证书编号',
  529. xiuZhengZhiXiu: '修正值/修正因子',
  530. wenDuYingYong: '温度应用修正值',
  531. shiDuYingYong: '湿度应用修正值',
  532. biXuDeHuanJin: '核查人',
  533. biXuSheShi: '核查日期',
  534. heChaXiaoZhun: '使用年限(年)'
  535. },
  536. projectColums: {
  537. yuanSheBeiBian: '设备编号*',
  538. sheBeiMingCheng: '设备名称*',
  539. weiHuLeiXing: '维护类型*(日保养/周保养/月保养/季度保养/半年保养/年保养/按需保养)',
  540. weiHuRiQi: '维护日期*',
  541. weiHuXiangMuC: '维护项目*'
  542. },
  543. dateFieldRange: ['chuChangRiQi', 'yanShouRiQi', 'jieShouRiQi', 'qiYongRiQi', 'yiXiaoRiQi', 'xiaoZhunYouXia', 'biXuSheShi'],
  544. requiredFieldMap: {
  545. bianZhiBuMen: '部门',
  546. sheBeiMingCheng: '设备名称',
  547. yuanSheBeiBian: '原设备编号',
  548. sheBeiZhuangTa: '设备状态',
  549. sheBeiLeiXing: '设备类型',
  550. shiFouWeiHu: '是否维护',
  551. shiFouXiaoZhun: '是否校准'
  552. },
  553. dateFieldsMap: {
  554. 'chuChangRiQi': '出厂日期',
  555. 'yanShouRiQi': '验收日期',
  556. 'jieShouRiQi': '接收日期',
  557. 'qiYongRiQi': '投入日期',
  558. 'yiXiaoRiQi': '已校日期',
  559. 'xiaoZhunYouXia': '校准有效期至',
  560. 'biXuSheShi': '核查日期'
  561. },
  562. validationRules: {
  563. '设备状态': {
  564. field: 'sheBeiZhuangTa',
  565. range: ['合格', '停用', '限用']
  566. },
  567. '设备类型': {
  568. field: 'sheBeiLeiXing',
  569. range: ['检验系统', '通用设备', '软件', '信息系统']
  570. },
  571. '接收时状态': {
  572. field: 'jieShouZhuangTai',
  573. range: ['新设备', '二手或翻新设备']
  574. },
  575. '是否校准': {
  576. field: 'shiFouXiaoZhun',
  577. range: ['是', '否']
  578. },
  579. '是否维护': {
  580. field: 'shiFouWeiHu',
  581. range: ['是', '否']
  582. }
  583. },
  584. numberFieldsMap: {
  585. 'xiaoZhunZQ': '检定/校准周期(以月为单位)',
  586. 'heChaXiaoZhun': '使用年限(年)'
  587. },
  588. maintenanceRequiredFieldMap: {
  589. yuanSheBeiBian: '原设备编号',
  590. sheBeiMingCheng: '设备名称',
  591. weiHuLeiXing: '维护类型',
  592. weiHuRiQi: '维护日期',
  593. weiHuXiangMuC: '维护项目'
  594. },
  595. maintenanceValidationRules: {
  596. '维护类型': {
  597. field: 'weiHuLeiXing',
  598. range: ['日保养', '周保养', '月保养', '季度保养', '半年保养', '年保养', '按需保养']
  599. }
  600. },
  601. maintenanceDateValidationRules: {
  602. '日保养': this.generateDayRule(),
  603. '周保养': this.generateRule(7, `每周`, ``),
  604. '月保养': this.generateRule(28, `每月第`, `日`),
  605. '半年保养': this.generateRule(6, `每半年第`, `个月`),
  606. '季度保养': this.generateRule(3, `每季度第`, `个月`),
  607. '年保养': this.generateRule(12, `每年第`, `个月`),
  608. '按需保养': ['/']
  609. },
  610. stateList: { '停用': '停用', '报废': '报废', '合格': '合格' }
  611. }
  612. },
  613. async mounted () {
  614. const stateList = await getSetting('device', 'stateList')
  615. if (stateList) {
  616. console.debug(stateList)
  617. this.stateList = stateList
  618. }
  619. this.getDatas()
  620. },
  621. methods: {
  622. ImageAllUrl (row) {
  623. if (row && row.buMen) {
  624. const imgId = row.buMen.split(',')
  625. return imgId.map(item => getImage(item))
  626. }
  627. return []
  628. },
  629. ImageUrl (row) {
  630. if (row && row.buMen) {
  631. const imgId = row.buMen.split(',')[0]
  632. return getImage(imgId)
  633. }
  634. return ''
  635. },
  636. async getDatas () {
  637. this.loading = true
  638. const parameters = {
  639. relation: 'AND',
  640. parameters: []
  641. }
  642. // 增加地点过滤
  643. const obj = { relation: 'AND', parameters: [] }
  644. obj.parameters.push({ key: 'Q^di_dian_^S', value: this.level, param: this.$utils.guid() })
  645. parameters.parameters.push(obj)
  646. // 部门搜索(可多选)
  647. if (this.search.pos) {
  648. const obj = { relation: 'OR', parameters: [] }
  649. this.search.pos.split(',').forEach(item => {
  650. obj.parameters.push({ key: 'Q^bian_zhi_bu_men_^S', value: item, param: this.$utils.guid() })
  651. })
  652. parameters.parameters.push(obj)
  653. }
  654. // 建档时间搜索
  655. if (this.search.time && this.search.time.length === 2) {
  656. const obj = { relation: 'AND', parameters: [] }
  657. obj.parameters.push({ key: 'Q^bian_zhi_shi_jian^DL^yyyy-MM-dd', value: this.search.time[0], param: this.$utils.guid() })
  658. obj.parameters.push({ key: 'Q^bian_zhi_shi_jian^DG^yyyy-MM-dd', value: this.search.time[1], param: this.$utils.guid() })
  659. parameters.parameters.push(obj)
  660. }
  661. // 设备编号搜索
  662. if (this.search.nowNumber) {
  663. const obj = { relation: 'AND', parameters: [] }
  664. obj.parameters.push({ key: 'Q^she_bei_shi_bie_h^SL', value: this.search.nowNumber, param: this.$utils.guid() })
  665. parameters.parameters.push(obj)
  666. }
  667. // 原设备编号搜索
  668. if (this.search.preNumber) {
  669. const obj = { relation: 'AND', parameters: [] }
  670. obj.parameters.push({ key: 'Q^yuan_she_bei_bian^SL', value: this.search.preNumber, param: this.$utils.guid() })
  671. parameters.parameters.push(obj)
  672. }
  673. // 设备名称搜索
  674. if (this.search.deviceName) {
  675. const obj = { relation: 'AND', parameters: [] }
  676. obj.parameters.push({ key: 'Q^she_bei_ming_cheng_^SL', value: this.search.deviceName, param: this.$utils.guid() })
  677. parameters.parameters.push(obj)
  678. }
  679. // 设备类型搜索
  680. if (this.search.deviceType) {
  681. const obj = { relation: 'AND', parameters: [] }
  682. obj.parameters.push({ key: 'Q^she_bei_lei_xing_^S', value: this.search.deviceType, param: this.$utils.guid() })
  683. parameters.parameters.push(obj)
  684. }
  685. // 设备状态搜索
  686. if (this.search.deviceStatus) {
  687. const obj = { relation: 'AND', parameters: [] }
  688. obj.parameters.push({ key: 'Q^she_bei_zhuang_ta^S', value: this.search.deviceStatus, param: this.$utils.guid() })
  689. parameters.parameters.push(obj)
  690. }
  691. // 放置地点搜索
  692. if (this.search.place) {
  693. const obj = { relation: 'AND', parameters: [] }
  694. obj.parameters.push({ key: 'Q^cun_fang_di_dian_^SL', value: this.search.place, param: this.$utils.guid() })
  695. parameters.parameters.push(obj)
  696. }
  697. // 保管人搜索(可多选)
  698. if (this.search.managePeople) {
  699. const obj = { relation: 'OR', parameters: [] }
  700. this.search.managePeople.split(',').forEach(item => {
  701. obj.parameters.push({ key: 'Q^guan_li_ren_^S', value: item, param: this.$utils.guid() })
  702. })
  703. parameters.parameters.push(obj)
  704. }
  705. // 设备分组搜索(可多选)
  706. if (this.search.deviceClass) {
  707. const obj = { relation: 'OR', parameters: [] }
  708. this.search.deviceClass.split(',').forEach(item => {
  709. obj.parameters.push({ key: 'Q^wei_hu_fang_shi_^S', value: item, param: this.$utils.guid() })
  710. })
  711. parameters.parameters.push(obj)
  712. }
  713. const params = {
  714. requestPage: {
  715. pageNo: this.pagination.page,
  716. limit: this.pagination.limit
  717. },
  718. sorts: this.sorts
  719. }
  720. if (parameters.parameters.length > 0) {
  721. params.parameters = [parameters]
  722. }
  723. const { data: { dataResult, pageResult }} = await queryequipmentCard(params)
  724. this.bianlistData.pageResult = pageResult
  725. this.bianlistData.dataResult = dataResult
  726. ActionUtils.handleListData(this, this.bianlistData) // 调用内置方法
  727. this.loading = false
  728. },
  729. // 查看表单
  730. goLookForm (row) {
  731. const first = this.$store.getters.level.first
  732. this.srcUrl = this.$reportPath.replace('show', 'pdf') + '设备/设备档案卡.rpx&id_=' + row.id + '&org_=' + first
  733. this.iframeVisible = true
  734. },
  735. // 按钮事件处理
  736. handleAction (command, position, selection, data, index, button) {
  737. switch (command) {
  738. case 'search':// 查询
  739. this.getDatas()
  740. break
  741. case 'customAdd':
  742. this.handleCustomAdd()
  743. break
  744. case 'customSetting':
  745. this.handleCustomSetting()
  746. break
  747. case 'customRemove':
  748. this.handleCustomRemove(selection)
  749. break
  750. case 'customExport':
  751. this.handleCustomExport(selection, data)
  752. break
  753. case 'customImport':
  754. this.handleCustomImport()
  755. break
  756. case 'customBpm':
  757. this.handleCustomBpm()
  758. break
  759. case 'customPrint':
  760. this.handleCustomPrint(selection)
  761. break
  762. default:
  763. break
  764. }
  765. },
  766. // 打印标签
  767. handleCustomPrint (selection = []) {
  768. if (selection.length === 0) {
  769. return this.$message.warning('请先选择需要打印标签的数据!')
  770. }
  771. this.printObj = selection
  772. this.printVisible = true
  773. },
  774. // 关闭标签
  775. scanOff () {
  776. this.printVisible = false
  777. },
  778. // 设备台账
  779. handleCustomBpm () {
  780. this.npmDialogFormVisible = true
  781. },
  782. // 处理分页事件
  783. async handlePaginationChange (page) {
  784. ActionUtils.setPagination(this.pagination, page)
  785. this.getDatas()
  786. },
  787. // 处理排序
  788. handleSortChange (sort) {
  789. function removeUnderscores (str) {
  790. return str.replace(/^_+|_+$/g, '')
  791. }
  792. const { order, sortBy } = sort
  793. let s = ''
  794. switch (sortBy) {
  795. case 'BIAN_ZHI_SHI_JIAN_':
  796. case 'SHE_BEI_SHI_BIE_H_':
  797. case 'YUAN_SHE_BEI_BIAN_':
  798. case 'SHE_BEI_ZHUANG_TA_':
  799. s = removeUnderscores(sortBy)
  800. break
  801. default:
  802. s = sortBy
  803. }
  804. let o = null
  805. if (order === 'descending') {
  806. o = 'desc'
  807. } else if (order === 'ascending') {
  808. o = 'asc'
  809. }
  810. this.sorts = [{ field: s, order: o }]
  811. this.getDatas()
  812. },
  813. handleCustomAdd () {
  814. this.params = {}
  815. this.deviceDialogShow = true
  816. },
  817. close () {
  818. this.deviceDialogShow = false
  819. this.getDatas()
  820. },
  821. goEdit (row) {
  822. this.params = row
  823. this.deviceDialogShow = true
  824. },
  825. handleCustomSetting () {
  826. this.customDialogVisible = true
  827. },
  828. handleCustomRemove (selection) {
  829. console.log('selection', selection)
  830. if (!selection || selection.length === 0) {
  831. return this.$message.warning('请选择要删除的数据!')
  832. }
  833. this.$confirm('确定删除所选项?删除后无法恢复!', '提示', {
  834. confirmButtonText: '继续',
  835. cancelButtonText: '取消',
  836. type: 'warning'
  837. }).then(async () => {
  838. await removeEquipmentCard({
  839. ids: selection + ''
  840. })
  841. // 删除后刷新
  842. await this.getDatas()
  843. this.$message.success('删除成功!')
  844. }).catch(() => {})
  845. },
  846. // 导出
  847. handleCustomExport (selection, data) {
  848. this.$confirm('请选择导出的类型', '提示', {
  849. confirmButtonText: '导出设备',
  850. cancelButtonText: '导出维护项目',
  851. closeOnClickModal: false,
  852. closeOnPressEscape: false,
  853. distinguishCancelAndClose: true,
  854. type: 'info'
  855. }).then(() => {
  856. this.ExportDevice(selection, data)
  857. }).catch((action) => {
  858. if (action === 'close') return
  859. if (action === 'cancel') {
  860. this.ExportProject(selection, data)
  861. }
  862. })
  863. },
  864. // 导入
  865. handleCustomImport () {
  866. this.$confirm('确请选择导入的类型', '提示', {
  867. confirmButtonText: '导入设备',
  868. cancelButtonText: '导入维护项目',
  869. closeOnClickModal: false,
  870. closeOnPressEscape: false,
  871. distinguishCancelAndClose: true,
  872. type: 'info'
  873. }).then(() => {
  874. this.ImportDevice()
  875. }).catch((action) => {
  876. if (action === 'close') return
  877. if (action === 'cancel') {
  878. this.$confirm('请选择设备维护项目导入类型', '提示', {
  879. confirmButtonText: '增量添加',
  880. cancelButtonText: '全量替换',
  881. closeOnClickModal: false,
  882. closeOnPressEscape: false,
  883. distinguishCancelAndClose: true,
  884. type: 'info'
  885. }).then(() => {
  886. this.ImportDeviceType = '增量添加'
  887. this.ImportProject()
  888. }).catch((action) => {
  889. if (action === 'close') return
  890. if (action === 'cancel') {
  891. this.ImportDeviceType = '全量替换'
  892. this.ImportProject()
  893. }
  894. })
  895. }
  896. })
  897. },
  898. // 导入设备
  899. ImportDevice () {
  900. this.$refs.file1.click()
  901. console.log('导入设备')
  902. },
  903. // 导入维护项目
  904. ImportProject () {
  905. this.$refs.file2.click()
  906. console.log('导入维护项目')
  907. },
  908. getTimeStamp () {
  909. return dayjs().format('YYYYMMDDHHmmss')
  910. },
  911. xlsx (json, fields, filename = '.xlsx') { // 导出xlsx
  912. json.forEach(item => {
  913. for (const i in item) {
  914. if (fields.hasOwnProperty(i)) {
  915. item[fields[i]] = item[i]
  916. }
  917. delete item[i] // 删除原先的对象属性
  918. }
  919. })
  920. const sheetName = filename // excel的文件名称
  921. const wb = xlsx.utils.book_new() // 工作簿对象包含一SheetNames数组,以及一个表对象映射表名称到表对象。XLSX.utils.book_new实用函数创建一个新的工作簿对象。
  922. const ws = xlsx.utils.json_to_sheet(json, { header: Object.values(fields) }) // 将JS对象数组转换为工作表。
  923. wb.SheetNames.push(sheetName)
  924. wb.Sheets[sheetName] = ws
  925. const defaultCellStyle = { font: { name: 'Verdana', sz: 13, color: 'FF00FF88' }, fill: { fgColor: { rgb: 'FFFFAA00' }}}// 设置表格的样式
  926. const wopts = { bookType: 'xlsx', bookSST: false, type: 'binary', cellStyles: true, defaultCellStyle: defaultCellStyle, showGridLines: false } // 写入的样式
  927. const wbout = xlsx.write(wb, wopts)
  928. const blob = new Blob([this.s2ab(wbout)], { type: 'application/octet-stream' })
  929. fs.saveAs(blob, filename + '.xlsx')
  930. },
  931. s2ab (s) {
  932. let buf
  933. if (typeof ArrayBuffer !== 'undefined') {
  934. buf = new ArrayBuffer(s.length)
  935. const view = new Uint8Array(buf)
  936. for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff
  937. return buf
  938. } else {
  939. buf = new Array(s.length)
  940. for (let i = 0; i !== s.length; ++i) buf[i] = s.charCodeAt(i) & 0xFF
  941. return buf
  942. }
  943. },
  944. async switchExportData (data) {
  945. const deviceGroupSql = `select id_,wei_hu_gang_wei_ from t_sbwhgwpzb` // 设备分组信息
  946. const supplierSql = `select id_,gong_ying_shang_m from t_gysxxb` // 供应商信息
  947. const { variables: { data: deviceGroupData }} = await this.$common.request('sql', deviceGroupSql)
  948. const { variables: { data: gysData }} = await this.$common.request('sql', supplierSql)
  949. const exportData = JSON.parse(JSON.stringify(data))
  950. for (let i = 0; i < exportData.length; i++) {
  951. const item = exportData[i]
  952. item.bianZhiBuMen = this.switchIdToDept(item.bianZhiBuMen.split(',')[0])
  953. item.guanLiRen = this.switchIdToUserName(item.guanLiRen.split(',')[0])
  954. item.biXuDeHuanJin = this.switchIdToUserName(item.biXuDeHuanJin.split(',')[0])
  955. item.weiHuFangShi = this.switchDeviceIdToName(item.weiHuFangShi, deviceGroupData)
  956. item.shiYongKeShi = this.switchGYSIdToName(item.shiYongKeShi, gysData)
  957. if (this.stateList[item.sheBeiZhuangTa]) {
  958. item.sheBeiZhuangTa = this.stateList[item.sheBeiZhuangTa]
  959. }
  960. }
  961. return exportData
  962. },
  963. // 设备分组id 转 设备分组名称
  964. switchDeviceIdToName (val, deviceGroupList) {
  965. const result = []
  966. const valList = val?.split(',') || []
  967. valList.forEach(item => result.push((deviceGroupList?.find(i => i.id_ === item)?.wei_hu_gang_wei_) || ''))
  968. return result.join(',')
  969. },
  970. // 供应商id 转 供应商名称 检定/校准单位
  971. switchGYSIdToName (val, gysList) {
  972. const result = gysList.find(item => item.id_ === val)?.gong_ying_shang_m || ''
  973. return result
  974. },
  975. // 部门id 转 部门名称
  976. switchIdToDept (id) {
  977. const { deptList } = this.$store.getters
  978. const temp = deptList.find(item => item.positionId === id)
  979. return temp ? temp.positionName : ''
  980. },
  981. // 部门名称 转 部门id
  982. switchDeptToId (dep) {
  983. const { deptList } = this.$store.getters
  984. const temp = deptList.find(item => item.positionName === dep)
  985. return temp ? temp.positionId : ''
  986. },
  987. // 人员id 转人员名称
  988. switchIdToUserName (id) {
  989. const { userList } = this.$store.getters
  990. const temp = userList.find(item => item.userId === id)
  991. return temp ? temp.userName : ''
  992. },
  993. // 人员名称 转 人员id
  994. switchUserNameToId (name) {
  995. const { userList } = this.$store.getters
  996. const temp = userList.find(item => item.userName === name)
  997. return temp ? temp.userId : ''
  998. },
  999. // 导出设备
  1000. async ExportDevice (selection, data = []) {
  1001. const exportData = await this.switchExportData(data)
  1002. this.xlsx(exportData, this.deviceColumns, '设备档案卡基本数据' + this.getTimeStamp())
  1003. this.$message.success('导出设备成功!')
  1004. },
  1005. // 导出维护项目
  1006. async ExportProject (selection = []) {
  1007. let exportData = []
  1008. console.log('导出维护项目')
  1009. if (selection.length > 0) {
  1010. const sql = `select b.yuan_she_bei_bian as yuanSheBeiBian,b.she_bei_ming_cheng_ as sheBeiMingCheng,a.parent_id_,a.wei_hu_xiang_mu_c as weiHuXiangMuC,a.wei_hu_ri_qi_ as weiHuRiQi,a.wei_hu_lei_xing_ as weiHuLeiXing,a.ri_qi_shu_zi_ as riQiShuZi from t_whzqjxm a,t_sbdj b where a.parent_id_=b.id_ and a.parent_id_ in (${selection.map(i => `'${i}'`).join(',')})`
  1011. const { variables: { data }} = await this.$common.request('sql', sql)
  1012. exportData = data
  1013. }
  1014. this.xlsx(exportData, this.projectColums, '设备维护项目数据' + this.getTimeStamp())
  1015. this.$message.success('导出维护项目成功!')
  1016. },
  1017. // value 转 key
  1018. switchV2K (value, obj) {
  1019. const key = Object.keys(obj).find(key => obj[key] === value)
  1020. return key || ''
  1021. },
  1022. // 转换对象的key
  1023. switchDeviceObj (data, originalObj) {
  1024. const result = []
  1025. // data.forEach(item => {
  1026. // const obj = {}
  1027. // for (const key in item) {
  1028. // obj[this.switchV2K(key, originalObj)] = item[key]
  1029. // }
  1030. // result.push(obj)
  1031. // })
  1032. data.forEach(item => {
  1033. const obj = {}
  1034. for (const key in originalObj) {
  1035. // 对日期格式的数据做兼容处理
  1036. if (item[originalObj[key]] instanceof Date) {
  1037. obj[key] = dayjs(item[originalObj[key]]).add(8, 'hour').format('YYYY-MM-DD') || ''
  1038. } else {
  1039. obj[key] = String(item[originalObj[key]] || '')
  1040. }
  1041. }
  1042. result.push(obj)
  1043. })
  1044. return result
  1045. },
  1046. /* 读取文件 将文件转换为二进制 */
  1047. readFile (file) {
  1048. return new Promise(resolve => {
  1049. const reader = new FileReader()
  1050. reader.readAsBinaryString(file)
  1051. reader.onload = ev => {
  1052. resolve(ev.target.result)
  1053. }
  1054. })
  1055. },
  1056. checkDeviceRequiredFieldsIfEmpty (list, requiredMap) {
  1057. const msgList = []
  1058. list.forEach((item, index) => {
  1059. const invalidFieldNames = []
  1060. Object.entries(requiredMap).forEach(([field, name]) => {
  1061. if (item.weiHuLeiXing === '按需保养' && field === 'weiHuRiQi') {
  1062. console.log('按需pass1')
  1063. } else {
  1064. if (!item[field]) {
  1065. invalidFieldNames.push(name)
  1066. }
  1067. }
  1068. })
  1069. if (invalidFieldNames.length > 0) {
  1070. msgList.push({ row: index + 2, field: invalidFieldNames })
  1071. }
  1072. })
  1073. return msgList
  1074. },
  1075. /**
  1076. * 专门发送提示
  1077. * @param {*} allResult
  1078. * @returns
  1079. */
  1080. sendWarningMessages (allResult, mark = 'range') {
  1081. if (allResult.length < 1) {
  1082. return
  1083. }
  1084. console.log('%c Msg Obj %c', 'background:#FF5733; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;', 'background:transparent', allResult)
  1085. const item = allResult[0]
  1086. switch (mark) {
  1087. case 'required':
  1088. this.$message.warning(`第${item.row}行,字段【${item.field.join(',')}】的值不能为空!`)
  1089. break
  1090. case 'range':
  1091. this.$message.warning(`第${item.row}行,字段【${item.field}】的值【${item.value}】不在限定范围内!`)
  1092. break
  1093. case 'dateFormat':
  1094. this.$message.warning(`第${item.row}行,字段【${item.field}】日期格式错误!格式支持【2024-01-01】、【2024/01/01】,请检查您的数据!`)
  1095. break
  1096. case 'duplicateOriginalDevice':
  1097. this.$message.warning(`${item.field}!`)
  1098. break
  1099. default:
  1100. throw new Error(`${mark}类型未定义!`)
  1101. }
  1102. },
  1103. /**
  1104. * 根据规则校验字段的限定范围
  1105. * @param {*} list
  1106. */
  1107. checkFieldsRange (list, rules) {
  1108. const msgList = []
  1109. for (const ruleKey in rules) {
  1110. const rule = rules[ruleKey]
  1111. const fieldName = ruleKey
  1112. list.forEach((item, index) => {
  1113. const fieldValue = item[rule.field]
  1114. if (fieldValue && !rule.range.includes(fieldValue)) {
  1115. msgList.push({ row: index + 2, field: fieldName, value: fieldValue })
  1116. }
  1117. })
  1118. }
  1119. return msgList
  1120. },
  1121. /**
  1122. * 校验日期字段填写的格式
  1123. * @param {*} list
  1124. * @returns
  1125. */
  1126. checkDateFields (list) {
  1127. const dateRegex = /^(\d{4})[-/](0[1-9]|1[0-2])[-/](0[1-9]|[12]\d|3[01])$/
  1128. for (let i = 0; i < list.length; i++) {
  1129. const row = list[i]
  1130. for (const field in this.dateFieldsMap) {
  1131. if (row[field] && !dateRegex.test(row[field])) {
  1132. console.error('error field:', row[field])
  1133. return [{ row: i + 2, field: this.dateFieldsMap[field] }]
  1134. }
  1135. }
  1136. }
  1137. return []
  1138. },
  1139. checkDuplicateOriginalDeviceNo (arr) {
  1140. const occurrences = {}
  1141. arr.forEach((item, index) => {
  1142. const key = item.yuanSheBeiBian
  1143. if (occurrences[key]) {
  1144. occurrences[key].push(index + 2)
  1145. } else {
  1146. occurrences[key] = [index + 2]
  1147. }
  1148. })
  1149. // 检查是否有重复项(数组长度大于1)
  1150. for (const [key, indices] of Object.entries(occurrences)) {
  1151. if (indices.length > 1) {
  1152. return [{ field: `发现重复的原设备编号:${key} 在第 ${indices.join(', ')} 行` }]
  1153. }
  1154. }
  1155. return []
  1156. },
  1157. /**
  1158. * 负责导入设备数据的第一部分校验,全部成功则返回true
  1159. * @param {*} data
  1160. * @returns
  1161. */
  1162. deviceInvalidPartOne (data) {
  1163. // 校验必填信息
  1164. const invalidResult = this.checkDeviceRequiredFieldsIfEmpty(data, this.requiredFieldMap)
  1165. if (invalidResult.length > 0) {
  1166. this.sendWarningMessages(invalidResult, 'required')
  1167. return false
  1168. }
  1169. // 根据规则校验字段的限定范围
  1170. const allResult = this.checkFieldsRange(data, this.validationRules)
  1171. if (allResult.length > 0) {
  1172. this.sendWarningMessages(allResult, 'range')
  1173. return false
  1174. }
  1175. // 校验日期字段是否符合要求
  1176. const dateResult = this.checkDateFields(data)
  1177. if (dateResult.length > 0) {
  1178. this.sendWarningMessages(dateResult, 'dateFormat')
  1179. return false
  1180. }
  1181. // 校验是否存在重复的原设备编号
  1182. const duplicateOriginalDeviceResult = this.checkDuplicateOriginalDeviceNo(data)
  1183. if (duplicateOriginalDeviceResult.length > 0) {
  1184. this.sendWarningMessages(duplicateOriginalDeviceResult, 'duplicateOriginalDevice')
  1185. return false
  1186. }
  1187. return true
  1188. },
  1189. /**
  1190. * 将日期字段信息格式化为 yyyy-MM-dd
  1191. * @param {*} list
  1192. * @returns
  1193. */
  1194. formatDateFieldsToReal (list) {
  1195. list.forEach(item => {
  1196. this.dateFieldRange.forEach(field => {
  1197. item[field] = item[field]?.replace(/\//g, '-')
  1198. })
  1199. })
  1200. return list
  1201. },
  1202. getNextAlias () {
  1203. return new Promise((resolve, reject) => {
  1204. this.$common.getNextIdByAlias({
  1205. 'alias': 'sbbh'
  1206. }).then(response => {
  1207. resolve(response.data)
  1208. }).catch((error) => {
  1209. reject(error)
  1210. })
  1211. })
  1212. },
  1213. /**
  1214. * 过滤出来excel 的原设备编号存在当前数据库中的数据
  1215. * @param {*} list 导入的数据
  1216. */
  1217. async filterOriginalDeviceNo (list, currentPosition) {
  1218. const uniqueArr = Array.from(new Set(list.map(i => i.yuanSheBeiBian.trim())))
  1219. const sql = `select id_,yuan_she_bei_bian from t_sbdj where find_in_set(yuan_she_bei_bian,'${uniqueArr.join(',')}')and di_dian_ = '${currentPosition}'`
  1220. const res = await this.$common.request('sql', sql)
  1221. const { data = [] } = res.variables || {}
  1222. const originalDeviceNoList = data.map(i => i.yuan_she_bei_bian.trim())
  1223. // 给要更新的数据加上id (接口需要!!!)
  1224. data.forEach(item => {
  1225. const temp = list.find(i => i.yuanSheBeiBian === item.yuan_she_bei_bian)
  1226. temp.id = item.id_
  1227. })
  1228. return originalDeviceNoList || ''
  1229. },
  1230. /**
  1231. *
  1232. * @param {*} list excel数据
  1233. * @param {*} currentTime 当前时间
  1234. * @param {*} currentApartment 当前部门ID
  1235. * @param {*} currentUser 当前用户ID
  1236. * @param {*} currentPosition 当前地点ID
  1237. */
  1238. async handleBasicData (list, currentTime, currentApartment, currentUser, currentPosition, deptList) {
  1239. // 使用map生成一个异步操作的数组
  1240. const promises = list.map(async (element) => {
  1241. element.bianZhiShiJian = currentTime
  1242. element.bianZhiRen = currentUser
  1243. element.diDian = currentPosition
  1244. element.shiFouGuoShen = '已完成'
  1245. const o = deptList?.find(i => i.positionName === element.bianZhiBuMen.trim())
  1246. const { positionId = currentApartment } = o || {}
  1247. element.bianZhiBuMen = positionId
  1248. if (!element.id) {
  1249. // 获取下一个编号
  1250. element.sheBeiShiBieH = await this.getNextAlias()
  1251. }
  1252. })
  1253. // 使用Promise.all并发执行所有异步操作
  1254. await Promise.all(promises)
  1255. },
  1256. /**
  1257. *
  1258. * @param {*} list excel数据
  1259. * @param {*} positionList 现有房间信息
  1260. * @param {*} supplierList 现有供应商信息
  1261. * @param {*} employeeList 现有人员信息
  1262. * @returns
  1263. */
  1264. handleExcelData (list, positionList, supplierList, employeeList, deviceGroupList) {
  1265. if (list.length < 1) {
  1266. return
  1267. }
  1268. this.handleSupplierInfo(list, supplierList)
  1269. this.handlePositionInfo(list, positionList)
  1270. this.handlePersonInfo(list, employeeList)
  1271. this.handleDeviceGroupInfo(list, deviceGroupList)
  1272. },
  1273. /**
  1274. *
  1275. * @param {*} list excel数据
  1276. * @param {*} supplierList 现有供应商信息
  1277. */
  1278. handleSupplierInfo (list, supplierList) {
  1279. list.forEach(element => {
  1280. if (element.gongYingShang.trim()) {
  1281. const supplier = supplierList.find(i => i.gong_ying_shang_m === element.gongYingShang.trim())
  1282. if (supplier) {
  1283. element.shiFouQiJianH = supplier.id_
  1284. } else {
  1285. element.shiFouQiJianH = ''
  1286. element.gongYingShang = ''
  1287. }
  1288. } else {
  1289. element.shiFouQiJianH = ''
  1290. }
  1291. if (element.shiYongKeShi.trim()) {
  1292. const supplier = supplierList.find(i => i.gong_ying_shang_m === element.shiYongKeShi.trim())
  1293. if (supplier) {
  1294. element.shiYongKeShi = supplier.id_
  1295. } else {
  1296. element.shiYongKeShi = ''
  1297. }
  1298. } else {
  1299. element.shiYongKeShi = ''
  1300. }
  1301. })
  1302. },
  1303. /**
  1304. *
  1305. * @param {*} list excel数据
  1306. * @param {*} positionList 现有房间信息
  1307. */
  1308. handlePositionInfo (list, positionList) {
  1309. list.forEach(element => {
  1310. if (element.cunFangDiDian !== '') {
  1311. const postion = positionList.find(i => (i.fang_jian_ming_ha.trim() || '') === element.cunFangDiDian.trim())
  1312. if (postion) {
  1313. const positionId = postion.id_
  1314. element.cunFangWeiZhi = positionId
  1315. } else {
  1316. element.cunFangWeiZhi = ''
  1317. element.cunFangDiDian = ''
  1318. }
  1319. } else {
  1320. element.cunFangWeiZhi = ''
  1321. }
  1322. })
  1323. },
  1324. handlePersonInfo (list, employeeList) {
  1325. list.forEach(element => {
  1326. // 处理保管人
  1327. if (element.guanLiRen !== '') {
  1328. const person = employeeList.find(i => i.userName === element.guanLiRen.trim())
  1329. if (person) {
  1330. const personId = person.userId
  1331. element.guanLiRen = personId
  1332. } else {
  1333. element.guanLiRen = ''
  1334. }
  1335. } else {
  1336. element.guanLiRen = ''
  1337. }
  1338. // 处理核查人
  1339. if (element.biXuDeHuanJin !== '') {
  1340. const checkPerson = employeeList.find(i => i.userName === element.biXuDeHuanJin.trim())
  1341. if (checkPerson) {
  1342. const checkPersonId = checkPerson.userId
  1343. element.biXuDeHuanJin = checkPersonId
  1344. } else {
  1345. element.biXuDeHuanJin = ''
  1346. }
  1347. } else {
  1348. element.biXuDeHuanJin = ''
  1349. }
  1350. })
  1351. },
  1352. handleDeviceGroupInfo (list, deviceGroupList) {
  1353. list.forEach(element => {
  1354. const result = []
  1355. const { weiHuFangShi } = element
  1356. const valList = weiHuFangShi.trim()?.split(',')
  1357. valList.forEach(item => result.push((deviceGroupList?.find(i => i.wei_hu_gang_wei_ === item)?.id_) || ''))
  1358. element.weiHuFangShi = result.join(',')
  1359. })
  1360. },
  1361. async handleUploadChange1 (file) {
  1362. const dataBinary = await this.readFile(file.target.files[0])
  1363. file.target.value = null // 注意上传后要将input的值设为空
  1364. const workBook = xlsx.read(dataBinary, { type: 'binary', cellDates: true })
  1365. const workSheet = workBook.Sheets[workBook.SheetNames[0]]
  1366. const data = xlsx.utils.sheet_to_json(workSheet)
  1367. let importData = this.switchDeviceObj(data, this.deviceColumns)
  1368. importData.forEach(i => {
  1369. delete i.sheBeiShiBieH // 设备编号需自动生成
  1370. // i.sheBeiZhuangTa = '合格'
  1371. const keyFound = Object.entries(this.stateList).find(([key, value]) => value === i.sheBeiZhuangTa)
  1372. if (keyFound) {
  1373. i.sheBeiZhuangTa = keyFound[0]
  1374. }
  1375. })
  1376. const currentPosition = this.level
  1377. const { userList = [], deptList = [] } = this.$store.getters || {}
  1378. const positionSql = `select id_,fang_jian_ming_ha from t_jjqfjb where di_dian_ = ${currentPosition}` // 房间信息
  1379. const supplierSql = `select id_,gong_ying_shang_m from t_gysxxb where di_dian_ = ${currentPosition}` // 供应商信息
  1380. const deviceGroupSql = `select id_,suo_shu_bu_men_,wei_hu_gang_wei_ from t_sbwhgwpzb where di_dian_ = ${currentPosition}` // 设备分组信息
  1381. const currentTime = dayjs().format('YYYY-MM-DD HH:mm')
  1382. const currentApartment = this.$store.getters.userInfo.employee.positions
  1383. const currentUser = this.userId
  1384. const partOneInvalidResult = this.deviceInvalidPartOne(importData)
  1385. if (!partOneInvalidResult) return
  1386. importData = this.formatDateFieldsToReal(importData)
  1387. console.log('%c partOne doCheck is completed! %c the result is %c', 'background:#35495E; padding: 1px; border-radius: 3px 0 0 3px; color: #fff;', 'background:#FF5733; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;', 'background:transparent', importData)
  1388. this.loading = true
  1389. Promise.all([this.$common.request('sql', positionSql), this.$common.request('sql', supplierSql), this.$common.request('sql', deviceGroupSql)]).then(async ([res1, res2, res3]) => {
  1390. const { data: positionList = [] } = res1.variables || {}
  1391. const { data: supplierList = [] } = res2.variables || {}
  1392. const { data: deviceGroupList = [] } = res3.variables || {}
  1393. // 根据原设备编号去重,检验原设备编号是否在数据库中存在
  1394. const originalDeviceNoList = await this.filterOriginalDeviceNo(importData, currentPosition)
  1395. await this.handleBasicData(importData, currentTime, currentApartment, currentUser, currentPosition, deptList)
  1396. this.handleExcelData(importData, positionList, supplierList, userList, deviceGroupList)
  1397. // 分离出 已存在的设备,和新设备
  1398. const newDeviceList = importData.filter(i => !originalDeviceNoList.includes(i.yuanSheBeiBian.trim()))
  1399. const existDeviceList = importData.filter(i => originalDeviceNoList.includes(i.yuanSheBeiBian.trim()))
  1400. console.log('%c new device %c', 'background:#FF5733; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;', 'background:transparent', newDeviceList)
  1401. console.log('%c already exist device %c', 'background:#43f80c; padding: 1px; border-radius: 0 3px 3px 0; color: #fff;', 'background:transparent', existDeviceList)
  1402. this.loading = false
  1403. this.$confirm(`<span style="color:#f56c6c; font-size: 18px; font-weight: 600;">
  1404. 经系统判定</span><br>1.预期新导入设备的数量为 ${newDeviceList.length} 台!<br>
  1405. 2.预期更新已存在的设备数量为 ${existDeviceList.length} 台!<br>
  1406. <span style="color:#f56c6c;">Tips:请确认数据无误再点击确定进行导入</span><br><span style="color:#f56c6c; font-size: 18px; font-weight: 600;">请谨慎操作!</span>`, '提示', {
  1407. confirmButtonText: '确定',
  1408. cancelButtonText: '取消',
  1409. dangerouslyUseHTMLString: true,
  1410. type: 'warning'
  1411. }).then(async () => {
  1412. await this.doImportDevice(importData)
  1413. this.loading = true
  1414. setTimeout(() => {
  1415. this.loading = false
  1416. this.$message.success('设备数据导入成功!')
  1417. this.getDatas()
  1418. }, 1000)
  1419. }).catch(() => { })
  1420. }).catch(() => {
  1421. this.loading = false
  1422. })
  1423. },
  1424. async doImportDevice (existDeviceList, currentPosition) {
  1425. this.loading = true
  1426. const allRequests = []
  1427. for (let index = 0; index < existDeviceList.length; index++) {
  1428. const item = existDeviceList[index]
  1429. let params = {}
  1430. if (item.id) {
  1431. // 获取设备数据
  1432. const { data: itemData } = await getequipmentCard({ id: item.id })
  1433. params = { ...itemData, ...item } // 合并数据
  1434. } else {
  1435. params = item // 直接使用当前 item
  1436. }
  1437. console.log(params)
  1438. // 将每个 saveEquipmentCard 请求加入到 allRequests 数组中
  1439. allRequests.push(saveEquipmentCard(params))
  1440. }
  1441. // 等待所有异步请求完成
  1442. await Promise.all(allRequests)
  1443. this.loading = false
  1444. },
  1445. generateDayRule () {
  1446. const days = ['每周1', '每周2', '每周3', '每周4', '每周5', '每周6', '每周7']
  1447. let combinations = []
  1448. for (let length = 1; length <= days.length; length++) {
  1449. this.generateCombinations([], days, combinations)
  1450. }
  1451. combinations = combinations.map(comb => '每周' + comb.map(item => item.slice(2)).join(','))
  1452. combinations.push('每天')
  1453. return combinations
  1454. },
  1455. generateRule (num, prefix, suffix) {
  1456. return Array.from({ length: num }, (_, i) => prefix + `${i + 1}` + suffix)
  1457. },
  1458. generateCombinations (current, remaining, results) {
  1459. if (remaining.length === 0) {
  1460. results.push(current)
  1461. return
  1462. }
  1463. const first = remaining[0]
  1464. const rest = remaining.slice(1)
  1465. // 包括当前数字和不包括当前数字两种情况
  1466. this.generateCombinations(current.concat([first]), rest, results)
  1467. this.generateCombinations(current, rest, results)
  1468. },
  1469. /**
  1470. * 负责导入设备维护项目数据的第一部分校验,全部成功则返回true
  1471. * @param {*} data
  1472. * @returns
  1473. */
  1474. maintenanceInvalidPartOne (data) {
  1475. // 1、对数据进行清洗校验(设备编号不能为空,维护周期、维护项目亦不可为空)
  1476. const invalidResult = this.checkDeviceRequiredFieldsIfEmpty(data, this.maintenanceRequiredFieldMap)
  1477. if (invalidResult.length > 0) {
  1478. this.sendWarningMessages(invalidResult, 'required')
  1479. return false
  1480. }
  1481. // 根据规则校验字段的限定范围
  1482. const allResult = this.checkFieldsRange(data, this.maintenanceValidationRules)
  1483. if (allResult.length > 0) {
  1484. this.sendWarningMessages(allResult, 'range')
  1485. return false
  1486. }
  1487. // 根据规则校验维护周期对应的维护日期的限定范围
  1488. const result = this.checkMaintenanceDateRange(data, this.maintenanceDateValidationRules)
  1489. if (result.length > 0) {
  1490. this.sendWarningMessages(result, 'range')
  1491. return false
  1492. }
  1493. return true
  1494. },
  1495. /**
  1496. * 根据规则校验维护周期对应的维护日期的限定范围
  1497. */
  1498. checkMaintenanceDateRange (list, rules) {
  1499. const msgList = []
  1500. list.forEach(({ weiHuLeiXing: type, weiHuRiQi: exactDate }, index) => {
  1501. if (type === '按需保养') {
  1502. console.log('按需pass2')
  1503. } else {
  1504. if (!rules[type].includes(exactDate)) {
  1505. msgList.push({ row: index + 2, field: '维护日期', value: exactDate })
  1506. }
  1507. }
  1508. })
  1509. return msgList
  1510. },
  1511. async handleUploadChange2 (file) {
  1512. const dataBinary = await this.readFile(file.target.files[0])
  1513. file.target.value = null // 注意上传后要将input的值设为空
  1514. const workBook = xlsx.read(dataBinary, { type: 'binary', cellDates: true })
  1515. const workSheet = workBook.Sheets[workBook.SheetNames[0]]
  1516. const data = xlsx.utils.sheet_to_json(workSheet)
  1517. const importData = this.switchDeviceObj(data, this.projectColums)
  1518. console.log(importData)
  1519. importData.forEach(item => {
  1520. if (item.weiHuLeiXing === '按需保养') {
  1521. item.weiHuRiQi = '/'
  1522. }
  1523. })
  1524. const currentPosition = this.level
  1525. const { userList = [], deptList = [] } = this.$store.getters || {}
  1526. const positionSql = `select id_,fang_jian_ming_ha from t_jjqfjb where di_dian_ = ${currentPosition}` // 房间信息
  1527. const supplierSql = `select id_,gong_ying_shang_m from t_gysxxb where di_dian_ = ${currentPosition}` // 供应商信息
  1528. const deviceGroupSql = `select id_,suo_shu_bu_men_,wei_hu_gang_wei_ from t_sbwhgwpzb where di_dian_ = ${currentPosition}` // 设备分组信息
  1529. const currentTime = dayjs().format('YYYY-MM-DD HH:mm')
  1530. const currentApartment = this.$store.getters.userInfo.employee.positions
  1531. const currentUser = this.userId
  1532. const partOneInvalidResult = this.maintenanceInvalidPartOne(importData)
  1533. if (!partOneInvalidResult) return
  1534. // 2、根据原设备编号去重,检验原设备编号是否在数据库中存在,如有不存在的数据,不进行导入,并提示用户
  1535. const uniqueArr = Array.from(new Set(importData.map(i => i.yuanSheBeiBian.trim())))
  1536. /* 3、根据去重的设备编号去查对应的设备ID,然后拼接data数据,赋值设备ID*/
  1537. const sql = `select id_,yuan_she_bei_bian from t_sbdj where find_in_set(yuan_she_bei_bian,'${uniqueArr.join(',')}')and di_dian_ = ${currentPosition}`
  1538. this.$common.request('sql', sql).then(async res => {
  1539. const deviceNoWithIdlist = res.variables.data
  1540. console.log(deviceNoWithIdlist, ' <=> ', uniqueArr)
  1541. const deviceNoSet = new Set(deviceNoWithIdlist.map(i => i.yuan_she_bei_bian))
  1542. const missStr = uniqueArr.filter(i => !deviceNoSet.has(i)).join(',') || ''
  1543. if (missStr !== '') {
  1544. this.$message.error(`设备维护项目数据中包含不存在于设备档案的原设备编号!具体原设备编号为:${missStr}`)
  1545. return
  1546. }
  1547. importData.forEach(item => {
  1548. item.id = deviceNoWithIdlist.find(i => i.yuan_she_bei_bian === item.yuanSheBeiBian).id_
  1549. })
  1550. const resultList = []
  1551. importData.forEach(item => {
  1552. const flag = item.weiHuLeiXing === '日保养'
  1553. const match = item.weiHuRiQi?.match(/\d+/g)
  1554. const o = {
  1555. id: item.id,
  1556. weiHuLeiXing: item.weiHuLeiXing.trim(),
  1557. weiHuRiQi: item.weiHuRiQi?.trim() || '',
  1558. weiHuXiangMuC: item.weiHuXiangMuC.trim(),
  1559. riQiShuZi: match ? (flag ? match.join(',') : match[0]) : ''
  1560. }
  1561. if (o.weiHuLeiXing === '日保养' && o.weiHuRiQi === '每天') o.riQiShuZi = '1,2,3,4,5,6,7'
  1562. resultList.push(o)
  1563. })
  1564. // 4、根据用户选择 进行全量替换|增量添加
  1565. await this.doProjectImport(deviceNoWithIdlist, resultList, this.ImportDeviceType)
  1566. this.$message.success('维护项目数据' + this.ImportDeviceType + '成功!')
  1567. })
  1568. },
  1569. async doProjectImport (deviceNoWithIdlist, resultList, type) {
  1570. this.loading = true
  1571. const allRequests = []
  1572. deviceNoWithIdlist.forEach(async item => {
  1573. const { data: itemData } = await getequipmentCard({ id: item.id_ })
  1574. const temp = resultList.filter(i => i.id === item.id_).map(ii => {
  1575. delete ii.id
  1576. return ii
  1577. })
  1578. let params = {}
  1579. if (type === '增量添加') { // 增量
  1580. params = {
  1581. ...itemData,
  1582. maintenanceItemPoList: [...itemData.maintenanceItemPoList, ...temp]
  1583. }
  1584. } else if (type === '全量替换') { // 全量
  1585. params = {
  1586. ...itemData,
  1587. maintenanceItemPoList: temp
  1588. }
  1589. }
  1590. console.log('params', params)
  1591. allRequests.push(saveEquipmentCard(params))
  1592. })
  1593. await Promise.all(allRequests)
  1594. this.loading = false
  1595. }
  1596. }
  1597. }
  1598. </script>
  1599. <style lang="scss" scoped>
  1600. .icon-image{
  1601. width: 36px;
  1602. }
  1603. .device-image{
  1604. width: 152px;
  1605. height: 110px;
  1606. }
  1607. .title{
  1608. font-size: 12px;
  1609. font-weight: 900;
  1610. color: #333;
  1611. margin: 0 0 14px 20px;
  1612. }
  1613. .ctx{
  1614. margin: 0 0 0 20px;
  1615. .item{
  1616. .cusitem{
  1617. margin: -8px 0;
  1618. display: flex;
  1619. align-items: center;
  1620. }
  1621. .span{
  1622. min-width:60px
  1623. }
  1624. margin: 6px 0;
  1625. color: #999;
  1626. }
  1627. }
  1628. ::v-deep {
  1629. .el-form-item__label{
  1630. text-align: left;
  1631. font-size: 12px !important;
  1632. }
  1633. .el-form-item__ctx{
  1634. font-size: 12px !important;
  1635. }
  1636. }
  1637. </style>