Ver Fonte

Merge branch 'work_order_version_5' into zengjun

# Conflicts:
#	app/Http/Controllers/TestController.php
#	app/Http/Controllers/WorkOrderController.php
#	app/Providers/AppServiceProvider.php
#	resources/views/order/index/delivering.blade.php
ajun há 4 anos atrás
pai
commit
60c164d72a
44 ficheiros alterados com 3247 adições e 636 exclusões
  1. 13 0
      app/Filters/WorkOrderFilters.php
  2. 15 0
      app/Http/Controllers/CommodityController.php
  3. 85 0
      app/Http/Controllers/WorkOrderCommoditiesController.php
  4. 59 17
      app/Http/Controllers/WorkOrderController.php
  5. 85 0
      app/Http/Controllers/WorkOrderDetailController.php
  6. 85 0
      app/Http/Controllers/WorkOrderImageController.php
  7. 49 0
      app/Http/Controllers/WorkOrderProcessLogController.php
  8. 76 0
      app/Http/Requests/WorkOrder/WorkOrderRequest.php
  9. 8 0
      app/Providers/AppServiceProvider.php
  10. 12 0
      app/Services/CommodityService.php
  11. 7 0
      app/Services/UserService.php
  12. 31 0
      app/Services/WorkOrderCommoditiesService.php
  13. 20 0
      app/Services/WorkOrderDetailService.php
  14. 113 0
      app/Services/WorkOrderImageService.php
  15. 102 0
      app/Services/WorkOrderProcessLogService.php
  16. 153 89
      app/Services/WorkOrderService.php
  17. 16 3
      app/User.php
  18. 196 17
      app/WorkOrder.php
  19. 33 0
      app/WorkOrderCommodities.php
  20. 31 0
      app/WorkOrderDetail.php
  21. 132 0
      app/WorkOrderImage.php
  22. 118 0
      app/WorkOrderProcessLog.php
  23. 7 0
      bootstrap/cache/packages.php
  24. 38 36
      bootstrap/cache/services.php
  25. 12 0
      database/factories/WorkOrderCommoditiesFactory.php
  26. 18 0
      database/factories/WorkOrderDetailFactory.php
  27. 12 0
      database/factories/WorkOrderImageFactory.php
  28. 12 0
      database/factories/WorkOrderProcessLogFactory.php
  29. 38 0
      database/migrations/2021_09_22_135941_create_work_order_details_table.php
  30. 35 0
      database/migrations/2021_09_22_160325_create_work_order_commodities_table.php
  31. 34 0
      database/migrations/2021_09_22_162827_create_work_order_images_table.php
  32. 38 0
      database/migrations/2021_09_24_110630_create_work_order_process_logs_table.php
  33. 32 0
      database/migrations/2021_09_27_092709_add_cloumn_to_workk_orders.php
  34. 16 0
      database/seeds/WorkOrderCommoditiesSeeder.php
  35. 17 0
      database/seeds/WorkOrderDetailSeeder.php
  36. 16 0
      database/seeds/WorkOrderImageSeeder.php
  37. 16 0
      database/seeds/WorkOrderProcessLogSeeder.php
  38. 159 18
      resources/views/order/index/_work_order_modal.blade.php
  39. 488 301
      resources/views/order/index/delivering.blade.php
  40. 77 0
      resources/views/order/workOrder/_edit_process_log.blade.php
  41. 90 0
      resources/views/order/workOrder/_fill_loss_work_order.blade.php
  42. 161 0
      resources/views/order/workOrder/_work_order_details.blade.php
  43. 478 154
      resources/views/order/workOrder/index.blade.php
  44. 14 1
      routes/apiLocal.php

+ 13 - 0
app/Filters/WorkOrderFilters.php

@@ -73,6 +73,19 @@ class WorkOrderFilters
         if (!isset($this->params['owner'])){
             $this->getOrderQuery()->whereIn('owner_id', app('UserService')->getPermittingOwnerIds(Auth::user())??[]);
         }
+
+        $user = Auth::user();
+        $logistic_ids = App('UserService')->getPermittingLogisticIds($user);
+        $owner_id = app('UserService')->getPermittingOwnerIds($user);
+        if (count($logistic_ids)>=0 && count($owner_id) == 0){
+            $orderIssueIds = OrderIssue::query()->select('id')->whereIn('name',['破损','快递丢件'])->get()->toArray();
+            $orderIssueIds = array_intersect($orderIssueIds,$this->array_filter['order_issue_type']);
+            if (count($orderIssueIds) == 0) unset($this->array_filter['order_issue_type']);
+            else $this->array_filter['order_issue_type'] = $orderIssueIds;
+        }
+        $this->getOrderQuery()->where(function($query)use ($owner_id,$logistic_ids){
+            $query->whereIn('owner_id',$owner_id)->orWhereIn('logistic_id',$logistic_ids);
+        });
     }
 
     public function beforeApply()

+ 15 - 0
app/Http/Controllers/CommodityController.php

@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
 
 use App\Commodity;
 use App\Imports\CommodityImport;
+use App\Owner;
 use App\Services\CommodityBarcodeService;
 use App\Services\CommodityService;
 use App\Services\OracleBasSkuService;
@@ -394,4 +395,18 @@ class CommodityController extends Controller
             app('LogService')->log(__METHOD__,"同步商品-录入条码",json_encode($barcodeInsert));
         }
     }
+
+    public function getCommodityApi(Request $request,CommodityService $service): array
+    {
+        $owner = Owner::query()->where('name',$request['owner_name'])->first();
+        if (!$owner){
+            return ['success' => false ,'message' => '货主未找到'];
+        }
+        $commodity = $service->getCommodityBy($owner,$request->input('sku'));
+        if ($commodity){
+            return ['success' => true,'data' => $commodity];
+        }
+        return ['success' => false,'message' => '请校验sku是否正确'];
+    }
+
 }

+ 85 - 0
app/Http/Controllers/WorkOrderCommoditiesController.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\WorkOrderCommodities;
+use Illuminate\Http\Request;
+
+class WorkOrderCommoditiesController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function create()
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     *
+     * @param  \App\WorkOrderCommodities  $workOrderCommodities
+     * @return \Illuminate\Http\Response
+     */
+    public function show(WorkOrderCommodities $workOrderCommodities)
+    {
+        //
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     *
+     * @param  \App\WorkOrderCommodities  $workOrderCommodities
+     * @return \Illuminate\Http\Response
+     */
+    public function edit(WorkOrderCommodities $workOrderCommodities)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \App\WorkOrderCommodities  $workOrderCommodities
+     * @return \Illuminate\Http\Response
+     */
+    public function update(Request $request, WorkOrderCommodities $workOrderCommodities)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\WorkOrderCommodities  $workOrderCommodities
+     * @return \Illuminate\Http\Response
+     */
+    public function destroy(WorkOrderCommodities $workOrderCommodities)
+    {
+        //
+    }
+}

+ 59 - 17
app/Http/Controllers/WorkOrderController.php

@@ -3,13 +3,11 @@
 namespace App\Http\Controllers;
 
 use App\Filters\WorkOrderFilters;
+use App\Http\Requests\WorkOrder\WorkOrderRequest;
 use App\Logistic;
 use App\OrderIssue;
-use App\OrderIssueType;
-use App\Services\OwnerService;
 use App\Services\WorkOrderService;
 use App\WorkOrder;
-use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Gate;
 
@@ -56,18 +54,11 @@ class WorkOrderController extends Controller
         if (OrderIssue::query()->whereIn('order_id',$work_orders->map(function($item){return $item['order_id'];}))->exists()){
             return ['success' => false, 'message' => '已有对应的问题件'];
         }
-        try {
-            $result = $service->buildOrderIssue($work_orders);
-            if (!$result['success']) return $result;
-            $workOrders = WorkOrder::query()->with(['type', 'creator', 'order' => function ($query) {
-                /** @var $query Builder */
-                $query->with('packages', 'issue', 'logistic');
-            }, 'reviewer', 'issueType'])->whereIn('id', $request['ids'])->get();
-            $service->tags($workOrders);
-            return ['success' => true, 'data' => $workOrders];
-        } catch (\Exception $e) {
-            return  ['success' => true, 'message' => '创建问题件失败,检查对应问题件是否存在,刷新重试'];
-        }
+        $result = $service->buildOrderIssue($work_orders);
+        if (!$result['success']) return $result;
+        $workOrders = WorkOrder::query()->defaultWith()->whereIn('id',$request['ids'])->get();
+        $service->tags($workOrders);
+        return ['success' => true ,'data' => $workOrders];
     }
 
     // 创建工单 api
@@ -75,12 +66,43 @@ class WorkOrderController extends Controller
     {
         if (Gate::denies('订单管理-订单-生成工单'))
             return ['success' => false, 'message' => '没有对应权限'];
-        $params = $request['params'];
+        $params = $request->all();
         if (count($params) == 0) return ['success' => false,'message' => '参数异常'];
         app('OrderService')->syncOrderByCodes(array_map(function($param){
             return $param['order_no'];
         },$params));
-        return  $service->build($request['params']);
+        return  $service->build($params);
+    }
+
+    // 破损工单
+    public function damagedApi(WorkOrderRequest $request, WorkOrderService $service): array
+    {
+        if (Gate::denies('订单管理-订单-生成工单'))
+            return ['success' => false, 'message' => '没有对应权限'];
+        app('OrderService')->syncOrderByCodes([$request->input('order_no')]);
+
+        $workOrder =  $service->createDamagedWorkOrder($request->all());
+
+        if ($workOrder) return ['success' => true];
+        else return  ['success' => false];
+    }
+
+    // 遗失工单信息 填充
+    public function updateLossApi(Request $request, WorkOrderService $service): array
+    {
+        if (Gate::denies('订单管理-工单处理-货主编辑'))
+            return ['success' => false, 'message' => '没有对应权限'];
+
+        /** @var WorkOrder $workOrder */
+        $workOrder = WorkOrder::query()->where('id',$request->input('id'))->first();
+
+        if (!$workOrder) return ['success' => false,'message' => '参数异常'];
+
+        $workOrder = $service->fillLossWorkOrder($workOrder,$request->all());
+
+        if(!$workOrder) return ['success' => false,'message' => '创建异常'];
+
+        return ['success' => true, 'data' => $workOrder];
     }
 
     // 修改问题类型
@@ -109,8 +131,28 @@ class WorkOrderController extends Controller
     {
         if (Gate::denies('订单管理-工单处理-删除'))
             return ['success' => false,'message' => '没有对应权限'];
+        $workOrder = WorkOrder::query()->where('id',$id)->first();
+        if (! $workOrder)
+            return ['success' => false,'message' => '对应工单信息未找到'];
+        if ($workOrder->status == '已处理'){
+            return ['success' => false,'message' => '对应工单已处理,拒绝删除'];
+        }
         WorkOrder::query()->where('id',$id)->Delete();
         return ['success' => true];
     }
 
+    public function updateWorkOrderStatusApi(Request $request): array
+    {
+        if (Gate::denies('订单管理-工单处理-宝时编辑'))
+            return  ['success' => false,'message' => '没有对应权限'];
+        try {
+            $workOrder = WorkOrder::query()->find($request['id']);
+            $workOrder->work_order_status = $request['work_order_status'];
+            $workOrder->update();
+        } catch (\Exception $e) {
+            return ['success' => false,'message' => '编辑工单状态失败'];
+        }
+        return ['success' => true];
+    }
+
 }

+ 85 - 0
app/Http/Controllers/WorkOrderDetailController.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\WorkOrderDetail;
+use Illuminate\Http\Request;
+
+class WorkOrderDetailController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function create()
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     *
+     * @param  \App\WorkOrderDetail  $workOrderDetail
+     * @return \Illuminate\Http\Response
+     */
+    public function show(WorkOrderDetail $workOrderDetail)
+    {
+        //
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     *
+     * @param  \App\WorkOrderDetail  $workOrderDetail
+     * @return \Illuminate\Http\Response
+     */
+    public function edit(WorkOrderDetail $workOrderDetail)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \App\WorkOrderDetail  $workOrderDetail
+     * @return \Illuminate\Http\Response
+     */
+    public function update(Request $request, WorkOrderDetail $workOrderDetail)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\WorkOrderDetail  $workOrderDetail
+     * @return \Illuminate\Http\Response
+     */
+    public function destroy(WorkOrderDetail $workOrderDetail)
+    {
+        //
+    }
+}

+ 85 - 0
app/Http/Controllers/WorkOrderImageController.php

@@ -0,0 +1,85 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\WorkOrderImage;
+use Illuminate\Http\Request;
+
+class WorkOrderImageController extends Controller
+{
+    /**
+     * Display a listing of the resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function index()
+    {
+        //
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     * @return \Illuminate\Http\Response
+     */
+    public function create()
+    {
+        //
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function store(Request $request)
+    {
+        //
+    }
+
+    /**
+     * Display the specified resource.
+     *
+     * @param  \App\WorkOrderImage  $workOrderImage
+     * @return \Illuminate\Http\Response
+     */
+    public function show(WorkOrderImage $workOrderImage)
+    {
+        //
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     *
+     * @param  \App\WorkOrderImage  $workOrderImage
+     * @return \Illuminate\Http\Response
+     */
+    public function edit(WorkOrderImage $workOrderImage)
+    {
+        //
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  \App\WorkOrderImage  $workOrderImage
+     * @return \Illuminate\Http\Response
+     */
+    public function update(Request $request, WorkOrderImage $workOrderImage)
+    {
+        //
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @param  \App\WorkOrderImage  $workOrderImage
+     * @return \Illuminate\Http\Response
+     */
+    public function destroy(WorkOrderImage $workOrderImage)
+    {
+        //
+    }
+}

+ 49 - 0
app/Http/Controllers/WorkOrderProcessLogController.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Services\WorkOrderProcessLogService;
+use App\WorkOrderProcessLog;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\Gate;
+
+class WorkOrderProcessLogController extends Controller
+{
+    public function logisticStoreApi(Request $request, WorkOrderProcessLogService $service): array
+    {
+        if (Gate::denies('订单管理-工单处理-承运商编辑')) return ['success' => false, 'message' => '没有对应的编辑权限'];
+        if ($service->hasLogisticProcessLog($request->input('work_order_id'))) {
+            return ['success' => false, 'message' => '对应处理日志已存在'];
+        }
+        try {
+            $log = $service->createLogisticProcessLog($request->all());
+            $log->workOrder()->update(['work_order_status'=>'3']);
+            return ['success' => true, 'data' => $log];
+        } catch (\Exception $e) {
+            return ['success' => false, 'message' => '添加处理信息失败'];
+        }
+    }
+
+    public function storeApi(Request $request, WorkOrderProcessLogService $service): array
+    {
+        if (Gate::denies('订单管理-工单处理-宝时编辑')) return ['success' => false, 'message' => '没有对应的编辑权限'];
+
+        if ($service->hasBaoShiProcessLog($request->input('work_order_id'))) {
+            return ['success' => false, 'message' => '对应处理日志已存在'];
+        }
+        $log = $service->createBaoShiProcessLog($request->all());
+        return ['success' => true, 'data' => $log];
+    }
+
+    public function updateApi(Request $request): array
+    {
+        $log = WorkOrderProcessLog::query()
+            ->where('id', $request->input('id'))->first();
+
+        $params = $request->all();
+        $params['creator_id'] = Auth::user()['id'];
+        $log->update($params);
+        return ['success' => true, 'data' => $log];
+    }
+}

+ 76 - 0
app/Http/Requests/WorkOrder/WorkOrderRequest.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace App\Http\Requests\WorkOrder;
+
+use App\Traits\RequestApiFormValidation;
+use Illuminate\Foundation\Http\FormRequest;
+use Illuminate\Support\Facades\Route;
+
+class WorkOrderRequest extends FormRequest
+{
+
+    use RequestApiFormValidation;
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize()
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules()
+    {
+        $routeName = Route::currentRouteName();
+        switch ($routeName ){
+            case 'workOrder.damagedApi':
+                return $this->damagedApiRule();
+            default:
+                return [];
+        }
+    }
+
+    public function messages(): array
+    {
+        $routeName = Route::currentRouteName();
+        switch ($routeName ){
+            case 'workOrder.damagedApi':
+                return $this->damagedApiMessage();
+            default:
+                return [];
+        }
+    }
+
+    public function damagedApiRule(): array
+    {
+        return [
+            'type' => 'required|string',
+            'order_no' => 'required|string',
+            'packageImages' => 'required|array',
+            'commodityImages' => 'required|array',
+            'dealImages' => 'required|array',
+            ];
+    }
+
+    public function damagedApiMessage(): array
+    {
+        return [
+            'type.required' => '未指定工单类型',
+            'type.string' => '工单类型格式不正确',
+            'order_no.required' => '未指定订单',
+            'order_no.string' => '订单数据格式不正确',
+            'packageImages.required' => '未上传外包装图片',
+            'packageImages.array' => '外包装图片按数组格式上传',
+            'commodityImages.required' => '未上传内物图片',
+            'commodityImages.array' => '内物图片按数组格式上传',
+            'dealImages.required' => '未上传交易图片',
+            'dealImages.array' => '交易图片按数组格式上传',
+        ];
+    }
+}

+ 8 - 0
app/Providers/AppServiceProvider.php

@@ -184,6 +184,10 @@ use App\Services\WorkOrderTypeService;
 use App\Services\OrderPackageRemarkService;
 use App\Services\LaborCompanyService;
 use App\Services\NotificationService;
+use App\Services\WorkOrderDetailService;
+use App\Services\WorkOrderCommoditiesService;
+use App\Services\WorkOrderImageService;
+use App\Services\WorkOrderProcessLogService;
 use App\Services\LaborApplyService;
 
 class AppServiceProvider extends ServiceProvider
@@ -379,6 +383,10 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('WaybillFinancialService', WaybillFinancialService::class);
         app()->singleton('WaybillService', WaybillService::class);
         app()->singleton('WeighExceptedService', WeighExceptedService::class);
+        app()->singleton('WorkOrderCommoditiesService',WorkOrderCommoditiesService::class);
+        app()->singleton('WorkOrderDetailService',WorkOrderDetailService::class);
+        app()->singleton('WorkOrderImageService',WorkOrderImageService::class);
+        app()->singleton('WorkOrderProcessLogService',WorkOrderProcessLogService::class);
         app()->singleton('WorkOrderService',WorkOrderService::class);
         app()->singleton('WorkOrderTypeService',WorkOrderTypeService::class);
     }

+ 12 - 0
app/Services/CommodityService.php

@@ -948,4 +948,16 @@ class CommodityService
        $sku = array_keys($map);
        if ($sku)$this->syncWMSOrderCode($ownerId,$sku);
     }
+
+    public function getCommodityBy($owner,$sku)
+    {
+        $commodity = Commodity::query()->where('owner_id',$owner->id)->where('sku',$sku)->first();
+        if ($commodity) return $commodity;
+        $barcode = CommodityBarcode::query()->with('commodity')->where('code',$sku)->whereHas('commodity',function ($query)use($owner){
+            /** @var Builder $query */
+            $query->where('id',$owner->id);
+        })->first();
+        if ($barcode) return $barcode->commodity;
+        return false;
+    }
 }

+ 7 - 0
app/Services/UserService.php

@@ -37,6 +37,13 @@ class UserService
         })??[];
     }
 
