InStorageEditSn.vue 7.7 KB

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