InStorageSn.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <script setup lang="ts">
  2. import { ref, reactive, nextTick } from 'vue'
  3. import * as XLSX from 'xlsx';
  4. import { Delete } from '@element-plus/icons-vue'
  5. import Drawer from '@/components/Drawer/index.vue'
  6. import type { FormInstance, FormRules } from 'element-plus'
  7. import { Storehouse_Device_Check } from '@/api/storehouse/index'
  8. import { ElMessage } from 'element-plus'
  9. type Fn = () => void
  10. interface FormSnType {
  11. Id?: number
  12. sn: string
  13. type?: number
  14. }
  15. const snTable = ref()
  16. const SNDataMap = new Map<number, FormSnType[]>()
  17. const tableSnData = ref<FormSnType[]>([])
  18. const ruleSnFormRef = ref<FormInstance>()
  19. const drawerSnRef = ref<InstanceType<typeof Drawer> | null>(null)
  20. const snColumns = [
  21. { type: 'index', label: '序号', width: 80, align: 'center ' },
  22. { label: 'SN', prop: 'sn', align: 'center ' },
  23. { prop: 'operation', label: '操作', width: 80, fixed: 'right' }
  24. ]
  25. const formSn = reactive<FormSnType>({
  26. Id: undefined,
  27. sn: '',
  28. type: undefined
  29. })
  30. const rulesSn = reactive<FormRules>({
  31. sn: [{ required: true, message: '请输入SN号', trigger: 'blur' }]
  32. })
  33. const addSn = (formEl: FormInstance | undefined) => {
  34. if (!formEl) return
  35. formEl.validate(async valid => {
  36. if (valid) {
  37. if (formSn.sn.length === 16 || formSn.sn.length === 24) {
  38. if(formSn.sn.length==24){
  39. formSn.sn = formSn.sn.substring(2, formSn.sn.length - 6)
  40. }
  41. const res: any = await Storehouse_Device_Check({ T_sn: formSn.sn, T_type: formSn.type })
  42. if (res.Code === 200) {
  43. tableSnData.value.unshift({ sn: formSn.sn })
  44. tableSnData.value = tableSnData.value.filter((value, index, self) => { //去重
  45. return self.findIndex(t => (t.sn === value.sn)) === index;
  46. });
  47. nextTick(() => {
  48. resetSnForm(ruleSnFormRef.value)
  49. })
  50. }
  51. } else {
  52. ElMessage.error('扫描设备异常')
  53. nextTick(() => {
  54. resetSnForm(ruleSnFormRef.value)
  55. })
  56. return
  57. }
  58. }
  59. })
  60. }
  61. const deleteSn = (row: any) => {
  62. const index = tableSnData.value.findIndex((item: any) => row.sn === item.sn)
  63. tableSnData.value.splice(index, 1)
  64. }
  65. const callbackSnDrawer = (done: Fn) => {
  66. SNDataMap.set(formSn.Id as number, tableSnData.value)
  67. emit('onCount', SNDataMap.get(formSn.Id as number)?.length, formSn.Id as number)
  68. done()
  69. nextTick(() => {
  70. resetSnForm(ruleSnFormRef.value)
  71. tableSnData.value = []
  72. })
  73. }
  74. const emit = defineEmits<{ (event: 'onCount', value: any, id: number): void }>()
  75. const resetSnForm = (formEl: FormInstance | undefined) => {
  76. if (!formEl) return
  77. formEl.resetFields()
  78. }
  79. const addDeviceSn = (id: number, type: number) => {
  80. formSn.Id = id
  81. formSn.type = type
  82. if (SNDataMap.has(id)) {
  83. tableSnData.value = SNDataMap.get(id) as FormSnType[]
  84. } else {
  85. SNDataMap.set(id, [])
  86. }
  87. drawerSnRef.value?.openDrawer()
  88. }
  89. const drawer = ref(false)
  90. /**
  91. * 导入xlsx
  92. */
  93. // 组件状态变化的回调
  94. const uploadExcelFile = (event:any) => {
  95. const files = event.target.files;
  96. if (files.length === 0) return;
  97. const file = files[0];
  98. const reader = new FileReader();
  99. reader.onload = (e:any) => {
  100. const data = new Uint8Array(e.target.result);
  101. const workbook = XLSX.read(data, { type: 'array' });
  102. // 假设我们知道第一个工作表包含我们需要的数据
  103. const firstSheetName = workbook.SheetNames[0];
  104. const worksheet:any = workbook.Sheets[firstSheetName];
  105. // 假设我们知道 t_sn 是第一列
  106. const tSnColumn = 'A'; // 或者使用 XLSX.utils.decode_col(columnNumber) 来从列号获取列名
  107. const columnNumber = XLSX.utils.decode_col(tSnColumn); // 但实际上我们可能直接知道列号,如 0
  108. // 使用 sheet_to_json 但只选择我们需要的列
  109. const json = XLSX.utils.sheet_to_json(worksheet, { header: 1, range: XLSX.utils.decode_range(worksheet['!ref']) });
  110. // 如果表头不是第一行,或者你不想要表头,可以调整 header 选项
  111. // 提取 t_sn 列的数据(假设表头在第一行,且 t_sn 是第一列)
  112. let tSnData:any = json.map((row:any) => row[0]); // 假设 t_sn 是第一列,所以使用 row[0]
  113. let snArray = tSnData.map((sn:any) => ({ sn: sn }));
  114. snArray = snArray.slice(1)
  115. let arrs = [...tableSnData.value,...snArray]
  116. // 去重
  117. let seen = new Set();
  118. tableSnData.value = arrs.filter((item:any) => {
  119. return seen.has(item.sn) ? false : seen.add(item.sn) || true;
  120. });
  121. console.log('打印',tableSnData.value)
  122. // 注意:如果 t_sn 列不是第一列,你需要调整索引号
  123. // 例如,如果 t_sn 是第三列,则使用 row[2]
  124. };
  125. reader.readAsArrayBuffer(file);
  126. };
  127. const getDeviceSn = () => SNDataMap
  128. const clearDeviceSn = () => SNDataMap.clear()
  129. const deleteDeviceSn = (id: number) => SNDataMap.delete(id)
  130. defineExpose({
  131. getDeviceSn,
  132. addDeviceSn,
  133. clearDeviceSn,
  134. deleteDeviceSn
  135. })
  136. </script>
  137. <template>
  138. <Drawer ref="drawerSnRef" :handleClose="callbackSnDrawer" size="50%">
  139. <el-card class="box-card" shadow="never">
  140. <template #header>
  141. <div class="sn-header">
  142. <el-form ref="ruleSnFormRef" :model="formSn" :rules="rulesSn">
  143. <el-form-item label="SN:" label-width="120px" prop="sn">
  144. <el-input v-model="formSn.sn" type="text" placeholder="请输入SN" @keyup.enter="addSn(ruleSnFormRef)" class="w-50" />
  145. </el-form-item>
  146. </el-form>
  147. <el-button type="primary" @click="addSn(ruleSnFormRef)">添加</el-button>
  148. <el-button type="success" @click="drawer = true">导入xlsx</el-button>
  149. </div>
  150. </template>
  151. <el-table
  152. ref="snTable"
  153. :data="tableSnData"
  154. style="width: 100%; height: 99%"
  155. :header-cell-style="{
  156. background: '#dedfe0',
  157. height: '50px'
  158. }"
  159. >
  160. <template v-for="item in snColumns" :key="item">
  161. <el-table-column v-if="item.type === 'index'" v-bind="item" />
  162. <el-table-column show-overflow-tooltip v-if="item.prop" align="center" v-bind="item">
  163. <template #default="{ row }">
  164. <el-button
  165. v-if="item.prop === 'operation'"
  166. link
  167. type="danger"
  168. size="small"
  169. :icon="Delete"
  170. @click="deleteSn(row)"
  171. >删除</el-button
  172. >
  173. </template>
  174. </el-table-column>
  175. </template>
  176. </el-table>
  177. </el-card>
  178. <el-drawer v-model="drawer" title="导入xlsx" size="50%" :destroy-on-close="true">
  179. <input type="file" @change="uploadExcelFile" accept=".xlsx, .xls" />
  180. </el-drawer>
  181. </Drawer>
  182. </template>
  183. <style scoped lang="scss">
  184. .box-card {
  185. height: 100%;
  186. :deep(.el-card__body) {
  187. height: calc(100% - 70px);
  188. }
  189. .sn-header {
  190. display: flex;
  191. justify-content: end;
  192. }
  193. }
  194. </style>