+    function getPermittingLogisticIds($user=null){
+        if(!$user)return [];
+        return $this->cacheService->getOrExecute("user{$user['id']}->getPermittingLogisticIds",function()use($user){
+                return $user->getPermittingLogisticIdsAttribute() ?? [];
+        })??[];
+    }
+
 
     /**
      * 检查用户的管理员身份

+ 31 - 0
app/Services/WorkOrderCommoditiesService.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\WorkOrder;
+use App\WorkOrderCommodities;
+
+class WorkOrderCommoditiesService
+{
+    use ServiceAppAop;
+    protected $modelClass=WorkOrderCommodities::class;
+
+    public function createWorkOrderCommodityByJson(WorkOrder  $workOrder,$json)
+    {
+        $obj =  json_decode($json);
+        $workOrder->commodities()->create(['sku'=>$obj->sku,'amount'=>$obj->amount,'commodity_id'=>$obj->commodity_id]);
+    }
+
+    /**
+     * 工单详情
+     * @param $workOrder
+     * @param $array
+     */
+    public function createWorkOrderCommoditiesByJsonArray($workOrder,$array)
+    {
+        foreach ($array as $json) {
+            $this->createWorkOrderCommodityByJson($workOrder,$json);
+        }
+    }
+}

+ 20 - 0
app/Services/WorkOrderDetailService.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace App\Services;
+
+ use App\Traits\ServiceAppAop;
+use App\WorkOrderDetail;
+
+class WorkOrderDetailService
+{
+    use ServiceAppAop;
+    protected $modelClass=WorkOrderDetail::class;
+
+    public function createWorkOrderDetail($workOrder,$params)
+    {
+        $param  = (new WorkOrderDetail($params))->getAttributes();
+        $workOrder->details()->create($param);
+        $workOrder->loadMissing('details');
+    }
+
+}

+ 113 - 0
app/Services/WorkOrderImageService.php

@@ -0,0 +1,113 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\WorkOrderImage;
+use Illuminate\Http\UploadedFile;
+
+class WorkOrderImageService
+{
+    use ServiceAppAop;
+    protected $modelClass=WorkOrderImage::class;
+
+    /**
+     * 外包装
+     * @param $workOrder
+     * @param $images
+     * @param bool $isLoadMissing
+     */
+    public function createWorkOrderPackageImages($workOrder,$images, bool $isLoadMissing = true)
+    {
+        foreach ($images as $image) {
+           $this->createWorkOrderPackageImage($workOrder,$image);
+        }
+        if ($isLoadMissing)$workOrder->loadMissing('packageImages');
+    }
+
+    /**
+     * @param $workOrder
+     * @param $image
+     */
+    public function createWorkOrderPackageImage($workOrder,$image)
+    {
+        /** @var UploadedFile  $image */
+        $count = $workOrder->packageImages()->count();
+        /** @var WorkOrderImage $workOrderImage */
+        $workOrderImage = $workOrder->packageImages()->create(['type' => 1, 'number' => ++$count]);
+        $workOrderImage->saveFile($image);
+    }
+
+    /**
+     * 工单商品
+     * @param $workOrder
+     * @param $images
+     * @param bool $isLoadMissing
+     */
+    public function createWorkOrderCommodityImages($workOrder,$images, bool $isLoadMissing = true)
+    {
+        foreach ($images as $image) {
+            $this->createWorkOrderCommodityImage($workOrder,$image);
+        }
+        if ($isLoadMissing)$workOrder->loadMissing('packageImages');
+    }
+
+    /**
+     * @param $workOrder
+     * @param $image
+     */
+    public function createWorkOrderCommodityImage($workOrder,$image)
+    {
+        /** @var UploadedFile  $image */
+        $count = $workOrder->commodityImages()->count();
+        /** @var WorkOrderImage $workOrderImage */
+        $workOrderImage = $workOrder->commodityImages()->create(['type' => 2, 'number' => ++$count]);
+        $workOrderImage->saveFile($image);
+    }
+
+    /**
+     * 交易截图
+     * @param $workOrder
+     * @param $images
+     * @param bool $isLoadMissing
+     */
+    public function createWorkOrderDealImages($workOrder,$images, bool $isLoadMissing = true)
+    {
+        foreach ($images as $image) {
+            $this->createWorkOrderCommodityImage($workOrder,$image);
+        }
+        if ($isLoadMissing)$workOrder->loadMissing('dealImages');
+    }
+
+    public function createWorkOrderDealImage($workOrder,$image)
+    {
+        /** @var UploadedFile  $image */
+        $count = $workOrder->commodityImages()->count();
+        /** @var WorkOrderImage $workOrderImage */
+        $workOrderImage = $workOrder->dealImages()->create(['type' => 3, 'number' => ++$count]);
+        $workOrderImage->saveFile($image);
+    }
+
+    /**
+     * 退款截图
+     * @param $workOrder
+     * @param $images
+     * @param bool $isLoadMissing
+     */
+    public function createWorkOrderRefundImages($workOrder, $images, bool $isLoadMissing = true)
+    {
+        foreach ($images as $image) {
+            $this->createWorkOrderCommodityImage($workOrder,$image);
+        }
+        if ($isLoadMissing)$workOrder->loadMissing('refundImages');
+    }
+
+    public function createWorkOrderRefundImage($workOrder,$image)
+    {
+        /** @var UploadedFile  $image */
+        $count = $workOrder->refundImages()->count();
+        /** @var WorkOrderImage $workOrderImage */
+        $workOrderImage = $workOrder->refundImages()->create(['type' => 4, 'number' => ++$count]);
+        $workOrderImage->saveFile($image);
+    }
+}

+ 102 - 0
app/Services/WorkOrderProcessLogService.php

@@ -0,0 +1,102 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\WorkOrderProcessLog;
+use Illuminate\Support\Facades\Auth;
+
+class WorkOrderProcessLogService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = WorkOrderProcessLog::class;
+
+    public function hasLogisticProcessLog($workOrderId): bool
+    {
+        return WorkOrderProcessLog::query()
+            ->where('work_order_id', $workOrderId)
+            ->where('type', '2')->exists();
+    }
+
+    public function hasBaoShiProcessLog($workOrderId): bool
+    {
+        return WorkOrderProcessLog::query()
+            ->where('work_order_id', $workOrderId)
+            ->where('type', '1')->exists();
+    }
+
+    public function createLogisticProcessLog($params)
+    {
+        // 赔偿方  是否赔偿
+        if ($params['is_indemnity'] == 1) {  // 赔偿
+            $params = [
+                'work_order_id' => $params['work_order_id'],
+                'is_indemnity' => $params['is_indemnity'],
+                'indemnity' => $params['indemnity'],
+                'indemnitor' => 1,
+                'remark' => $params['remark'],
+                'type' => 2,
+            ];
+        } else if ($params['is_indemnity'] == 2){ // 不赔偿
+            $params = [
+                'work_order_id' => $params['work_order_id'],
+                'is_indemnity' => $params['is_indemnity'],
+                'remark' => $params['remark'],
+                'indemnitor' => 0,
+            ];
+        }
+
+        $params['creator_id'] = Auth::user()['id'];
+        $params['type'] = '2';
+        $log = WorkOrderProcessLog::query()->create($params);
+        $log->workOrder()->update(['work_order_status' => '3']);
+        $this->syncOrderIssueIndemnity($log);
+        $log->loadMissing('creator');
+        return $log;
+    }
+
+    public function createBaoShiProcessLog($params)
+    {
+        if ($params['is_indemnity'] == 1){  // 赔偿
+            $params = [
+                'work_order_id' => $params['work_order_id'],
+                'is_indemnity' => $params['is_indemnity'],
+                'indemnitor' => $params['indemnitor'],
+                'indemnity' => $params['indemnity'],
+            ];
+        } else if ($params['is_indemnity'] == 2){ // 不赔偿
+            $params = [
+                'work_order_id' => $params['work_order_id'],
+                'is_indemnity' => $params['is_indemnity'],
+                'remark' => $params['remark'],
+                'indemnity' => $params['indemnity'],
+            ];
+        }
+        $params['creator_id'] = Auth::user()['id'];
+        $params['type'] = 1;
+        if ($params['is_indemnity'] == 2) $params['indemnity'] = 0;
+        $log = WorkOrderProcessLog::query()->create($params);
+        $this->syncOrderIssueIndemnity($log);
+        $log->loadMissing('creator');
+        return $log;
+    }
+
+    public function syncOrderIssueIndemnity($log)
+    {
+        if ($log->is_indemnity == '否') return;
+        $orderIssue = $log->workOrder->orderIssue;
+        if ($log->isBaoShiLog()) {
+            if ($log->indemnitor == '宝时'){
+                if ($orderIssue->baoshi_indemnity_money) return ;
+                $orderIssue->update(['baoshi_indemnity_money'=>$log->indemnity]);
+            } else if ($log->indemnitor == '承运商'){
+                if ($orderIssue->logistic_indemnity_money) return ;
+                $orderIssue->update(['logistic_indemnity_money'=>$log->indemnity]);
+            }
+        } else if ($log->isLogisticLog()){
+            if ($orderIssue->logistic_indemnity_money) return ;
+            $orderIssue->update(['logistic_indemnity_money'=>$log->indemnity]);
+        }
+    }
+}

+ 153 - 89
app/Services/WorkOrderService.php

@@ -8,8 +8,8 @@ use App\OrderIssueType;
 use App\OrderPackage;
 use App\Traits\ServiceAppAop;
 use App\WorkOrder;
-use App\WorkOrderType;
 use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Support\Carbon;
 use Illuminate\Support\Facades\Auth;
 
@@ -19,22 +19,18 @@ class WorkOrderService
 
     protected $modelClass = WorkOrder::class;
 
