InStorageEditSn.vue 7.9 KB

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