Kaynağa Gözat

手持-加工登记初始化

zhaohuanhuan 8 ay önce
ebeveyn
işleme
89feff34d4

+ 24 - 0
src/api/processing/index.ts

@@ -0,0 +1,24 @@
+// @ts-ignore
+import request from '@/utils/request'
+/**
+ * 加工-获取加工类型
+ * @param params
+ */
+export function getProcessingTypeList() {
+  return request({
+    url: 'api/device/check/processing/typeList',
+    method: 'get',
+  })
+}
+/**
+ * 加工-创建加工单
+ * @param params
+ */
+export function createProcessing(data:any) {
+  return request({
+    url: 'api/device/check/processing/reference',
+    method: 'post',
+    data,
+  })
+}
+

+ 6 - 0
src/router/index.ts

@@ -115,6 +115,12 @@ const routes: RouteRecordRaw[] = [
     meta:{title:'退货登记'},
     component: () => import('@/views/returned/register/index.vue')
   },
+  {
+    path: '/processing',
+    name: 'Processing',
+    meta:{title:'加工登记'},
+    component: () => import('@/views/processing/register/index.vue')
+  },
 ];
 
 // 创建路由实例

+ 5 - 1
src/utils/dataType.js

@@ -24,7 +24,11 @@ export function toMap(data, key, val=undefined, optional = {}) {
 
   return map;
 }