-    protected $work_type_relation = [
-        '拦截' => '订单拦截',
-        '信息更改' => '订单收件信息修改',
-        '其他' => '订单其他异常',
-        '快递异常' => '订单快递异常',
-        '错漏发' => '快递错漏发',
-        '破损' => '快递破损',
-    ];
-
     /**
      * 获取可生成工单的问题件类型
-     * @return Builder[]|\Illuminate\Database\Eloquent\Collection
+     * @return Builder[]|Collection
      */
     public function getIssueType()
     {
-        return OrderIssueType::query()->whereIn('name', ['拦截', '信息更改', '其他', '快递异常', '错漏发', '破损'])->get();
+        $user = Auth::user();
+        $logistic_ids = App('UserService')->getPermittingLogisticIds($user);
+        if ($user->isSuperAdmin() || count($logistic_ids) == 0) {
+            return OrderIssueType::query()->whereIn('name', ['拦截', '信息更改', '其他', '快递异常', '错漏发', '破损', '快递丢件'])->get();
+        }
+        return OrderIssueType::query()->whereIn('name', ['破损', '快递丢件'])->get();
     }
 
     /**
@@ -44,75 +40,152 @@ class WorkOrderService
      */
     public function build($params): array
     {
-        $types = [];
-        $work_order_types = [];
-        $data = Carbon::now();
-        $creator_id = Auth::user()['id'];
-        $parent = WorkOrderType::query()->firstOrCreate(['name' => '订单', 'prent_id' => 0, 'level' => 1]);
         foreach ($params as $param) {
-            $order = Order::query()->where('code', $param['order_no'])->first();
             $type_name = $param['order_issue_type'];
-            if (!$param['order_issue_type']) {
-                $order_issue_type_id = 0;
-                $work_order_type_id = 0;
+            if ('拦截' == $type_name) {
+                $this->createInterceptWorkOrder($param);
+            } else if ('信息更改' == $type_name) {
+                $this->createInformationChangeWorkOrder($param);
+            } else if ('快递异常' == $type_name) {
+                $this->createExpressAbnormalWorkOrder($param);
+            } else if ('错漏发' == $type_name) {
+                $this->createMistakeWorkOrder($param);
+            } else if ('破损' == $type_name) {
+                $this->createDamagedWorkOrder($param);
+            } else if ('快递丢件' == $type_name) {
+                $this->createLossWorkOrder($param);
             } else {
-                if (!array_key_exists($type_name, $types)) {
-                    $types[$type_name] = OrderIssueType::query()->where('name', $type_name)->first();
-                    $work_order_type_name = $this->work_type_relation[$type_name] ?? $type_name;
-                    $work_order_types[$type_name] = WorkOrderType::query()->firstOrCreate(
-                        ['name' => $work_order_type_name],
-                        ['prent_id' => $parent['id'], 'table_name' => 'orders', 'level' => 2]
-                    );
-                }
-                $order_issue_type_id = $types[$type_name]['id'] ?? 0;
-                $work_order_type_id = $work_order_types[$type_name]['id'] ?? 0;
-            }
-            switch ($type_name) {
-                case '拦截':
-                    $remark = "拦截订单";
-                    break;
-                case '信息更改':
-                case '其他':
-                case '快递异常':
-                case '错漏发':
-                case '破损':
-                    $remark = $param['remark'] ?? '';
-                    break;
-                default:
-                    $work_order_type_id = 0;
-                    $remark = $param['remark'] ?? '';
-                    $order_issue_type_id = 0;
-                    break;
-            }
-            $inner_params[] = [
-                'order_id' => $order['id'],
-                'creator_id' => $creator_id,
-                'work_order_type_id' => $work_order_type_id ?? 0,
-                'owner_id' => $order['owner_id'] ?? '',
-                'grad' => $param['grad'] ?? 1,
-                'remark' => $remark,
-                'outer_table_name' => 'orders',
-                'order_issue_type_id' => $order_issue_type_id ?? 0,
-                'uniquely_tag' => $order['code'] ?? null,
-                'status' => 1,
-                'created_at' => $data,
-                'updated_at' => $data,
-            ];
-        }
-        if (isset($inner_params)) {
-            WorkOrder::query()->insert($inner_params);
-            $workOrders = WorkOrder::query()->with('owner','order')->whereIn('order_id',data_get($inner_params,"*.order_id"))->where('created_at',">=",$data)->get();
-            foreach ($workOrders as $workOrder){
-                $user = Auth::user();
-                $remark = $workOrder->remark;
-                $ownerName = $workOrder->owner->name ?? '';
-                $clientCode = $workOrder->order->client_code ?? '';
-                $msg = $user["name"]."建立了新工单<br/>".$ownerName.":".$clientCode."<br/>".$remark;
-                NotificationService::SingleRegister($msg,$clientCode,"订单管理-问题件");
+                $this->createDefaultWorkOrder($param);
             }
-            return ['success' => true];
         }
-        return ['success' => false, 'message' => '参数异常'];
+        return ['success' => true];
+    }
+
+    public function createAndNotification($order, $orderIssueType, $remake): WorkOrder
+    {
+        $user = Auth::user();
+        /** @var WorkOrder $workOrder */
+        $workOrder = WorkOrder::query()->create([
+            'order_id' => $order->id,
+            'creator_id' => $user["id"],
+            'owner_id' => $order->owner_id,
+            'remark' => $remake,
+            'outer_table_name' => 'orders',
+            'order_issue_type_id' => $orderIssueType->id,
+            'uniquely_tag' => $order->code,
+            'status' => 1,
+        ]);
+        $workOrder->notification();
+        return $workOrder;
+    }
+
+    /**
+     * 拦截
+     */
+    public function createInterceptWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '拦截']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = '拦截订单';
+        return $this->createAndNotification($order, $orderIssueType, $remake);
+    }
+
+    /**
+     * 错漏发
+     */
+    public function createMistakeWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '错漏发']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = $param['remark'] ?? '';
+        return $this->createAndNotification($order, $orderIssueType, $remake);
+    }
+
+    /**
+     * 破损
+     */
+    public function createDamagedWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '破损']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = $param['remark'] ?? '';
+
+        $workOrderImageService = app('WorkOrderImageService');
+
+        $workOrder = $this->createAndNotification($order, $orderIssueType, $remake);
+
+        $workOrderImageService->createWorkOrderPackageImages($workOrder, $param['packageImages']);
+
+        $workOrderImageService->createWorkOrderCommodityImages($workOrder, $param['commodityImages']);
+
+        $workOrderImageService->createWorkOrderDealImages($workOrder, $param['dealImages']);
+
+        app('WorkOrderDetailService')->createWorkOrderDetail($workOrder, $param);
+
+        app('WorkOrderCommoditiesService')->createWorkOrderCommoditiesByJsonArray($workOrder, $param['commodities']); // 登记商品信息
+
+        $workOrder->update(['work_order_status' => 2]); // 标记信息已填写
+
+        return $workOrder;
+    }
+
+    /***
+     * 快递异常
+     */
+    public function createExpressAbnormalWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '快递异常']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = $param['remark'] ?? '';
+        return $this->createAndNotification($order, $orderIssueType, $remake);
+    }
+
+    /***
+     * 信息更改
+     */
+    public function createInformationChangeWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '信息更改']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = $param['remark'] ?? '';
+        return $this->createAndNotification($order, $orderIssueType, $remake);
+    }
+
+    /**
+     * 其他
+     */
+    public function createDefaultWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '其他']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = $param['remark'] ?? '';
+        return $this->createAndNotification($order, $orderIssueType, $remake);
+    }
+
+    /**
+     * 快递丢件
+     */
+    public function createLossWorkOrder($param): WorkOrder
+    {
+        $orderIssueType = OrderIssueType::query()->firstOrCreate(['name' => '快递丢件']);
+        $order = Order::query()->where('code', $param['order_no'])->first();
+        $remake = $param['remark'] ?? '';
+        $workOrder = $this->createAndNotification($order, $orderIssueType, $remake);
+        $workOrder->update(['work_order_status' => 1]); // 标记信息未填写
+        return $workOrder;
+    }
+
+    /**
+     *  填充丢件信息
+     */
+    public function fillLossWorkOrder(WorkOrder $workOrder, $param): WorkOrder
+    {
+        $workOrder->saveWorkOrderDetail($param);        // 收方信息 丢件价值 补发单号
+        $workOrder->addDealImage($param['dealImages']); // 交易截图
+        $workOrder->addRefundImage($param['refundImages'] ?? []); // 退款截图
+        $workOrder->update(['work_order_status' => 2]); // 标记信息填写
+        $workOrder->loadDefaultWith();
+        return $workOrder;
     }
 
     /**
@@ -123,10 +196,7 @@ class WorkOrderService
     public function review($wordOrder): array
     {
         $wordOrder->update(['reviewer_id' => Auth::user()['id'], 'review_at' => Carbon::now(), 'status' => '2',]);
-        $workOrders = WorkOrder::query()->with(['type', 'creator', 'order' => function ($query) {
-            /** @var $query Builder */
-            $query->with('packages', 'issue', 'logistic');
-        }, 'reviewer'])->whereIn('id', [$wordOrder['id']])->get();
+        $workOrders = WorkOrder::query()->defaultWith()->whereIn('id', [$wordOrder['id']])->get();
         $this->tags($workOrders);
         return ['success' => true, 'data' => $workOrders->first()];
     }
@@ -142,11 +212,7 @@ class WorkOrderService
             return $item->id;
         })->toArray();
         WorkOrder::query()->whereIn('id', $ids)->update(['review_at' => Carbon::now(), 'reviewer_id' => Auth::user()['id'], 'status' => '2']);
-        $wordOrders = WorkOrder::query()->with(['type', 'creator', 'order' => function ($query) {
-            /** @var $query Builder */
-            $query->with('packages', 'issue', 'logistic');
-        }, 'reviewer'])->whereIn('id', $ids)->get();
-        $this->tags($wordOrders);
+        $wordOrders = WorkOrder::query()->defaultWith()->whereIn('id', $ids)->get();
         return ['success' => true, 'data' => $wordOrders];
     }
 
@@ -160,9 +226,7 @@ class WorkOrderService
     {
         foreach ($work_orders as $work_order) {
             $inner_params[] = [
-                'order_id' => $work_order->order_id,
-                'order_issue_type_id' => $work_order->order_issue_type_id,
-                'result_explain' => $work_order->remark,
+                'order_id' => $work_order->order_id, 'order_issue_type_id' => $work_order->order_issue_type_id, 'result_explain' => $work_order->remark,
             ];
         }
         if (!isset($inner_params)) return ['success' => false, 'message' => '创建问题件失败'];
@@ -191,7 +255,7 @@ class WorkOrderService
     }
 
     /**
-     * 标记已有 问题件的工单
+     * 标记已有 问题件 的工单
      * @param $workOrders
      */
     public function tags(&$workOrders)

+ 16 - 3
app/User.php

@@ -49,7 +49,8 @@ class User extends Authenticatable
 //    function hasRole($roles){
 //        return !!$roles->intersect($this->roles()->get())->count();
 //    }
