index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. <script setup lang="ts" name="InspectionRule">
  2. import apis from "@a"
  3. import dayjs from "dayjs"
  4. import CheckinLog from "./_checkinLog.vue"
  5. import { Vue3NextQrcode } from "vue3-next-qrcode"
  6. const deviceUsers = ref([])
  7. const tableRef = ref()
  8. const modalRef = ref()
  9. const opts = reactive({
  10. columns: [
  11. { field: "id", name: "点检规则ID", width: 100, isSort: true, visible: false, tooltip: true },
  12. {
  13. field: "taskName",
  14. name: "任务名称",
  15. visible: true,
  16. isSort: false,
  17. width: "auto",
  18. tooltip: true
  19. },
  20. { field: "location", name: "地点", visible: true, isSort: false, width: "auto", tooltip: true },
  21. {
  22. field: "cycleHours",
  23. name: "周期(单位:时)",
  24. visible: true,
  25. isSort: false,
  26. width: 140,
  27. tooltip: true
  28. },
  29. {
  30. field: "toleranceHours",
  31. name: "误差(单位:时)",
  32. visible: true,
  33. isSort: false,
  34. width: 140,
  35. tooltip: true
  36. },
  37. { field: "startTime", name: "开始时间", visible: true, isSort: false, width: 185 },
  38. { field: "endTime", name: "结束时间", visible: true, isSort: false, width: 185 },
  39. // {
  40. // field: "requiredCount",
  41. // name: "需点检总次数",
  42. // visible: true,
  43. // isSort: false,
  44. // width: "auto",
  45. // tooltip: true
  46. // },
  47. // {
  48. // field: "actualCount",
  49. // name: "实际点检次数",
  50. // visible: true,
  51. // isSort: false,
  52. // width: "auto",
  53. // tooltip: true
  54. // },
  55. // {
  56. // field: "missedCount",
  57. // name: "漏检次数",
  58. // visible: true,
  59. // isSort: false,
  60. // width: "auto",
  61. // tooltip: true
  62. // },
  63. {
  64. field: "executorName",
  65. name: "执行人",
  66. visible: true,
  67. isSort: false,
  68. width: 100,
  69. tooltip: true
  70. },
  71. { field: "status", name: "启用状态", visible: true, isSort: false, width: 100 },
  72. { field: "actions", name: `操作`, width: 150 }
  73. ] as any[],
  74. queryParams: {
  75. taskName: undefined,
  76. location: undefined,
  77. status: undefined
  78. },
  79. searchFormItems: [
  80. {
  81. field: "taskName",
  82. label: "任务名称",
  83. class: "w-100",
  84. required: false,
  85. placeholder: "请输入任务名称",
  86. component: "I",
  87. listeners: {
  88. keyup: (e: KeyboardEvent) => {
  89. if (e.code == "Enter") {
  90. handleQuery()
  91. }
  92. }
  93. }
  94. },
  95. {
  96. field: "location",
  97. label: "地点",
  98. class: "w-100",
  99. required: false,
  100. placeholder: "请输入地点",
  101. component: "I",
  102. listeners: {
  103. keyup: (e: KeyboardEvent) => {
  104. if (e.code == "Enter") {
  105. handleQuery()
  106. }
  107. }
  108. }
  109. },
  110. {
  111. field: "status",
  112. label: "启用状态",
  113. class: "w-100",
  114. required: false,
  115. component: "Dict",
  116. props: {
  117. placeholder: "请选择启用状态",
  118. dictType: "sys_normal_disable",
  119. valueIsNumber: 1,
  120. type: "select"
  121. },
  122. listeners: {
  123. change: () => {
  124. handleQuery()
  125. }
  126. }
  127. }
  128. ] as any,
  129. permission: "device:inspection",
  130. handleBtns: [],
  131. handleFuns: {
  132. handleCreate,
  133. handleUpdate: () => {
  134. const row = tableRef.value.getSelected()
  135. handleUpdate(row)
  136. },
  137. handleDelete: () => {
  138. const rows = tableRef.value.getSelecteds()
  139. handleDelete(rows)
  140. }
  141. },
  142. customBtns: [],
  143. tableListFun: apis.device.inspectionRuleApi.list,
  144. getEntityFun: apis.device.inspectionRuleApi.get,
  145. deleteEntityFun: apis.device.inspectionRuleApi.del,
  146. exportUrl: apis.device.inspectionRuleApi.exportUrl,
  147. exportName: "InspectionRule",
  148. modalTitle: "点检规则管理",
  149. formItems: [
  150. {
  151. field: "taskName",
  152. label: "任务名称",
  153. class: "w-100",
  154. required: true,
  155. placeholder: "请输入任务名称",
  156. component: "I"
  157. },
  158. {
  159. field: "location",
  160. label: "地点",
  161. class: "w-100",
  162. required: false,
  163. placeholder: "请输入地点",
  164. component: "I"
  165. },
  166. {
  167. field: "cycleHours",
  168. label: "点检周期",
  169. class: "w-100",
  170. required: true,
  171. placeholder: "请输入点检周期",
  172. component: "I"
  173. },
  174. {
  175. field: "toleranceHours",
  176. label: "误差",
  177. class: "w-100",
  178. required: false,
  179. placeholder: "请输入误差",
  180. component: "I"
  181. },
  182. {
  183. field: "startTime",
  184. label: "开始时间",
  185. class: "w-100",
  186. required: false,
  187. component: "D",
  188. props: {
  189. placeholder: "请选择开始时间",
  190. type: "datetime",
  191. valueFormat: "YYYY-MM-DD HH:mm:ss"
  192. }
  193. },
  194. {
  195. field: "endTime",
  196. label: "结束时间",
  197. class: "w-100",
  198. required: false,
  199. component: "D",
  200. props: {
  201. placeholder: "请选择结束时间",
  202. type: "datetime",
  203. valueFormat: "YYYY-MM-DD HH:mm:ss"
  204. }
  205. },
  206. {
  207. field: "executorId",
  208. label: "执行人",
  209. class: "w-100",
  210. required: false,
  211. placeholder: "请选择执行人",
  212. component: "VS",
  213. data: () => {
  214. return deviceUsers.value.map((v) => {
  215. return { label: v.nickname + "(" + v.username + ")", value: v.userId }
  216. })
  217. },
  218. props: {
  219. placeholder: "请选择执行人",
  220. type: "select",
  221. valueIsNumber: 1
  222. }
  223. }
  224. ] as any,
  225. resetForm: () => {
  226. form.value = emptyFormData.value
  227. },
  228. labelWidth: "80px",
  229. emptyFormData: {
  230. id: undefined,
  231. taskName: undefined,
  232. location: undefined,
  233. cycleHours: undefined,
  234. toleranceHours: undefined,
  235. startTime: undefined,
  236. endTime: undefined,
  237. executorId: undefined
  238. }
  239. })
  240. const { queryParams, emptyFormData } = toRefs(opts)
  241. const form = ref<any>(emptyFormData.value)
  242. /** 搜索按钮操作 */
  243. function handleQuery(query?: any) {
  244. query = query || tableRef.value?.getQueryParams() || queryParams.value
  245. addDateRange(query, query.dateRangeStartTime, "StartTime")
  246. addDateRange(query, query.dateRangeEndTime, "EndTime")
  247. addDateRange(query, query.dateRangeCreateTime)
  248. addDateRange(query, query.dateRangeUpdateTime, "UpdateTime")
  249. tableRef.value?.query(query)
  250. }
  251. /** 重置按钮操作 */
  252. function resetQuery(query?: any) {
  253. query = query || tableRef.value?.getQueryParams() || queryParams.value
  254. query.dateRangeStartTime = [] as any
  255. addDateRange(query, query.dateRangeStartTime, "StartTime")
  256. query.dateRangeEndTime = [] as any
  257. addDateRange(query, query.dateRangeEndTime, "EndTime")
  258. query.dateRangeCreateTime = [] as any
  259. addDateRange(query, query.dateRangeCreateTime)
  260. query.dateRangeUpdateTime = [] as any
  261. addDateRange(query, query.dateRangeUpdateTime, "UpdateTime")
  262. //
  263. }
  264. function handleCreate() {
  265. tableRef.value.defaultHandleFuns.handleCreate()
  266. }
  267. /** 修改按钮操作 */
  268. function handleUpdate(row: any) {
  269. tableRef.value.defaultHandleFuns.handleUpdate("", row)
  270. }
  271. /** 删除按钮操作 */
  272. function handleDelete(rows: any[]) {
  273. tableRef.value.defaultHandleFuns.handleDelete("", rows)
  274. }
  275. /** 提交按钮 */
  276. function submitForm() {
  277. apis.device.inspectionRuleApi.addOrUpdate(form.value).then(() => {
  278. handleQuery()
  279. })
  280. }
  281. const qrModalRef = ref()
  282. const qrModalData = ref()
  283. const qrCode = ref({
  284. logo: "/media/logo.png",
  285. size: 300,
  286. // colorDark: "#0095e8",
  287. colorDark: "#000000",
  288. text: ""
  289. })
  290. function handleQrCode(row) {
  291. qrModalData.value = row
  292. qrCode.value.text = `vb@device@/checkin/${row.id}`
  293. qrModalRef.value.show()
  294. }
  295. function handleDownloadQr(id: string) {
  296. let myImg = document.querySelector("#" + id + " img") as HTMLImageElement
  297. let url = myImg?.src
  298. let a = document.createElement("a")
  299. a.href = url
  300. a.download = `${qrModalData.value.taskName}签到二维码`
  301. a.click()
  302. }
  303. // function handlePrintQr(id: string) {
  304. // const printContent = document.querySelector("#" + id)
  305. // if (!printContent) return
  306. // // 创建打印样式
  307. // const style = document.createElement("style")
  308. // style.innerHTML = `
  309. // @media print {
  310. // body * {
  311. // display: none !important;
  312. // }
  313. // #print-area, #print-area * {
  314. // display: block !important;
  315. // }
  316. // #print-area {
  317. // position: absolute;
  318. // top: 0;
  319. // left: 0;
  320. // width: 100vw;
  321. // height: 600px;
  322. // display: flex !important;
  323. // flex-direction: column;
  324. // align-items: center;
  325. // justify-content: center;
  326. // font-family: Arial, sans-serif;
  327. // }
  328. // }
  329. // `
  330. // document.head.appendChild(style)
  331. // // 创建打印区域
  332. // const printArea = document.createElement("div")
  333. // printArea.id = "print-area"
  334. // // printArea.innerHTML = `
  335. // // <div style="width:100%;display:flex;flex-direction: column;align-items: center;justify-content: center;">${printContent.innerHTML}</div>
  336. // // `
  337. // printArea.innerHTML = printContent.innerHTML
  338. // document.body.appendChild(printArea)
  339. // // 打印
  340. // window.print()
  341. // // 清理
  342. // setTimeout(() => {
  343. // document.head.removeChild(style)
  344. // document.body.removeChild(printArea)
  345. // }, 500)
  346. // }
  347. const logModalRef = ref()
  348. const logsRef = ref()
  349. function handleLogs(row: any) {
  350. logsRef.value.load(row.id).then(() => {
  351. logModalRef.value.show()
  352. })
  353. }
  354. function init() {
  355. apis.device.deviceOrderApi.getAssUsers().then((res: any) => {
  356. deviceUsers.value = res.data
  357. })
  358. }
  359. onMounted(() => {
  360. init()
  361. })
  362. </script>
  363. <template>
  364. <div class="app-container">
  365. <VbDataTable
  366. ref="tableRef"
  367. keyField="id"
  368. :columns="opts.columns"
  369. :handle-perm="opts.permission"
  370. :handle-btns="opts.handleBtns"
  371. :handle-funs="opts.handleFuns"
  372. :search-form-items="opts.searchFormItems"
  373. :custom-btns="opts.customBtns"
  374. :remote-fun="opts.tableListFun"
  375. :get-entity-fun="opts.getEntityFun"
  376. :delete-entity-fun="opts.deleteEntityFun"
  377. :export-url="opts.exportUrl"
  378. :export-name="opts.exportName"
  379. :modal="modalRef"
  380. :reset-form-fun="opts.resetForm"
  381. v-model:form-data="form"
  382. v-model:query-params="queryParams"
  383. :check-multiple="true"
  384. :reset-search-form-fun="resetQuery"
  385. :custom-search-fun="handleQuery">
  386. <template #startTime="{ row }">
  387. <template v-if="row.startTime">
  388. {{ dayjs(row.startTime).format("YYYY-MM-DD HH:mm:ss") }}
  389. </template>
  390. <template v-else>-</template>
  391. </template>
  392. <template #endTime="{ row }">
  393. <template v-if="row.startTime">
  394. {{ dayjs(row.endTime).format("YYYY-MM-DD HH:mm:ss") }}
  395. </template>
  396. <template v-else>-</template>
  397. </template>
  398. <template #status="{ row }">
  399. <DictTag type="sys_normal_disable" :value-is-number="1" :value="row.status"></DictTag>
  400. </template>
  401. <template #actions="{ row }">
  402. <vb-tooltip content="生成二维码" placement="top">
  403. <el-button link type="success" @click="handleQrCode(row)">
  404. <template #icon>
  405. <VbIcon icon-name="scan-barcode" icon-type="duotone" class="fs-3"></VbIcon>
  406. </template>
  407. </el-button>
  408. </vb-tooltip>
  409. <vb-tooltip content="签到日志" placement="top">
  410. <el-button link type="primary" @click="handleLogs(row)">
  411. <template #icon>
  412. <VbIcon icon-name="book" icon-type="duotone" class="fs-3"></VbIcon>
  413. </template>
  414. </el-button>
  415. </vb-tooltip>
  416. <vb-tooltip content="修改" placement="top">
  417. <el-button
  418. link
  419. type="primary"
  420. @click="handleUpdate(row)"
  421. v-hasPermission="'device:inspectionRule:edit'">
  422. <template #icon>
  423. <VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
  424. </template>
  425. </el-button>
  426. </vb-tooltip>
  427. <vb-tooltip content="删除" placement="top">
  428. <el-button
  429. link
  430. type="primary"
  431. @click="handleDelete([row])"
  432. v-hasPermission="'device:inspectionRule:remove'">
  433. <template #icon>
  434. <VbIcon icon-name="trash-square" icon-type="duotone" class="fs-3"></VbIcon>
  435. </template>
  436. </el-button>
  437. </vb-tooltip>
  438. </template>
  439. </VbDataTable>
  440. <VbModal
  441. v-model:modal="modalRef"
  442. :title="opts.modalTitle"
  443. :form-data="form"
  444. :form-items="opts.formItems"
  445. :label-width="opts.labelWidth"
  446. append-to-body
  447. @confirm="submitForm"></VbModal>
  448. <VbModal
  449. v-model:modal="qrModalRef"
  450. title="签到二维码"
  451. :confirm-btn="false"
  452. :close-btn-class="'btn btn-danger'"
  453. append-to-body>
  454. <template #body>
  455. <div
  456. id="qr"
  457. class="w-100 w-100 d-flex flex-column justify-content-center align-items-center">
  458. <span class="fs-5 fw-bold my-5 qr-title">{{ qrModalData?.taskName }} 签到二维码</span>
  459. <Vue3NextQrcode
  460. :text="qrCode.text"
  461. :size="qrCode.size"
  462. :color-dark="qrCode.colorDark"
  463. :logo-image="qrCode.logo"
  464. :logo-scale="0.15"
  465. :logo-margin="0"
  466. :logo-corner-radius="20"
  467. :margin="20" />
  468. </div>
  469. <div class="text-center">
  470. <el-button type="primary" class="mx-5 w-150px" @click="handleDownloadQr('qr')">
  471. 保存
  472. </el-button>
  473. <!-- <el-button type="primary" class="mx-5 w-150px" @click="handlePrintQr('qr')">
  474. 打印
  475. </el-button> -->
  476. </div>
  477. </template>
  478. </VbModal>
  479. <VbModal
  480. v-model:modal="logModalRef"
  481. title="签到记录"
  482. :confirm-btn="false"
  483. :close-btn-class="'btn btn-danger'"
  484. append-to-body>
  485. <template #body>
  486. <CheckinLog :is-self="false" ref="logsRef" />
  487. </template>
  488. </VbModal>
  489. </div>
  490. </template>