-
+//字符串分割数组
+export function toArray(code) {
+  // 使用正则表达式分割字符串,去除多余的空白字符或换行符
+  return code.split(/[,,|\n\r\s]+/).filter(Boolean); // 使用 filter 去除空元素
+}
 
  export function barcodeToUpperCase(code){
    if (/[a-zA-Z]/.test(code)) {

+ 9 - 0
src/utils/date.ts

@@ -31,3 +31,12 @@ export function getCurrentTime() {
   const seconds = String(now.getSeconds()).padStart(2, '0');
   return `${hours}:${minutes}:${seconds}`;
 };
+/*
+ *获取当前时间
+ */
+export function formatDate(date:any) {
+  const year = date.getFullYear(); // 获取年份
+  const month = String(date.getMonth() + 1).padStart(2, '0'); // 获取月份
+  const day = String(date.getDate()).padStart(2, '0'); // 获取日期
+  return `${year}-${month}-${day}`;
+}

+ 1 - 0
src/views/index.vue

@@ -14,6 +14,7 @@
     <div class="home" @click="onRouter('check-activity')">复核-活动单</div>
     <div class="home" @click="onRouter('check-large')">复核-大件单</div>
     <div class="home" @click="onRouter('move-list')">调拨-移库</div>
+    <div class="home" @click="onRouter('processing')">加工登记</div>
     <div class="home" @click="onRouter('returned-register')">退货-登记</div>
   </div>
 </template>

+ 180 - 0
src/views/processing/register/components/addRegisterType.vue

@@ -0,0 +1,180 @@
+<template>
+  <div class="container-no-container">
+    <van-dialog v-model:show="typeTrueFalseBy"
+                :beforeClose="beforeClose"
+                title="登记类型/数量"
+                show-cancel-button>
+      <van-field name="checkboxGroup" label="加工类型" required label-width="70px">
+        <template #input>
+          <van-checkbox-group v-model="type" direction="horizontal">
+            <div v-for="(item,index) in props.processingTypeList" :key="item.code" style="margin-bottom: 8px">
+              <van-checkbox :name="item.code" shape="square"
+                            v-if=" item.code !== 'PROFESSIONAL' && item.code !== 'DEEP' &&item.code !== 'FINE'"
+              >{{ item.name }}
+              </van-checkbox>
+            </div>
+          </van-checkbox-group>
+        </template>
+      </van-field>
+      <van-field name="radio" label="质检类型" label-width="70px" :required="type.includes('ALL_CHECK')">
+        <template #input>
+          <van-radio-group v-model="checkType" direction="horizontal" label-width="70px">
+            <div v-for="(item,index) in props.processingTypeList" :key="item.code" style="margin-bottom: 8px">
+              <van-radio v-if="item.code == 'PROFESSIONAL' || item.code == 'DEEP' || item.code == 'FINE'"
+                         :name="item.code">{{ item.name }}
+              </van-radio>
+            </div>
+          </van-radio-group>
+        </template>
+      </van-field>
+      <van-field v-model="count" type="digit" ref="countRef" @keydown.enter="onKeydown" clearable autocomplete="off"
+                 label="加工数量" placeholder="请输入加工数量" required />
+    </van-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import { ref } from 'vue'
+import { showToast } from 'vant'
+import { scanError } from '@/utils/android'
+const typeTrueFalseBy = ref(false)
+const type = ref([])
+const count = ref('')
+const countRef = ref(null)
+const checkType = ref('')
+// 接收父组件传递的数据
+const props = defineProps({
+  processingTypeList: Array
+})
+const typeList=ref([])
+const show = async (list) => {
+  typeTrueFalseBy.value = true
+  type.value = []
+  count.value = ''
+  checkType.value = ''
+  typeList.value=list
+}
+//输入拣货容器号
+const emit = defineEmits(['setProcessingType'])
+const validateData = () => {
+  // 验证加工类型
+  if (type.value.length === 0) {
+    showToast('请先选择加工类型')
+    scanError()
+    return false
+  }
+  // 验证质检类型
+  if (type.value.includes('ALL_CHECK') && !checkType.value) {
+    showToast('选择的加工类型包含质检,请选择质检类型')
+    scanError()
+    return false
+  }
+  // 验证加工数量
+  if (count.value <= 0) {
+    showToast('请输入加工数量')
+    scanError()
+    countRef.value?.focus()
+    return false
+  }
+  return true
+}
+const getType=()=>{
+  let operate = [...type.value]
+  operate = operate.filter(item => item !== 'ALL_CHECK')
+  if (checkType.value) {
+    operate.push(checkType.value)
+  }
+  return  { operate, expectAmount: count.value }
+}
+const beforeClose = (action) => new Promise(async (resolve) => {
+  if (action === 'confirm') {
+    if (!validateData()) {
+      return resolve(false)
+    }
+    const data=getType()
+    const isExist = typeList.value.some(item =>
+      item.operate.length === data.operate.length &&
+      item.operate.every(op => data.operate.includes(op)) &&
+      data.operate.every(op => item.operate.includes(op))
+    );
+    if(isExist){
+      showToast('选择的加工类型已存在,请检查')
+      scanError()
+      return resolve(false)
+    }
+    emit('setProcessingType', data)
+    resolve(true)
+  } else {
+    resolve(true)
+  }
+})
+const onKeydown = () => {
+  if (!validateData()) {
+    return
+  }
+  const data=getType()
+  const isExist = typeList.value.some(item =>
+    item.operate.length === data.operate.length && // 长度必须相同
+    item.operate.every(op => data.operate.includes(op)) && // 所有元素都在 data.operate 中
+    data.operate.every(op => item.operate.includes(op)) // 所有元素都在 item.operate 中
+  );
+  if(isExist){
+    showToast('选择的加工类型已存在,请检查')
+    scanError()
+    return
+  }
+  emit('setProcessingType', data)
+}
+
+defineExpose({ show })
+</script>
+<style scoped lang="sass">
+.container-no-container
+  .code-input
+    font-size: 22px
+    font-weight: bold
+    border-bottom: 2px solid #0077ff
+    margin-top: 10px
+
+  .completion
+    text-align: right
+    font-size: 12px
+    padding: 5px 20px
+    cursor: pointer
+    text-decoration: underline
+
+  .container-list
+    max-height: 100px
+    font-size: 13px
+    font-weight: bold
+    display: flex
+    padding: 5px 20px
+    justify-content: space-between
+    flex-wrap: wrap
+    overflow: auto
+
+    .container-item
+      width: 50%
+      display: flex
+      justify-items: center
+      align-items: center
+      padding: 2px 0
+      cursor: pointer
+      text-decoration: underline
+
+      .container-item-line
+        width: 5px
+        height: 5px
+        background: #0077ff
+        border-radius: 50%
+        margin-right: 5px
+
+      .container-item-no
+        flex: 1
+        text-align: left
+
+.tips
+  font-size: 14px
+  color: #ff4141
+  line-height: 20px
+
+</style>

+ 204 - 0
src/views/processing/register/index.vue

@@ -0,0 +1,204 @@
+<template>
+  <div class="register">
+    <van-nav-bar
+      title="加工建单" left-arrow fixed placeholder @click-left="goBack" @click-right="onConfirm()">
+      <template #left>
+        <van-icon name="arrow-left" size="25" />
+        <div style="color: #fff">返回</div>
+      </template>
+      <template #right>
+        <div style="color: #fff">提交</div>
+      </template>
+    </van-nav-bar>
+    <van-field v-model="date" type="text" label="登记日期" placeholder="请选择日期"
+               readonly is-link input-align="right" @click="dateTrueFalseBy=true" required
+    />
+    <van-field v-model="ownerCode" type="text" label="登记货主" placeholder="请选择货主" readonly
+               is-link @click="ownerRef.show(1)" input-align="right" required
+    />
+    <van-field v-model="code" type="textarea" label="单据信息" placeholder="请填写快递单号/收货任务号,多个用逗号/空格/换行符进行分割"
+               show-word-limit maxlength="500" rows="2" autosize required
+    />
+    <van-field v-model="note" type="textarea" label="&nbsp;&nbsp;备注信息" placeholder="请输入备注信息"
+               show-word-limit  maxlength="500" rows="1" autosize
+    />
+    <div class="register-content">
+      <div class="add-type">
+        <van-button type="primary" round size="small" @click="addType">添加类型</van-button>
+      </div>
+      <div class="type-box">
+        <div class="type-title">类型表一览</div>
+        <div class="type-list">
+          <div class="type-item">
+            <div class="type-item-item">类型</div>
+            <div class="type-item-operate">数量</div>
+            <div class="type-item-operate">操作</div>
+          </div>
+          <div  class="type-item" v-for="(item,index) in typeList" v-if="typeList.length>0">
+            <div  class="type-item-item">{{getOperate(item.operate)}}</div>
+            <div class="type-item-operate">{{item.expectAmount}}</div>
+            <div class="type-item-delete" @click="del(item,index)" >删除</div>
+          </div>
+          <div v-else>
+            <van-empty :image="<string>nodataUrl" image-size="140" >
+              <van-button round type="primary" class="bottom-button" size="small" @click="addType">添加</van-button>
+            </van-empty>
+          </div>
+        </div>
+      </div>
+    </div>
+    <owner ref="ownerRef" @onOwner="onOwner" />
+    <van-calendar first-day-of-week="1" v-model:show="dateTrueFalseBy" @confirm="dateConfirm" />
+    <add-register-type ref="addRegisterTypeRef"
+                       :processingTypeList="processingTypeList"
+                       @setProcessingType="setProcessingType"  />
+  </div>
+</template>
+<script setup lang="ts">
+import { ref } from 'vue'
+import { getHeader, goBack, scanError, scanSuccess } from '@/utils/android'
+import { showConfirmDialog, showNotify, showToast } from 'vant'
+import { useStore } from '@/store/modules/user'
+import { closeLoading, showLoading } from '@/utils/loading'
+import Owner from '@/components/Owner.vue'
+import { formatDate } from '@/utils/date'
+import { useRouter } from 'vue-router'
+import nodataUrl from '@/assets/nodata.png'
+import AddRegisterType from '@/views/processing/register/components/addRegisterType.vue'
+import { createProcessing, getProcessingTypeList } from '@/api/processing'
+import { toArray, toMap } from '@/utils/dataType'
+const router = useRouter()
+try {
+  getHeader()
+}catch (error) {
+  router.push('/login')
+}
+const store = useStore()
+const warehouse = store.warehouse
+const ownerRef = ref(null)
+const ownerItem = ref({})
+const ownerCode = ref('')
+const date = ref(formatDate(new Date()))
+const note = ref('')
+const code=ref('')
+const processingTypeList=ref([])
+const processingTypeMap=ref({})
+//添加类型
+const addRegisterTypeRef=ref(null)
+const typeList = ref([])
+const dateTrueFalseBy = ref(false)
+//选择货主
+const onOwner = (item) => {
+  ownerItem.value = item
+  ownerCode.value = item.name
+}
+//选择日期
+const dateConfirm = (e) => {
+  date.value = formatDate(e)
+  dateTrueFalseBy.value = false
+}
+//提交登记
+const onConfirm = () => {
+  if(!ownerCode.value){
+    showNotify({ type: 'warning', duration: 3000, message: '请选择货主' })
+    scanError()
+    return
+  }
+  if(typeList.value.length==0){
+    showNotify({ type: 'warning', duration: 3000, message: '请先添加单据类型' })
+    scanError()
+    return
+  }
+  if(!code.value){
+    showNotify({ type: 'warning', duration: 3000, message: '请先添加单据信息' })
+    scanError()
+    return
+  }
+  showConfirmDialog({
+    title: '温馨提示', message:'是否确认建单',keyboardEnabled:false })
+    .then(() => {
+    _createProcessing()
+    }).catch(() => {})
+}
+const _createProcessing=()=>{
+  const data={owner:ownerItem.value.id,ownerCode:ownerItem.value.code,note:note.value,warehouse,typeList:typeList.value,noList:toArray(code.value)}
+  showLoading()
+  createProcessing(data).then(res=>{
+    closeLoading()
+    scanSuccess()
+    showNotify({ type: 'success', duration: 3000, message: '建单成功' })
+    reset()
+  }).catch(err=>{
+    scanError()
+    closeLoading()
+  })
+}
+const reset=()=>{
+  code.value=""
+  typeList.value=[]
+  note.value=""
+  ownerCode.value=''
+}
+//添加类型
+const addType=()=>{
+  addRegisterTypeRef.value?.show(typeList.value)
+}
+const setProcessingType=(data)=>{
+  typeList.value.push(data)
+}
+//转换类型名称
+const getOperate=(operate)=>{
+  const result = operate.map(item => processingTypeMap.value[item] || item)
+  return result.join(',')
+}
+//获取类型
+const _getProcessingTypeList=()=>{
+  getProcessingTypeList().then(res=>{
+    processingTypeList.value=res.data
+    processingTypeMap.value=toMap(res.data,'code','name')
+  })
+}
+const del=(item,index)=>{
+  typeList.value.splice(index,1)
+}
+_getProcessingTypeList()
+</script>
+<style scoped lang="sass">
+.register
+  .register-content
+    background: #fff
+    padding: 10px
+    font-size: 14px
+    .add-type
+      text-align: right
+    .type-box
+      text-align: left
+      .type-title
+        line-height: 30px
+        color: #999
+      .type-list
+        border: 1px solid #e9e9e9
+        border-radius: 6px
+        padding: 5px 10px
+        .type-item
+          display: flex
+          align-items: center
+          border-bottom: 1px solid #e9e9e9
+
+          padding: 3px 0
+          .type-item-item
+            flex: 1
+          .type-item-operate
+            width: 70px
+            text-align: center
+          .type-item-delete
+            width: 70px
+            text-align: center
+            color: #ff4141
+            text-decoration: underline
+        .type-item:last-child
+          border-bottom: none
+
+
+
+</style>