-    function isSuperAdmin(){
+    function isSuperAdmin(): bool
+    {
         $superAdmins=config("users.superAdmin");
         foreach ($superAdmins as $superAdmin){
             if($this['name']==$superAdmin){
@@ -111,7 +112,8 @@ class User extends Authenticatable
         });
         return $authorities;
     }
-    function getPermittingOwnerIdsAttribute(){
+    function getPermittingOwnerIdsAttribute(): array
+    {
         $ownerIds=[];
         if($this->isSuperAdmin()||Gate::allows('货主-可见全部')){
             $owners=Owner::query()->whereNull('deleted_at')->get();
@@ -135,7 +137,8 @@ class User extends Authenticatable
         }
         return  array_unique(array_merge($ownerIds, $old_owner));
     }
-    function getPermittingWorkgroupIds($allowAll=false){
+    function getPermittingWorkgroupIds($allowAll=false): array
+    {
         $workgroupIds=[];
         if ($this->isSuperAdmin()||$allowAll){
             $workgroups=UserWorkgroup::all();
@@ -151,6 +154,7 @@ class User extends Authenticatable
         }
         return $workgroupIds;
     }
+
     function getPermittingLaborCompanyIdsAttribute(): array
     {
         $labor_company_ids=array();
@@ -170,6 +174,15 @@ class User extends Authenticatable
         return array_unique($labor_company_ids);
     }
 
+    // 用户可见货主
+    public function getPermittingLogisticIdsAttribute(): array
+    {
+        if ($this->isSuperAdmin()){
+            return Logistic::query()->get()->map(function($logistic){return $logistic->id;})->toArray();
+        }
+        return $this->logistics()->get()->map(function($logistic){return $logistic->id;})->toArray();
+    }
+
     public function workGroups()
     {
         return $this->morphedByMany(UserWorkgroup::class, 'user_authable');

+ 196 - 17
app/WorkOrder.php

@@ -2,13 +2,18 @@
 
 namespace App;
 
+use App\Services\NotificationService;
 use App\Traits\ModelTimeFormat;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Model;
 
 use App\Traits\ModelLogChanging;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Database\Eloquent\Relations\HasOne;
 use Illuminate\Database\Eloquent\SoftDeletes;
+use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Gate;
 
 class WorkOrder extends Model
@@ -19,7 +24,7 @@ class WorkOrder extends Model
 
     // 工单 信息
     protected $fillable = [
-        'status',           // 状态
+        'status',           // 审核状态
         'creator_id',       // 创建人
         'reviewer_id',      // 审核人
         'owner_id',      // 货主
@@ -31,6 +36,7 @@ class WorkOrder extends Model
         'review_at',            // 审核时间
         'order_id',             // 订单id
         'uniquely_tag',         // 唯一标识
+        'work_order_status', // 工单状态
     ];
 
     static public $enums = [
@@ -39,6 +45,13 @@ class WorkOrder extends Model
             '待审核' => 1,
             '已处理' => 2,
         ],
+        'work_order_status' => [
+            '' => 0,
+            '信息未填写' => 1,
+            '信息已填写' => 2,
+            '快递已处理' => 3,
+            '工单完成' => 4,
+        ],
         'grad' => [
             '' => 0,
             '一般' => 1,
@@ -64,7 +77,7 @@ class WorkOrder extends Model
 
     public function setStatusAttribute($value)
     {
-        if (!$value) return 0;
+        if (!$value) return ;
         if (is_numeric($value)) {
             $this->attributes['status'] = $value;
         } else {
@@ -72,6 +85,22 @@ class WorkOrder extends Model
         }
     }
 
+    public function getWorkOrderStatusAttribute($value)
+    {
+        if (!$value) return '';
+        return self::$enums['work_order_status'][$value];
+    }
+
+    public function setWorkOrderStatusAttribute($value)
+    {
+        if (!$value) return ;
+        if (is_numeric($value)) {
+            $this->attributes['work_order_status'] = $value;
+        } else {
+            $this->attributes['work_order_status'] = self::$enums['work_order_status'][$value];
+        }
+    }
+
     public function getGradAttribute($value)
     {
         if (!$value) return '';
@@ -80,7 +109,7 @@ class WorkOrder extends Model
 
     public function setGradAttribute($value)
     {
-        if (!$value) return 0;
+        if (!$value) return ;
         if (is_numeric($value)) {
             $this->attributes['grad'] = $value;
         } else {
@@ -109,25 +138,79 @@ class WorkOrder extends Model
     // 关联货主
     public function owner(): BelongsTo
     {
-        return $this->belongsTo(Owner::class,'owner_id');
+        return $this->belongsTo(Owner::class, 'owner_id');
     }
 
     // 工单类型
     public function type(): BelongsTo
     {
-        return $this->BelongsTo(WorkOrderType::class,'work_order_type_id');
+        return $this->BelongsTo(WorkOrderType::class, 'work_order_type_id');
     }
 
     // 生成问题件类型
-    public function issueType():BelongsTo
+    public function issueType(): BelongsTo
     {
-        return $this->belongsTo(OrderIssueType::class,'order_issue_type_id');
+        return $this->belongsTo(OrderIssueType::class, 'order_issue_type_id');
     }
 
     /** 对应问题件 */
     public function orderIssue(): BelongsTo
     {
-        return $this->belongsTo(OrderIssue::class,'order_id','order_id');
+        return $this->belongsTo(OrderIssue::class, 'order_id', 'order_id');
+    }
+
+    // 图片
+    public function image(): HasMany
+    {
+        return $this->hasMany(WorkOrderImage::class);
+    }
+
+    // 外包装图片
+    public function packageImages(): HasMany
+    {
+        return $this->hasMany(WorkOrderImage::class)->where('type', 1);
+    }
+
+    // 内物破损图片
+    public function commodityImages(): HasMany
+    {
+        return $this->hasMany(WorkOrderImage::class)->where('type', 2);
+    }
+
+    // 交易图片
+    public function dealImages(): HasMany
+    {
+        return $this->hasMany(WorkOrderImage::class)->where('type', 3);
+    }
+
+    // 退款图片
+    public function refundImages(): HasMany
+    {
+        return $this->hasMany(WorkOrderImage::class)->where('type', 4);
+    }
+
+    // 工单详情
+    public function details(): HasMany
+    {
+        return $this->hasMany(WorkOrderDetail::class);
+    }
+
+    // 工单商品信息
+    public function commodities(): HasMany
+    {
+        return $this->hasMany(WorkOrderCommodities::class);
+    }
+
+    // 宝时处理日志
+    public function processLog(): HasOne
+    {
+        return $this->hasOne(WorkOrderProcessLog::class)->where('type','1');
+    }
+
+    // 承运商处理日志
+    public function logisticLog(): HasOne
+    {
+        return $this->hasOne(WorkOrderProcessLog::class)->where('type','2');
     }
 
     public function scopeFilter($query, $filters)
@@ -139,19 +222,115 @@ class WorkOrder extends Model
     /** 默认 with 参数 */
     public function scopeDefaultWith($query)
     {
-        $query->with(['type','owner','issueType','creator','reviewer','order'=>function($query){
-            /** @var $query Builder */
-            $query->with('packages','logistic','owner');
-        },'orderIssue'=>function($query){
-            /** @var $query Builder */
-            $query->with(['issueType','logs'=>function($query){
-                if (Gate::denies('订单管理-问题件-客户不可见')){
+        $query->with(['type', 'owner', 'issueType', 'creator','details','commodities.commodity',
+            'processLog.creator',
+            'logisticLog.creator',
+            'packageImages.uploadFile',
+            'commodityImages.uploadFile',
+            'dealImages.uploadFile',
+            'refundImages.uploadFile',
+            'reviewer',
+            'order' => function ($query) {
+            /** @var Builder $query  */
+            $query->with('packages', 'logistic', 'owner');
+        }, 'orderIssue' => function ($query) {
+            /** @var Builder $query  */
+            $query->with(['issueType', 'logs' => function ($query) {
+                if (Gate::denies('订单管理-问题件-客户不可见')) {
                     $query->with('user')->orderByDesc('created_at');
-                } else{
-                    $query->with('user')->where('tag','=',0)->orderByDesc('created_at');
+                } else {
+                    $query->with('user')->where('tag', '=', 0)->orderByDesc('created_at');
                 }
             }]);
         }]);
     }
 
+    public function loadDefaultWith()
+    {
+        $this->loadMissing(['owner','type', 'issueType', 'creator','details','commodities.commodity',
+            'processLog.creator',
+            'logisticLog.creator',
+            'packageImages.uploadFile',
+            'commodityImages.uploadFile',
+            'dealImages.uploadFile',
+            'refundImages.uploadFile',
+            'reviewer',
+            'order' => function ($query) {
+                /** @var Builder $query  */
+                $query->with('packages', 'logistic', 'owner');
+            }, 'orderIssue' => function ($query) {
+                /** @var Builder $query  */
+                $query->with(['issueType', 'logs' => function ($query) {
+                    if (Gate::denies('订单管理-问题件-客户不可见')) {
+                        $query->with('user')->orderByDesc('created_at');
+                    } else {
+                        $query->with('user')->where('tag', '=', 0)->orderByDesc('created_at');
+                    }
+                }]);
+            }]);
+    }
+
+    public function notification()
+    {
+        $user = Auth::user();
+        $this->loadMissing('owner', 'order');
+        $remark = $this->remark;
+        $ownerName = $this->owner->name ?? '';
+        $clientCode = $this->order->client_code ?? '';
+        $msg = $user["name"] . "建立了新工单<br/>" . $ownerName . ":" . $clientCode . "<br/>" . $remark;
+        NotificationService::SingleRegister($msg, $clientCode, "订单管理-问题件");
+    }
+
+    public function addPackageImage($images)
+    {       // 外包装图
+        foreach ($images as $image) {
+            /** @var UploadedFile  $image */
+            $count = $this->packageImages()->count();
+            /** @var WorkOrderImage $workOrderImage */
+            $workOrderImage = $this->packageImages()->create(['type' => 0, 'amount' => ++$count]);
+            $workOrderImage->saveFile($image);
+        }
+        $this->loadMissing('packageImages');
+    }
+
+    public function addCommodityImage($images)
+    {       // 内物破损图
+        foreach ($images as $image) {
+            $count = $this->commodityImages()->count();
+            /** @var WorkOrderImage $workOrderImage */
+            $workOrderImage = $this->commodityImages()->create(['type' => 1, 'amount' => ++$count]);
+            $workOrderImage->saveFile($image);
+        }
+        $this->loadMissing('commodityImages');
+    }
+
+    public function addDealImage($images)
+    {       // 交易截图
+        foreach ($images as $image) {
+            $count = $this->dealImages()->count();
+            /** @var WorkOrderImage $workOrderImage */
+            $workOrderImage = $this->dealImages()->create(['type' => 2, 'amount' => ++$count]);
+            $workOrderImage->saveFile($image);
+        }
+        $this->loadMissing('dealImages');
+    }
+
+    public function addRefundImage($images)
+    {        // 退款截图
+        foreach ($images as $image) {
+            $count = $this->refundImages()->count();
+            /** @var WorkOrderImage $workOrderImage */
+            $workOrderImage = $this->refundImages()->create(['type' => 3, 'amount' => ++$count]);
+            $workOrderImage->saveFile($image);
+        }
+        $this->loadMissing('dealImages');
+    }
+
+    public function saveWorkOrderDetail($params)
+    {
+        $param  = (new WorkOrderDetail($params))->getAttributes();
+        $this->details()->create($param);
+        $this->loadMissing('details');
+    }
+
 }

+ 33 - 0
app/WorkOrderCommodities.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class WorkOrderCommodities extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    //
+    protected $fillable = [
+        'work_order_id',
+        'commodity_id',
+        'sku',
+        'amount',
+    ];
+
+    public function workOrder(): BelongsTo
+    {
+        return $this->belongsTo(WorkOrder::class);
+    }
+
+    public function commodity(): BelongsTo
+    {
+        return $this->belongsTo(Commodity::class);
+    }
+}

+ 31 - 0
app/WorkOrderDetail.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class WorkOrderDetail extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $fillable = [
+        'work_order_id',
+        'price',  // 商品价值
+        'sku_amount', // 破损sku数
+        'receive_address', // 收方信息
+        'reissue_logistic_number', // 补发单号
+        'return_logistic_number',   // 退回单号
+        'logistic_number',
+    ];
+
+    public function workOrder(): BelongsTo
+    {
+        return $this->belongsTo(WorkOrder::class);
+    }
+
+}

+ 132 - 0
app/WorkOrderImage.php

@@ -0,0 +1,132 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasOne;
+use Illuminate\Http\UploadedFile;
+use Intervention\Image\Facades\Image;
+use Ramsey\Uuid\Uuid;
+
+class WorkOrderImage extends Model
+{
+    use ModelLogChanging;
+
+    protected $fillable = [
+        'work_order_id',
+        'type',
+        'number',
+    ];
+
+    static public $enums = [
+        'type' => [
+            '' => 0,
+            '外包装图片' => 1,
+            '内物破碎图片' => 2,
+            '交易截图' => 3,
+            '退款成功截图' => 4,
+        ],
+    ];
+
+    function __construct(array $attributes = [])
+    {
+        foreach (self::$enums as &$enum) {
+            $enum = $enum + array_flip($enum);
+        }
+        parent::__construct($attributes);
+    }
+
+    public function getStatusAttribute($value)
+    {
+        if (!$value) return '';
+        return self::$enums['type'][$value];
+    }
+
+    public function setTypeAttribute($value)
+    {
+        if (!$value) return ;
+        if (is_numeric($value)) {
+            $this->attributes['type'] = $value;
+        } else {
+            $this->attributes['type'] = self::$enums['type'][$value];
+        }
+    }
+
+    public function workOrder(): BelongsTo
+    {
+        return $this->belongsTo(WorkOrder::class);
+    }
+
+    public function uploadFile(): HasOne
+    {
+        return $this->hasOne(UploadFile::class,'table_id','id')->where('table_name',$this->getTable());
+    }
+
+    public function saveFile(UploadedFile $image): bool
+    {
+        if (!$this->checkFile($image)) return false;
+        $tmpFile = $image->getRealPath();
+        $fileSuffix = $image->getClientOriginalExtension();
+        $dirPath = $this->getStorageDirPath();
+        $fileName = date('ymd') . '-' . Uuid::uuid1();
+        $pathName = $dirPath . $fileName .'.' . $fileSuffix;
+        $result = move_uploaded_file($tmpFile, $pathName);
+        if(!$result) return false;
+        $this->uploadFile()->create(
+            [ 'url' => '/files/workOrder/'.$fileName, 'type' => $fileSuffix,'table_name'=>$this->getTable()]
+        );
+        return true;
+    }
+
+    public function checkFile($image): bool
+    {
+        $tmpFile = $image->getRealPath();
+        $fileSuffix = $image->getClientOriginalExtension();
+
+        if (!is_uploaded_file($tmpFile)) return false;
+        if ($image->getSize() > 5 * 1024 * 1024) return false;
+        if (!in_array($fileSuffix,[ 'gif','image','jpeg','jpg','png','svg'])) return false;
+        return true;
+    }
+
+    public function getStorageDirPath(): string
+    {
+        $path = ['app','public','files','workOrder'];
+        $path = join(DIRECTORY_SEPARATOR,$path);
+        $dirPath = storage_path($path);
+        if (!file_exists($dirPath)) {
+            mkdir($dirPath);
+        }
+        return $dirPath.DIRECTORY_SEPARATOR;
+    }
+
+    public function updateFile($image)
+    {
+        $this->deleteStorageFile();
+        $this->uploadFile()->delete();
+        $this->saveFile($image);
+    }
+
+    public function deleteStorageFile()
+    {
+        $path = $this->getPathName();
+        if (!$path) return;
+        if (file_exists($path)){
+            unlink($path);
+        }
+    }
+
+    public function getPathName(): ?string
+    {
+        if (!$this->uploadFile) return null;
+        $names = [];
+        preg_match("/([\w]+)$/", $this->uploadFile->url,$names);
+        $path = ['app','public','files','workOrder' , $names[0],".{$this->uploadFile->type}"];
+        $path = join(DIRECTORY_SEPARATOR,$path);
+        return storage_path($path);
+    }
+
+}

+ 118 - 0
app/WorkOrderProcessLog.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class WorkOrderProcessLog extends Model
+{
+    use ModelLogChanging;
+
+    //
+    protected $fillable = [
+        'work_order_id',  // 工单
+        'type',           // 处理类型 0 无 1宝时 2 承运商
+        'is_indemnity',   // 是否赔偿 0 无 1是 2否
+        'indemnitor',   // 赔偿人 0 无 1宝时 2承运商
+        'indemnity',      // 赔偿金
+        'creator_id',     // 创建人
+        'remark',         // 描述
+    ];
+
+    static public $enums = [
+        'type' => [
+            '' => 0,
+            '宝时' => 1,
+            '承运商' => 2,
+        ],
+        'is_indemnity' => [
+            '' => 0,
+            '是' => 1,
+            '否' => 2,
+        ],
+        'indemnitor'=> [
+            '' => 0,
+            '宝时' => 1,
+            '承运商' => 2,
+        ],
+    ];
+
+    function __construct(array $attributes = [])
+    {
+        foreach (self::$enums as &$enum) {
+            $enum = $enum + array_flip($enum);
+        }
+        parent::__construct($attributes);
+    }
+
+    public function getTypeAttribute($value)
+    {
+        if (!$value) return '';
+        return self::$enums['type'][$value];
+    }
+
+    public function setTypeAttribute($value)
+    {
+        if (!$value) return ;
+        if (is_numeric($value)) {
+            $this->attributes['type'] = $value;
+        } else {
+            $this->attributes['type'] = self::$enums['type'][$value];
+        }
+    }
+
+    public function getIsIndemnityAttribute($value)
+    {
+        if (!$value) return '';
+        return self::$enums['is_indemnity'][$value];
+    }
+
+    public function setIsIndemnityAttribute($value)
+    {
+        if (!$value) return ;
+        if (is_numeric($value)) {
+            $this->attributes['is_indemnity'] = $value;
+        } else {
+            $this->attributes['is_indemnity'] = self::$enums['is_indemnity'][$value];
+        }
+    }
+
+    public function getIndemnitorAttribute($value)
+    {
+        if (!$value) return '';
+        return self::$enums['indemnitor'][$value];
+    }
+
+    public function setIndemnitorAttribute($value)
+    {
+        if (!$value) return ;
+        if (is_numeric($value)) {
+            $this->attributes['indemnitor'] = $value;
+        } else {
+            $this->attributes['indemnitor'] = self::$enums['indemnitor'][$value];
+        }
+    }
+
+    public function workOrder(): BelongsTo
+    {
+        return $this->belongsTo(WorkOrder::class);
+    }
+
+    public function creator(): BelongsTo
+    {
+        return $this->belongsTo(User::class);
+    }
+
+    public function isLogisticLog(): bool
+    {
+        return $this->type == "承运商";
+    }
+
+    public function isBaoShiLog(): bool
+    {
+        return $this->type == "宝时";
+    }
+}

+ 7 - 0
bootstrap/cache/packages.php

@@ -1,4 +1,11 @@
 <?php return array (
+  'beyondcode/laravel-dump-server' => 
+  array (
+    'providers' => 
+    array (
+      0 => 'BeyondCode\\DumpServer\\DumpServerServiceProvider',
+    ),
+  ),
   'facade/ignition' => 
   array (
     'providers' => 

+ 38 - 36
bootstrap/cache/services.php

@@ -23,24 +23,25 @@
     19 => 'Illuminate\\Translation\\TranslationServiceProvider',
     20 => 'Illuminate\\Validation\\ValidationServiceProvider',
     21 => 'Illuminate\\View\\ViewServiceProvider',
-    22 => 'Facade\\Ignition\\IgnitionServiceProvider',
-    23 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
-    24 => 'Intervention\\Image\\ImageServiceProvider',
-    25 => 'Laravel\\Horizon\\HorizonServiceProvider',
-    26 => 'Laravel\\Ui\\UiServiceProvider',
-    27 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
-    28 => 'Carbon\\Laravel\\ServiceProvider',
-    29 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
-    30 => 'Oursdreams\\Export\\ExportServiceProvider',
-    31 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
-    32 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
-    33 => 'Yajra\\Oci8\\Oci8ServiceProvider',
-    34 => 'App\\Providers\\AppServiceProvider',
-    35 => 'App\\Providers\\AuthServiceProvider',
-    36 => 'App\\Providers\\BroadcastServiceProvider',
-    37 => 'App\\Providers\\EventServiceProvider',
-    38 => 'App\\Providers\\HorizonServiceProvider',
-    39 => 'App\\Providers\\RouteServiceProvider',
+    22 => 'BeyondCode\\DumpServer\\DumpServerServiceProvider',
+    23 => 'Facade\\Ignition\\IgnitionServiceProvider',
+    24 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
+    25 => 'Intervention\\Image\\ImageServiceProvider',
+    26 => 'Laravel\\Horizon\\HorizonServiceProvider',
+    27 => 'Laravel\\Ui\\UiServiceProvider',
+    28 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
+    29 => 'Carbon\\Laravel\\ServiceProvider',
+    30 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
+    31 => 'Oursdreams\\Export\\ExportServiceProvider',
+    32 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
+    33 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
+    34 => 'Yajra\\Oci8\\Oci8ServiceProvider',
+    35 => 'App\\Providers\\AppServiceProvider',
+    36 => 'App\\Providers\\AuthServiceProvider',
+    37 => 'App\\Providers\\BroadcastServiceProvider',
+    38 => 'App\\Providers\\EventServiceProvider',
+    39 => 'App\\Providers\\HorizonServiceProvider',
+    40 => 'App\\Providers\\RouteServiceProvider',
   ),
   'eager' => 
   array (
@@ -54,24 +55,25 @@
     7 => 'Illuminate\\Pagination\\PaginationServiceProvider',
     8 => 'Illuminate\\Session\\SessionServiceProvider',
     9 => 'Illuminate\\View\\ViewServiceProvider',
-    10 => 'Facade\\Ignition\\IgnitionServiceProvider',
-    11 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
-    12 => 'Intervention\\Image\\ImageServiceProvider',
-    13 => 'Laravel\\Horizon\\HorizonServiceProvider',
-    14 => 'Laravel\\Ui\\UiServiceProvider',
-    15 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
-    16 => 'Carbon\\Laravel\\ServiceProvider',
-    17 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
-    18 => 'Oursdreams\\Export\\ExportServiceProvider',
-    19 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
-    20 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
-    21 => 'Yajra\\Oci8\\Oci8ServiceProvider',
-    22 => 'App\\Providers\\AppServiceProvider',
-    23 => 'App\\Providers\\AuthServiceProvider',
-    24 => 'App\\Providers\\BroadcastServiceProvider',
-    25 => 'App\\Providers\\EventServiceProvider',
-    26 => 'App\\Providers\\HorizonServiceProvider',
-    27 => 'App\\Providers\\RouteServiceProvider',
+    10 => 'BeyondCode\\DumpServer\\DumpServerServiceProvider',
+    11 => 'Facade\\Ignition\\IgnitionServiceProvider',
+    12 => 'Fideloper\\Proxy\\TrustedProxyServiceProvider',
+    13 => 'Intervention\\Image\\ImageServiceProvider',
+    14 => 'Laravel\\Horizon\\HorizonServiceProvider',
+    15 => 'Laravel\\Ui\\UiServiceProvider',
+    16 => 'Maatwebsite\\Excel\\ExcelServiceProvider',
+    17 => 'Carbon\\Laravel\\ServiceProvider',
+    18 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
+    19 => 'Oursdreams\\Export\\ExportServiceProvider',
+    20 => 'Overtrue\\LaravelPinyin\\ServiceProvider',
+    21 => 'Te7aHoudini\\LaravelTrix\\LaravelTrixServiceProvider',
+    22 => 'Yajra\\Oci8\\Oci8ServiceProvider',
+    23 => 'App\\Providers\\AppServiceProvider',
+    24 => 'App\\Providers\\AuthServiceProvider',
+    25 => 'App\\Providers\\BroadcastServiceProvider',
+    26 => 'App\\Providers\\EventServiceProvider',
+    27 => 'App\\Providers\\HorizonServiceProvider',
+    28 => 'App\\Providers\\RouteServiceProvider',
   ),
   'deferred' => 
   array (

+ 12 - 0
database/factories/WorkOrderCommoditiesFactory.php

@@ -0,0 +1,12 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\WorkOrderCommodities;
+use Faker\Generator as Faker;
+
+$factory->define(WorkOrderCommodities::class, function (Faker $faker) {
+    return [
+        //
+    ];
+});

+ 18 - 0
database/factories/WorkOrderDetailFactory.php

@@ -0,0 +1,18 @@
+<?php
+
+
+use App\WorkOrderDetail;
+use Faker\Generator as Faker;
+use Illuminate\Database\Eloquent\Factory;
+
+/** @var Factory $factory */
+$factory->define(WorkOrderDetail::class, function (Faker $faker) {
+    return [
+        'work_order_id' => $faker->randomNumber(0,200),
+        'price' => $faker->randomFloat(null,0.1,999),
+        'skus' => $faker->text(20),
+        'receive_address' => $faker->address,
+        'reissue_logistic_number' => $faker->numberBetween(10000000,999999999),
+        'return_logistic_number' => $faker->numberBetween(10000000,999999999),
+    ];
+});

+ 12 - 0
database/factories/WorkOrderImageFactory.php

@@ -0,0 +1,12 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\WorkOrderImage;
+use Faker\Generator as Faker;
+
+$factory->define(WorkOrderImage::class, function (Faker $faker) {
+    return [
+        //
+    ];
+});

+ 12 - 0
database/factories/WorkOrderProcessLogFactory.php

@@ -0,0 +1,12 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\WorkOrderProcessLog;
+use Faker\Generator as Faker;
+
+$factory->define(WorkOrderProcessLog::class, function (Faker $faker) {
+    return [
+        //
+    ];
+});

+ 38 - 0
database/migrations/2021_09_22_135941_create_work_order_details_table.php

@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateWorkOrderDetailsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('work_order_details', function (Blueprint $table) {
+            $table->id();
+            $table->integer('work_order_id')->index()->comment('工单');
+            $table->tinyInteger('sku_amount')->comment('破损sku数量');
+            $table->decimal('price',11,3)->comment('价格');
+            $table->string('receive_address')->comment('收方信息');
+            $table->string('reissue_logistic_number')->comment('补发单号');
+            $table->string('return_logistic_number')->comment('退回单号');
+            $table->string('logistic_number')->comment('快递单号');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('work_order_details');
+    }
+}

+ 35 - 0
database/migrations/2021_09_22_160325_create_work_order_commodities_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateWorkOrderCommoditiesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('work_order_commodities', function (Blueprint $table) {
+            $table->id();
+            $table->integer('work_order_id')->index()->comment('工单');
+            $table->integer('commodity_id')->comment('商品id');
+            $table->string('sku')->comment('商品sku');
+            $table->integer('amount')->comment('数量');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('work_order_commodities');
+    }
+}

+ 34 - 0
database/migrations/2021_09_22_162827_create_work_order_images_table.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateWorkOrderImagesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('work_order_images', function (Blueprint $table) {
+            $table->id();
+            $table->integer('work_order_id')->index()->comment('工单');
+            $table->tinyInteger('type')->comment('类别');
+            $table->tinyInteger('number')->comment('序号');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('work_order_images');
+    }
+}

+ 38 - 0
database/migrations/2021_09_24_110630_create_work_order_process_logs_table.php

@@ -0,0 +1,38 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateWorkOrderProcessLogsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('work_order_process_logs', function (Blueprint $table) {
+            $table->id();
+            $table->integer('work_order_id')->index()->comment('工单');
+            $table->tinyInteger('type')->index()->comment('类型 0无 1宝时 2承运商');
+            $table->tinyInteger('indemnitor')->index()->comment('赔偿方 0无 1宝时 2承运商');
+            $table->tinyInteger('is_indemnity')->comment('是否赔偿 0无 1是 2否');
+            $table->decimal('indemnity',11,3)->comment('赔偿金额');
+            $table->integer('creator_id')->comment('创建人');
+            $table->string('remark')->nullable()->comment('理由');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('work_order_process_logs');
+    }
+}

+ 32 - 0
database/migrations/2021_09_27_092709_add_cloumn_to_workk_orders.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddCloumnToWorkkOrders extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('work_orders', function (Blueprint $table) {
+            $table->tinyInteger('work_order_status')->comment('工单状态');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('work_orders', function (Blueprint $table) {
+            $table->dropColumn('work_order_status');
+        });
+    }
+}

+ 16 - 0
database/seeds/WorkOrderCommoditiesSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class WorkOrderCommoditiesSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //
+    }
+}

+ 17 - 0
database/seeds/WorkOrderDetailSeeder.php

@@ -0,0 +1,17 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class WorkOrderDetailSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        $params = factory(\App\WorkOrder::class)->times(30)->make();
+        \App\WorkOrder::query()->insert($params);
+    }
+}

+ 16 - 0
database/seeds/WorkOrderImageSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class WorkOrderImageSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //
+    }
+}

+ 16 - 0
database/seeds/WorkOrderProcessLogSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class WorkOrderProcessLogSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        //
+    }
+}

+ 159 - 18
resources/views/order/index/_work_order_modal.blade.php

@@ -1,44 +1,185 @@
-<div class="modal fade " id="intercept-modal" tabindex="-1" role="dialog" aria-labelledby="checkModalLabel" aria-hidden="true">
-    <div class="modal-dialog modal-dialog-centered">
+<div class="modal fade " id="intercept-modal" tabindex="-1" role="dialog" aria-labelledby="checkModalLabel"
+     aria-hidden="true">
+    <div class="modal-dialog modal-xl modal-dialog-centered">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title" id="checkModalLabel" >创建工单</h5>
+                <h5 class="modal-title" id="checkModalLabel">创建工单</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
             </div>
             <div class="modal-body">
                 {{--工单类型--}}
-                <div class="form-group">
-                    <label for="order_issue_type">工单类型</label>
-                    <div class="pl-1">
-                        <select name="type" id="order_issue_type" class="form-control" v-model="workOrder.orderIssueType">
+                <div class="form-group row">
+                    <label for="order_issue_type" class="col-sm-2 col-form-label text-right">工单类型</label>
+                    <div class="col-sm-10">
+                        <select name="type" id="order_issue_type" class="form-control"
+                                v-model="workOrder.orderIssueType">
                             <option value=""></option>
                             <option v-for="type in workOrder.types" :value="type">@{{ type }}</option>
                         </select>
                     </div>
                 </div>
                 {{--其他--}}
-                <template v-if="['其他',null,'快递异常','错漏发','破损'].includes(workOrder.orderIssueType)">
+                <div class="form-group row" v-if="['其他',null,'快递异常','错漏发','破损','快递丢件'].includes(workOrder.orderIssueType)">
                     <hr>
-                    <label for="remake_info">问题描述</label>
-                    <div class="pl-1">
-                        <textarea class="form-control" name="" id="remake_info" cols="30" rows="5" v-model="workOrder.remark.info"></textarea>
+                    <label for="remake_info" class="col-sm-2 col-form-label text-right">问题描述</label>
+                    <div class="col-sm-10">
+                        <textarea class="form-control" name="" id="remake_info" cols="30" rows="2"
+                                  v-model="workOrder.remark.info"></textarea>
                     </div>
-                </template>
+                </div>
+
+                {{--破损商品价格--}}
+                <div class="form-group row" v-if="'破损' === workOrder.orderIssueType">
+                    <label for="work-order-price" class="col-sm-2 col-form-label text-right text-primary">破损商品价格</label>
+                    <div class="col-sm-10">
+                        <input type="number" id="work-order-price" class="form-control" v-model="workOrder.price"
+                               placeholder="破损商品价格">
+                    </div>
+                </div>
+                {{--快递单号--}}
+                <div class="form-group row" v-if="'破损' === workOrder.orderIssueType">
+                    <label for="work-order-logistic-number"
+                           class="col-sm-2 col-form-label text-right text-primary">快递单号</label>
+                    <div class="col-sm-10">
+                        <input type="text" id="work-order-logistic-number" class="form-control"
+                               v-model="workOrder['logistic_number']" placeholder="快递单号">
+                    </div>
+                </div>
+
+
                 {{--信息更改--}}
-                <template v-else-if="workOrder.orderIssueType === '信息更改'">
+                <div class="form-group row" v-if="'信息更改' === workOrder.orderIssueType ">
                     <hr>
-                    <label for="remake_info">新的收方信息</label>
-                    <div class="pl-1">
-                        <textarea class="form-control" name="" id="remake_info" cols="30" rows="5" v-model="workOrder.remark.info"></textarea>
+                    <label for="order_issue_type" class="col-sm-2 col-form-label">新的收方信息</label>
+                    <div class="col-sm-10">
+                        <textarea class="form-control" name="" id="remake_info" cols="30" rows="5"
+                                  v-model="workOrder.remark.info"></textarea>
+                    </div>
+                </div>
+
+                <hr v-if="'破损' === workOrder.orderIssueType">
+                <div class="form-group " v-if="'破损' === workOrder.orderIssueType">
+                    <div class="pl-1 custom-file col-10 offset-2">
+                        <input type="file" id="package-images" name="package-images" multiple class="custom-file-input"
+                               accept="image/*" @change="pushImagesAndShow($event,workOrder.packageImages)">
+                        <label class="custom-file-label" for="package-images">点击添加外包装破损图片</label>
+                    </div>
+                    <div class="border-dark mt-1 col-10 offset-2">
+                        <div v-for="(image,i) in  workOrder.packageImages"
+                             class="d-inline-block col-4 position-relative card">
+                            <div class="card-body">
+                                <img :src="image.src" class="card-img-top" :alt="image.file.name">
+                                <div class="float-right position-relative">
+                                    <button type="button" class="btn btn-sm btn-outline-danger"
+                                            @click="spliceImage(i,workOrder.packageImages)">取消
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <hr v-if="'破损' === workOrder.orderIssueType">
+                <div class="form-group " v-if="'破损' === workOrder.orderIssueType">
+                    <div class="pl-1 custom-file col-10 offset-2">
+                        <input type="file" id="commodity-images" name="package-images" multiple
+                               class="custom-file-input" accept="image/*"
+                               @change="pushImagesAndShow($event,workOrder.commodityImages)">
+                        <label class="custom-file-label" for="commodity-images">点击添加内物破损图片</label>
+                    </div>
+                    <div class="border-dark mt-1 col-10 offset-2">
+                        <div v-for="(image,i) in  workOrder.commodityImages"
+                             class="d-inline-block col-4 position-relative card">
+                            <div class="card-body">
+                                <img :src="image.src" class="card-img-top" :alt="image.file.name">
+                                <div class="float-right position-relative">
+                                    <button type="button" class="btn btn-sm btn-outline-danger"
+                                            @click="spliceImage(i,workOrder.commodityImages)">取消
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
                     </div>
-                </template>
+                </div>
+
+                <hr v-if="'破损' === workOrder.orderIssueType">
+                <div class="form-group " v-if="'破损' === workOrder.orderIssueType">
+                    <div class="pl-1 custom-file col-10 offset-2">
+                        <input type="file" id="deal-images" name="package-images" multiple class="custom-file-input"
+                               accept="image/*" @change="pushImagesAndShow($event,workOrder.dealImages)">
+                        <label class="custom-file-label" for="deal-images">点击添加外交易截图</label>
+                    </div>
+                    <div class="border-dark mt-1 col-10 offset-2">
+                        <div v-for="(image,i) in  workOrder.dealImages"
+                             class="d-inline-block col-4 position-relative card">
+                            <div class="card-body">
+                                <img :src="image.src" class="card-img-top" :alt="image.file.name">
+                                <div class="float-right position-relative">
+                                    <button type="button" class="btn btn-sm btn-outline-danger"
+                                            @click="spliceImage(i,workOrder.dealImages)">取消
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+
+                <div class="form-group row" v-if="'破损' === workOrder.orderIssueType">
+                    <label for="work-order-sku-amount"
+                           class="col-sm-2 col-form-label text-right text-primary">破损sku编号数量</label>
+                    <div class="col-sm-10">
+                        <input type="number" id="work-order-sku-amount" class="form-control"
+                               v-model="workOrder['sku_amount']" placeholder="破损sku编号数量">
+                    </div>
+                </div>
+                <hr v-if="'破损' === workOrder.orderIssueType">
+                <div class="form-group row" v-if="'破损' === workOrder.orderIssueType">
+                    <label for="work-order-sku-amount"
+                           class="col-sm-2 col-form-label text-right text-primary">破损sku信息</label>
+                    <div class="col-sm-10">
+                        <table class="table table-sm table-active table-grid-row">
+                            <tr class="text-center">
+                                <th>sku</th>
+                                <th>商品名称</th>
+                                <th>数量</th>
+                                <th>操作</th>
+                            </tr>
+                            <tr v-for="(item,i) in workOrder.commodities" :key="i" class="text-center">
+                                <td>@{{ item.sku }}</td>
+                                <td>@{{ item.commodity.name }}</td>
+                                <td>@{{ item.amount }}</td>
+                                <td>
+                                    <button type="button" class="btn btn-sm btn-danger"
+                                            @click="workOrder.commodities.splice(i,1)">取消
+                                    </button>
+                                </td>
+                            </tr>
+                        </table>
+                    </div>
+                </div>
+                <div class="form-group row" v-if="'破损' === workOrder.orderIssueType">
+                    <label for="work-order-sku-amount" class="col-sm-2 col-form-label text-right "> 添加</label>
+                    <div class="col-sm-10  col-sm-offset-2 form-group form-inline">
+                        <div class="col-4">
+                            <input type="text" class="form-control" placeholder="sku编号" name="sku" id="commodity-sku" ref="commodity-sku">
+                         </div>
+                        <div class="col-4">
+                            <input type="number" class="form-control form-control-static" placeholder="数量" name="amount" ref="commodity-amount"
+                               id="commodity-amount">
+                        </div>
+                        <div class="col-4">
+                            <button type="button" class="btn btn-sm btn-outline-success col-4" @click="addCommodity">添加</button>
+                        </div>
+                    </div>
+                </div>
             </div>
+            <hr v-if="'破损' === workOrder.orderIssueType">
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭
                 </button>
-                {{--生成问题件--}}
+                {{--生成工单--}}
                 <button type="button" class="btn btn-outline-primary" @click="buildWorkOrder">提交
                 </button>
             </div>

Diff do ficheiro suprimidas por serem muito extensas
+ 488 - 301
resources/views/order/index/delivering.blade.php


+ 77 - 0
resources/views/order/workOrder/_edit_process_log.blade.php

@@ -0,0 +1,77 @@
+<div class="modal fade " id="work-order-process-log-modal" tabindex="-1" role="dialog" aria-labelledby="checkModalLabel"
+     aria-hidden="true">
+    <div class="modal-dialog modal-lg modal-dialog-centered">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="checkModalLabel">@{{ processLog.type === '1' ? '宝时处理' : '承运商处理' }}</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                {{--是否赔偿--}}
+                <div class="form-group row">
+                    <label for="work-order-logistic-number" class="text-primary col-sm-2 col-form-label text-right">是否赔偿</label>
+                    <div class="col-sm-10">
+                        <div class="custom-control custom-radio">
+                            <input type="radio" id="is-indemnity-yes" name="isIndemnity" class="custom-control-input"
+                                   value="1"
+                                   v-model="processLog['is_indemnity']">
+                            <label class="custom-control-label" for="is-indemnity-yes">赔偿</label>
+                        </div>
+                        <div class="custom-control custom-radio">
+                            <input type="radio" id="is-indemnity-no" name="isIndemnity" class="custom-control-input"
+                                   value="2"
+                                   v-model="processLog['is_indemnity']">
+                            <label class="custom-control-label" for="is-indemnity-no">不赔偿</label>
+                        </div>
+                    </div>
+                </div>
+                {{-- 宝时处理 确认赔偿方 --}}
+                <div class="form-group row" v-show="processLog['is_indemnity'] === '1' &&  processLog.type === 1">
+                    <label for="process-log-indemnity" class="text-primary col-sm-2 col-form-label text-right">赔偿方</label>
+                    <div class="col-sm-10">
+                        <div class="custom-control custom-radio">
+                            <input type="radio" id="indemnitor-baoshi" name="indemnitor" class="custom-control-input"
+                                   value="1"
+                                   v-model="processLog['indemnitor']">
+                            <label class="custom-control-label" for="indemnitor-baoshi">宝时</label>
+                        </div>
+                        <div class="custom-control custom-radio">
+                            <input type="radio" id="indemnitor-logistic" name="indemnitor" class="custom-control-input"
+                                   value="2"
+                                   v-model="processLog['indemnitor']">
+                            <label class="custom-control-label" for="indemnitor-logistic">承运商</label>
+                        </div>
+                    </div>
+                </div>
+                {{--描述--}}
+                <div class="form-group row" v-show="processLog['is_indemnity'] === '2'">
+                    <label for="process-log-remark" class="text-primary col-sm-2 col-form-label text-right ">理由</label>
+                    <div class="col-sm-10">
+                        <textarea name="process-log-remark" id="process-log-remark" cols="30" rows="5" class="form-control"
+                                  v-model="processLog.remark" ref="process-log-remark"></textarea>
+                    </div>
+                </div>
+                {{--赔偿金额--}}
+                <div class="form-group row" v-show="processLog['is_indemnity'] === '1'">
+                    <label for="process-log-indemnity" class="text-primary col-sm-2 col-form-label text-right">赔偿金额</label>
+                    <div class="col-sm-10">
+                        <input type="number" id="process-log-indemnity" class="form-control"
+                               v-model="processLog.indemnity" placeholder="赔偿金额" ref="process-log-indemnity">
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭
+                </button>
+
+                {{--填充工单--}}
+                {{--宝时--}}
+                <button type="button" class="btn btn-outline-primary"   v-show="processLog['type'] === 1" @click="storeProcessLog">提交</button>
+                {{--承运商--}}
+                <button type="button" class="btn btn-outline-primary"   v-show="processLog['type'] === 2" @click="storeLogisticProcessLog">提交</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 90 - 0
resources/views/order/workOrder/_fill_loss_work_order.blade.php

@@ -0,0 +1,90 @@
+<div class="modal fade " id="fill-loss-work-order-modal" tabindex="-1" role="dialog" aria-labelledby="checkModalLabel" aria-hidden="true">
+    <div class="modal-dialog modal-xl modal-dialog-centered">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title" id="checkModalLabel" >信息填写</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="form-group row">
+                    <div class="col-sm-2"></div>
+                    <div class="col-sm-10  text-primary">
+                        补发单号,退款成功截图 二选一
+                    </div>
+                </div>
+                {{--破损商品价格--}}
+                <div class="form-group row" >
+                    <label for="work-order-price" class="col-sm-2 col-form-label text-right text-primary">丢件价值</label>
+                    <div class="col-sm-10">
+                        <input type="number" id="work-order-price" class="form-control" v-model="workOrder.price" placeholder="破损商品价格">
+                    </div>
+                </div>
+                {{--快递单号--}}
+                <div class="form-group row" >
+                    <label for="work-order-logistic-number" class="col-sm-2 col-form-label text-right">丢件快递单号</label>
+                    <div class="col-sm-10">
+                        <input type="text" id="work-order-logistic-number" class="form-control" v-model="workOrder['logistic_number']" placeholder="快递单号">
+                    </div>
+                </div>
+                {{--补发快递--}}
+                <div class="form-group row" v-show="workOrder.dealImages.length === 0">
+                    <label for="work-order-reissue-logistic-number" class="col-sm-2 col-form-label text-right">补发快递单号</label>
+                    <div class="col-sm-10">
+                        <input type="text" id="work-order-reissue-logistic-number" class="form-control" v-model="workOrder['reissue_logistic_number']" placeholder="快递单号">
+                    </div>
+                </div>
+                {{--信息更改--}}
+                <div class="form-group row" >
+                    <hr>
+                    <label for="order_issue_type" class="col-sm-2 col-form-label text-right">收方信息</label>
+                    <div class="col-sm-10">
+                        <textarea class="form-control" name="" id="remake_info" cols="30" rows="2" v-model="workOrder.remake"></textarea>
+                    </div>
+                </div>
+
+                <div class="form-group" >
+                    <div class="offset-2 pl-1 custom-file col-10">
+                        <input type="file" id="deal-images" name="deal-images" multiple class="custom-file-input" accept="image/*" @change="pushImagesAndShow($event,workOrder.dealImages)">
+                        <label class="custom-file-label" for="deal-images">点击添加交易截图</label>
+                    </div>
+                    <div class="offset-2 mt-1 col-10">
+                        <div v-for="(image,i) in  workOrder.dealImages" class="d-inline-block col-4 position-relative card">
+                            <div class="card-body">
+                                <img :src="image.src" class="card-img-top" :alt="image.file.name">
+                                <div class="float-right position-relative">
+                                    <button type="button" class="btn btn-sm btn-outline-danger" @click="spliceImage(i,workOrder.dealImages)">取消</button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <hr>
+                <div class="form-group" v-show="!workOrder['reissue_logistic_number']">
+                    <div class="pl-1 custom-file col-10 offset-2">
+                        <input type="file" id="refund-images" name="refund-images" multiple class="custom-file-input" accept="image/*" @change="pushImagesAndShow($event,workOrder.refundImages)">
+                        <label class="custom-file-label" for="refund-images">添加退款成功截图</label>
+                    </div>
+                    <div class="offset-2 mt-1 col-10">
+                        <div v-for="(image,i) in  workOrder.refundImages" class="d-inline-block col-4 position-relative card">
+                            <div class="card-body">
+                                <img :src="image.src" class="card-img-top" :alt="image.file.name">
+                                <div class="float-right position-relative">
+                                    <button type="button" class="btn btn-sm btn-outline-danger" @click="spliceImage(i,workOrder.refundImages)">取消</button>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭
+                </button>
+                {{--填充工单--}}
+                <button type="button" class="btn btn-outline-primary" @click="updateLossWorkOrder">提交
+                </button>
+            </div>
+        </div>
+    </div>
+</div>

+ 161 - 0
resources/views/order/workOrder/_work_order_details.blade.php

@@ -0,0 +1,161 @@
+{{-- 工单详情 --}}
+<div v-if="item.details.length > 0" class="alert alert-light mb-0">
+    <div class="header-alert">
+        <span class="text-monospace">工单详情</span>
+        <button type="button" class="btn btn-sm btn-outline-primary float-right"
+                v-show="selectDetailId !== item.id"
+                @click="selectDetailId = item.id">显示
+        </button>
+        <button type="button" class="btn btn-sm btn-outline-primary float-right"
+                v-show="selectDetailId === item.id"
+                @click="selectDetailId = null">隐藏
+        </button>
+    </div>
+    <transition name="fade">
+        <div class="body-alert" v-if="selectDetailId === item.id">
+            <div class="card-body row col-12 mb-0 pb-0"
+                 v-for="(detail,i) in item.details">
+                {{-- 快递丢件工单详情 --}}
+                <div v-if="item['issue_type']['name'] === '快递丢件'">
+                    <div v-if="detail.logistic_number">
+                        <span class="mr-3">丢件快递单号:</span>
+                        <span class="text-truncate"
+                              v-text="detail.logistic_number"></span>
+                    </div>
+                    <div>
+                        <span class="mr-3">丢件价值:</span>
+                        <span class="text-truncate"
+                              v-text="detail.price"></span>
+                    </div>
+                    {{-- 工单登记商品详情 --}}
+                    <div v-for="(item,i) in item.commodities">
+                        <span class="mr-2">sku</span>
+                        <span v-text="item.sku"></span>
+                        <span class="mr-2">商品名</span>
+                        <span
+                            v-text="item.commodity ? item.commodity.name : ''"></span>
+                        <span class="mr-2">数量</span>
+                        <span v-text="item.amount"></span>
+                    </div>
+                </div>
+
+                {{-- 破损工单详情 --}}
+                <div v-if="item['issue_type']['name'] === '破损'">
+                    <div>
+                        <div>
+                            <span class="mr-3">破损sku数:</span>
+                            <span class="text-truncate"
+                                  v-text="detail.sku_amount"></span>
+                        </div>
+                        <div>
+                            <span class="mr-3">快递单号:</span>
+                            <span class="text-truncate"
+                                  v-text="detail.logistic_number !== 'null' ? detail.logistic_number : ''"></span>
+                        </div>
+                        <div>
+                            <span class="mr-3">破损商品价值:</span>
+                            <span v-text="detail.price"></span>
+                        </div>
+                    </div>
+
+                </div>
+                {{-- 交易截图 --}}
+                <div class="row"
+                     v-if="item['deal_images'] && item['deal_images'].length > 0">
+                    <div class="col-12">
+                        <hr>
+                        <span class="text-monospace" v-text="'交易截图'"></span>
+                    </div>
+                    <div class="card-body col-sm-6"
+                         v-for="(dealImage,i) in item['deal_images']">
+                        <div class="">
+                            <a  target="_blank"
+                                :href="filePrefix+dealImage.upload_file.url + '.'+dealImage.upload_file.type">
+                                <img class="image-w"
+                                     :src="filePrefix+dealImage.upload_file.url + '.'+dealImage.upload_file.type"
+                                     alt="交易截图">
+                            </a>
+                        </div>
+                    </div>
+                </div>
+                {{-- 退款截图 --}}
+                <div class="row"
+                     v-if="item['refund_image'] && item['refund_image'].length > 0">
+                    <div class="col-12">
+                        <hr>
+                        <span class="text-monospace" v-text="'退款截图'"></span>
+                    </div>
+                    <div class="card-body col-sm-6"
+                         v-for="(refundImage,i) in item['refund_images']">
+                        <a target="_blank"
+                           :href="filePrefix+refundImage.upload_file.url + '.'+refundImage.upload_file.type">
+                            <img class="image-w"
+                                 :src="filePrefix+refundImage.upload_file.url + '.'+refundImage.upload_file.type"
+                                 alt="退款截图">
+                        </a>
+                    </div>
+                </div>
+                {{-- 外包装截图 --}}
+                <div class="row"
+                     v-if="item['package_images'] && item['package_images'].length > 0">
+                    <div class="col-12">
+                        <hr>
+                        <span class="text-monospace" v-text="'外包装截图'"></span>
+                    </div>
+                    <div class="card-body col-sm-6"
+                         v-for="(packageImage,i) in item['package_images']">
+                        <a target="_blank"
+                           :href="filePrefix+packageImage.upload_file.url + '.'+packageImage.upload_file.type">
+                            <img class="image-w"
+                                 :src="filePrefix+packageImage.upload_file.url + '.'+packageImage.upload_file.type"
+                                 alt="外包装截图">
+                        </a>
+                    </div>
+                </div>
+                {{-- 内物破损图片 --}}
+                <div class="row"
+                     v-if="item['commodity_images'] && item['commodity_images'].length > 0">
+                    <div class="col-12">
+                        <hr>
+                        <span class="text-monospace" v-text="'内物破损图片'"></span>
+                    </div>
+                    <div class="card-body col-sm-6"
+                         v-for="(commodityImage,i) in item['commodity_images']">
+                        <a target="_blank"
+                           :href="filePrefix+commodityImage.upload_file.url  + '.'+commodityImage.upload_file.type">
+                            <img class="image-w"
+                                 :src="filePrefix+commodityImage.upload_file.url + '.'+commodityImage.upload_file.type"
+                                 alt="内物破损图片">
+                        </a>
+                    </div>
+                </div>
+            </div>
+
+            {{-- 破损商品详情 --}}
+            <div class="row"
+                 v-if="item['issue_type']['name'] === '破损'">
+                <div class="col-12">
+                    <hr>
+                    <div class="text-monospace">破损商品详情</div>
+                </div>
+                <div class="card-body col-12"
+                     v-for="(commodity,i) in item.commodities">
+                    <div>
+                        <span class="mr-3">SKU:</span>
+                        <span class="text-truncate" v-text="commodity.sku"></span>
+                    </div>
+                    <div>
+                        <span class="mr-3">商品名称:</span>
+                        <span class="text-truncate"
+                              v-text="commodity.commodity ? commodity.commodity.name : '' "></span>
+                    </div>
+                    <div>
+                        <span class="mr-3">数量:</span>
+                        <span class="text-truncate"
+                              v-text="commodity.amount"></span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </transition>
+</div>

+ 478 - 154
resources/views/order/workOrder/index.blade.php

@@ -7,41 +7,37 @@
                 <div id="form_div" style="min-width: 1220px;"></div>
                 <div class="form-inline mt-1" id="btn">
                     @can('订单管理-订单问题件生成')
-                        <span class="ml-1">
-                       <button type="button" class="btn btn-outline-dark btn-sm form-control-sm  tooltipTarget"
-                               @click="createOrderIssue(null,false)" style="background: #dad7e8;">生成问题件</button>
-                       </span>
+                        <button type="button"
+                                class="ml-1 btn btn-outline-dark btn-sm form-control-sm  tooltipTarget"
+                                @click="createOrderIssue(null,false)" style="background: #dad7e8;">生成问题件
+                        </button>
                     @endcan
                     @can('订单管理-订单问题件生成')
-                        <span class="ml-1">
-                       <button type="button" class="btn btn-outline-primary btn-sm form-control-sm  tooltipTarget"
-                               @click="exportText()">导出文本</button>
-                       </span>
+                        <button type="button"
+                                class="ml-1 btn btn-outline-primary btn-sm form-control-sm  tooltipTarget"
+                                @click="exportText()">导出文本
+                        </button>
                     @endcan
                     @can('订单管理-工单处理-审核')
-                        <span class="ml-1">
-                        <button type="button" class="btn btn-outline-success btn-sm form-control-sm  tooltipTarget"
-                                @click="batchReview">
-                            批量审核
+                        <button type="button"
+                                class="ml-1 btn btn-outline-success btn-sm form-control-sm tooltipTarget"
+                                @click="batchReview">批量审核
                         </button>
-                        </span>
                     @endcan
-                    <span class="ml-1">
-                    <button type="button" class="btn btn-outline-dark btn-sm form-control-sm  tooltipTarget"
-                            @click="copyLogisticNumber()" style="background: #dad7e8;">批量复制单号</button>
-                    </span>
+                    <button type="button"
+                            class="ml-1 btn btn-outline-dark btn-sm form-control-sm tooltipTarget"
+                            @click="copyLogisticNumber()" style="background: #dad7e8;">批量复制单号
+                    </button>
                     @can('订单管理-工单处理-审核')
-                        <span class="ml-1">
-                        <button type="button" class="btn btn-outline-success btn-sm form-control-sm  tooltipTarget"
-                                @click="showEditIssueType">
-                            批量修改问题件类型
+                        <button type="button"
+                                class="ml-1 btn btn-outline-success btn-sm form-control-sm  tooltipTarget"
+                                @click="showEditIssueType">批量修改问题件类型
                         </button>
-                        </span>
                     @endcan
                 </div>
 
                 <div>
-                    <table class="table table-sm table-striped table-hover table-bordered td-min-width-80" id="table">
+                    <table class="table table-sm table-striped table-hover table-bordered td-min-width-80 " id="table">
                         <tbody class="">
                         <template v-for="(item,i) in workOrders">
                             <tr @click="selectTr===i+1?selectTr=0:selectTr=i+1" :class="selectTr===i+1?'focusing' : ''">
@@ -49,54 +45,163 @@
                                     <label><input type="checkbox" :value="item.id"></label>
                                 </td>
                                 <td class="text-center">
-                                    <span>@{{ i+1 }}</span>
+                                    <span v-text="i+1"></span>
                                     <span v-show="item.is_issue_order" class="badge badge-primary">问题件</span>
                                 </td>
-                                <td class="text-center">
+                                <td class="text-left">
                                     @can('订单管理-订单问题件生成')
                                         <button class="btn btn-sm btn-outline-primary"
-                                                @click="createOrderIssue(item,true)" v-show="!item.is_issue_order ">
-                                            生成问题件
+                                                v-show="!item.is_issue_order "
+                                                @click="createOrderIssue(item,true)">生成问题件
                                         </button>
                                     @endcan
                                     @can('订单管理-工单处理-审核')
-                                        <button class="btn btn-sm btn-outline-success" v-show="item.status !== '已处理'"
-                                                @click="review(item,i)">
-                                            审核
+                                        <button class="btn btn-sm btn-outline-success"
+                                                v-show="item.status !== '已处理'"
+                                                @click="review(item,i)">审核
+                                        </button>
+                                    @endcan
+                                    @can('订单管理-工单处理-货主编辑')
+                                        <button class="btn btn-sm btn-outline-secondary"
+                                                v-if="item['issue_type']['name'] ==='快递丢件'"
+                                                @click="showFillModel(item,i)">信息填充
+                                        </button>
+                                    @endcan
+                                    @can('订单管理-工单处理-承运商编辑')
+                                        <button class="btn btn-sm btn-outline-secondary"
+                                                v-if="['快递丢件','破损'].includes(item['issue_type']['name'])"
+                                                @click="showEditLog(item,i,2)">快递处理
+                                        </button>
+                                    @endcan
+                                    @can('订单管理-工单处理-宝时编辑')
+                                        <button class="btn btn-sm btn-outline-secondary"
+                                                v-if="['快递丢件','破损'].includes(item['issue_type']['name'])"
+                                                @click="showEditLog(item,i,1)">宝时处理
                                         </button>
                                     @endcan
-
+                                </td>
+                                <td class="text-center">
+                                    @can('订单管理-工单处理-宝时编辑')
+                                        <select class="form-control form-control-sm"
+                                                :value="item['work_order_status']"
+                                                :disabled="item['work_order_status'] === '工单完成'"
+                                                @change="updateWorkOrderStatus(item,$event)">
+                                            <option value="">无</option>
+                                            <option :value="status" v-for="status in workOrderStatus"
+                                                    v-text="status"></option>
+                                        </select>
+                                    @else
+                                        @{{ item.work_order_status }}
+                                    @endcan
                                 </td>
                                 <td class="text-center">@{{ item.status }}</td>
                                 <td class="text-center">@{{ item.owner ? item.owner.name : '' }}</td>
                                 <td class="text-center">@{{ item.order ? item.order.client_code : ''}}</td>
                                 <td>@{{item.order ? (item.order.logistic ? item.order.logistic.name : '') : '' }}</td>
                                 <td class="text-center">
-                                    <template v-if="item.order">
-                                        <template v-if="item.order.packages && item.order.packages.length === 1">
-                                            <p>@{{ item.order.packages[0].logistic_number }}</p>
-                                        </template>
-                                        <template v-else-if="item.order.packages && item.order.packages.length > 0">
-                                            <template v-if="selectOrder === item.order.id">
-                                                <template v-for="(package,index) in item.order.packages">
-                                                    @{{ package.logistic_number }}
-                                                </template>
-                                            </template>
-                                            <template v-else>
-                                                <p>@{{ item.order.packages[0].logistic_number }}</p>
-                                            </template>
-                                            <button class="btn btn-sm btn-outline-primary" v-show="selectOrder === null"
-                                                    @click="selectOrder = item.order.id">
-                                                展开
+                                    <div v-if="item.order">
+                                        <p v-if="item.order.packages && item.order.packages.length === 1"
+                                           v-text="item.order.packages[0].logistic_number">
+                                        </p>
+                                        <div v-else-if="item.order.packages && item.order.packages.length > 0">
+                                            <div v-if="selectOrder === item.order.id">
+                                                <p v-for="(package,index) in item.order.packages"
+                                                   v-text="package.logistic_number"></p>
+                                            </div>
+                                            <div v-else>
+                                                <p v-text="item.order.packages[0].logistic_number"></p>
+                                            </div>
+                                            <button class="btn btn-sm btn-outline-primary"
+                                                    v-show="selectOrder === null"
+                                                    @click="selectOrder = item.order.id">展开
                                             </button>
                                             <button class="btn btn-sm btn-outline-primary"
                                                     v-show="selectOrder === item.order.id"
-                                                    @click="selectOrder = null">
-                                                收起
+                                                    @click="selectOrder = null">收起
                                             </button>
+                                        </div>
+                                    </div>
+                                </td>
+                                <td>
+                                    {{--  承运商处理日志   --}}
+                                    <div v-if="item['logistic_log']" class="alert alert-light">
+                                        <div>
+                                            <span class="text-muted">承运商处理</span>
+                                            <button class="btn btn-sm btn-outline-info float-right"
+                                                    v-show="selectLogisticLogId === item['logistic_log']['id']"
+                                                    @click="selectLogisticLogId = null">隐藏
+                                            </button>
+                                            <button class="btn btn-sm btn-outline-info float-right"
+                                                    v-show="selectLogisticLogId !== item['logistic_log']['id']"
+                                                    @click="selectLogisticLogId = item['logistic_log']['id']">显示
+                                            </button>
+                                        </div>
+                                        <transition name="fade">
+                                            <div v-show="selectLogisticLogId === item['logistic_log']['id']">
+                                                <div>
+                                                    <span class="mr-2">是否赔偿:</span>
+                                                    <span class="text-truncate"
+                                                          v-text="item['logistic_log']['is_indemnity']"></span>
+                                                </div>
+                                                <div v-if="item['logistic_log']['is_indemnity'] === '是'">
+                                                    <span class="mr-2">赔偿金额:</span>
+                                                    <span class="text-truncate"
+                                                          v-text="item['logistic_log']['indemnity']"></span>
+                                                </div>
+                                                <div v-if="item['logistic_log']['is_indemnity'] === '否'">
+                                                    <span class="mr-2">不赔偿理由:</span>
+                                                    <span v-text="item['logistic_log']['remark']"></span>
+                                                </div>
+                                            </div>
+                                        </transition>
+                                    </div>
+                                    <div v-if="item['process_log']" class="alert alert-light">
+                                        <div>
+                                            <span class="text-muted">宝时处理</span>
+                                            <button class="btn btn-sm btn-outline-info float-right"
+                                                    v-if="selectBaoShiLogId === item['process_log']['id']"
+                                                    @click="selectBaoShiLogId = null">隐藏
+                                            </button>
+                                            <button class="btn btn-sm btn-outline-info float-right"
+                                                    v-if="selectBaoShiLogId !== item['process_log']['id']"
+                                                    @click="selectBaoShiLogId = item['process_log']['id']">显示
+                                            </button>
+                                        </div>
+                                        <transition name="fade">
+                                            <div v-show="selectBaoShiLogId === item['process_log']['id']">
+                                                <div>
+                                                    <span class="text-muted">是否赔偿: </span>
+                                                    <span v-text="item['process_log']['is_indemnity']"
+                                                          class="ml-2 text-truncate"></span></div>
+                                                <div v-if="item['process_log']['is_indemnity'] === '是'">
+                                                    <span class="text-muted">赔偿方:</span>
+                                                    <span class="text-truncate"
+                                                          v-text="item['process_log']['indemnitor']"></span>
+                                                </div>
+                                                <div v-if="item['process_log']['is_indemnity'] === '是'">
+                                                    <span class="text-muted">赔偿金额:</span>
+                                                    <span class="ml-2 text-truncate"
+                                                          v-text="item['process_log']['indemnity']"></span>
+                                                </div>
+                                                <div v-if="item['process_log']['is_indemnity'] === '否'">
+                                                    <span class="text-muted">不赔偿理由:</span>
+                                                    <span class="ml-2" v-text=" item['process_log']['remark']"></span>
+                                                </div>
+                                            </div>
+                                        </transition>
+                                    </div>
+                                </td>
+                                <td>
+                                    {{-- 工单详情 --}}
+                                    @can('订单管理-工单处理-宝时编辑')
+                                        @include('order.workOrder._work_order_details')
+                                    @elsecan('订单管理-工单处理-承运商编辑')
+                                        <template v-if="item.status === '已处理' && item.is_issue_order">
+                                            @include('order.workOrder._work_order_details')
                                         </template>
-
-                                    </template>
+                                    @elsecan('订单管理-工单处理-货主编辑')
+                                        @include('order.workOrder._work_order_details')
+                                    @endcan
                                 </td>
                                 <td class="text-center">
                                     @can('订单管理-工单处理-审核')
@@ -105,16 +210,16 @@
                                                 :value="item.order_issue_type_id"
                                                 @change="changeIssueType(item,$event)">
                                             <option value="0"></option>
-                                            <option v-for="type in orderIssueTypes" :value="type.name">@{{ type.value
-                                                }}
+                                            <option v-for="type in orderIssueTypes"
+                                                :value="type.name"
+                                                v-text="type.value">
                                             </option>
                                         </select>
                                     @else
-                                        @{{ item.issue_type ?  item.issue_type.name : '' }}
+                                        <span v-text="item['issue_type'] ?  item['issue_type'].name : ''"></span>
                                     @endcan
                                 </td>
                                 <td class="text-center">@{{ item.remark }}</td>
-
                                 <td class="text-center">
                                     @{{ item.result_explain ?item.result_explain : '' }}
                                 </td>
@@ -128,18 +233,17 @@
                                                 <tr class="table table-sm">
                                                     <td>@{{ item.issue_logs[0].content }}</td>
                                                     <td>@{{ item.issue_logs[0].username }}</td>
-                                                    <td>@{{ item.issue_logs[0].created_at }}</td>
+                                                    <td>@{{ item.issue_logs[0].created_at | dataTime}}</td>
                                                 </tr>
                                             </table>
                                         </template>
-                                        <template v-else>
+                                        <template v-else-if="item.issue_logs.length > 1">
                                             <transition name="fade">
                                                 <table class="table table-sm m-0" v-if="selectOrderIssue === item.id">
-
                                                     <tr v-for="log in item.issue_logs">
                                                         <td>@{{ log.content }}</td>
                                                         <td>@{{ log.username }}</td>
-                                                        <td>@{{ log.created_at }}</td>
+                                                        <td>@{{ log.created_at | dataTime}}</td>
                                                     </tr>
                                                 </table>
                                             </transition>
@@ -148,7 +252,7 @@
                                                     <tr>
                                                         <td>@{{ item.issue_logs[0].content }}</td>
                                                         <td>@{{ item.issue_logs[0].username }}</td>
-                                                        <td>@{{ item.issue_logs[0].created_at }}</td>
+                                                        <td>@{{ item.issue_logs[0].created_at | dataTime}}</td>
                                                     </tr>
                                                 </table>
                                             </transition>
@@ -193,10 +297,10 @@
                                         </div>
                                     </template>
                                 </td>
-                                <td class="text-center">@{{ item.creator.name }}</td>
-                                <td class="text-center">@{{ item.created_at }}</td>
+                                <td class="text-center">@{{ item.creator ? item.creator.name : '' }}</td>
+                                <td class="text-center">@{{ item.created_at | dataTime}}</td>
                                 <td>@{{ item.reviewer ? item.reviewer.name : ''}}</td>
-                                <td>@{{ item.review_at }}</td>
+                                <td>@{{ item.review_at |dataTime}}</td>
                                 @can('订单管理-工单处理-删除')
                                     <td>
                                         <button class="btn btn-sm btn-outline-danger"
@@ -216,6 +320,8 @@
             @can('订单管理-工单处理-审核')
                 @include('order.workOrder._edit_issue_type')
             @endcan
+            @include('order.workOrder._fill_loss_work_order')
+            @include('order.workOrder._edit_process_log')
         </div>
     </div>
 @endsection()
@@ -225,19 +331,26 @@
     <script type="text/javascript" src="{{mix('js/queryForm/header.js')}}"></script>
     <style>
         .fade-enter-active, .fade-leave-active {
-            transition: opacity .5s;
+            transition: opacity .3s;
         }
 
         .fade-enter, .fade-leave-to {
             opacity: 0;
         }
+
+        .image-div {
+        }
+
+        .image-w {
+            width: 100%;
+        }
     </style>
     <script>
         let list = new Vue({
             el: "#list",
             data: {
                 workOrders: {!! $workOrders->toJson() !!}['data'],
-                selectTr: null,
+
                 form: null,
                 logistics: [
                         @foreach($logistics as $logistic)
@@ -253,15 +366,41 @@
                     },
                     @endforeach
                 ],
-                owners:[
-                    @foreach($owners as $owner)
-                    {name:'{{$owner->id}}',value:'{{$owner->name}}'},
+                owners: [
+                        @foreach($owners as $owner)
+                    {
+                        name: '{{$owner->id}}', value: '{{$owner->name}}'
+                    },
                     @endforeach
                 ],
+                workOrder: {
+                    id: null,
+                    index: null,
+                    reissue_logistic_number: null, // 补发单号
+                    logistic_number: null, // 丢件快递单号
+                    refundImages: [], // 退款图
+                    dealImages: [], // 交易图
+                },
+                processLog: {
+                    id: null,        // log->id
+                    index: '', // 下标
+                    work_order_id: null, // work_order_id
+                    type: null,  // 类型
+                    is_indemnity: null, // 是否赔偿
+                    indemnity: null,  // 金额
+                    remark: null,
+                    indemnitor: null, // 赔偿方
+                },
+                workOrderStatus: ['信息未填写', '信息已填写', '快递已处理', '工单完成'],
+                selectTr: null,
                 selectOrderPackage: null,
                 selectOrder: null,
+                selectLogisticLogId: null,
+                selectBaoShiLogId: null,
+                selectDetailId: null,
                 selectOrderIssue: null,
                 selectIssueType: '',
+                filePrefix: "{{asset("/storage")}}",
             },
             mounted() {
                 let data = [[
@@ -274,7 +413,7 @@
                         tip: ['输入关键词快速定位下拉列表,回车确定', '选择要显示的承运商'],
                         placeholder: ['承运商', '定位或多选承运商']
                     },
-                    @can('订单管理-订单问题件生成')
+                        @can('订单管理-订单问题件生成')
                     {
                         name: 'owner',
                         type: 'select_multiple_select',
@@ -282,8 +421,10 @@
                         tip: ['输入关键词快速定位下拉列表,回车确定', '选择要显示的货主'],
                         placeholder: ['货主', '定位或多选货主']
                     },
-                    @endcan
-                    {name: 'logistic_number', type: 'input', placeholder: '快递单号'},
+                        @endcan
+                    {
+                        name: 'logistic_number', type: 'input', placeholder: '快递单号'
+                    },
                     {
                         name: 'is_issue_order',
                         type: 'select',
@@ -305,32 +446,34 @@
                 let column = [
                     {name: 'no', value: '序号', neglect: true},
                     {name: 'operation', value: '操作', neglect: true},
-                    {name: 'status', value: '状态',neglect: true},
-                    {name: 'owner', value: '货主',neglect: true},
-                    {name: 'client_code', value: '订单号',neglect: true},
-                    {name: 'logisticName', value: '承运商',neglect: true},
+                    {name: 'work_order_status', value: '工单状态', neglect: true},
+                    {name: 'status', value: '状态', neglect: true},
+                    {name: 'owner', value: '货主', neglect: true},
+                    {name: 'client_code', value: '订单号', neglect: true},
+                    {name: 'logisticName', value: '承运商', neglect: true},
                     {name: 'logisticNumber', value: '快递单号'},
+                    {name: 'processLog', value: '处理日志'},
+                    {name: 'workOrderDetails', value: '工单详情'},
                     {name: 'issueType', value: '问题件类型'},
-                    {name: 'workOrderInfo', value: '问题描述',neglect: true},
-                    {name: 'result_explain', value: '情况说明',neglect: true},
+                    {name: 'workOrderInfo', value: '问题描述', neglect: true},
+                    {name: 'result_explain', value: '情况说明', neglect: true},
                     {name: 'orderIssueType', value: '问题件类别'},
                     {
                         name: 'orderIssueProcessLogs', type: 'multi', title: "处理结果", rows: [
                             {value: "内容", col: "4"},
                             {value: "操作人", col: "4"},
                             {value: "时间", col: "4"},
-                        ],neglect: true
+                        ], neglect: true
                     },
                     {name: 'Info', value: '物流跟踪信息', neglect: true},
-                    {name: 'creator', value: '创建人',neglect: true},
-                    {name: 'submit_at', value: '提交时间',neglect: true},
-                    {name: 'reviewer', value: '审核人',neglect: true},
-                    {name: 'review_at', value: '审核时间',neglect: true},
+                    {name: 'creator', value: '创建人', neglect: true},
+                    {name: 'submit_at', value: '提交时间', neglect: true},
+                    {name: 'reviewer', value: '审核人', neglect: true},
+                    {name: 'review_at', value: '审核时间', neglect: true},
                     @can('订单管理-工单处理-删除')
-                    {name: 'delete_operation', value: '其他操作',neglect: true},
-                    @endcan()
+                    {name: 'delete_operation', value: '其他操作', neglect: true}
+                    @endcan
                 ];
-
                 new Header({
                     el: "table",
                     name: "workOrders",
@@ -338,41 +481,49 @@
                     data: this.workOrders,
                     fixedTop: ($('#form_div').height()) + 2,
                 }).init();
-
                 $("#list").removeClass("d-none");
             },
             created() {
-                let self = this;
-                $.each(this.workOrders, function (index, workOrder) {
-                    if (!workOrder.order) return;
-                    if (!workOrder.order.packages) return;
-                    self.sortOrder(workOrder);
+                this.workOrders.forEach(item => {
+                    if (!item.order) return;
+                    if (!item.order.packages) return;
+                    this.sortOrder(item);
                 });
             },
+            filters: {
+                dataTime: function (value) {
+                    if (value !== null) {
+                        return moment(value).format('yyyy-MM-DD');
+                    }
+                    return value
+                },
+            },
             methods: {
                 sortOrder(workOrder) {
-                    let self = this;
                     if (!workOrder.order) return;
-                    if (workOrder.order_issue) {
-                        workOrder.result_explain = workOrder.order_issue.result_explain;
-                        if (workOrder.order_issue.issue_type) {
-                            workOrder.issue_order_type = workOrder.order_issue.issue_type.name;
+                    if (workOrder['order_issue']) {
+                        workOrder.result_explain = workOrder['order_issue'].result_explain;
+                        if (workOrder['order_issue']['issue_type']) {
+                            workOrder.issue_order_type = workOrder['order_issue']['issue_type']['name'];
                         }
-                        if (workOrder.order_issue.logs) {
-                            workOrder.issue_logs = workOrder.order_issue.logs.map(item => {
-                                return {
-                                    username: item.user ? item.user.name : '',
-                                    content: item.content,
-                                    created_at: item.created_at
-                                };
-                            });
+                        if (workOrder['order_issue'].logs) {
+                            workOrder.issue_logs = this.mapLogs(workOrder['order_issue'].logs);
                         }
                     }
                     if (!workOrder.order.packages) return;
-                    $.each(workOrder.order.packages, function (i, item) {
-                        self.sortTransfer(item);
+                    workOrder.order.packages.forEach(item => {
+                        this.sortTransfer(item)
                     })
                 },
+                mapLogs(logs) {
+                    return logs.map(item => {
+                        return {
+                            username: item.user ? item.user.name : '',
+                            content: item.content,
+                            created_at: item.created_at
+                        };
+                    });
+                },
                 sortTransfer(item) {
                     if (!("transfer_status" in item)) return;
                     if (item.transfer_status == null || !(item.transfer_status instanceof Array)) return;
@@ -390,8 +541,8 @@
                     window.axios.post(url, data).then(res => {
                         if (res.data.success) {
                             res.data.data.is_issue_order = item.is_issue_order;
-                            this.$set(this.workOrders, i, res.data.data);
                             this.sortOrder(res.data.data);
+                            this.$set(this.workOrders, i, res.data.data);
                             window.tempTip.showSuccess("审核完成");
                         } else {
                             window.tempTip.show(res.data.message ? res.data.message : '审核异常');
@@ -406,18 +557,10 @@
                     if (tag) data.ids = [item.id];
                     else data.ids = checkData;
                     if (!confirm('是否生成对应的问题件')) return;
-                    let _this = this;
                     window.tempTip.waitingTip('生成中........');
                     window.axios.post(url, data).then(res => {
                         if (res.data.success) {
-                            res.data.data.forEach(item => {
-                                this.workOrders.forEach((workOrder, i) => {
-                                    if (item.id === workOrder.id) {
-                                        _this.sortOrder(item);
-                                        _this.$set(_this.workOrders, i, item);
-                                    }
-                                });
-                            });
+                            this.replaceWorkOrder(res.data.data);
                             this.$forceUpdate();
                             window.tempTip.cancelWaitingTip();
                             window.tempTip.showSuccess('已生成对应的问题件');
@@ -430,6 +573,20 @@
                         window.tempTip.show(err)
                     });
                 },
+                replaceWorkOrder(workOrders) {
+                    let data = [];
+                    workOrders.forEach(workOrder => {
+                        data[workOrder.id] = workOrder;
+                    });
+                    this.workOrders.forEach((workOrder, i) => {
+                        if (data[workOrder.id]) {
+                            let item = data[workOrder.id];
+                            this.sortOrder(item);
+                            this.$set(this.workOrders, i, item);
+                        }
+                    });
+                    this.$forceUpdate();
+                },
                 getMessageWorkOrder() {
                     let selected = checkData;
                     if (!selected) {
@@ -455,7 +612,7 @@
                     if (!item.order) return '';
                     if (!item.order.packages) return '';
                     let message = '';
-                    let issue_type = item.issue_type? item.issue_type.name : '';
+                    let issue_type = item['issue_type'] ? item['issue_type'].name : '';
                     switch (issue_type) {
                         case '拦截':
                             message = this.interceptMessage(item);
@@ -471,15 +628,15 @@
                 },
                 interceptMessage(item) {
                     let message = '';
-                    if (item.order.logistic.code.includes('SF') || item.order.logistic.code.includes('ZTO')) {
+                    if (item.order['logistic']['code'].includes('SF') || item.order['logistic'].code.includes('ZTO')) {
                         item.order.packages.forEach(node => {
                             message += node.logistic_number + '\n';
                         });
                         message = message.trim('\n') + ' ——拦截\n';
                     } else {
-                        let item_order_logistic_name = item.order.logistic.name;
-                        let item_order_adder = item.order.consignee_name + ' '
-                            + item.order.consignee_phone + ' '
+                        let item_order_logistic_name = item.order['logistic']['name'];
+                        let item_order_adder = item.order['consignee_name'] + ' '
+                            + item.order['consignee_phone'] + ' '
                             + ' ' + item.order.address;
                         item.order.packages.forEach(p => {
                             if (p) message += item_order_logistic_name + ' ' + p.logistic_number + ' ' + item_order_adder + '\n';
@@ -490,17 +647,16 @@
                 },
                 modificationMessage(item) {
                     let message = '';
-                    let logistic_code = item.order.logistic.code;
-                    let adder = item.order.consignee_name + ' ' + item.order.consignee_phone + ' '
+                    let logistic_code = item.order['logistic']['code'];
+                    let adder = item.order['consignee_name'] + ' ' + item.order['consignee_phone'] + ' '
                         + item.order.province + ' ' + item.order.city + ' ' + item.order.district + ' ' + item.order.address;
-
                     item.order.packages.forEach(node => {
                         if (logistic_code.includes('SF')) { // 顺丰订单
-                            message += node.logistic_number + ' ——改信息: ' + item.remark + ',运费到付或月结' + '\n';
+                            message += node['logistic_number'] + ' ——改信息: ' + item.remark + ',运费到付或月结' + '\n';
                         } else if (logistic_code.includes('ZTO')) {
-                            message += node.logistic_number + ' ——改信息:' + item.remark + '\n';
+                            message += node['logistic_number'] + ' ——改信息:' + item.remark + '\n';
                         } else {
-                            message += node.logistic_number + ' ' + adder + ' ——改地址' + item.remark + '\n';
+                            message += node['logistic_number'] + ' ' + adder + ' ——改地址' + item.remark + '\n';
                         }
                     });
                     return message;
@@ -508,7 +664,7 @@
                 getMessage(item) {
                     let message = '';
                     if (!item.order.packages) return message;
-                    let adder = item.order.consignee_name + ' ' + item.order.consignee_phone + ' '
+                    let adder = item.order['consignee_name'] + ' ' + item.order['consignee_phone'] + ' '
                         + item.order.province + ' ' + item.order.city + ' ' + item.order.district + ' ' + item.order.address;
                     item.order.packages.forEach(p => {
                         message += p.logistic_number + '  ' + adder + ' ——描述 ' + item.remark + '\n';
@@ -534,19 +690,12 @@
                 batchReview() {
                     let url = '{{route('workOrder.batchReviewApi')}}';
                     let data = {ids: checkData};
-                    let _this = this;
                     window.tempTip.setIndex('1999');
                     if (!confirm('是否对当前选中订单进行审核')) return;
                     window.tempTip.waitingTip('审核中........');
                     window.axios.post(url, data).then(res => {
                         if (res.data.success) {
-                            $.each(res.data.data, (i, data) => {
-                                $.each(_this.workOrders, (index, item) => {
-                                    if (item.id === data.id) {
-                                        _this.$set(this.workOrders, index, data);
-                                    }
-                                });
-                            });
+                            this.replaceWorkOrder(res.data.data);
                             this.$forceUpdate();
                             window.tempTip.cancelWaitingTip();
                             window.tempTip.showSuccess('审核完成');
@@ -597,20 +746,11 @@
                 editOrderIssueType() {
                     let url = '{{route('workOrder.batchUpdateIssueTypeApi')}}'
                     let data = {ids: checkData, type: this.selectIssueType};
-                    let _this = this;
                     window.tempTip.setIndex(1999);
                     window.axios.post(url, data).then(res => {
                         if (res.data.success) {
                             window.tempTip.showSuccess('修改问题件类型成功');
-                            res.data.data.forEach(item => {
-                                _this.sortOrder(item);
-                                _this.workOrders.forEach((workOrder, i, array) => {
-                                    if (workOrder.id === item.id) {
-                                        array[i] = item;
-                                    }
-                                })
-                            });
-                            this.$forceUpdate();
+                            this.replaceWorkOrder(res.data.data);
                             $("#edit-issue-type-type-modal").modal('hide');
                             return;
                         }
@@ -619,23 +759,207 @@
                         window.tempTip.show(err)
                     })
                 },
-                destroy(item,i){
-                    let url = '{{url('apiLocal/workOrder/')}}'+'/'+item.id;
-                    if(!confirm('是否删除当前工单')) return ;
+                destroy(item, i) {
+                    let url = '{{url('apiLocal/workOrder/')}}' + '/' + item.id;
+                    if (!confirm('是否删除当前工单')) return;
                     window.tempTip.waitingTip('删除.........');
-                    window.axios.delete(url).then(res=>{
-                        if (res.data.success){
-                            this.$delete(this.workOrders,i);
+                    window.axios.delete(url).then(res => {
+                        if (res.data.success) {
+                            this.$delete(this.workOrders, i);
                             window.tempTip.cancelWaitingTip();
                             window.tempTip.showSuccess('删除成功');
-                        }else {
+                        } else {
                             window.tempTip.cancelWaitingTip();
                             window.tempTip.show(res.data.message ? res.data.message : '');
                         }
-                    }).catch(err=>{
+                    }).catch(err => {
                         window.tempTip.cancelWaitingTip();
                         window.tempTip.show(err);
                     })
+                },
+                pushImagesAndShow(e, images) {
+                    let map = [];
+                    for (let i = 0; i < e.target.files.length; i++) {
+                        let image = e.target.files[i];
+                        if (this.imageExist(image, images)) {
+                            map.push(image.name);
+                            continue;
+                        }
+                        let src = window.URL.createObjectURL(image);
+                        images.push({src: src, file: image});
+                    }
+                    e.target.value = '';
+                    if (map.length === 0) return;
+                    window.tempTip.setIndex(1999);
+                    window.tempTip.show(map.join('\n,') + '图片重复');
+                },
+                spliceImage(i, images) {
+                    if (!confirm('是否取消选择该图片')) return;
+                    images.splice(i, 1);
+                },
+                imageExist(image, images) {
+                    let arr = images.filter(item => {
+                        return item.file.name === image.name;
+                    });
+                    return arr.length > 0;
+                },
+                showFillModel(item, index) {
+                    this.workOrder.id = item.id;
+                    this.workOrder.index = index;
+                    this.workOrder.reissue_logistic_number = null; // 补发单号
+                    this.workOrder.logistic_number = null; // 补发单号
+                    this.workOrder.dealImages = []; // 交易图
+                    this.workOrder.refundImages = []; // 退款图
+                    $("#fill-loss-work-order-modal").modal('show');
+                },
+                updateLossWorkOrder() {
+                    let formData = new FormData();
+                    formData.append('id', this.workOrder.id);
+                    formData.append('reissue_logistic_number', this.workOrder.reissue_logistic_number);
+                    formData.append('logistic_number', this.workOrder.logistic_number);
+                    formData.append('price', this.workOrder.price);
+                    let dealImages = this.getImages(this.workOrder.dealImages);
+                    let refundImages = this.getImages(this.workOrder.refundImages);
+                    this.setFormDataImagePrefix(formData, 'dealImages', dealImages);
+                    this.setFormDataImagePrefix(formData, 'refundImages', refundImages);
+                    this.fillLossWorkOrder(formData);
+                },
+                setFormDataImagePrefix(formData, prefix, images) {
+                    images.forEach((item, i) => {
+                        formData.append(`${prefix}[]`, item);
+                    });
+                },
+                getImages(images) {
+                    return images.map((item) => {
+                        return item.file;
+                    })
+                },
+                fillLossWorkOrder(data) {
+                    let url = "{{route('workOrder.lossApi')}}";
+                    window.tempTip.setIndex(1999);
+                    window.axios.post(url, data, {'Content-Type': 'multipart/form-data'}).then(res => {
+                        if (res.data.success) {
+                            this.sortOrder(res.data.data);
+                            this.$set(this.workOrders, this.workOrder.index, res.data.data);
+                            window.tempTip.showSuccess('工单信息填充成功');
+                        } else {
+                            window.tempTip.show(res.data.message);
+                        }
+                    }).catch(err => {
+                        window.tempTip.show(err);
+                    });
+                },
+                showEditLog(item, index, type) {
+                    this.processLog.type = type;
+                    this.processLog.index = index;
+                    this.processLog.work_order_id = item.id;
+                    this.processLog.indemnity = null;
+                    this.processLog.is_indemnity = null;
+                    this.processLog.remark = null;
+                    this.processLog.indemnitor = null;
+                    $("#work-order-process-log-modal").modal('show');
+                },
+                storeLogisticProcessLog() {
+                    let url = "{{route('workOrderProcessLog.logisticLogApi')}}";
+                    let data = {
+                        indemnity: this.processLog.indemnity,
+                        work_order_id: this.processLog.work_order_id,
+                        is_indemnity: this.processLog.is_indemnity,
+                        remark: this.processLog.remark,
+                    };
+                    if (!this.verifiedProcessLog()) return;
+                    window.tempTip.setDuration(9999);
+                    window.tempTip.setIndex(1999);
+                    window.tempTip.waitingTip('操作中请稍后');
+                    window.axios.post(url, data).then(res => {
+                        window.tempTip.cancelWaitingTip();
+                        window.tempTip.setIndex(1999);
+                        window.tempTip.setDuration(2000);
+                        if (res.data.success) {
+                            window.tempTip.showSuccess('创建成功');
+                            this.$set(this.workOrders[this.processLog.index], 'logistic_log', res.data.data);
+                            $('#work-order-process-log-modal').modal('hide');
+                        } else {
+                            window.tempTip.show(res.data.message ? res.data.message : '创建异常,刷新页面重试');
+                        }
+                    }).catch(err => {
+                        window.tempTip.setIndex(1999);
+                        window.tempTip.setDuration(2000);
+                        window.tempTip.show(err);
+                    });
+                },
+                storeProcessLog() {
+                    let url = "{{route('workOrderProcessLog.LogApi')}}";
+                    let data = {
+                        indemnity: this.processLog.indemnity,
+                        work_order_id: this.processLog.work_order_id,
+                        is_indemnity: this.processLog.is_indemnity,
+                        remark: this.processLog.remark,
+                        indemnitor: this.processLog.indemnitor,
+                    };
+                    if (!this.verifiedProcessLog()) return;
+                    window.tempTip.setDuration(9999);
+                    window.tempTip.setIndex(1999);
+                    window.tempTip.waitingTip('操作中请稍后');
+                    window.axios.post(url, data).then(res => {
+                        window.tempTip.cancelWaitingTip();
+                        window.tempTip.setDuration(2000);
+                        if (res.data.success) {
+                            window.tempTip.showSuccess('创建成功');
+                            this.$set(this.workOrders[this.processLog.index], 'process_log', res.data.data);
+                            $('#work-order-process-log-modal').modal('hide');
+                        } else {
+                            window.tempTip.show(res.data.message ? res.data.message : '创建异常,刷新页面重试');
+                        }
+                    }).catch(err => {
+                        window.tempTip.setDuration(2000);
+                        window.tempTip.show(err);
+                    });
+                },
+                updateWorkOrderStatus(item, e, i) {
+                    let url = "{{route('workOrder.updateWorkOrderStatusApi')}}";
+                    let data = {
+                        id: item.id,
+                        work_order_status: e.target.value
+                    }
+                    window.tempTip.waitingTip('处理中......');
+                    window.tempTip.setDuration('1999');
+                    window.axios.post(url, data).then(res => {
+                        window.tempTip.cancelWaitingTip();
+                        window.tempTip.setDuration('1999');
+                        if (res.data.success) {
+                            item.work_order_status = data.work_order_status;
+                            window.tempTip.showSuccess('修改成功');
+                            return;
+                        }
+                        window.tempTip.show(res.data.message ? res.data.message : '修改失败');
+                    }).catch(err => {
+                        window.tempTip.show('修饰异常:' + err);
+                    });
+                },
+                verifiedProcessLog() {   // 校验处理信息
+                    window.tempTip.setIndex(1999);
+                    window.tempTip.setDuration(2000);
+                    if (this.processLog.is_indemnity === null) {
+                        window.tempTip.show('选择处理方式');
+                        return false;
+                    }
+                    if (parseInt(this.processLog.type) === 1 && this.processLog.indemnitor == null) {
+                        window.tempTip.show('指定赔偿方');
+                        return false;
+                    }
+                    if (this.processLog.is_indemnity === '1') {
+                        if (!this.processLog.indemnity) {
+                            window.tempTip.show('填写赔偿金额');
+                            return false;
+                        }
+                    } else if (this.processLog.is_indemnity === '2') {
+                        if (this.processLog.remark === null || this.processLog.remark.trim(' ').length === 0) {
+                            window.tempTip.show('填写不赔偿理由');
+                            return false;
+                        }
+                    }
+                    return true;
                 }
             },
         });

+ 14 - 1
routes/apiLocal.php

@@ -29,7 +29,11 @@ Route::group(['prefix' => 'rejected' ],function(){
 Route::post('logistic/numberFeatures/computeLogisticByNumber', 'LogisticNumberFeatureController@apiComputeLogisticByNumber');
 Route::post('logistic/logisticNumberReturnIsUnique', 'RejectedBillController@apiLogisticNumberReturnIsUnique');
 
-Route::post('commodity/getCommodityByBarcode', 'CommodityController@apiGetCommodityByBarcode');
+/** 商品 */
+Route::prefix('commodity')->group(function (){
+    Route::post('getCommodityByBarcode', 'CommodityController@apiGetCommodityByBarcode');
+    Route::post('getCommodity','CommodityController@getCommodityApi')->name('commodity.getCommodityApi');
+});
 
 Route::post('rejectedBill/getRejectedByLogisticNumberReturn', 'RejectedController@apiGetRejectedByLogisticNumberReturn');
 
@@ -261,6 +265,10 @@ Route::group(['prefix' => 'print'],function (){
 });
 
 Route::prefix('workOrder')->group(function(){
+    Route::post('store','WorkOrderController@storeApi')->name('workOrder.storeApi'); // 创建
+    Route::post('damaged','WorkOrderController@damagedApi')->name('workOrder.damagedApi'); // 创建 破损
+    Route::post('loss','WorkOrderController@updateLossApi')->name('workOrder.lossApi'); // 客户提供丢件信息
+    Route::post('workOrderStatus','WorkOrderController@updateWorkOrderStatusApi')->name('workOrder.updateWorkOrderStatusApi');
     Route::post('store','WorkOrderController@storeApi')->name('workOrder.storeApi'); // 创建
     Route::post('review','WorkOrderController@reviewApi')->name('workOrder.reviewApi'); // 审核
     Route::post('batchReview','WorkOrderController@batchReviewApi')->name('workOrder.batchReviewApi');  // 批量审核
@@ -268,6 +276,11 @@ Route::prefix('workOrder')->group(function(){
     Route::post('updateIssueType','WorkOrderController@updateIssueTypeApi')->name('workOrder.updateIssueTypeApi'); // 修改问题类型
     Route::post('batchUpdateIssueType','WorkOrderController@batchUpdateIssueTypeApi')->name('workOrder.batchUpdateIssueTypeApi'); // 修改问题类型
     Route::delete('/{id}','WorkOrderController@destroyApi')->name('workOrder.destroyApi');
+
+    Route::prefix('process')->group(function (){
+        Route::post('log/logistic','WorkOrderProcessLogController@logisticStoreApi')->name('workOrderProcessLog.logisticLogApi');
+        Route::post('log','WorkOrderProcessLogController@storeApi')->name('workOrderProcessLog.LogApi');
+    });
  });
 /*出库*/
 Route::group(['prefix'=>'storeOut'],function(){

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff