Parcourir la source

完成出入库,盘点订单扫码及自提发货

yangg il y a 1 an
Parent
commit
99013e5042

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "axios": "^1.6.8",
     "dotenv": "^16.4.5",
     "easymde": "^2.18.0",
+    "element-china-area-data": "^6.1.0",
     "element-plus": "^2.7.3",
     "file-upload-with-preview": "^4.2.0",
     "highlight.js": "^11.3.1",

+ 56 - 1
src/api/api.ts

@@ -33,6 +33,26 @@ export class orderApi {     // 订单接口
     static async getSendOrderDetail(order_id) {
         return request(`/admin/erp/order/getSendOrderDetail/${order_id}`,'', 'get')
     }
+    ///admin/erp/kdn/kdShip/${id}快递鸟发货
+    static async getkdShip(id,params) {
+        return request(`/admin/erp/kdn/kdShip/${id}`,params, 'post')
+    }
+    //扫sku
+    static async getProduct(warehouseId,productCode) {
+        return request(`/admin/erp/scancode/getProduct/${warehouseId}/${productCode}`,'', 'get')
+    }
+    //扫序列号
+    static async getsnCode(type,sku,snCode) {
+        return request(`/admin/erp/scancode/getsnCode/${type}/${sku}/${snCode}`,'', 'get')
+    }
+    //
+    static async orderSend(id,params) {
+        return request(`/admin/erp/order/orderSend/${id}`,params, 'post')
+    }
+    //
+    static async queryLogistics(params) {
+        return request(`/admin/erp/kdn/queryLogistics`,params, 'post')
+    }
 }
 
 export class infoApi {     // 字典,仓库,客户
@@ -50,6 +70,14 @@ export class infoApi {     // 字典,仓库,客户
     static async collectingAccount(params) {//支付方式列表
         return request('/admin/erp/collectingAccount', params, 'get')
     }
+    //登录人员名单admin/systems/manager/admin-users
+    static async getAdmin(params) {//客户列表
+        return request('/admin/systems/manager/admin-users', params, 'get')
+    }
+    //获取物流公司列表admin/systems/logi-companies
+    static async getExpressList(params) {//客户列表
+        return request('/admin/systems/logi-companies', params, 'get')
+    }
 }
 export class dictionaryAPI {
     static async getPage(params) {//字典
@@ -65,6 +93,10 @@ export class allocationAPI {
     static async stockTransfer(params) {//字典
             return request('/admin/erp/stockTransfer', params, 'get')
         }
+        //调拨详情admin/erp/stockTransfer/${id}
+    static async getstockTransfer(id) {//字典
+        return request(`/admin/erp/stockTransfer/${id}`, '', 'get')
+    }
 }
 /* /admin/erp/warehouseEntry */
 export class entryOutAPI {
@@ -74,11 +106,34 @@ export class entryOutAPI {
     static async getWarehouseOut(params) {//出库列表
         return request('/admin/erp/warehouseOut', params, 'get')
     }
+    //入库查询
+    static async warehouseEntry(id) {//出库列表
+        return request(`/admin/erp/warehouseEntry/${id}`,'', 'get')
+    }
+    //出库查询
+    static async warehouseOut(id) {//出库列表
+        return request(`/admin/erp/warehouseOut/${id}`,'', 'get')
+    }
+    //编辑入库
+    static async addWarehouseEntry(id,params) {
+        return request(`/admin/erp/warehouseEntry/${id}`,params, 'put')
+    }
+    //新增出库  /admin/erp/warehouseOut
+    static async addWarehouseOut(id,params) {
+        return request(`/admin/erp/warehouseOut/${id}`,params, 'put')
+    }
 }
 /* 盘点列表 */
 export class inventoryAPI {
     static async getInventory(params) {//入库列表
             return request('/admin/stock/inventory', params, 'get')
         }
-   
+        //盘点详情admin/stock/inventory/${id}
+    static async inventory(id) {//入库列表
+        return request(`/admin/stock/inventory/${id}`, '', 'get')
+    }
+    //编辑盘点/admin/stock/inventory/{id}
+    static async editInventory(id,params) {
+        return request(`/admin/stock/inventory/${id}`,params, 'post')
+    }
 }

+ 2 - 2
src/i18n.ts

@@ -1,5 +1,5 @@
 import { createI18n } from 'vue-i18n';
-import messages from '@intlify/vite-plugin-vue-i18n/messages';
+//import messages from '@intlify/vite-plugin-vue-i18n/messages';
 
 export default createI18n({
     legacy: false,
@@ -7,5 +7,5 @@ export default createI18n({
     locale: 'en',
     globalInjection: true,
     fallbackLocale: 'en',
-    messages,
+    //messages,
 });

+ 39 - 1
src/router/index.ts

@@ -50,6 +50,15 @@ const routes: RouteRecordRaw[] = [
         },
         component: () => import(/* webpackChunkName: "apps-chat" */ '../views/allocation/index.vue'),
     },
+    {
+        path: '/allocation/check/:id',
+        name: 'check',
+        meta: {
+            requiresAuth: false,
+            hideButtonGroup: true
+        },
+        component: () => import(/* webpackChunkName: "apps-scrumboard" */ '../views/allocation/check.vue'),
+    },
     //出入库
     {
         path: '/InoutStorage/index',
@@ -58,6 +67,26 @@ const routes: RouteRecordRaw[] = [
             requiresAuth: true
         },
         component: () => import(/* webpackChunkName: "apps-chat" */ '../views/InoutStorage/index.vue'),
+    },
+    //入库
+    {
+        path: '/InoutStorage/inStorage/:id',
+        name: 'inStorage',
+        meta: {
+            requiresAuth: false,
+            hideButtonGroup: true
+        },
+        component: () => import(/* webpackChunkName: "apps-scrumboard" */ '../views/InoutStorage/inStorage.vue'),
+    },
+    //出库
+    {
+        path: '/InoutStorage/outStorage/:id',
+        name: 'outStorage',
+        meta: {
+            requiresAuth: false,
+            hideButtonGroup: true
+        },
+        component: () => import(/* webpackChunkName: "apps-scrumboard" */ '../views/InoutStorage/outStorage.vue'),
     },
      //盘点
      {
@@ -68,9 +97,18 @@ const routes: RouteRecordRaw[] = [
         },
         component: () => import(/* webpackChunkName: "apps-chat" */ '../views/Inventory/index.vue'),
     },
+    {
+        path: '/Inventory/addInventory/:id',
+        name: 'addInventory',
+        meta: {
+            requiresAuth: false,
+            hideButtonGroup: true
+        },
+        component: () => import(/* webpackChunkName: "apps-scrumboard" */ '../views/Inventory/addInventory.vue'),
+    },
     //发货页面
     {
-        path: '/apps/shippingPage/:id',
+        path: '/apps/shippingPage/:id/:send_type',
         name: 'shippingPage',
         meta: {
             requiresAuth: false,

+ 38 - 13
src/utils/axios.ts

@@ -9,7 +9,7 @@ axios.defaults.timeout = 60000;
 
 // 请求地址,这里是动态赋值的的环境变量,下一篇会细讲,这里跳过
 // @ts-ignore
-axios.defaults.baseURL = 'https://apidoc.raycos.net'//import.meta.env.VITE_API_URL;   
+axios.defaults.baseURL = 'http://192.168.8.103:9999'//import.meta.env.VITE_API_URL;   apidoc.raycos.net
 
 // 请求拦截器
 axios.interceptors.request.use(function (config) {
@@ -26,7 +26,7 @@ axios.interceptors.request.use(function (config) {
 
 // 控制登录过期的锁(是否正在处理登录过期)
 let isRefreshing = false
-
+let isShowingError = false;
 // 响应拦截器
 axios.interceptors.response.use(function (response) {
   // 统一处理接口响应错误,如 token 过期无效、服务端异常等
@@ -81,7 +81,14 @@ axios.interceptors.response.use(function (response) {
     return Promise.reject(response.data);
   } else {
     // 处理网络错误或请求未发出的情况
+   // 处理网络错误或请求未发出的情况
+   if (!isShowingError) {
+    isShowingError = true;
     ElMessage.error('网络连接异常,请稍后再试!');
+    setTimeout(() => {
+      isShowingError = false; // 重置标志位
+    }, 3000); // 3秒后允许再次显示错误
+  }
     return Promise.reject(error);
   }
 })
@@ -93,17 +100,35 @@ export function request(url = '', params = {}, type = 'POST') {
   //设置 url params type 的默认值
   return new Promise((resolve, reject) => {
     let promise
-    if (type.toUpperCase() === 'GET') {
-      promise = axios({
-        url,
-        params
-      })
-    } else if (type.toUpperCase() === 'POST') {
-      promise = axios({
-        method: 'POST',
-        url,
-        data: params
-      })
+    // 根据请求类型设置请求方法
+    switch (type.toUpperCase()) {
+      case 'GET':
+        promise = axios({
+          url,
+          params,
+          method: 'GET'
+        });
+        break;
+      case 'POST':
+        promise = axios({
+          url,
+          data: params,
+          method: 'POST'
+        });
+        break;
+      case 'PUT':
+        promise = axios({
+          url,
+          data: params,
+          method: 'PUT'
+        });
+        break;
+      default:
+        promise = axios({
+          url,
+          data: params,
+          method: 'POST' // 默认为POST
+        });
     }
     //处理返回
     promise.then(res => {

+ 354 - 0
src/views/InoutStorage/inStorage.vue

@@ -0,0 +1,354 @@
+<template>
+    <div class="p-4 bg-white">
+        <el-button type="text" @click="goBack">返回</el-button>
+        <!-- <h2 class="text-center my-4">发货管理</h2> -->
+        <div class="text-center my-4">
+            <!--  <Vue3Barcode v-if="barcodeValue" :value='barcodeValue' width="1" height='30'></Vue3Barcode> -->
+            <!--  <p>RYKS_DDD-20240527-00201</p> -->
+        </div>
+        <el-form :model="goodsExchangeAddForm" ref="ruleFormRef" :rules="rules" label-width="auto"
+            style="max-width: 600px">
+            <el-form-item prop="warehouse_id" label="入库仓库" class="change-form-item">
+
+                <el-select v-model="goodsExchangeAddForm.warehouse_id" clearable>
+                    <el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="类型" prop="type" class="change-form-item">
+                <el-select v-model="goodsExchangeAddForm.type" filterable allow-create clearable default-first-option
+                    :disabled="goodsExchangeAddForm.type == 'SALE_AFTER' || goodsExchangeAddForm.type == 'HAIL_ENTRY'">
+                    <el-option v-for="item in typeList" :key="item.id" :label="item.label" :value="item.value"
+                        :disabled="item.value == 'TRANSFER_ENTRY' || item.value == 'HAIL_ENTRY' || item.value == 'SALE_AFTER'" />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="备注" prop="change_desc" class="change-form-item" style="width: 100%">
+                <el-input style="flex: 1" v-model="goodsExchangeAddForm.memo" type="textarea"
+                    :autosize="{ minRows: 3, maxRows: 4 }" placeholder="请输入"></el-input>
+            </el-form-item>
+            <el-button type="primary" size="large" @click="scanCode">扫码入库</el-button>
+            <el-table :data="goodsExchangeAddForm.product_list" style="width: 100%">
+                <el-table-column prop="goods_vo.name" label="商品名称" />
+                <el-table-column prop="product_vo.sku" label="SKU" />
+                <el-table-column prop="num" label="入库数量" />
+                <el-table-column prop="orderNum" label="待入库数量" />
+            </el-table>
+        </el-form>
+        <el-dialog v-model="dialogVisible" :modal=false title="扫码入库" :close-on-click-modal="false" width="80%"
+            :before-close="handleClose">
+            <el-form :model="snCodeForm" label-width="auto" style="max-width: 600px">
+                <el-form-item label="SKU">
+                    <el-input v-model="snCodeForm.bar_code" ref="inputRef" @keyup.enter="toSearch()" />
+                </el-form-item>
+                <el-form-item label="序列号">
+                    <el-input v-model="snCodeForm.sn_code" ref="snInputRef" @keyup.enter="toSnCode()" />
+                </el-form-item>
+            </el-form>
+            <!-- <template #footer>
+                <div class="dialog-footer">
+                    <el-button @click="dialogVisible = false"></el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                        Confirm
+                    </el-button>
+                </div>
+            </template> -->
+        </el-dialog>
+        <div class="flex justify-center mt-4">
+            <el-button type="primary" size="large" @click="save(ruleFormRef)">保存</el-button>
+            <el-button type="primary" size="large" @click="getOrderNumber(ruleFormRef)">入库</el-button>
+            <!-- <el-button type="primary" size="large" @click="ship">发货</el-button> -->
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, watch, reactive, nextTick } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+import { orderApi, infoApi, dictionaryAPI, entryOutAPI } from '@/api/api'
+//引入
+import Vue3Barcode from 'vue3-barcode'
+//定义要生成条形码的值
+import { ElMessage, FormRules, FormInstance } from 'element-plus'
+const appointment_time = ref('')
+const defaultTime = new Date(2000, 1, 1, 12, 0, 0)
+let dialogVisible = ref(false)
+const tableData = ref([])
+let barcodeValue = ref('')
+const router = useRouter();
+const route = useRoute()
+const activeTab = ref('basic-info');
+const id = ref(route.params.id);
+const goodsExchangeAddForm: any = reactive({
+    warehouse_id: '',
+    type: '',
+    memo: '',
+    product_list: []
+})
+interface RuleForm {
+    warehouse_id: string,
+    type: string
+}
+const ruleFormRef = ref<FormInstance>()
+//校验表单
+const rules = reactive<FormRules<RuleForm>>({
+    warehouse_id: [
+        {
+            required: true,
+            message: 'Please select Activity zone',
+            trigger: 'change',
+        },
+    ],
+    type: [
+        {
+            required: true,
+            message: 'Please select Activity count',
+            trigger: 'change',
+        },
+    ],
+})
+//表单
+const snCodeForm = reactive({
+    bar_code: "",
+    sn_code: '',
+})
+const inputRef = ref<any>(null);
+const snInputRef = ref<any>(null)
+const customerList: any = ref([])//初始化客户列表
+const warehouseList: any = ref([])//初始化客户列表
+const collectingAccountList: any = ref([])//初始化客户列表
+const typeList: any = ref([])//初始化客户列表
+const orderTypeList: any = ref([])//初始化客户列表
+const index = ref(-1);
+let sku = ref('');
+watch(activeTab, (newValue, oldValue) => {
+    console.log(`Tab changed from ${oldValue} to ${newValue}`);
+    // 这里可以添加一些逻辑,但要确保不会再次修改 activeTab
+});
+
+/* 扫sku */
+const toSearch = async () => {
+    activeTab.value = 'product-info'
+    try {
+        const res: any = await orderApi.getProduct(
+            goodsExchangeAddForm.warehouse_id,
+            snCodeForm.bar_code.replace(/\s+/g, '')
+        );
+        res.data.sn_code_list = [];
+        sku.value = res.data.product_vo.sku;
+        // 查找当前商品是否已存在于列表中
+        const indexs = goodsExchangeAddForm.product_list.findIndex(
+            (el) => {
+                console.log("Comparing with:", el);
+                return (
+                    el.goods_vo.id === res.data.goods_id &&
+                    el.product_id === res.data.product_id &&
+                    el.product_vo.sku === res.data.product_vo.sku
+                );
+            }
+        );
+        // 如果商品不存在,则添加到列表
+        if (indexs === -1) {
+            index.value = goodsExchangeAddForm.product_list.length;
+            goodsExchangeAddForm.product_list.push(res.data);
+            console.log(goodsExchangeAddForm.product_list);
+        } else {
+            index.value = indexs;
+        }
+        if (res.data.product_vo.have_sn) {
+            nextTick(() => {
+                snInputRef.value.focus();
+            });
+        } else {
+            /* this.scanDialogVisible = false; */
+        }
+    } catch (error) {
+        console.error('Error fetching product:', error);
+    }
+};
+//扫描序列号
+const toSnCode = async () => {
+    try {
+        const res: any = await orderApi.getsnCode(
+            "in",
+            sku.value,
+            snCodeForm.sn_code.replace(/\s+/g, '')
+        );
+
+        snCodeForm.sn_code = "";
+        const exists = goodsExchangeAddForm.product_list[index.value].sn_code_list.find(
+            item => item.sn_code === res.sn_code && item.sku === res.sku
+        );
+
+        if (!exists) {
+            goodsExchangeAddForm.product_list[index.value].sn_code_list.push(res.data);
+        }
+        goodsExchangeAddForm.product_list[index.value].num = goodsExchangeAddForm.product_list[index.value].sn_code_list.length;
+        snCodeForm.bar_code = "";
+        nextTick(() => {
+            inputRef.value.focus();
+        });
+    } catch (error) {
+        console.error('Error fetching SN code:', error);
+    }
+}
+
+//关闭弹窗
+const handleClose = (done: () => void) => {
+    done()
+}
+onMounted(async () => {
+    await getInforList();
+    init();
+});
+const goBack = () => {
+    router.push({ name: 'InoutStorage' });
+};
+
+const scanCode = () => {
+    dialogVisible.value = true
+    nextTick(() => {
+        if (inputRef.value && inputRef.value.focus) {
+            inputRef.value.focus();
+        } else {
+            console.error('Input reference is not available');
+        }
+    });
+};
+//入库
+const getOrderNumber = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+        if (valid) {
+            if (!goodsExchangeAddForm.product_list.length) {
+                ElMessage.error("请选择商品!");
+                return;
+            }
+            goodsExchangeAddForm.save_or_submit = "submit"
+            goodsExchangeAddForm.product_list.map((el) => {
+                el.product_stock_id = el.id;
+                el.product_id = el.product_vo.id;
+                el.id = "";
+            });
+            entryOutAPI.addWarehouseEntry(id.value,goodsExchangeAddForm).then(res => {
+                ElMessage.success("入库成功!");
+                router.replace({
+                    name: 'InoutStorage',
+                });
+            })
+        } else {
+            console.log('error submit!', fields)
+        }
+    })
+};
+//保存
+const save = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+        if (valid) {
+            if (!goodsExchangeAddForm.product_list.length) {
+                ElMessage.error("请选择商品!");
+                return;
+            }
+            goodsExchangeAddForm.save_or_submit = "save"
+            goodsExchangeAddForm.product_list.map((el) => {
+                el.product_stock_id = el.id;
+                el.product_id = el.product_vo.id;
+                el.id = "";
+            });
+            entryOutAPI.addWarehouseEntry(id.value,goodsExchangeAddForm).then(res => {
+                ElMessage.success("保存成功!");
+                router.replace({
+                    name: 'InoutStorage',
+                });
+            })
+        } else {
+            console.log('error submit!', fields)
+        }
+    })
+};
+const ship = () => {
+    console.log('发货');
+};
+const init = async () => {
+    try {
+        const res: any = await entryOutAPI.warehouseEntry(id.value);
+        res.data.product_list.map(el => {
+            el.orderNum = el.num
+            el.num=0
+            el.sn_code_list = []
+        })
+        goodsExchangeAddForm.warehouse_id = res.data.warehouse_id;
+        goodsExchangeAddForm.type = res.data.type;
+        goodsExchangeAddForm.memo = res.data.memo;
+        goodsExchangeAddForm.product_list = res.data.product_list;
+        console.log(goodsExchangeAddForm);
+    } catch (error) {
+        console.error("Failed to fetch order details:", error);
+    }
+}
+/* 处理客户名称 */
+const customerName = (value) => {
+    if (value && customerList.value && customerList.value.data && customerList.value.data.data) {
+        const type = customerList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 仓库名称 */
+const warehouseName = (value) => {
+    if (value && warehouseList.value && warehouseList.value.data) {
+        const type = warehouseList.value.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 支付列表*/
+const collectingAccountName = (value) => {
+    if (value && collectingAccountList.value && collectingAccountList.value.data && collectingAccountList.value.data.data) {
+        const type = collectingAccountList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 配送方式*/
+const deliveryName = (value) => {
+    if (value && typeList.value && typeList.value.data && typeList.value.data.data) {
+        const type = typeList.value.data.data.find(type => type.value === value);
+        return type ? type.label : '';
+    }
+}
+/* 订单状态*/
+const orderTypeName = (value) => {
+    if (value && orderTypeList.value && orderTypeList.value.data && orderTypeList.value.data.data) {
+        const type = orderTypeList.value.data.data.find(type => type.value === value);
+        return type ? type.label : '';
+    }
+}
+/* 获取仓库,字典,客户信息 */
+const getInforList = async () => {
+    const warehouseData: any = await infoApi.warehouseList({})//获取仓库列表
+    warehouseList.value = warehouseData.data
+    customerList.value = await infoApi.customer({})//获取客户列表
+    collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表
+    const typeData: any = await dictionaryAPI.getPage({ dictType: "warehouse_entry_type" })//获取配送方式
+    typeList.value = typeData.data.data
+    console.log(typeList.value);
+    orderTypeList.value = await dictionaryAPI.getPage({ dictType: "Sale_order_send_status" })//获取订单状态
+}
+</script>
+
+<style scoped>
+.text-center {
+    text-align: center;
+}
+
+.my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+}
+
+.mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.p-4 {
+    padding: 1rem;
+}
+</style>

+ 34 - 40
src/views/InoutStorage/index.vue

@@ -23,7 +23,7 @@
                             class="align-middle">
                             <button type="button"
                                 class="flex items-center border font-semibold border-[#e0e6ed] dark:border-[#253b5c] rounded-md px-4 py-2 text-sm dark:bg-[#1b2e4b] dark:text-white-dark">
-                                <span class="ltr:mr-1 rtl:ml-1">Columns</span>
+                                <span class="ltr:mr-1 rtl:ml-1">选择</span>
                                 <icon-caret-down class="w-5 h-5" />
                             </button>
                             <template #content>
@@ -71,10 +71,10 @@
                         {{ deliveryName(data.value.has_submit) }}
                     </template>
                     <!-- 定义操作栏的模板 -->
-                    <!--  <template #actions="{ row }">
-                        <button @click="deliveryOrder(row)" class="btn btn-primary">发货</button>
-                    </template> -->
-                    <template #isActive="data">
+                     <template #actions="data">
+                        <button v-if="data.value.has_submit=='N'" @click="inStorage(data)" class="btn btn-primary">编辑</button>
+                    </template>
+                    <template #isActive="">
                         <!--  <span class="capitalize" :class="data.value.isActive ? 'text-success' : 'text-danger'">{{
                             data._rawValue }}</span> -->
                     </template>
@@ -91,8 +91,8 @@
                             class="align-middle">
                             <button type="button"
                                 class="flex items-center border font-semibold border-[#e0e6ed] dark:border-[#253b5c] rounded-md px-4 py-2 text-sm dark:bg-[#1b2e4b] dark:text-white-dark">
-                                <span class="ltr:mr-1 rtl:ml-1">Columns</span>
-                                <icon-caret-down class="w-5 h-5" />
+                                <span class="ltr:mr-1 rtl:ml-1">选择</span>
+                                <icon-caret-down class="w-4 h-5" />
                             </button>
                             <template #content>
                                 <ul class="whitespace-nowrap">
@@ -136,13 +136,13 @@
                         {{ typeOutName(data.value.type) }}
                     </template>
                     <!-- 定义操作栏的模板 -->
-                    <!--  <template #actions="{ row }">
-                        <button @click="deliveryOrder(row)" class="btn btn-primary">发货</button>
-                    </template> -->
-                    <template #isActive="data">
-                        <!--  <span class="capitalize" :class="data.value.isActive ? 'text-success' : 'text-danger'">{{
-                            data._rawValue }}</span> -->
+                     <template #actions="data">
+                        <button v-if="data.value.type!=='SALE_OUT'" @click="outStorage(data)" class="btn btn-primary">编辑</button>
                     </template>
+                    <!--   <template #isActive="data">
+                       <span class="capitalize" :class="data.value.isActive ? 'text-success' : 'text-danger'">{{
+                            data._rawValue }}</span>
+                    </template> -->
                 </vue3-datatable>
             </div>
         </div>
@@ -164,14 +164,13 @@ useMeta({ title: 'Column Chooser Table' });
 const router = useRouter();
 const store = useAppStore();
 const search = ref('');
-const rows = ref([]); // 初始化rows为响应式引用
-const outrows = ref([]);
-const customerList = ref([])//初始化客户列表
-const warehouseList = ref([])//初始化客户列表
-const collectingAccountList = ref([])//初始化客户列表
-const deliveryList = ref([])//初始化客户列表
-const entryTypeList = ref([])//初始化入库状态列表
-const outTypeList = ref([])//初始化出库状态列表
+const rows:any = ref([]); // 初始化rows为响应式引用
+const outrows:any = ref([]);
+const customerList:any = ref([])//初始化客户列表
+const warehouseList:any = ref([])//初始化客户列表
+const deliveryList:any = ref([])//初始化客户列表
+const entryTypeList:any = ref([])//初始化入库状态列表
+const outTypeList:any = ref([])//初始化出库状态列表
 //入库列表
 const cols = ref([
     { field: 'sn', title: '入库单号', isUnique: true, hide: false },
@@ -196,7 +195,7 @@ const params = ref([
     { page_no: 1, page_size: 10 }
 ])
 let activeName = ref('first')
-const handleClick = (tab: TabsPaneContext, event: Event) => {
+const handleClick = (tab: TabsPaneContext) => {
     if (tab.props.name == 'first') {
         getentryList()
     } else {
@@ -204,23 +203,21 @@ const handleClick = (tab: TabsPaneContext, event: Event) => {
     }
     //activeName = tab.props.name
 }
+//入库
+const inStorage=(row)=>{
+    router.replace({
+        name: 'inStorage',
+        params: { id: row.value.id }
+    });
+}
+const outStorage=(row)=>{
+    router.replace({
+        name: 'outStorage',
+        params: { id: row.value.id }
+    });
+}
 /* 处理订单时间格式 */
-const formatDate = (date) => {
-    if (date) {
-        const dt = new Date(date * 1000);
-        const month = dt.getMonth() + 1 < 10 ? '0' + (dt.getMonth() + 1) : dt.getMonth() + 1;
-        const day = dt.getDate() < 10 ? '0' + dt.getDate() : dt.getDate();
-        return dt.getFullYear() + '/' + month + '/' + day;
-    }
-    return '';
-};
 /* 处理客户名称 */
-const customerName = (value) => {
-    if (value && customerList.value && customerList.value.data && customerList.value.data.data) {
-        const type = customerList.value.data.data.find(type => type.id === value);
-        return type ? type.name : '';
-    }
-}
 /* 仓库名称 */
 const warehouseName = (value) => {
     if (value && warehouseList.value && warehouseList.value.data) {
@@ -287,8 +284,5 @@ const getInforList = async () => {
      */
     entryTypeList.value = await dictionaryAPI.getPage({ dictType: "warehouse_entry_type" })//获取入库类型 
     outTypeList.value = await dictionaryAPI.getPage({ dictType: "out_warehouse_type" })//获取出库类型
-}
-const deliveryOrder = async (row?: any) => {
-
 }
 </script>

+ 358 - 0
src/views/InoutStorage/outStorage.vue

@@ -0,0 +1,358 @@
+<template>
+    <div class="p-4 bg-white">
+        <el-button type="text" @click="goBack">返回</el-button>
+        <!-- <h2 class="text-center my-4">发货管理</h2> -->
+        <div class="text-center my-4">
+            <!--  <Vue3Barcode v-if="barcodeValue" :value='barcodeValue' width="1" height='30'></Vue3Barcode> -->
+            <!--  <p>RYKS_DDD-20240527-00201</p> -->
+        </div>
+        <el-form :model="goodsExchangeAddForm" ref="ruleFormRef" :rules="rules" label-width="auto"
+            style="max-width: 600px">
+            <el-form-item prop="warehouse_id" label="出库仓库" class="change-form-item">
+
+                <el-select v-model="goodsExchangeAddForm.warehouse_id" clearable>
+                    <el-option v-for="item in warehouseList" :key="item.id" :label="item.name" :value="item.id" />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="类型" prop="type" class="change-form-item">
+                <el-select v-model="goodsExchangeAddForm.type" filterable allow-create clearable default-first-option
+                    :disabled="goodsExchangeAddForm.type == 'SALE_AFTER' || goodsExchangeAddForm.type == 'HAIL_ENTRY'">
+                    <el-option v-for="item in typeList" :key="item.id" :label="item.label" :value="item.value"
+                        :disabled="item.value == 'TRANSFER_ENTRY' || item.value == 'HAIL_ENTRY' || item.value == 'SALE_AFTER'" />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="备注" prop="change_desc" class="change-form-item" style="width: 100%">
+                <el-input style="flex: 1" v-model="goodsExchangeAddForm.notes" type="textarea"
+                    :autosize="{ minRows: 3, maxRows: 4 }" placeholder="请输入"></el-input>
+            </el-form-item>
+            <el-button type="primary" size="large" @click="scanCode">扫码出库</el-button>
+            <el-table :data="goodsExchangeAddForm.product_list" style="width: 100%;font-size: 12px;">
+                <el-table-column prop="goods_vo.name" label="商品名称"  />
+                <el-table-column prop="product_vo.sku" label="SKU" />
+                <el-table-column prop="out_num" label="出库数量" />
+                <el-table-column prop="orderNum" label="待出库数量" />
+                <el-table-column  label="操作">
+                    <template #default="scope">
+                        <el-button size="mini" type="danger" @click="
+                            goodsExchangeAddForm.product_list.splice(scope.$index, 1)
+                            ">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </el-form>
+        <el-dialog v-model="dialogVisible" :modal=false title="扫码出库" :close-on-click-modal="false" width="80%"
+            :before-close="handleClose">
+            <el-form :model="snCodeForm" label-width="auto" style="max-width: 600px">
+                <el-form-item label="SKU">
+                    <el-input v-model="snCodeForm.bar_code" ref="inputRef" @keyup.enter="toSearch()" />
+                </el-form-item>
+                <el-form-item label="序列号">
+                    <el-input v-model="snCodeForm.sn_code" ref="snInputRef" @keyup.enter="toSnCode()" />
+                </el-form-item>
+            </el-form>
+            <!-- <template #footer>
+                <div class="dialog-footer">
+                    <el-button @click="dialogVisible = false"></el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                        Confirm
+                    </el-button>
+                </div>
+            </template> -->
+        </el-dialog>
+        <div class="flex justify-center mt-4">
+            <el-button type="primary" size="large" @click="save(ruleFormRef)">保存</el-button>
+            <el-button type="primary" size="large" @click="getOrderNumber(ruleFormRef)">出库</el-button>
+            <!-- <el-button type="primary" size="large" @click="ship">发货</el-button> -->
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, watch, reactive, nextTick } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+import { orderApi, infoApi, dictionaryAPI, entryOutAPI } from '@/api/api'
+//引入
+import Vue3Barcode from 'vue3-barcode'
+//定义要生成条形码的值
+import { ElMessage, FormRules, FormInstance } from 'element-plus'
+const appointment_time = ref('')
+const defaultTime = new Date(2000, 1, 1, 12, 0, 0)
+let dialogVisible = ref(false)
+const tableData = ref([])
+let barcodeValue = ref('')
+const router = useRouter();
+const route = useRoute()
+const activeTab = ref('basic-info');
+const id = ref(route.params.id);
+const goodsExchangeAddForm: any = reactive({
+    warehouse_id: '',
+    type: '',
+    notes: '',
+    product_list: []
+})
+interface RuleForm {
+    warehouse_id: string,
+    type: string
+}
+const ruleFormRef = ref<FormInstance>()
+//校验表单
+const rules = reactive<FormRules<RuleForm>>({
+    warehouse_id: [
+        {
+            required: true,
+            message: 'Please select Activity zone',
+            trigger: 'change',
+        },
+    ],
+    type: [
+        {
+            required: true,
+            message: 'Please select Activity count',
+            trigger: 'change',
+        },
+    ],
+})
+//表单
+const snCodeForm = reactive({
+    bar_code: "",
+    sn_code: '',
+})
+const inputRef = ref<any>(null);
+const snInputRef = ref<any>(null)
+const customerList: any = ref([])//初始化客户列表
+const warehouseList: any = ref([])//初始化客户列表
+const collectingAccountList: any = ref([])//初始化客户列表
+const typeList: any = ref([])//初始化客户列表
+const orderTypeList: any = ref([])//初始化客户列表
+const index = ref(-1);
+let sku = ref('');
+const currentTimeStamp = ref(Date.now());
+watch(activeTab, (newValue, oldValue) => {
+    console.log(`Tab changed from ${oldValue} to ${newValue}`);
+    // 这里可以添加一些逻辑,但要确保不会再次修改 activeTab
+});
+
+/* 扫sku */
+const toSearch = async () => {
+    activeTab.value = 'product-info'
+    try {
+        const res: any = await orderApi.getProduct(
+            goodsExchangeAddForm.warehouse_id,
+            snCodeForm.bar_code.replace(/\s+/g, '')
+        );
+        res.data.sn_code_list = [];
+        sku.value = res.data.product_vo.sku;
+        // 查找当前商品是否已存在于列表中
+        const indexs = goodsExchangeAddForm.product_list.findIndex(
+            (el) => {
+                console.log("Comparing with:", el);
+                return (
+                    el.goods_vo.id === res.data.goods_id &&
+                    el.product_id === res.data.product_id &&
+                    el.product_vo.sku === res.data.product_vo.sku
+                );
+            }
+        );
+        // 如果商品不存在,则添加到列表
+        if (indexs === -1) {
+            index.value = goodsExchangeAddForm.product_list.length;
+            goodsExchangeAddForm.product_list.push(res.data);
+            console.log(goodsExchangeAddForm.product_list);
+        } else {
+            index.value = indexs;
+        }
+        if (res.data.product_vo.have_sn) {
+            nextTick(() => {
+                snInputRef.value.focus();
+            });
+        } else {
+            /* this.scanDialogVisible = false; */
+        }
+    } catch (error) {
+        console.error('Error fetching product:', error);
+    }
+};
+//扫描序列号
+const toSnCode = async () => {
+    try {
+        const res: any = await orderApi.getsnCode(
+            "out",
+            sku.value,
+            snCodeForm.sn_code.replace(/\s+/g, '')
+        );
+
+        snCodeForm.sn_code = "";
+        const exists = goodsExchangeAddForm.product_list[index.value].sn_code_list.find(
+            item => item.sn_code === res.sn_code && item.sku === res.sku
+        );
+
+        if (!exists) {
+            goodsExchangeAddForm.product_list[index.value].sn_code_list.push(res.data);
+        }
+        goodsExchangeAddForm.product_list[index.value].out_num = goodsExchangeAddForm.product_list[index.value].sn_code_list.length;
+        snCodeForm.bar_code = "";
+        nextTick(() => {
+            inputRef.value.focus();
+        });
+    } catch (error) {
+        console.error('Error fetching SN code:', error);
+    }
+}
+
+//关闭弹窗
+const handleClose = (done: () => void) => {
+    done()
+}
+onMounted(async () => {
+    await getInforList();
+    init();
+});
+const goBack = () => {
+    router.push({ name: 'InoutStorage' });
+};
+
+const scanCode = () => {
+    dialogVisible.value = true
+    nextTick(() => {
+        inputRef.value.focus();
+    });
+};
+const getOrderNumber = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+        if (valid) {
+            goodsExchangeAddForm.out_time = Date.now()
+            goodsExchangeAddForm.save_or_submit = "submit"
+            if (!goodsExchangeAddForm.product_list.length) {
+                ElMessage.error("请选择商品!");
+                return;
+            }
+            goodsExchangeAddForm.product_list.map((el) => {
+                el.product_stock_id = el.id;
+                el.product_id = el.product_vo.id;
+                el.id = "";
+            });
+            entryOutAPI.addWarehouseOut(id.value, goodsExchangeAddForm).then(res => {
+                ElMessage.success("出库成功!");
+                router.replace({
+                    name: 'InoutStorage',
+                });
+            })
+        } else {
+            console.log('error submit!', fields)
+        }
+    })
+};
+const save = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+        if (valid) {
+            goodsExchangeAddForm.out_time = Date.now()
+            goodsExchangeAddForm.save_or_submit = "save"
+            if (!goodsExchangeAddForm.product_list.length) {
+                ElMessage.error("请选择商品!");
+                return;
+            }
+            goodsExchangeAddForm.product_list.map((el) => {
+                el.product_stock_id = el.id;
+                el.product_id = el.product_vo.id;
+                el.id = "";
+            });
+            entryOutAPI.addWarehouseOut(id.value, goodsExchangeAddForm).then(res => {
+                ElMessage.success("保存成功!");
+                router.replace({
+                    name: 'InoutStorage',
+                });
+            })
+        } else {
+            console.log('error submit!', fields)
+        }
+    })
+};
+const ship = () => {
+    console.log('发货');
+};
+const init = async () => {
+    try {
+        const res: any = await entryOutAPI.warehouseOut(id.value);
+        res.data.product_list.map(el => {
+            el.orderNum = el.out_num
+            el.out_num = 0
+            el.sn_code_list = []
+        })
+        goodsExchangeAddForm.warehouse_id = res.data.warehouse_id;
+        goodsExchangeAddForm.type = res.data.type;
+        goodsExchangeAddForm.notes = res.data.notes;
+        goodsExchangeAddForm.product_list = res.data.product_list;
+        console.log(goodsExchangeAddForm);
+    } catch (error) {
+        console.error("Failed to fetch order details:", error);
+    }
+}
+/* 处理客户名称 */
+const customerName = (value) => {
+    if (value && customerList.value && customerList.value.data && customerList.value.data.data) {
+        const type = customerList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 仓库名称 */
+const warehouseName = (value) => {
+    if (value && warehouseList.value && warehouseList.value.data) {
+        const type = warehouseList.value.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 支付列表*/
+const collectingAccountName = (value) => {
+    if (value && collectingAccountList.value && collectingAccountList.value.data && collectingAccountList.value.data.data) {
+        const type = collectingAccountList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 配送方式*/
+const deliveryName = (value) => {
+    if (value && typeList.value && typeList.value.data && typeList.value.data.data) {
+        const type = typeList.value.data.data.find(type => type.value === value);
+        return type ? type.label : '';
+    }
+}
+/* 订单状态*/
+const orderTypeName = (value) => {
+    if (value && orderTypeList.value && orderTypeList.value.data && orderTypeList.value.data.data) {
+        const type = orderTypeList.value.data.data.find(type => type.value === value);
+        return type ? type.label : '';
+    }
+}
+/* 获取仓库,字典,客户信息 */
+const getInforList = async () => {
+    const warehouseData: any = await infoApi.warehouseList({})//获取仓库列表
+    warehouseList.value = warehouseData.data
+    customerList.value = await infoApi.customer({})//获取客户列表
+    collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表
+    const typeData: any = await dictionaryAPI.getPage({ dictType: "out_warehouse_type" })//获取配送方式
+    typeList.value = typeData.data.data
+    console.log(typeList.value);
+    orderTypeList.value = await dictionaryAPI.getPage({ dictType: "Sale_order_send_status" })//获取订单状态
+}
+</script>
+
+<style scoped>
+.text-center {
+    text-align: center;
+}
+
+.my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+}
+
+.mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.p-4 {
+    padding: 1rem;
+}
+</style>

+ 359 - 0
src/views/Inventory/addInventory.vue

@@ -0,0 +1,359 @@
+<template>
+    <div class="p-4 bg-white">
+        <el-button type="text" @click="goBack">返回</el-button>
+        <!-- <h2 class="text-center my-4">发货管理</h2> -->
+        <div class="text-center my-4">
+            <!--  <Vue3Barcode v-if="barcodeValue" :value='barcodeValue' width="1" height='30'></Vue3Barcode> -->
+            <!--  <p>RYKS_DDD-20240527-00201</p> -->
+        </div>
+        <el-form :model="goodsInventoryAddForm" ref="ruleFormRef" :rules="rules" label-width="auto"
+            style="max-width: 600px">
+            <el-form-item prop="inventory_time" label="盘点时间:" class="change-form-item">
+                <el-date-picker v-model="goodsInventoryAddForm.inventory_time" style="width: 170px" size="small"
+                    type="datetime" placeholder="Select date and time" :default-time="defaultTime" />
+            </el-form-item>
+            <el-form-item prop="warehouse_id" label="仓库:" class="change-form-item">
+                <span>{{ warehouseName(goodsInventoryAddForm.warehouse_id) }}</span>
+            </el-form-item>
+            <el-form-item label="盘点人:" prop="inventory_person_id" class="change-form-item">
+                <span> {{ getadminList(goodsInventoryAddForm.inventory_person_id) }}</span>
+            </el-form-item>
+            <el-form-item label="制单人:" prop="creator_id" class="change-form-item">
+                <span>{{ getadminList(goodsInventoryAddForm.creator_id) }}</span>
+            </el-form-item>
+            <el-form-item label="盘点类型:" prop="inventory_type" class="change-form-item">
+                <span>{{ deliveryName(goodsInventoryAddForm.inventory_type) }}</span>
+            </el-form-item>
+            <el-button type="primary" size="large" @click="scanCode">商品扫码</el-button>
+            <el-table :data="goodsInventoryAddForm.product_list" style="width: 100%">
+                <el-table-column prop="goods_vo.name" label="商品名称" />
+                <el-table-column prop="product_vo.sku" label="SKU" />
+                <el-table-column prop="inventory_num" label="盘点数量" />
+                <el-table-column prop="actual_stock" label="库存数量" />
+            </el-table>
+        </el-form>
+        <el-dialog v-model="dialogVisible" :modal=false title="商品扫码" :close-on-click-modal="false" width="80%"
+            :before-close="handleClose">
+            <el-form :model="snCodeForm" label-width="auto" style="max-width: 600px">
+                <el-form-item label="SKU">
+                    <el-input v-model="snCodeForm.bar_code" ref="inputRef" @keyup.enter="toSearch()" />
+                </el-form-item>
+                <el-form-item label="序列号">
+                    <el-input v-model="snCodeForm.sn_code" ref="snInputRef" @keyup.enter="toSnCode()" />
+                </el-form-item>
+            </el-form>
+            <!-- <template #footer>
+                <div class="dialog-footer">
+                    <el-button @click="dialogVisible = false"></el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                        Confirm
+                    </el-button>
+                </div>
+            </template> -->
+        </el-dialog>
+        <div class="flex justify-center mt-4">
+
+            <el-button type="primary" size="large" @click="getOrderNumber(ruleFormRef)">保存</el-button>
+            <!-- <el-button type="primary" size="large" @click="ship">发货</el-button> -->
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, watch, reactive, nextTick } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+import { orderApi, infoApi, dictionaryAPI, inventoryAPI } from '@/api/api'
+//引入
+import Vue3Barcode from 'vue3-barcode'
+//定义要生成条形码的值
+import { ElMessage, FormRules, FormInstance } from 'element-plus'
+const appointment_time = ref('')
+const defaultTime = new Date()
+let dialogVisible = ref(false)
+const tableData = ref([])
+let barcodeValue = ref('')
+const router = useRouter();
+const route = useRoute()
+const activeTab = ref('basic-info');
+const id = ref(route.params.id);
+const goodsInventoryAddForm: any = reactive({
+    warehouse_id: '',
+    inventory_person_id: '',
+    product_list: [],
+    creator_id: '',
+    inventory_type: '',
+    inventory_time: new Date(),
+})
+interface RuleForm {
+    warehouse_id: string,
+    type: string
+}
+const ruleFormRef = ref<FormInstance>()
+//校验表单
+const rules = reactive<FormRules<RuleForm>>({
+    warehouse_id: [
+        {
+            required: true,
+            message: 'Please select Activity zone',
+            trigger: 'change',
+        },
+    ],
+    type: [
+        {
+            required: true,
+            message: 'Please select Activity count',
+            trigger: 'change',
+        },
+    ],
+})
+//表单
+const snCodeForm = reactive({
+    bar_code: "",
+    sn_code: '',
+})
+const inputRef = ref<any>(null);
+const snInputRef = ref<any>(null)
+const customerList: any = ref([])//初始化客户列表
+const warehouseList: any = ref([])//初始化客户列表
+const collectingAccountList: any = ref([])//初始化客户列表
+const typeList: any = ref([])//初始化客户列表
+const orderTypeList: any = ref([])//初始化客户列表
+const index = ref(-1);
+let sku = ref('');
+const adminList: any = ref([])
+watch(activeTab, (newValue, oldValue) => {
+    console.log(`Tab changed from ${oldValue} to ${newValue}`);
+    // 这里可以添加一些逻辑,但要确保不会再次修改 activeTab
+});
+
+/* 扫sku */
+const toSearch = async () => {
+    activeTab.value = 'product-info'
+    try {
+        const res: any = await orderApi.getProduct(
+            goodsInventoryAddForm.warehouse_id,
+            snCodeForm.bar_code.replace(/\s+/g, '')
+        );
+        res.data.sn_code_list = [];
+        sku.value = res.data.product_vo.sku;
+        // 查找当前商品是否已存在于列表中
+        const indexs = goodsInventoryAddForm.product_list.findIndex(
+            (el) => {
+                console.log("Comparing with:", el);
+                return (
+                    el.goods_vo.id === res.data.goods_id &&
+                    el.product_id === res.data.product_id &&
+                    el.product_vo.sku === res.data.product_vo.sku
+                );
+            }
+        );
+        // 如果商品不存在,则添加到列表
+        if (indexs === -1) {
+            index.value = goodsInventoryAddForm.product_list.length;
+            goodsInventoryAddForm.product_list.push(res.data);
+            console.log(goodsInventoryAddForm.product_list);
+        } else {
+            index.value = indexs;
+        }
+        if (res.data.product_vo.have_sn) {
+            nextTick(() => {
+                snInputRef.value.focus();
+            });
+        } else {
+            /* this.scanDialogVisible = false; */
+        }
+    } catch (error) {
+        console.error('Error fetching product:', error);
+    }
+};
+//扫描序列号
+const toSnCode = async () => {
+    try {
+        const res: any = await orderApi.getsnCode(
+            "inventory",
+            sku.value,
+            snCodeForm.sn_code.replace(/\s+/g, '')
+        );
+
+        snCodeForm.sn_code = "";
+        const exists = goodsInventoryAddForm.product_list[index.value].sn_code_list.find(
+            item => item.sn_code === res.sn_code && item.sku === res.sku
+        );
+
+        if (!exists) {
+            goodsInventoryAddForm.product_list[index.value].sn_code_list.push(res.data);
+        }
+        goodsInventoryAddForm.product_list[index.value].inventory_num = goodsInventoryAddForm.product_list[index.value].sn_code_list.length;
+        snCodeForm.bar_code = "";
+        nextTick(() => {
+            inputRef.value.focus();
+        });
+    } catch (error) {
+        console.error('Error fetching SN code:', error);
+    }
+}
+
+//关闭弹窗
+const handleClose = (done: () => void) => {
+    done()
+}
+onMounted(async () => {
+    await getInforList();
+    init();
+});
+const goBack = () => {
+    router.push({ name: 'Inventory' });
+};
+
+const scanCode = () => {
+    dialogVisible.value = true
+    nextTick(() => {
+        if (inputRef.value && inputRef.value.focus) {
+            inputRef.value.focus();
+        } else {
+            console.error('Input reference is not available');
+        }
+    });
+};
+// 格式化日期时间为本地格式
+function formatDate(date) {
+    const y = date.getFullYear();
+    const m = date.getMonth() + 1; // 月份是从0开始的
+    const d = date.getDate();
+    const hr = date.getHours();
+    const min = date.getMinutes();
+    const sec = date.getSeconds();
+    return `${y}-${pad(m)}-${pad(d)} ${pad(hr)}:${pad(min)}:${pad(sec)}`;
+}
+
+// 补零函数
+function pad(n) {
+    return n < 10 ? '0' + n : n;
+}
+const getOrderNumber = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+        if (valid) {
+            if (!goodsInventoryAddForm.product_list.length) {
+                ElMessage.error("请选择商品!");
+                return;
+            }
+            goodsInventoryAddForm.product_list.map(el => {
+                el.diff_num = el.usable_stock - el.inventory_num
+            })
+            goodsInventoryAddForm.inventory_time = formatDate(goodsInventoryAddForm.inventory_time)
+            inventoryAPI.editInventory(id.value, goodsInventoryAddForm).then(res => {
+                ElMessage.success("保存成功!");
+                router.replace({
+                    name: 'Inventory',
+                });
+            })
+        } else {
+            console.log('error submit!', fields)
+        }
+    })
+};
+
+const ship = () => {
+    console.log('发货');
+};
+const init = async () => {
+    try {
+        const res: any = await inventoryAPI.inventory(id.value);
+        res.data.product_list.map(el => {
+            if (!el.sn_code_list) {
+                el.sn_code_list = []
+            }
+
+        })
+        goodsInventoryAddForm.warehouse_id = res.data.warehouse_id;
+        goodsInventoryAddForm.inventory_person_id = res.data.inventory_person_id;
+        goodsInventoryAddForm.creator_id = res.data.creator_id;
+        goodsInventoryAddForm.inventory_type = res.data.inventory_type;
+        //goodsInventoryAddForm.inventory_time = res.data.inventory_time;
+        goodsInventoryAddForm.type = res.data.type;
+        goodsInventoryAddForm.product_list = res.data.product_list;
+        console.log(goodsInventoryAddForm);
+    } catch (error) {
+        console.error("Failed to fetch order details:", error);
+    }
+}
+/* 处理客户名称 */
+const customerName = (value) => {
+    if (value && customerList.value && customerList.value.data && customerList.value.data.data) {
+        const type = customerList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 仓库名称 */
+const warehouseName = (value) => {
+    if (value && warehouseList.value) {
+        const type = warehouseList.value.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 支付列表*/
+const collectingAccountName = (value) => {
+    if (value && collectingAccountList.value && collectingAccountList.value.data && collectingAccountList.value.data.data) {
+        const type = collectingAccountList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 盘点类型*/
+const deliveryName = (value) => {
+    if (value && typeList.value) {
+        const type = typeList.value.find(type => type.id === value);
+        return type ? type.label : '';
+    }
+}
+/* 订单状态*/
+const orderTypeName = (value) => {
+    if (value && orderTypeList.value && orderTypeList.value.data && orderTypeList.value.data.data) {
+        const type = orderTypeList.value.data.data.find(type => type.value === value);
+        return type ? type.label : '';
+    }
+}
+const getadminList = (value) => {
+    if (value && adminList.value && adminList.value.data && adminList.value.data.data) {
+        // Access orderTypeList directly from the component's data
+        const type = adminList.value.data.data.find(type => type.id === value);
+        return type ? type.real_name : '未知类型';
+    }
+}
+/* 获取仓库,字典,客户信息 */
+const getInforList = async () => {
+    const warehouseData: any = await infoApi.warehouseList({})//获取仓库列表
+    warehouseList.value = warehouseData.data
+    customerList.value = await infoApi.customer({})//获取客户列表
+    adminList.value = await infoApi.getAdmin({
+        page_no: 1,
+        page_size: 9999,
+        user_state: 0
+    })//获取管理员列表
+    console.log(adminList);
+    collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表
+    const typeData: any = await dictionaryAPI.getPage({ dictType: "inventory_type" })//获取配送方式
+    typeList.value = typeData.data.data
+    orderTypeList.value = await dictionaryAPI.getPage({ dictType: "Sale_order_send_status" })//获取订单状态
+}
+</script>
+
+<style scoped>
+.text-center {
+    text-align: center;
+}
+
+.my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+}
+
+.mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.p-4 {
+    padding: 1rem;
+}
+</style>

+ 26 - 23
src/views/Inventory/index.vue

@@ -49,12 +49,12 @@
 
             <div class="datatable">
                 <vue3-datatable :rows="rows" :columns="cols" :totalRows="rows?.length" :sortable="true" :search="search"
-                    skin="whitespace-nowrap bh-table-hover"
+                    style="min-height: 500px; " skin="whitespace-nowrap bh-table-hover"
                     firstArrow='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4.5 h-4.5 rtl:rotate-180"> <path d="M13 19L7 12L13 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path opacity="0.5" d="M16.9998 19L10.9998 12L16.9998 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>'
                     lastArrow='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4.5 h-4.5 rtl:rotate-180"> <path d="M11 19L17 12L11 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> <path opacity="0.5" d="M6.99976 19L12.9998 12L6.99976 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg> '
                     previousArrow='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4.5 h-4.5 rtl:rotate-180"> <path d="M15 5L9 12L15 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>'
                     nextArrow='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4.5 h-4.5 rtl:rotate-180"> <path d="M9 5L15 12L9 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>'>
-                   <!--  <template #transfer_time="data">
+                    <!--  <template #transfer_time="data">
                         {{ formatDate(data.value.transfer_time) }}
                     </template> -->
                     <template #warehouse_id="data">
@@ -69,15 +69,15 @@
                     <!-- <template #order_vo.pay_type="data">
                         {{ collectingAccountName(data.value.order_vo.pay_type) }}
                     </template>-->
-                    
+
                     <template #status="data">
                         {{ typeName(data.value.status) }}
-                    </template> 
+                    </template>
                     <!-- 定义操作栏的模板 -->
-                   <!--  <template #actions="{ row }">
-                        <button @click="deliveryOrder(row)" class="btn btn-primary">发货</button>
-                    </template> -->
-                    <template #isActive="data">
+                    <template #actions="data">
+                        <button v-if="data.value.status!=='HAS_HANDLE'" @click="deliveryOrder(data)" class="btn btn-primary m-0">编辑</button>
+                    </template>
+                    <template #isActive="">
                         <!--  <span class="capitalize" :class="data.value.isActive ? 'text-success' : 'text-danger'">{{
                             data._rawValue }}</span> -->
                     </template>
@@ -101,19 +101,19 @@ useMeta({ title: 'Column Chooser Table' });
 const router = useRouter();
 const store = useAppStore();
 const search = ref('');
-const rows = ref([]); // 初始化rows为响应式引用
-const customerList = ref([])//初始化客户列表
-const warehouseList = ref([])//初始化客户列表
-const collectingAccountList = ref([])//初始化客户列表
-const deliveryList = ref([])//初始化客户列表
-const orderTypeList = ref([])//初始化客户列表
-const cols = ref([
+const rows: any = ref([]); // 初始化rows为响应式引用
+const customerList: any = ref([])//初始化客户列表
+const warehouseList: any = ref([])//初始化客户列表
+const collectingAccountList: any = ref([])//初始化客户列表
+const deliveryList: any = ref([])//初始化客户列表
+const orderTypeList: any = ref([])//初始化客户列表
+const cols: any = ref([
     { field: 'sn', title: '盘点单编号', isUnique: true, hide: false },
     { field: 'inventory_time', title: '盘点日期', hide: false },
     { field: 'warehouse_id', title: '仓库', hide: false },
     { field: 'status', title: '状态', hide: false },
-   /*  { field: 'send_type', title: '配送方式', hide: false },
-    { field: 'status', title: '状态', hide: false }, */
+    /*  { field: 'send_type', title: '配送方式', hide: false },
+     { field: 'status', title: '状态', hide: false }, */
     { field: 'actions', title: '操作', hide: true }
 ]);
 const params = ref([
@@ -152,8 +152,8 @@ const deliveryName = (value) => {
 }
 /* 状态*/
 const typeName = (value) => {
-    if (value ) {
-        return value=='HAS_HANDLE' ? '已处理' : '未处理';
+    if (value) {
+        return value == 'HAS_HANDLE' ? '已处理' : '未处理';
     }
 }
 onMounted(() => {
@@ -170,11 +170,14 @@ const getOrderList = async () => {
 const getInforList = async () => {
     warehouseList.value = await infoApi.warehouseList({})//获取仓库列表
     deliveryList.value = await dictionaryAPI.getExpressList(params)//获取配送方式
-   /*  customerList.value = await infoApi.customer({})//获取客户列表
-    collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表*/
+    /*  customerList.value = await infoApi.customer({})//获取客户列表
+     collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表*/
     orderTypeList.value = await dictionaryAPI.getPage({ dictType: "transfer_status" })//获取状态 
 }
-const deliveryOrder = async (row?: any) => {
-   
+const deliveryOrder = async (row) => {
+    router.replace({
+        name: 'addInventory',
+        params: { id: row.value.id }
+    });
 }
 </script>

+ 270 - 0
src/views/allocation/check.vue

@@ -0,0 +1,270 @@
+<template>
+    <div class="p-4 bg-white">
+        <el-button type="text" @click="goBack">返回</el-button>
+        <!-- <h2 class="text-center my-4">发货管理</h2> -->
+        <div class="text-center my-4">
+            <!--  <Vue3Barcode v-if="barcodeValue" :value='barcodeValue' width="1" height='30'></Vue3Barcode> -->
+            <!--  <p>RYKS_DDD-20240527-00201</p> -->
+        </div>
+        <el-form :model="GoodsTransferForm" ref="ruleFormRef" :rules="rules" label-width="auto"
+            style="max-width: 600px">
+            <el-form-item prop="inventory_time" label="调拨时间:" class="change-form-item">
+                <span>{{ GoodsTransferForm.transfer_time }}</span>
+                <!-- <el-date-picker v-model="GoodsTransferForm.inventory_time" style="width: 170px" size="small"
+                    type="datetime" placeholder="Select date and time" :default-time="defaultTime" /> -->
+            </el-form-item>
+            <el-form-item prop="out_warehouse_id" label="调出仓库:" class="change-form-item">
+                <span>{{ warehouseName(GoodsTransferForm.out_warehouse_id) }}</span>
+            </el-form-item>
+            <el-form-item label="调入仓库:" prop="in_warehouse_id" class="change-form-item">
+                <span> {{ warehouseName(GoodsTransferForm.in_warehouse_id) }}</span>
+            </el-form-item>
+            <el-form-item label="配送方式:" prop="creator_id" class="change-form-item">
+                <span>{{ getadminList(GoodsTransferForm.send_type) }}</span>
+            </el-form-item>
+            <el-form-item label="备注:" prop="remark" class="change-form-item">
+                <span>{{ GoodsTransferForm.remark }}</span>
+            </el-form-item>
+            <el-table :data="GoodsTransferForm.product_list" style="width: 100%">
+                <el-table-column prop="goods_vo.name" label="商品名称" />
+                <el-table-column prop="product_vo.sku" label="SKU" />
+                <el-table-column prop="use_able_num" label="库存数量" />
+                <el-table-column prop="num" label="调拨数量" />
+            </el-table>
+        </el-form>
+        <div class="flex justify-center mt-4">
+
+            <el-button type="primary" size="large" @click="goBack()">返回</el-button>
+            <!-- <el-button type="primary" size="large" @click="ship">发货</el-button> -->
+        </div>
+    </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, watch, reactive, nextTick } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
+import { orderApi, infoApi, dictionaryAPI, allocationAPI } from '@/api/api'
+//引入
+import Vue3Barcode from 'vue3-barcode'
+//定义要生成条形码的值
+import { ElMessage, FormRules, FormInstance } from 'element-plus'
+const appointment_time = ref('')
+const defaultTime = new Date()
+let dialogVisible = ref(false)
+const tableData = ref([])
+let barcodeValue = ref('')
+const router = useRouter();
+const route = useRoute()
+const activeTab = ref('basic-info');
+const id = ref(route.params.id);
+const GoodsTransferForm: any = reactive({
+    out_warehouse_id: '',
+    in_warehouse_id: '',
+    product_list: [],
+    send_type: '',
+    remark: '',
+    transfer_time: '',
+})
+interface RuleForm {
+    out_warehouse_id: string,
+    type: string
+}
+const ruleFormRef = ref<FormInstance>()
+//校验表单
+const rules = reactive<FormRules<RuleForm>>({
+    type: [
+        {
+            required: true,
+            message: 'Please select Activity count',
+            trigger: 'change',
+        },
+    ],
+})
+//表单
+const snCodeForm = reactive({
+    bar_code: "",
+    sn_code: '',
+})
+const inputRef = ref<any>(null);
+const snInputRef = ref<any>(null)
+const customerList: any = ref([])//初始化客户列表
+const warehouseList: any = ref([])//初始化客户列表
+const collectingAccountList: any = ref([])//初始化客户列表
+const typeList: any = ref([])//初始化客户列表
+const orderTypeList: any = ref([])//初始化客户列表
+const index = ref(-1);
+let sku = ref('');
+const deliveryTypeList:any=ref('')
+const adminList: any = ref([])
+watch(activeTab, (newValue, oldValue) => {
+    console.log(`Tab changed from ${oldValue} to ${newValue}`);
+    // 这里可以添加一些逻辑,但要确保不会再次修改 activeTab
+});
+
+
+//关闭弹窗
+const handleClose = (done: () => void) => {
+    done()
+}
+onMounted(async () => {
+    await getInforList();
+    init();
+});
+const goBack = () => {
+    router.push({ name: 'allocation' });
+};
+
+const scanCode = () => {
+    dialogVisible.value = true
+    nextTick(() => {
+        if (inputRef.value && inputRef.value.focus) {
+            inputRef.value.focus();
+        } else {
+            console.error('Input reference is not available');
+        }
+    });
+};
+// 格式化日期时间为本地格式
+function formatDate(date) {
+    const y = date.getFullYear();
+    const m = date.getMonth() + 1; // 月份是从0开始的
+    const d = date.getDate();
+    const hr = date.getHours();
+    const min = date.getMinutes();
+    const sec = date.getSeconds();
+    return `${y}-${pad(m)}-${pad(d)} ${pad(hr)}:${pad(min)}:${pad(sec)}`;
+}
+
+// 补零函数
+function pad(n) {
+    return n < 10 ? '0' + n : n;
+}
+/* const getOrderNumber = async (formEl: FormInstance | undefined) => {
+    if (!formEl) return
+    await formEl.validate((valid, fields) => {
+        if (valid) {
+            if (!GoodsTransferForm.product_list.length) {
+                ElMessage.error("请选择商品!");
+                return;
+            }
+            GoodsTransferForm.product_list.map(el => {
+                el.diff_num = el.usable_stock - el.inventory_num
+            })
+            GoodsTransferForm.inventory_time = formatDate(GoodsTransferForm.inventory_time)
+            inventoryAPI.editInventory(id.value, GoodsTransferForm).then(res => {
+                ElMessage.success("保存成功!");
+                router.replace({
+                    name: 'Inventory',
+                });
+            })
+        } else {
+            console.log('error submit!', fields)
+        }
+    })
+}; */
+
+const ship = () => {
+    console.log('发货');
+};
+const init = async () => {
+    try {
+        const res: any = await allocationAPI.getstockTransfer(id.value);
+        /*  res.data.product_list.map(el => {
+             if (!el.sn_code_list) {
+                 el.sn_code_list = []
+             }
+         }) */
+        GoodsTransferForm.out_warehouse_id = res.data.out_warehouse_id;
+        GoodsTransferForm.in_warehouse_id = res.data.in_warehouse_id;
+        GoodsTransferForm.send_type = res.data.send_type;
+        GoodsTransferForm.remark = res.data.remark;
+        GoodsTransferForm.transfer_time = res.data.transfer_time;
+        GoodsTransferForm.product_list = res.data.product_list;
+        console.log(GoodsTransferForm);
+    } catch (error) {
+        console.error("Failed to fetch order details:", error);
+    }
+}
+/* 处理客户名称 */
+const customerName = (value) => {
+    if (value && customerList.value && customerList.value.data && customerList.value.data.data) {
+        const type = customerList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 仓库名称 */
+const warehouseName = (value) => {
+    if (value && warehouseList.value) {
+        const type = warehouseList.value.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 支付列表*/
+const collectingAccountName = (value) => {
+    if (value && collectingAccountList.value && collectingAccountList.value.data && collectingAccountList.value.data.data) {
+        const type = collectingAccountList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '';
+    }
+}
+/* 盘点类型*/
+const deliveryName = (value) => {
+    if (value && typeList.value) {
+        const type = typeList.value.find(type => type.id === value);
+        return type ? type.label : '';
+    }
+}
+/* 订单状态*/
+const orderTypeName = (value) => {
+    if (value && orderTypeList.value && orderTypeList.value.data && orderTypeList.value.data.data) {
+        const type = orderTypeList.value.data.data.find(type => type.value === value);
+        return type ? type.label : '';
+    }
+}
+const getadminList = (value) => {
+    if (value && deliveryTypeList.value && deliveryTypeList.value.data && deliveryTypeList.value.data.data) {
+        // Access orderTypeList directly from the component's data
+        const type = deliveryTypeList.value.data.data.find(type => type.id === value);
+        return type ? type.name : '未知类型';
+    }
+}
+/* 获取仓库,字典,客户信息 */
+const getInforList = async () => {
+    const warehouseData: any = await infoApi.warehouseList({})//获取仓库列表
+    warehouseList.value = warehouseData.data
+    customerList.value = await infoApi.customer({})//获取客户列表
+    adminList.value = await infoApi.getAdmin({
+        page_no: 1,
+        page_size: 9999,
+        user_state: 0
+    })//获取管理员列表
+    console.log(adminList);
+    collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表
+    const typeData: any = await dictionaryAPI.getPage({ dictType: "inventory_type" })//获取配送方式
+    typeList.value = typeData.data.data
+    orderTypeList.value = await dictionaryAPI.getPage({ dictType: "Sale_order_send_status" })//获取订单状态
+    deliveryTypeList.value=await infoApi.getExpressList({//获取物流公司
+        page_no: 1,
+        page_size: 9999,
+    })
+}
+</script>
+
+<style scoped>
+.text-center {
+    text-align: center;
+}
+
+.my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+}
+
+.mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.p-4 {
+    padding: 1rem;
+}
+</style>

+ 16 - 13
src/views/allocation/index.vue

@@ -19,7 +19,7 @@
                             class="align-middle">
                             <button type="button"
                                 class="flex items-center border font-semibold border-[#e0e6ed] dark:border-[#253b5c] rounded-md px-4 py-2 text-sm dark:bg-[#1b2e4b] dark:text-white-dark">
-                                <span class="ltr:mr-1 rtl:ml-1">Columns</span>
+                                <span class="ltr:mr-1 rtl:ml-1">选择</span>
                                 <icon-caret-down class="w-5 h-5" />
                             </button>
                             <template #content>
@@ -74,9 +74,9 @@
                         {{ typeName(data.value.status) }}
                     </template> 
                     <!-- 定义操作栏的模板 -->
-                   <!--  <template #actions="{ row }">
-                        <button @click="deliveryOrder(row)" class="btn btn-primary">发货</button>
-                    </template> -->
+                    <template #actions="data">
+                        <button @click="deliveryOrder(data)" class="btn btn-primary">查看</button>
+                    </template>
                     <template #isActive="data">
                         <!--  <span class="capitalize" :class="data.value.isActive ? 'text-success' : 'text-danger'">{{
                             data._rawValue }}</span> -->
@@ -101,13 +101,13 @@ useMeta({ title: 'Column Chooser Table' });
 const router = useRouter();
 const store = useAppStore();
 const search = ref('');
-const rows = ref([]); // 初始化rows为响应式引用
-const customerList = ref([])//初始化客户列表
-const warehouseList = ref([])//初始化客户列表
-const collectingAccountList = ref([])//初始化客户列表
-const deliveryList = ref([])//初始化客户列表
-const orderTypeList = ref([])//初始化客户列表
-const cols = ref([
+const rows:any = ref([]); // 初始化rows为响应式引用
+const customerList:any = ref([])//初始化客户列表
+const warehouseList:any= ref([])//初始化客户列表
+const collectingAccountList:any = ref([])//初始化客户列表
+const deliveryList:any = ref([])//初始化客户列表
+const orderTypeList:any = ref([])//初始化客户列表
+const cols:any = ref([
     { field: 'sn', title: '调拨单编号', isUnique: true, hide: false },
     { field: 'transfer_time', title: '调拨时间', hide: false },
     { field: 'out_warehouse_id', title: '调出仓库', hide: false },
@@ -175,7 +175,10 @@ const getInforList = async () => {
     collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表*/
     orderTypeList.value = await dictionaryAPI.getPage({ dictType: "transfer_status" })//获取状态 
 }
-const deliveryOrder = async (row?: any) => {
-   
+const deliveryOrder = async (row) => {
+    router.replace({
+        name: 'check',
+        params: { id: row.value.id }
+    });
 }
 </script>

+ 9 - 9
src/views/apps/order.vue

@@ -74,7 +74,7 @@
                     </template>
                     <!-- 定义操作栏的模板 -->
                     <template #actions="data">
-                        <button @click="deliveryOrder(data)" class="btn btn-primary">发货</button>
+                        <button v-if="data.value.status!=='SENDING'" @click="deliveryOrder(data)" class="btn btn-primary">发货</button>
                     </template>
                     <template #isActive="data">
                         <!--  <span class="capitalize" :class="data.value.isActive ? 'text-success' : 'text-danger'">{{
@@ -100,13 +100,13 @@ useMeta({ title: 'Column Chooser Table' });
 const router = useRouter();
 const store = useAppStore();
 const search = ref('');
-const rows = ref([]); // 初始化rows为响应式引用
-const customerList = ref([])//初始化客户列表
-const warehouseList = ref([])//初始化客户列表
-const collectingAccountList = ref([])//初始化客户列表
-const deliveryList = ref([])//初始化客户列表
-const orderTypeList = ref([])//初始化客户列表
-const cols = ref([
+const rows:any = ref([]); // 初始化rows为响应式引用
+const customerList:any = ref([])//初始化客户列表
+const warehouseList:any = ref([])//初始化客户列表
+const collectingAccountList:any = ref([])//初始化客户列表
+const deliveryList:any = ref([])//初始化客户列表
+const orderTypeList:any = ref([])//初始化客户列表
+const cols:any = ref([
     { field: 'sn', title: '订单编号', isUnique: true, hide: false },
     { field: 'order_vo.member_id', title: '客户', hide: false },
     { field: 'order_vo.order_time', title: '下单时间', hide: false },
@@ -185,7 +185,7 @@ const getInforList = async () => {
 const deliveryOrder = async (row) => {
     router.replace({
         name: 'shippingPage',
-        params: { id: row.value.id }
+        params: { id: row.value.id,send_type:row.value.order_vo.send_type }
     });
 }
 </script>

+ 375 - 30
src/views/apps/shippingPage.vue

@@ -1,56 +1,137 @@
 <template>
-    <div class="p-4">
+    <div class="p-4 bg-white">
         <el-button type="text" @click="goBack">返回</el-button>
         <!-- <h2 class="text-center my-4">发货管理</h2> -->
         <div class="text-center my-4">
-            <Vue3Barcode v-if="barcodeValue"  :value='barcodeValue' width="1" height='30'></Vue3Barcode >
-           <!--  <p>RYKS_DDD-20240527-00201</p> -->
+            <Vue3Barcode v-if="barcodeValue" :value='barcodeValue' width="1" height='30'></Vue3Barcode>
+            <!--  <p>RYKS_DDD-20240527-00201</p> -->
         </div>
         <el-tabs v-model="activeTab">
-            <el-tab-pane label="商品信息" name="product-info">
-                
-            </el-tab-pane>
             <el-tab-pane label="基本信息" name="basic-info">
                 <div class="p-4">
+                    <span>预约类型:</span>
+                    <el-select v-model="shippingForm.appointment_type" clearable placeholder="Select" size="small"
+                        style="width: 100px">
+                        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+                    </el-select>
+                    <p class="mt-1 mb-1" v-if="shippingForm.appointment_type !== 0"><span>上门时间:</span>
+                        <el-date-picker v-model="shippingForm.appointment_time" style="width: 170px" size="small"
+                            type="datetime" placeholder="Select date and time" :default-time="defaultTime" />
+                    </p>
                     <p>订单状态:{{ orderTypeName(shippingForm.order_vo.status) }}</p>
-                    <p>订单类型:{{ shippingForm.order_vo.type=='TO_B'?'销售订单':'租赁订单' }}</p>
+                    <p>订单类型:{{ shippingForm.order_vo.type == 'TO_B' ? '销售订单' : '租赁订单' }}</p>
                     <p>下单客户:{{ customerName(shippingForm.order_vo.member_id) }}</p>
                     <p>付款方式:{{ collectingAccountName(shippingForm.order_vo.pay_type) }}</p>
                     <p>实付金额:{{ shippingForm.order_vo.pay_price }}</p>
                     <p>仓库:{{ warehouseName(shippingForm.warehouse_id) }}</p>
                     <p>收货人:{{ shippingForm.order_vo.customer_name }}</p>
                     <p>联系电话:{{ shippingForm.order_vo.customer_phone }}</p>
-                    <p>地址:{{  shippingForm.order_vo.province_city_area_address}}</p>
+                    <p>地址:{{ shippingForm.order_vo.province_city_area_address }}</p>
                     <p>客户备注:{{ shippingForm.order_vo.remark }}</p>
-                    <p>运单号:</p>
+                    <p v-if="send_type.value !== 'EXPRESS_DELIVERY'">
+                      <span v-if="shippingForm.order_vo.send_type !== 'SELF_PICKUP'">运单号:{{ shippingForm.kdn_order_code }}</span>  
+                      <span v-else>自提单号:{{ shippingForm.self_pickup_num }}</span>
+                    </p>
+                    <div v-else>
+                        <p>
+                            快递公司: <el-select v-model="params.logistics_type" clearable>
+                                <el-option v-for="(item, index) in deliveryTypeList" :key="index" :label="item.name"
+                                    :value="item.code"></el-option>
+                            </el-select>
+                        </p>
+                        <p>
+                            物流单号: <el-input v-model="params.logistics_code" :minlength="1" :maxlength="20"
+                                placeholder="请输入物流单号"></el-input>
+                        </p>
+                        <p>
+                            寄件人手机号: <el-input v-model="params.logistics_phone" :minlength="1" :maxlength="20"
+                                placeholder="请输入寄件人手机号后四位"></el-input>
+                        </p>
+                    </div>
+                    <p class="mt-1 mb-1">物品总重量:<el-input v-model.number="params.total_weight" size="small"
+                            :minlength="1" style="width: 170px" :maxlength="20" type="number" max="50" clearable
+                            placeholder="请输入物品总重量"><template #append>KG</template></el-input>
+                    </p>
+                    <p class="mt-1 mb-1">物品总体积: <el-input v-model="params.total_volume" size="small" :minlength="1"
+                            style="width: 170px" :maxlength="20" clearable placeholder="请输入物品总体积"><template
+                                #append>m³</template></el-input>
+                    </p>
+                    <p>物品价格: <el-input v-model="params.declared_value" size="small" :minlength="1" style="width: 170px"
+                            :maxlength="20" clearable placeholder="请输入物品价格"><template #append>元</template></el-input>
+                    </p>
+
                 </div>
                 <!-- <div class="p-4">
                     <p>3.12 快递已收货</p>
                     <p>3.13 快递已到达中转站</p>
                 </div> -->
             </el-tab-pane>
+            <el-tab-pane label="商品信息" name="product-info">
+                <el-table :data="shippingForm.order_item_vos" style="width: 100%">
+                    <el-table-column prop="goods_vo.name" label="商品名称" />
+                    <el-table-column prop="product_vo.sku" label="SKU" />
+                    <el-table-column prop="num" label="数量" />
+                    <el-table-column prop="orderNum" label="出库数量" />
+                </el-table>
+            </el-tab-pane>
+
         </el-tabs>
-        <div class="flex justify-center mt-4">
+        <el-dialog v-model="dialogVisible" :modal=false title="扫码出库" :close-on-click-modal="false" width="80%"
+            :before-close="handleClose">
+            <el-form :model="snCodeForm" label-width="auto" style="max-width: 600px">
+                <el-form-item label="SKU">
+                    <el-input v-model="snCodeForm.bar_code" ref="inputRef" @keyup.enter="toSearch()" />
+                </el-form-item>
+                <el-form-item label="序列号">
+                    <el-input v-model="snCodeForm.sn_code" ref="snInputRef" @keyup.enter="toSnCode()" />
+                </el-form-item>
+            </el-form>
+            <!-- <template #footer>
+                <div class="dialog-footer">
+                    <el-button @click="dialogVisible = false"></el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                        Confirm
+                    </el-button>
+                </div>
+            </template> -->
+        </el-dialog>
+        <div class="flex justify-center pt-2 pb-2 mt-4 w-full fixed bottom-0 bg-white left-0"
+            v-if="send_type.value == 'EXPRESS_DELIVERY'">
+            <el-button type="primary" size="large" @click="scanCode">扫码出库</el-button>
+            <el-button type="primary" size="large"
+                v-if="shippingForm.kdn_order_code == '' || shippingForm.kdn_order_code == undefined"
+                @click="getOrderNumber">获取运单号</el-button>
+            <el-button type="primary" size="large" v-if="shippingForm.kdn_order_code !== undefined"
+                @click="ship">发货</el-button>
+        </div>
+        <div class="flex justify-center pt-2 pb-2 mt-4 w-full fixed bottom-0 bg-white left-0" v-else>
             <el-button type="primary" size="large" @click="scanCode">扫码出库</el-button>
-            <el-button type="primary" size="large" @click="getOrderNumber">获取运单号</el-button>
             <el-button type="primary" size="large" @click="ship">发货</el-button>
         </div>
     </div>
 </template>
 
 <script lang="ts" setup>
-import { ref,onMounted,watch} from 'vue';
-import { useRouter,useRoute } from 'vue-router';
+import { ref, onMounted, watch, reactive, nextTick } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
 import { orderApi, infoApi, dictionaryAPI } from '@/api/api'
+import { regionData, codeToText } from 'element-china-area-data'
 //引入
 import Vue3Barcode from 'vue3-barcode'
 //定义要生成条形码的值
+import { ElMessage } from 'element-plus'
+
+const appointment_time = ref('')
+const defaultTime = new Date(2000, 1, 1, 12, 0, 0)
+let dialogVisible = ref(false)
+const tableData = ref([])
 let barcodeValue = ref('')
 const router = useRouter();
-const route=useRoute()
-const activeTab = ref('product-info');
-const id=ref(route.params.id);
-let shippingForm=ref({
+const route = useRoute()
+const activeTab = ref('basic-info');
+const id = ref(route.params.id);
+const send_type = ref(route.params.send_type);
+let shippingForm: any = ref({
     order_vo: {
         status: '',
         type: '',
@@ -61,41 +142,300 @@ let shippingForm=ref({
         customer_phone: '',
         province_city_area_address: '',
         remark: ''
-    }
+    },
+    warehouse_id: '',
+    appointment_type: 0,
+    order_item_vos: []
 });
-const customerList = ref([])//初始化客户列表
-const warehouseList = ref([])//初始化客户列表
-const collectingAccountList = ref([])//初始化客户列表
-const deliveryList = ref([])//初始化客户列表
-const orderTypeList = ref([])//初始化客户列表
+const options = [
+    {
+        label: "立即单",
+        value: 0,
+    },
+    {
+        label: "预约上门",
+        value: 1,
+    },
+]
+//表单
+const snCodeForm = reactive({
+    bar_code: "",
+    sn_code: '',
+})
+const inputRef = ref<any>(null);
+const snInputRef = ref<any>(null)
+const customerList: any = ref([])//初始化客户列表
+const warehouseList: any = ref([])//初始化客户列表
+const collectingAccountList: any = ref([])//初始化客户列表
+const deliveryList: any = ref([])//初始化客户列表
+const orderTypeList: any = ref([])//初始化客户列表
+const index = ref(-1);
+const deliveryTypeList: any = ref([])
+const params: any = ref({
+    logistics_type: "",
+    logistics_code: "",
+    logistics_phone: "",
+    send_type: '',
+    selfPickupNum: '',
+    remark: "",//备注
+    declared_value: "",//物品价格
+    appointment_type: 0,
+    total_weight: "",//物品总重量
+    total_volume: "",//物品总体积
+    //发货人
+    sender: {
+        name: "",
+        phone: "",
+        address: "",
+        province_city_area_address: ""
+    },
+    //收货人
+    receiver: {
+        name: "",
+        phone: "",
+        address: "",
+        province_city_area_address: ""
+    },
+    //商品信息列表
+    order_item_vos: [],
+    goods_list:[]
+})
+let sku = ref('');
 watch(activeTab, (newValue, oldValue) => {
     console.log(`Tab changed from ${oldValue} to ${newValue}`);
     // 这里可以添加一些逻辑,但要确保不会再次修改 activeTab
 });
+
+/* 扫sku */
+const toSearch = async () => {
+    activeTab.value = 'product-info'
+    try {
+        const res: any = await orderApi.getProduct(
+            shippingForm.value.warehouse_id,
+            snCodeForm.bar_code.replace(/\s+/g, '')
+        );
+        res.data.sn_code_list = [];
+        sku.value = res.data.product_vo.sku;
+
+        let indexs = shippingForm.value.order_item_vos.findIndex(el => (
+            el.goods_vo.id === res.data.goods_id &&
+            el.product_id === res.data.product_id &&
+            el.product_vo.sku === res.data.product_vo.sku
+        ));
+
+        if (indexs === -1) {
+            let existingProduct = shippingForm.value.order_item_vos.find(el => (
+                el.goods_vo.id === res.data.goods_id &&
+                el.product_id === res.data.product_id &&
+                el.product_vo.sku === res.data.product_vo.sku
+            ));
+            console.log(existingProduct);
+            if (!existingProduct) {
+                //ElMessageBox.error('订单内没有该商品,请重新录入');
+                nextTick(() => {
+                    inputRef.value.focus();
+                });
+                return;
+            }
+            index.value = shippingForm.value.order_item_vos.length;
+            shippingForm.value.order_item_vos.push(res.data);
+            console.log(res.data, shippingForm.value.order_item_vos);
+
+        } else {
+            index.value = indexs;
+        }
+
+        if (res.data.product_vo.have_sn) {
+            nextTick(() => {
+                // Assuming there's a ref named snInputRef
+                snInputRef.value.focus();
+            });
+        } else {
+            dialogVisible.value = false;
+        }
+    } catch (error) {
+        console.error('Error fetching product:', error);
+    }
+};
+//扫描序列号
+const toSnCode = async () => {
+    try {
+        const res: any = await orderApi.getsnCode(
+            "out",
+            sku.value,
+            snCodeForm.sn_code.replace(/\s+/g, '')
+        );
+
+        snCodeForm.sn_code = "";
+        const exists = shippingForm.value.order_item_vos[index.value].sn_code_list.find(
+            item => item.sn_code === res.sn_code && item.sku === res.sku
+        );
+
+        if (!exists) {
+            shippingForm.value.order_item_vos[index.value].sn_code_list.push(res.data);
+        }
+
+        shippingForm.value.order_item_vos[index.value].num = shippingForm.value.order_item_vos[index.value].sn_code_list.length;
+        const indexs = shippingForm.value.order_item_vos.findIndex(el => (
+            el.goods_id === res.goods_id &&
+            el.product_id === res.product_id &&
+            el.product_vo.sku === res.sku
+        ));
+
+        //shippingForm.value.order_item_vos[index.value].id = shippingForm.value.order_item_vos[indexs].id;
+        if (shippingForm.value.order_item_vos[index.value].num === shippingForm.value.order_item_vos[index.value].orderNum) {
+            snCodeForm.bar_code = "";
+            dialogVisible.value = false;
+        } else {
+            snCodeForm.bar_code = "";
+            nextTick(() => {
+                inputRef.value.focus();
+            });
+        }
+    } catch (error) {
+        console.error('Error fetching SN code:', error);
+    }
+}
+
+//关闭弹窗
+const handleClose = (done: () => void) => {
+    done()
+}
 onMounted(() => {
-   init()
-   getInforList()
+    console.log(send_type);
+    init()
+    getInforList()
 });
 const goBack = () => {
     router.push({ name: 'order' });
 };
 
 const scanCode = () => {
-    console.log('扫码出库');
+    dialogVisible.value = true
+    nextTick(() => {
+        if (inputRef.value && inputRef.value.focus) {
+            inputRef.value.focus();
+        } else {
+            console.error('Input reference is not available');
+        }
+    });
 };
 
 const getOrderNumber = () => {
-    console.log('获取运单号');
+    //发货详细地址
+    params.value.receiver.province_city_area_address =
+        params.value.receiver.province_name +
+        params.value.receiver.city_name +
+        params.value.receiver.area_name +
+        params.value.receiver.address;
+    //收货详细地址
+    params.value.sender.province_city_area_address =
+        params.value.sender.province_name +
+        params.value.sender.city_name +
+        params.value.sender.area_name +
+        params.value.sender.address;
+    params.value.order_item_vos = shippingForm.value.order_item_vos
+    if (shippingForm.appointment_type !== 0) {
+        params.value.appointment_time = shippingForm.appointment_time
+    }
+    console.log(shippingForm);
+    //商品信息
+    /*  const item = {};
+     shippingForm.order_item_vos.map((el) => {
+       item.goods_name = el.goods_vo.name;
+       item.quantity = el.orderNum;
+       item.unit = el.product_vo.unit;
+       return params.goods_list.push(item);
+     });
+     params.goods_num = this.product_list.reduce(
+       (sum, e) => sum + Number(e.orderNum || 0),
+       0
+     );
+     params.total_weight = Number(params.total_weight);
+     params.total_volume = Number(params.total_volume);
+     params.declared_value = Number(params.declared_value);
+     //地址
+     params.send_address = this.send_address;
+     params.receive_address = this.receive_address;
+     params.order_code = this.id; */
+    orderApi.getkdShip(id.value, params.value).then(res => {
+        console.log(res);
+    })
 };
 
 const ship = () => {
-    console.log('发货');
+    //发货详细地址
+    params.value.receiver.province_city_area_address =
+        params.value.receiver.province_name +
+        params.value.receiver.city_name +
+        params.value.receiver.area_name +
+        params.value.receiver.address;
+    //收货详细地址
+    params.value.sender.province_city_area_address =
+        params.value.sender.province_name +
+        params.value.sender.city_name +
+        params.value.sender.area_name +
+        params.value.sender.address;
+    params.value.order_item_vos = shippingForm.value.order_item_vos
+    if (shippingForm.appointment_type !== 0) {
+        params.value.appointment_time = shippingForm.appointment_time
+    }
+    const item = {
+        goods_name: "",
+        quantity: '',
+        unit: "",
+    };
+    params.value.goods_list.map((el) => {
+        item.goods_name = el.goods_vo.name;
+        item.quantity = el.num;
+        item.unit = el.product_vo.unit;
+        return params.value.goods_list.push(item);
+    });
+    orderApi.orderSend(id.value, params.value).then(res => {
+        ElMessage.success("发货成功!");
+        router.push({ name: 'order' });
+    })
+    orderApi.queryLogistics({ shipper_code: params.value.logistics_type, logistic_code: params.value.logistics_code, customer_name: params.value.logistics_phone }).then(res => {
+        console.log(res);
+    })
+
 };
-const init= async()=>{
+const init = async () => {
     try {
-        const res = await orderApi.getSendOrderDetail(id.value);
+        const res: any = await orderApi.getSendOrderDetail(id.value);
         if (res && res.data && res.data.order_vo && res.data.order_vo.sn) {
+            res.data.order_item_vos.map(el => {
+                params.value.total_weight = Number(
+                    el.product_vo.weight * el.num
+                );
+                el.orderNum = el.num
+                el.num = 0
+                el.sn_code_list = []
+            })
             barcodeValue.value = res.data.order_vo.sn;
+            //物品价格
+            console.log(res.data.kdn_order_code);
+            if (res.data.total_volume !== undefined) {
+                params.value.total_volume = Number(res.data.total_volume)
+            }
+            params.value.declared_value = Number(res.data.order_vo.real_price)
+            res.data.appointment_type = 0
+            /* 发货人地址 */
+            const addressLiat = res.data.warehouse_vo.ware_pro_city_area.split(",")
+            params.value.sender.name = res.data.warehouse_vo.user_name;
+            params.value.sender.phone = res.data.warehouse_vo.phone;
+            params.value.sender.province_name = codeToText[addressLiat[0]]
+            params.value.sender.city_name = codeToText[addressLiat[1]]
+            params.value.sender.area_name = codeToText[addressLiat[2]]
+            params.value.sender.address = res.data.warehouse_vo.warehouse_address;
+            /* 收货地址 */
+            const receiverList = res.data.order_vo.province_city_area.split(",")
+            params.value.receiver.name = res.data.order_vo.customer_name;
+            params.value.receiver.phone = res.data.order_vo.customer_phone;
+            params.value.receiver.province_name = codeToText[receiverList[0]];
+            params.value.receiver.city_name = codeToText[receiverList[1]];
+            params.value.receiver.area_name = codeToText[receiverList[2]];
+            params.value.receiver.address = res.data.order_vo.address;
             shippingForm.value = res.data;
             console.log("Barcode value set to:", barcodeValue.value);
         } else {
@@ -147,6 +487,11 @@ const getInforList = async () => {
     collectingAccountList.value = await infoApi.collectingAccount({})//获取支付方式列表
     deliveryList.value = await dictionaryAPI.getPage({ dictType: "order_send_type" })//获取配送方式
     orderTypeList.value = await dictionaryAPI.getPage({ dictType: "Sale_order_send_status" })//获取订单状态
+    const expressList: any = await infoApi.getExpressList({//获取物流公司
+        page_no: 1,
+        page_size: 9999,
+    })
+    deliveryTypeList.value = expressList.data.data
 }
 </script>
 

+ 12 - 0
yarn.lock

@@ -1222,6 +1222,11 @@ chalk@^2.4.1:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+china-division@^2.7.0:
+  version "2.7.0"
+  resolved "https://registry.npmmirror.com/china-division/-/china-division-2.7.0.tgz#4060a4d243be66c7833dea64a48a4038f3e53e74"
+  integrity sha512-4uUPAT+1WfqDh5jytq7omdCmHNk3j+k76zEG/2IqaGcYB90c2SwcixttcypdsZ3T/9tN1TTpBDoeZn+Yw/qBEA==
+
 chokidar@^3.5.3:
   version "3.6.0"
   resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
@@ -1674,6 +1679,13 @@ electron-to-chromium@^1.4.251:
   resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.270.tgz"
   integrity sha512-KNhIzgLiJmDDC444dj9vEOpZEgsV96ult9Iff98Vanumn+ShJHd5se8aX6KeVxdc0YQeqdrezBZv89rleDbvSg==
 
+element-china-area-data@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.npmmirror.com/element-china-area-data/-/element-china-area-data-6.1.0.tgz#f14b90c0762b21432e097ed5be8423514a0b57e3"
+  integrity sha512-IkpcjwQv2A/2AxFiSoaISZ+oMw1rZCPUSOg5sOCwT5jKc96TaawmKZeY81xfxXsO0QbKxU5LLc6AirhG52hUmg==
+  dependencies:
+    china-division "^2.7.0"
+
 element-plus@^2.7.3:
   version "2.7.3"
   resolved "https://registry.npmjs.org/element-plus/-/element-plus-2.7.3.tgz"