Prechádzať zdrojové kódy

Merge branch 'print_service_2' into zengjun

# Conflicts:
#	app/Http/Controllers/TestController.php
#	app/Providers/AppServiceProvider.php
ajun 4 rokov pred
rodič
commit
233781aa3d
83 zmenil súbory, kde vykonal 4945 pridanie a 359 odobranie
  1. 14 0
      app/Http/Controllers/OrderPackageExpressBillPrintRecordController.php
  2. 40 0
      app/Http/Controllers/PrintController.php
  3. 3 49
      app/Http/Controllers/PrintPartController.php
  4. 64 0
      app/Http/Controllers/PrintPartImageController.php
  5. 46 15
      app/Http/Controllers/PrintTemplateController.php
  6. 73 0
      app/Http/Controllers/TerminalController.php
  7. 62 0
      app/Http/Controllers/TerminalPrinterController.php
  8. 4 0
      app/Http/Controllers/TestController.php
  9. 30 0
      app/Http/Requests/Printer/TerminalPrinterRequest.php
  10. 24 0
      app/Http/Requests/Printer/TerminalRequest.php
  11. 1 1
      app/OracleActAllocationDetails.php
  12. 7 0
      app/OracleDOCOrderHeader.php
  13. 46 0
      app/OracleDocOrderDeliveryInfo.php
  14. 26 0
      app/OrderPackageExpressBillPrintRecords.php
  15. 30 0
      app/OwnerLogisticPrintTemplate.php
  16. 25 0
      app/PrintPartImage.php
  17. 11 0
      app/PrintTemplate.php
  18. 26 0
      app/Providers/AppServiceProvider.php
  19. 112 0
      app/Services/DeliveryService.php
  20. 165 0
      app/Services/Express/CaiNiaoExpress.php
  21. 52 0
      app/Services/Express/ExpressInterface.php
  22. 68 0
      app/Services/Express/PDDExpress.php
  23. 33 0
      app/Services/Interfaces/DeliveryInterface.php
  24. 95 0
      app/Services/JDDeliveryService.php
  25. 36 0
      app/Services/OrderPackageExpressBillPrintRecordService.php
  26. 72 0
      app/Services/PDDDeliveryService.php
  27. 75 0
      app/Services/PrintPartImageService.php
  28. 5 4
      app/Services/PrintPartService.php
  29. 8 0
      app/Services/PrintService.php
  30. 113 3
      app/Services/PrintTemplateService.php
  31. 67 0
      app/Services/SFDeliveryService.php
  32. 70 0
      app/Services/SFQHDDeliveryService.php
  33. 71 0
      app/Services/TBDeliveryService.php
  34. 37 0
      app/Services/TerminalPrinterLogisticService.php
  35. 13 0
      app/Services/TerminalPrinterService.php
  36. 12 0
      app/Services/TerminalService.php
  37. 21 0
      app/Terminal.php
  38. 29 0
      app/TerminalPrinter.php
  39. 25 0
      app/TerminalPrinterLogistic.php
  40. 295 0
      app/Traits/DeliveryProcess.php
  41. 254 0
      app/Traits/DrawImage.php
  42. 0 4
      composer.lock
  43. 16 0
      database/factories/OrderPackageExpressBillPrintRecordFactory.php
  44. 19 0
      database/factories/OwnerLogisticPrintTemplateFactory.php
  45. 13 0
      database/factories/TerminalFactory.php
  46. 16 0
      database/factories/TerminalPrinterFactory.php
  47. 41 0
      database/migrations/2021_06_18_150046_create_terminals_table.php
  48. 42 0
      database/migrations/2021_06_18_150129_create_terminal_printers_table.php
  49. 34 0
      database/migrations/2021_06_22_172006_create_owner_logistic_print_templates_table.php
  50. 32 0
      database/migrations/2021_06_28_154631_create_print_part_images_table.php
  51. 37 0
      database/migrations/2021_07_29_114758_create_order_package_express_bill_print_records_table.php
  52. 32 0
      database/migrations/2021_07_29_151248_create_terminal_printer_logistics_table.php
  53. 32 0
      database/migrations/2021_07_29_163550_change_terminals_ip_unique.php
  54. 16 0
      database/seeds/OrderPackageExpressBillPrintRecordSeeder.php
  55. 19 0
      database/seeds/OwnerLogisticPrintTemplateSeeder.php
  56. 16 0
      database/seeds/TerminalPrinterSeeder.php
  57. 16 0
      database/seeds/TerminalSeeder.php
  58. 1 0
      package.json
  59. 34 0
      resources/views/maintenance/expressPrinting/image/_create.blade.php
  60. 30 0
      resources/views/maintenance/expressPrinting/image/_table.blade.php
  61. 97 0
      resources/views/maintenance/expressPrinting/image/index.blade.php
  62. 199 94
      resources/views/maintenance/expressPrinting/part/create.blade.php
  63. 358 0
      resources/views/maintenance/expressPrinting/print/template.blade.php
  64. 109 0
      resources/views/maintenance/expressPrinting/setting/printer/_create.blade.php
  65. 104 0
      resources/views/maintenance/expressPrinting/setting/printer/_edit.blade.php
  66. 25 0
      resources/views/maintenance/expressPrinting/setting/printer/_table.blade.php
  67. 178 0
      resources/views/maintenance/expressPrinting/setting/printer/index.blade.php
  68. 44 0
      resources/views/maintenance/expressPrinting/setting/terminal/_create.blade.php
  69. 45 0
      resources/views/maintenance/expressPrinting/setting/terminal/_edit.blade.php
  70. 27 0
      resources/views/maintenance/expressPrinting/setting/terminal/_table.blade.php
  71. 117 0
      resources/views/maintenance/expressPrinting/setting/terminal/index.blade.php
  72. 29 25
      resources/views/maintenance/expressPrinting/template/_compile.blade.php
  73. 97 0
      resources/views/maintenance/expressPrinting/template/_edit.blade.php
  74. 54 12
      resources/views/maintenance/expressPrinting/template/_produce.blade.php
  75. 27 9
      resources/views/maintenance/expressPrinting/template/_select.blade.php
  76. 150 102
      resources/views/maintenance/expressPrinting/template/create.blade.php
  77. 277 0
      resources/views/maintenance/expressPrinting/template/edit.blade.php
  78. 117 6
      resources/views/maintenance/expressPrinting/template/index.blade.php
  79. 34 5
      routes/apiLocal.php
  80. 32 28
      routes/web.php
  81. 32 0
      tests/Services/DeliveryService/GetDeliveryTest.php
  82. 21 0
      tests/Unit/BaseTest.php
  83. 66 2
      yarn.lock

+ 14 - 0
app/Http/Controllers/OrderPackageExpressBillPrintRecordController.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Services\OrderPackageExpressBillPrintRecordService;
+
+class OrderPackageExpressBillPrintRecordController extends Controller
+{
+    public function updateRecordApi($logistic_number,OrderPackageExpressBillPrintRecordService  $service,$task_id): array
+    {
+        return $service->expressBillPrintRecord($logistic_number,$task_id);
+    }
+
+}

+ 40 - 0
app/Http/Controllers/PrintController.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Components\AsyncResponse;
+use App\Services\DeliveryService;
+use App\Services\TerminalPrinterLogisticService;
+use Illuminate\Http\Request;
+
+class PrintController extends Controller
+{
+    //
+    use AsyncResponse;
+
+    public function index()
+    {
+        return view('maintenance.expressPrinting.print.template');
+    }
+
+    public function getPrintDataApi(Request $request): array
+    {
+        if (!$request->has('printStr')) return ['success' => false, 'message' => '为空'];
+        $results = app(DeliveryService::class)->getDelivery($request['printStr']);
+        // 匹配打印机
+        $results = app(TerminalPrinterLogisticService::class)->setPrinterName($results);
+
+        //
+        return ['success' => true, 'data' => $results];
+    }
+
+    public function uploadPrintDataApi(Request $request, DeliveryService $service): array
+    {
+        if (is_string($request['printData']))
+            $request['printData'] = json_decode($request['printData']);
+
+        $item = $service->customProcessing($request['printData']);
+        return ['success' => true, 'data' => $item];
+    }
+
+}

+ 3 - 49
app/Http/Controllers/PrintPartController.php

@@ -3,64 +3,18 @@
 namespace App\Http\Controllers;
 
 use App\Components\AsyncResponse;
-use App\Order;
-use App\PrintPart;
-use App\PrintTemplate;
 use Illuminate\Http\Request;
-use Illuminate\Support\Facades\Http;
 
 class PrintPartController extends Controller
 {
     use AsyncResponse;
-    public function index(Request $request)
-    {
-        $printParts = PrintPart::query()->paginate($request['paginate'] ?? 50);
-        return view('/maintenance/expressPrinting/part/index',compact('printParts'));
-    }
 
     public function create(Request $request)
     {
-        return view('/maintenance/expressPrinting/part/create');
+        // 废弃
+        //$imgPrintPart = app(PrintPartService::class)->getImagePart();
+        //return view('maintenance.expressPrinting.part.create',compact('imgPrintPart'));
     }
 
-    public function storeApi(Request $request): \Illuminate\Http\RedirectResponse
-    {
-        PrintPart::query()->create($request->all());
-        $this->success('添加成功');
-    }
-
-    public function destroyApi(Request $request)
-    {
-        $printPart = PrintPart::query()->find($request['id']);
-        $printPart->delete();
-        $this->success('删除成功');
-    }
 
-    public function print(Request $request)
-    {
-        $template = PrintTemplate::query()->where('name','test-快递单号打印')->first();
-        $items = Order::query()->whereIn('code',["SO190628000378","SO190628000347"])->with('packages')->get();
-        return view("maintenance.expressPrinting.print.index",compact("template",'items'));
-    }
-
-    public function printTemplateApi(Request $request)
-    {
-        $file = $request->file("blob");
-        $content = $file->getContent();
-        $content = base64_encode($content);
-        $files = $request->file("blobs");
-
-        $content = [
-            "type" => "print",
-            "aliasName"=>"admin",
-            "printerName"=>"admin123",
-            "content" => $content
-        ];
-        if(!$files)return Http::post("http://127.0.0.1:3000",$content);
-        $contents = [];
-        foreach ($files as $item) {
-            $contents[] = base64_encode($item->getContent());
-        }
-        return Http::post("http://127.0.0.1:3000",['file'=>$content,'files'=>$contents]);
-    }
 }

+ 64 - 0
app/Http/Controllers/PrintPartImageController.php

@@ -0,0 +1,64 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\PrintPartImage;
+use App\Services\PrintPartImageService;
+use App\UploadFile;
+use Illuminate\Http\Request;
+
+class PrintPartImageController extends Controller
+{
+
+    public function index(Request $request)
+    {
+        $printPartImages = PrintPartImage::query()->with('file')->paginate($request['paginate'] ?? 50);
+        return view('maintenance.expressPrinting.image.index', compact('printPartImages'));
+    }
+
+    /**
+     * 保存图片
+     * @param Request $request
+     * @param PrintPartImageService $service
+     * @return array
+     */
+    public function saveFileApi(Request $request, PrintPartImageService $service): array
+    {
+        $file = $request->file('file');
+        /** @var PrintPartImage $printPartImage */
+        $printPartImage = PrintPartImage::query()->with('file')->where('name', $request['name'])->first();
+        if (!$printPartImage) $printPartImage = PrintPartImage::query()->firstOrCreate(['name' => $request['name']]);
+        if ($printPartImage['file']) return ['success' => false, 'message' => '该需求已有描述图片'];
+        return $service->saveFile($printPartImage, $file);
+    }
+
+    /**
+     * 修改图片
+     * @param Request $request
+     * @param PrintPartImageService $service
+     * @return array
+     */
+    public function updateFileApi(Request $request, PrintPartImageService $service): array
+    {
+        $file = $request->file('file');
+        /** @var PrintPartImage $printPartImage */
+        $printPartImage = PrintPartImage::query()->with('file')->where('name', $request['name'])->first();
+        return $service->updateFile($printPartImage, $file);
+    }
+
+    /**
+     * 删除图片
+     * @param $id
+     * @param PrintPartImageService $service
+     * @return bool[]
+     * @throws \Exception
+     */
+    public function destroyApi($id, PrintPartImageService $service): array
+    {
+        $item = PrintPartImage::query()->with('file')->find($id);
+        UploadFile::query()->where(['table_name' => (new PrintPartImage())->getTable(), 'table_id' => $item['id']])->delete();
+        $item->delete();
+        return ['success' => true];
+    }
+
+}

+ 46 - 15
app/Http/Controllers/PrintTemplateController.php

@@ -3,47 +3,78 @@
 namespace App\Http\Controllers;
 
 use App\Components\AsyncResponse;
-use App\PrintPart;
+use App\Logistic;
+use App\Owner;
+use App\PrintPartImage;
 use App\PrintTemplate;
+use App\Services\PrintTemplateService;
 use Illuminate\Http\Request;
 
 class PrintTemplateController extends Controller
 {
     use AsyncResponse;
+
     public function index(Request $request)
     {
-        $templates = PrintTemplate::all();
-        return view('/maintenance/expressPrinting/template/index',compact('templates'));
+        $templates = PrintTemplate::query()->with(['ownerLogisticPrintTemplate' => function ($query) {
+            $query->with(['owner', 'logistic']);
+        }])->paginate($request['paginate'] ?? 50);
+        $owners = Owner::query()->get();
+        $logistics = Logistic::query()->get();
+        return view('maintenance.expressPrinting.template.index', compact('templates', 'owners', 'logistics'));
     }
 
-    public function create(Request $request)
+    public function create(PrintTemplateService $service)
     {
-        $printParts = PrintPart::all();
-        return view('/maintenance/expressPrinting/template/create',compact('printParts'));
+        $printParts = $service->getParts();
+        $printPartImages = PrintPartImage::query()->with('file')->get();
+        return view('maintenance.expressPrinting.template.create', compact('printParts', 'printPartImages'));
     }
 
+    public function edit($id, PrintTemplateService $service)
+    {
+        $template = PrintTemplate::query()->find($id);
+        $printParts = $service->getParts();
+        $printPartImages = PrintPartImage::query()->with('file')->get();
+        return view('maintenance.expressPrinting.template.edit', compact('template', 'printParts', 'printPartImages'));
+    }
 
     public function storeApi(Request $request)
     {
-        $data = [
-            'name' =>$request['name'],
-            'value'=>json_encode($request['value'])
-        ];
-        $printTemplate = PrintTemplate::query()->create($data);
+        $printTemplate = PrintTemplate::query()->create([
+            'name' => $request['name'],
+            'value' => $request['value']
+        ]);
         $this->success(['data' => $printTemplate]);
     }
 
-    public function updateApi(Request $request)
+    public function updateApi(Request $request): array
     {
-
+        $print_template = PrintTemplate::query()->find($request['id']);
+        $print_template->update([
+                'name' => $request['name'],
+                'value' => $request['value']]
+        );
+        return ['success' => true, 'data' => $print_template];
     }
 
-    public function destroyApi(Request $request)
+    public function destroyApi($id)
     {
-        $printTemplate = PrintTemplate::query()->find($request['id']);
+        $printTemplate = PrintTemplate::query()->find($id);
         $printTemplate->delete();
         $this->success('删除成功');
     }
 
+    // 保存 打印模板和 货主 承运商 之间的关系
+    public function saveRelationApi(Request $request, PrintTemplateService $service): array
+    {
+        $relations = $request['relations'];
+        $service->saveRelation($relations);
+        $item = PrintTemplate::query()->with(['ownerLogisticPrintTemplate' => function ($query) {
+            $query->with(['owner', 'logistic']);
+        }])->where('id',$request['template_id'])->first();
+        return ['success' => true, 'data' => $item];
+    }
+
 
 }

+ 73 - 0
app/Http/Controllers/TerminalController.php

@@ -0,0 +1,73 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Components\AsyncResponse;
+use App\Http\Requests\Printer\TerminalRequest;
+use App\Http\Requests\Request;
+use App\Terminal;
+use Illuminate\Support\Facades\Gate;
+
+class TerminalController extends Controller
+{
+    use AsyncResponse;
+
+    // index 页面
+    public function index(TerminalRequest $request)
+    {
+        if (Gate::denies('基础设置-快递打印-终端'))return redirect("/");
+
+        $terminals =  Terminal::query()->orderByDesc('id')->paginate($request['paginate'] ?? 50);
+
+        return view('maintenance.expressPrinting.setting.terminal.index',
+            compact('terminals')
+        );
+    }
+
+    public function storeApi(TerminalRequest  $request): array
+    {
+        if (Gate::denies('基础设置-快递打印-终端-添加'))
+            return ['success' => false, 'message' => '没有对应权限'];
+
+        $terminal = Terminal::query()->create($request->all());
+        return ['success' => true, 'data' => $terminal];
+    }
+
+    public function destroyApi($id): array
+    {
+        if (Gate::denies('基础设置-快递打印-终端-删除'))
+            return ['success' => false, 'message' => '没有对应权限'];
+
+        $terminal = Terminal::query()->find($id);
+        if (!$terminal) return ['success' => false , 'message' => '对应的终端不存在'];
+
+        $terminal->delete();
+        return ['success' => true];
+    }
+
+    public function updateApi(TerminalRequest $request): array
+    {
+        if (Gate::denies('基础设置-快递打印-终端-编辑'))
+            return ['success' => false, 'message' => '没有对应权限'];
+
+        $terminal = Terminal::query()->find($request['id']);
+        if (!$terminal) return ['success' => false, 'message' => '对应的终端不存在'];
+
+        $terminal->update($request->all());
+        return ['success' => true, 'data' => $terminal];
+    }
+
+    public function getTerminalApi($id) :array
+    {
+        $terminal = Terminal::query()->with('printers')->find($id);
+        if (!$terminal)return ['success' => false,'message' => 'was没有记录该ip下的打印机'];
+        return ['success' => true,'data' => $terminal];
+    }
+
+    public function getTerminalByIPApi(Request $request): array
+    {
+        $clientIp = $request->getClientIp();
+        $terminal = Terminal::query()->with('printers')->where('ip')->first();
+        return ['success' => true, 'data' => $terminal];
+    }
+}

+ 62 - 0
app/Http/Controllers/TerminalPrinterController.php

@@ -0,0 +1,62 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Requests\Printer\TerminalPrinterRequest;
+use App\Logistic;
+use App\Terminal;
+use App\TerminalPrinter;
+
+class TerminalPrinterController extends Controller
+{
+
+    public function index(TerminalPrinterRequest $request)
+    {
+//        if (!Gate::allows('基础设置-快递打印-打印机-添加')) return redirect('/');
+
+        $terminalPrinters = TerminalPrinter::query()->with('terminal','logistics')->orderByDesc('terminal_id')->orderByDesc('id')->paginate($request['paginate'] ?? 50);
+        $terminals =  Terminal::query()->get();
+
+        $logistics = Logistic::query()->get();
+
+        return view('maintenance/expressPrinting/setting/printer/index',
+            compact('terminalPrinters','terminals','logistics')
+        );
+    }
+
+    public function storeApi(TerminalPrinterRequest  $request): array
+    {
+//        if (!Gate::allows('基础设置-快递打印-打印机-添加'))
+//            return ['success' => false, 'message' => '没有对应权限'];
+
+        $terminal_printer = TerminalPrinter::query()->create($request->all());
+        $terminal_printer->logistics()->attach($request['logistic_ids']);
+        $terminal_printer->loadMissing('terminal','logistics');
+        return ['success' => true, 'data' => $terminal_printer];
+    }
+
+    public function destroyApi($id): array
+    {
+//        if (!Gate::allows('基础设置-快递打印-打印机-删除'))
+//            return ['success' => false, 'message' => '没有对应权限'];
+
+        $terminal_printer = TerminalPrinter::query()->find($id);
+        if (!$terminal_printer) return ['success' => false , 'message' => '对应的打印机不存在'];
+        $terminal_printer->logistics()->detach();
+        $terminal_printer->delete();
+        return ['success' => true];
+    }
+
+    public function updateApi(TerminalPrinterRequest $request): array
+    {
+//        if (!Gate::allows('基础设置-快递打印-打印机-编辑'))
+//            return ['success' => false, 'message' => '没有对应权限'];
+        $terminal_printer = TerminalPrinter::query()->find($request['id']);
+        if (!$terminal_printer) return ['success' => false, 'message' => '对应的打印机不存在'];
+        $terminal_printer->logistics()->sync($request['logistic_ids']);
+        $terminal_printer->update($request->all());
+        $terminal_printer->load(['terminal','logistics']);
+        return ['success' => true, 'data' => $terminal_printer];
+    }
+
+}

+ 4 - 0
app/Http/Controllers/TestController.php

@@ -46,6 +46,10 @@ use App\StationTaskMaterialBox;
 use App\Store;
 use App\TaskTransaction;
 use App\Unit;
+use App\User;
+use App\UserDetail;
+use App\UserDutyCheck;
+use App\ValueStore;
 use App\Waybill;
 use Carbon\Carbon;
 use Carbon\CarbonPeriod;

+ 30 - 0
app/Http/Requests/Printer/TerminalPrinterRequest.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Http\Requests\Printer;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class TerminalPrinterRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     *
+     * @return bool
+     */
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array
+     */
+    public function rules(): array
+    {
+        return [
+            //
+        ];
+    }
+}

+ 24 - 0
app/Http/Requests/Printer/TerminalRequest.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Requests\Printer;
+
+use App\Traits\RequestApiFormValidation;
+use Illuminate\Foundation\Http\FormRequest;
+
+class TerminalRequest extends FormRequest
+{
+    use RequestApiFormValidation;
+
+
+    public function authorize(): bool
+    {
+        return true;
+    }
+
+    public function rules(): array
+    {
+        return [
+
+        ];
+    }
+}

+ 1 - 1
app/OracleActAllocationDetails.php

@@ -22,7 +22,7 @@ class OracleActAllocationDetails extends Model
 
     public function oracleDocOrderHeader(): BelongsTo
     {
-        return $this->belongsTo('App\OracleDOCOrderHeader','orderno','orderno');
+        return $this->belongsTo(OracleDOCOrderHeader::class,'orderno','orderno');
     }
 
     public function oracleDocOrderSerialNos(): HasMany

+ 7 - 0
app/OracleDOCOrderHeader.php

@@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Model;
 
 use App\Traits\ModelLogChanging;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 
 class OracleDOCOrderHeader extends Model
 {
@@ -67,10 +68,16 @@ class OracleDOCOrderHeader extends Model
         return $this->hasOne('App\OracleBasCode','code','ordertype')->where('codeid','SO_TYP');
     }
 
+    public function docOrderDeliveryInfo(): HasMany
+    {
+        return $this->hasMany(OracleDocOrderDeliveryInfo::class,'orderno','orderno');
+    }
+
     public function oracleDOCWaveDetail(): BelongsTo
     {
         return $this->belongsTo(OracleDOCWaveDetails::class,'orderno','orderno');
     }
+
     public function getLogistic()
     {
         /** @var LogisticService $logistic_service */

+ 46 - 0
app/OracleDocOrderDeliveryInfo.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class OracleDocOrderDeliveryInfo extends Model
+{
+    use ModelLogChanging;
+
+    use ModelLogChanging;
+
+    protected $connection = "oracle";
+    protected $table = "DOC_ORDER_DELIVERYINFO";
+    public $timestamps = false;
+
+    protected $fillable = [
+        'orderno','trackingno','userdefine1','userdefine3','userdefine4','userdefine5',
+        'formtransit','fromcode','formdepot',
+        'toransitcode','presorting','tocode',
+        'toroute','tocity','servicetype','UDF08','QRCODE'
+    ];
+
+    protected $casts = [
+        'userdefine1' => 'array',
+    ];
+
+    public function docOrderHeader(): BelongsTo
+    {
+        return $this->belongsTo(OracleDOCOrderHeader::class,'orderno','orderno');
+    }
+
+    /**
+     * OrderNo        订单单号
+     * TrackingNo     快递单号
+     * userDefine1    打印组件参数
+     * userDefine2    打印组件参数
+     * userDefine3    解码
+     * userDefine4    打印组件解析模板url
+     * userDefine5    打印版本
+     */
+
+}

+ 26 - 0
app/OrderPackageExpressBillPrintRecords.php

@@ -0,0 +1,26 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class OrderPackageExpressBillPrintRecord extends Model
+{
+    use ModelLogChanging;
+    protected $table = 'order_package_express_bill_print_records';
+
+    protected $fillable = ['order_id','order_package_id','process_id','logistic_number','print_count','task_id'];
+
+    public function order(): BelongsTo
+    {
+        return $this->belongsTo(Order::class);
+    }
+
+    public function orderPackage(): BelongsTo
+    {
+        return $this->belongsTo(OrderPackage::class);
+    }
+}

+ 30 - 0
app/OwnerLogisticPrintTemplate.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class OwnerLogisticPrintTemplate extends Model
+{
+    use ModelLogChanging;
+    public $timestamps = false;
+    protected $fillable = ['owner_id','logistic_id','print_template_id'];
+
+    public function printTemplate(): BelongsTo
+    {
+        return $this->belongsTo(PrintTemplate::class);
+    }
+
+    public function owner():BelongsTo
+    {
+        return $this->belongsTo(Owner::class);
+    }
+
+    public function logistic():BelongsTo
+    {
+        return $this->belongsTo(Logistic::class);
+    }
+}

+ 25 - 0
app/PrintPartImage.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\HasOne;
+
+class PrintPartImage extends Model
+{
+    use ModelLogChanging;
+
+    protected $fillable = ['name'];
+
+    public function file(): HasOne
+    {
+        return $this->hasOne(UploadFile::class,'table_id','id')->where('table_name','print_part_images');
+    }
+
+    public function saveFile($file,$fileName){
+        $fileSuffix=strtolower($file->getClientOriginalExtension());
+        return UploadFile::query()->updateOrCreate(['table_name' => $this->getTable(), 'table_id' => $this['id'], 'url' => '/files/'.$fileName, 'type' => $fileSuffix]);
+    }
+}

+ 11 - 0
app/PrintTemplate.php

@@ -5,10 +5,21 @@ namespace App;
 use Illuminate\Database\Eloquent\Model;
 
 use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 
 class PrintTemplate extends Model
 {
     use ModelLogChanging;
 
     protected $fillable = ['name','value'];
+
+    protected $casts = [
+        'value' => 'array'
+    ];
+
+
+    public function ownerLogisticPrintTemplate(): HasMany
+    {
+        return $this->hasMany(OwnerLogisticPrintTemplate::class,'print_template_id','id');
+    }
 }

+ 26 - 0
app/Providers/AppServiceProvider.php

@@ -141,11 +141,21 @@ use App\Services\ForeignZhenCangService;
 use App\Services\StorageService;
 use App\Services\LogisticAliJiSuApiService;
 use App\Services\CommodityMaterialBoxModelService;
+use App\Services\PrintService;
+use App\Services\TerminalService;
+use App\Services\TerminalPrinterService;
 use App\Services\OwnerLogisticFeeDetailService;
 use App\Services\OwnerLogisticFeeReportService;
 use App\Services\LogisticSyncRecordService;
 use App\Services\OwnerBillReportArchiveService;
 use App\Services\SettlementBillsAreaFeeService;
+use App\Services\PDDDeliveryService;
+use App\Services\TBDeliveryService;
+use App\Services\SFDeliveryService;
+use App\Services\JDDeliveryService;
+use App\Services\SFQHDDeliveryService;
+use App\Services\DeliveryService;
+use App\Services\PrintPartImageService;
 use App\Services\OwnerStoreFeeDetailService;
 use App\Services\OwnerStoreFeeReportService;
 use App\Services\OwnerStoreOutFeeDetailService;
@@ -161,6 +171,8 @@ use App\Services\SettlementIndemnityFeeService;
 use App\Services\DbOpenService;
 use App\Services\DeliveryTypeService;
 use App\Services\ErrorPushService;
+use App\Services\OrderPackageExpressBillPrintRecordService;
+use App\Services\TerminalPrinterLogisticService;
 use App\Services\MaterialBoxModelService;
 use App\Services\HandInStorageService;
 use App\Services\RequirementService;
@@ -253,6 +265,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('DataHandlerService',DataHandlerService::class);
         app()->singleton('DbOpenService',DbOpenService::class);
         app()->singleton('DeliveryAppointmentService',DeliveryAppointmentService::class);
+        app()->singleton('DeliveryService',DeliveryService::class);
         app()->singleton('DeliveryTypeService',DeliveryTypeService::class);
         app()->singleton('DemandProcessService',DemandProcessService::class);
         app()->singleton('DemandService',DemandService::class);
@@ -271,6 +284,8 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('InventoryAccountMissionService',InventoryAccountMissionService::class);
         app()->singleton('InventoryCompareService', InventoryCompareService::class);
         app()->singleton('InventoryDailyLogService', InventoryDailyLogService::class);
+        app()->singleton('JDDeliveryService',JDDeliveryService::class);
+        app()->singleton('LaborCompanyService',LaborCompanyService::class);
         app()->singleton('LaborReportsCountingRecordService', LaborReportsCountingRecordService::class);
         app()->singleton('LogService', LogService::class);
         app()->singleton('LogisticAliJiSuApiService',LogisticAliJiSuApiService::class);
@@ -282,6 +297,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('LogisticYTOService', LogisticYTOService::class);
         app()->singleton('LogisticZopService', LogisticZopService::class);
         app()->singleton('LogisticZopService', LogisticZopService::class);
+        app()->singleton('LogisticZopService', LogisticZopService::class);
         app()->singleton('MaterialBoxModelService',MaterialBoxModelService::class);
         app()->singleton('MaterialBoxService', MaterialBoxService::class);
         app()->singleton('MenuService',MenuService::class);
@@ -303,6 +319,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('OrderPackageCommoditiesService', OrderPackageCommoditiesService::class);
         app()->singleton('OrderPackageCommoditySerialNumberService', OrderPackageCommoditySerialNumberService::class);
         app()->singleton('OrderPackageExceptionTypeCountingRecordService', OrderPackageExceptionTypeCountingRecordService::class);
+        app()->singleton('OrderPackageExpressBillPrintRecordService',OrderPackageExpressBillPrintRecordService::class);
         app()->singleton('OrderPackageReceivedSyncRecordService', OrderPackageReceivedSyncRecordService::class);
         app()->singleton('OrderPackageReceivedSyncService', OrderPackageReceivedSyncService::class);
         app()->singleton('OrderPackageRemarkService',OrderPackageRemarkService::class);
@@ -329,6 +346,7 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('OwnerReportService', OwnerReportService::class);
         app()->singleton('OwnerService', OwnerService::class);
         app()->singleton('OwnerStoragePriceModelService', OwnerStoragePriceModelService::class);
+        app()->singleton('PDDDeliveryService',PDDDeliveryService::class);
         app()->singleton('OwnerStoreFeeDetailService',OwnerStoreFeeDetailService::class);
         app()->singleton('OwnerStoreFeeReportService',OwnerStoreFeeReportService::class);
         app()->singleton('OwnerStoreOutFeeDetailService',OwnerStoreOutFeeDetailService::class);
@@ -338,7 +356,9 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('OwnerWaybillSettlementBillService',OwnerWaybillSettlementBillService::class);
         app()->singleton('PackageService', PackageService::class);
         app()->singleton('PackageStatisticsService', PackageStatisticsService::class);
+        app()->singleton('PrintPartImageService',PrintPartImageService::class);
         app()->singleton('PrintPartService',PrintPartService::class);
+        app()->singleton('PrintService',PrintService::class);
         app()->singleton('PrintTemplateService',PrintTemplateService::class);
         app()->singleton('ProcessMethodService', ProcessMethodService::class);
         app()->singleton('ProcessService', ProcessService::class);
@@ -356,6 +376,8 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('RequirementService',RequirementService::class);
         app()->singleton('RequirementUserService',RequirementUserService::class);
         app()->singleton('RoleService',RoleService::class);
+        app()->singleton('SFDeliveryService',SFDeliveryService::class);
+        app()->singleton('SFQHDDeliveryService',SFQHDDeliveryService::class);
         app()->singleton('SettlementBillsAreaFeeService',SettlementBillsAreaFeeService::class);
         app()->singleton('SettlementIndemnityFeeService',SettlementIndemnityFeeService::class);
         app()->singleton('ShopService', ShopService::class);
@@ -377,6 +399,10 @@ class AppServiceProvider extends ServiceProvider
         app()->singleton('StoreItemService', StoreItemService::class);
         app()->singleton('StoreService', StoreService::class);
         app()->singleton('SupplierService', SupplierService::class);
+        app()->singleton('TBDeliveryService',TBDeliveryService::class);
+        app()->singleton('TerminalPrinterLogisticService',TerminalPrinterLogisticService::class);
+        app()->singleton('TerminalPrinterService',TerminalPrinterService::class);
+        app()->singleton('TerminalService',TerminalService::class);
         app()->singleton('UnitService', UnitService::class);
         app()->singleton('UserOwnerGroupService', UserOwnerGroupService::class);
         app()->singleton('UserService', UserService::class);

+ 112 - 0
app/Services/DeliveryService.php

@@ -0,0 +1,112 @@
+<?php
+
+namespace App\Services;
+
+use App\OracleDocOrderDeliveryInfo;
+use App\OracleDOCOrderHeader;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Cache;
+
+class DeliveryService
+{
+
+    public function getDelivery($printStr): array
+    {
+        list($batchCodes, $orderCodes, $logisticNumbers) = $this->conversionPrintData($printStr);
+
+        if ($batchCodes) {
+            $orderHeaders = OracleDOCOrderHeader::query()->selectRaw('orderno')->whereIn('WaveNo', $batchCodes)->get()->toArray();
+            $orderCodes = array_unique(array_merge($orderCodes, array_column($orderHeaders, 'orderno')));
+        }
+
+        if ($orderCodes) {
+            $deliveryInfo = OracleDocOrderDeliveryInfo::query()->selectRaw('trackingNo')->whereIn('orderno', $orderCodes)->get()->toArray();
+            $deliveryNos = OracleDOCOrderHeader::query()->selectRaw('deliveryno')->whereIn('orderno', $orderCodes)->get()->toArray();
+            $logisticNumbers = array_unique(array_merge($logisticNumbers, array_column($deliveryInfo, 'trackingno'), array_column($deliveryNos, 'deliveryno')));
+        }
+
+        $OracleDocOrderDeliveryInfos = OracleDocOrderDeliveryInfo::query()->with('docOrderHeader.docOrderDeliveryInfo')->whereIn('trackingNo', $logisticNumbers)->get();
+
+        $tbParams = app(TBDeliveryService::class)->getDeliveryInfo($OracleDocOrderDeliveryInfos);
+
+        $pddParams = app(PDDDeliveryService::class)->getDeliveryInfo($OracleDocOrderDeliveryInfos);
+
+        $sfParams = app(SFDeliveryService::class)->getDeliveryInfo($OracleDocOrderDeliveryInfos);
+
+        $jdParams = app(JDDeliveryService::class)->getDeliveryInfo($logisticNumbers);
+
+        $sfQhdParams = app(SFQHDDeliveryService::class)->getDeliveryInfo($logisticNumbers);
+
+        return array_merge($tbParams, $pddParams, $sfParams, $jdParams, $sfQhdParams);
+    }
+
+    public function conversionPrintData($printStr): array
+    {
+        preg_match_all('/[\w]+/', $printStr, $nos);
+
+        foreach ($nos[0] as $no) {
+            if (strstr($no, 'SO') || strstr($no, 'so')) $orderCodes[] = $no;
+            elseif (strstr($no, 'W') || strstr($no, 'w')) $batchesCodes[] = $no;
+            else $logisticNumbers[] = $no;
+        }
+        return [$batchesCodes ?? [], $orderCodes ?? [], $logisticNumbers ?? []];
+    }
+
+
+    /**
+     * 快递面单填充自定义区域内容 或 自制面单
+     * @param array $items
+     * @return array
+     */
+    public function customProcessing(array $items): array
+    {
+        foreach ($items as $key => &$item) {
+            if ($item['is_process'] == true) continue;
+            switch ($item['component_type']) {
+                case 'TB':
+                    $items[$key] = app(TBDeliveryService::class)->processing($item);
+                    break;
+                case 'PDD':
+                    $items[$key] = app(PDDDeliveryService::class)->processing($item);
+                    break;
+                default:
+                    break;
+            }
+        }
+        return $items;
+    }
+
+    /**
+     * 根据缓存删除十分钟前的文件
+     */
+    public function destroyFileOfCache()
+    {
+        if (Cache::has('print-template-file-list')) {
+            $arr = Cache::get('print-template-file-list');
+            $now = Carbon::now();
+            foreach ($arr as $key => $item) {
+                $time = (new Carbon($item['time']))->addMilliseconds(10);
+                if ($now->lt($time)) {
+                    if (file_exists($item['path'])) {
+                        unlink($item['path']);
+                    }
+                    unset($arr[$key]);
+                }
+            }
+            Cache::put('print-template-file-list', $arr);
+        }
+    }
+
+    public function pushClearCache($path)
+    {
+        if (!Cache::has("print-template-file-list")){
+            Cache::put('print-template-file-list',[$path]);
+            return;
+        }
+        $arr = Cache::get("print-template-file-list");
+        $arr[] = $path;
+        Cache::put('print-template-file-list',$arr);
+    }
+
+
+}

+ 165 - 0
app/Services/Express/CaiNiaoExpress.php

@@ -0,0 +1,165 @@
+<?php
+
+
+namespace App\Services\Express;
+
+
+use Illuminate\Support\Facades\Http;
+
+class CaiNiaoExpress implements expressinterface
+{
+
+    private $app_key = '';
+    private $sign_method = '';
+    private $target_app_key = '';
+
+    private $searchurl = '';
+    private $getelectronicsingleurl = '';
+    private $cancelelectronicsingleurl = '';
+    private $updateelectronicsingleurl = '';
+
+    /**
+     * @inheritdoc
+     */
+    function searchbalance()
+    {
+        $response = http::get($this->searchurl,[
+            'cp_code' => "",         //  物流服务商编码 require
+            'shipping_address' => [
+                'area' => '',        // 区地址
+                'province' => '',    // 省地址 require
+                'town' => '',        // 街道\镇名(四级地址)
+                'address_detail' => '',// 详细地址 require
+                'city' => '', // 市级
+            ],
+            // 订单数据
+            'trade_order_info_cols' => [
+                'consignee_name' => '', // 收货人 require
+                'order_channels_type' => '', // 订单渠道 require
+                'trade_order_list' => [], // 交易订单列表 require
+                // 收\发货地址
+                'consignee_address' => [
+                    'area' => '',        // 区地址
+                    'province' => '',    // 省地址 require
+                    'town' => '',        // 街道\镇名(四级地址)
+                    'address_detail' => '',// 详细地址 require
+                    'city' => '', // 市级
+                ],
+                'send_phone' => '', // 发货人联系方式
+                'weight' => '',     // 包裹重量
+                'send_name' => '',   // 发货人姓名
+                // 包裹里面的商品名称 require
+                'package_items' => [
+                    [
+                        'item_name' => '', // 商品名称  require
+                        'count' => '',     // 商品数量  require
+                    ]
+                ],
+                'logistics_service_list' => [
+                    [
+                        'service_value4_json' => '', // 服务类型值,json格式标识
+                        'service_code' => ''    // 服务编码
+                    ]
+                ],
+                'product_type' => '',  // 快递服务产品类型编码
+                'real_user_id' => '',    // 使用者id
+                'volume' => '',    // 包裹体积(立方厘米)
+                'package_id' => '', // 包裹号(或者erp订单号)
+            ]
+        ]);
+
+    }
+
+    /**
+     * @inheritdoc
+     */
+    function getelectronicsingle()
+    {
+    }
+
+    /**
+     * @inheritdoc
+     */
+    function cancelelectronicsingle()
+    {
+    }
+
+    function searElectronicSingle()
+    {
+        // TODO: Implement searElectronicSingle() method.
+    }
+
+    /**
+     * @inheritdoc
+     */
+    function saveelectronicsingle()
+    {
+    }
+
+    /**
+     * @inheritdoc
+     */
+    function updateelectronicsingle()
+    {
+    }
+
+
+    private $response = [
+        [
+            'short_address' => '', // 根据收货地址返回大头笔信息
+            // 面单对应的订单列
+            'trade_order_info' => [
+                'item_name' => '', // 商品名称
+                'consignee_name' => '', // 收货人
+                'ali_order' => '', //是否阿里系订单
+                'short_address' => '' , // 大头笔
+                'order_channels_type' => '', // 订单渠道
+                // 交易订单列表
+                'trade_order_list' => [],
+                'waybill_code' => '', // 面单号
+                'consignee_phone' => '', // 收货人联系方式
+                // 收货人地址
+                'consignee_address' => [
+                    'area' => '',        // 区地址
+                    'province' => '',    // 省地址 require
+                    'town' => '',        // 街道\镇名(四级地址)
+                    'address_detail' => '',// 详细地址 require
+                    'city' => '',       // 市级
+                ],
+                'send_phone' => '',    // 发货人联系方式
+                'weight' => '',        // 包裹重量(克)
+                'send_name' => '',     // 发货人姓名
+                'order_type' => '' ,   // 订单渠道来源
+                'package_items' => [
+                    [
+                        'item_name' => '',    // 商品名称
+                        'count' => '' ,       // 商品数量
+                    ]
+                ],
+                'logistics_service_list' => [
+                    [
+                        [
+                            'service_value4_json' => '', // 服务类型值,json格式表示
+                            'service_code' => '',       // 服务编码
+                        ]
+                    ]
+                ],
+                'product_type' => '',    // 快递服务产品类型编码
+                'real_user_id' => '',    // 使用者id
+                'volume' => '',          // 包裹体积(立方厘米)
+                'package_id' => '',      // 包裹号(或者erp订单号)
+            ],
+            'waybill_code' => '',        // 返回的面单号
+            'package_center_code' => '', // 集包地代码
+            'package_center_name' => '', // 集包地名称
+            'print_config' => '',        // 打印配置项,传给ali-print组件
+            'shipping_branch_code' => '',// 面单号对应的物流服务商网点(分支机构)代码
+            'consignee_branch_name' => '',// 包裹对应的派件(收件)物流服务商网点(分支机构)名称
+            'shipping_branch_name' => '', // 面单号对于的物流服务商网点(分支机构)名称
+            'consignee_branch_code' => '',// 包裹对应的派件(收件)物流服务商网点(分支机构)代码
+
+        ]
+    ];
+
+
+}

+ 52 - 0
app/Services/Express/ExpressInterface.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Services\Express;
+
+interface ExpressInterface
+{
+
+    /**
+     * 查询余额
+     *
+     * @return mixed
+     */
+    function searchBalance();
+
+    /**
+     * 获取快递面单
+     *
+     * @return mixed
+     */
+    function getElectronicSingle();
+
+    /**
+     * 取消快递面单
+     *
+      * @return mixed
+     */
+    function cancelElectronicSingle();
+
+
+    /**
+     * 查询面单
+     *
+     * @return mixed
+     */
+    function searElectronicSingle();
+
+    /**
+     * 对获取的快递面单进行保存
+     *
+     * @return mixed
+     */
+    function saveElectronicSingle();
+
+
+    /**
+     * 更新快递信息
+     * @return mixed
+     */
+    function updateElectronicSingle();
+
+
+}

+ 68 - 0
app/Services/Express/PDDExpress.php

@@ -0,0 +1,68 @@
+<?php
+
+
+namespace App\Services\Express;
+
+
+class PDDExpress implements ExpressInterface
+{
+    // TODO 目前获取快递单号请求参数
+
+    private $app_key = '';
+    private $sign_method = '';
+    private $target_app_key = '';
+
+    private $searchUrl = '';
+    private $getElectronicSingleUrl = '';
+    private $cancelElectronicSingleUrl = '';
+    private $updateElectronicSingleUrl = '';
+
+    /**
+     * @inheritDoc
+     */
+    function searchBalance()
+    {
+    }
+
+    /**
+     * @inheritDoc
+     */
+    function getElectronicSingle()
+    {
+        // TODO: Implement getElectronicSingle() method.
+    }
+
+    /**
+     * @inheritDoc
+     */
+    function cancelElectronicSingle()
+    {
+        // TODO: Implement cancelElectronicSingle() method.
+    }
+
+    function searElectronicSingle()
+    {
+        // TODO: Implement searElectronicSingle() method.
+    }
+
+    /**
+     * @inheritDoc
+     */
+    function saveElectronicSingle()
+    {
+        // TODO: Implement saveElectronicSingle() method.
+    }
+
+    /**
+     * @inheritDoc
+     */
+    function updateElectronicSingle()
+    {
+        // TODO: Implement updateElectronicSingle() method.
+    }
+
+    private $response = [
+
+    ];
+
+}

+ 33 - 0
app/Services/Interfaces/DeliveryInterface.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace App\Services\Interfaces;
+
+
+use Illuminate\Support\Str;
+
+interface DeliveryInterface
+{
+
+    /*
+     *     'type' => 'CAINIAO',              // 需要前台过沟通的组件
+     *     'task_id' => Str::uuid()          // 组件打印任务id
+     *     'is_process' => false,            // is_process 是否已通过自定义模板处理
+     *     'data' => $item['userdefine1'],   // 组件所需要的数据
+     *     'component_type' => 'TB',         // 富勒下发的接口
+     *     'owner_code' => h                 // 货主
+     *     'logistic_code' =>                // 承运商
+     *     'logistic_number' =>              // 快递单号
+     *     'delivery' => $this->getDelivery($item),
+     *     'base64' =>                       // 快递面单的base64编码
+     */
+    function getDeliveryInfo($oracleDocOrderDeliveryInfos);
+
+    function getDelivery($item);
+
+    /**
+     * 获取打印图片的base64编码
+     * @param $item
+     * @return mixed
+     */
+    function getBase64($item);
+}

+ 95 - 0
app/Services/JDDeliveryService.php

@@ -0,0 +1,95 @@
+<?php
+
+namespace App\Services;
+
+use App\OracleDocOrderDeliveryInfo;
+use App\OracleDOCOrderHeader;
+use App\OwnerLogisticPrintTemplate;
+use App\Services\Interfaces\DeliveryInterface;
+use App\Traits\DeliveryProcess;
+use App\Traits\DrawImage;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Support\Str;
+
+class JDDeliveryService implements DeliveryInterface
+{
+    use DeliveryProcess;
+
+    function getDeliveryInfo($logistic_number): array
+    {
+        $oracleDocOrderHeaders = OracleDOCOrderHeader::query()
+            ->with('oracleBASCustomer','oracleDOCWaveDetail')
+            ->whereIn('deliveryno',$logistic_number)
+            ->whereIn('consigneeId',['JD','BSZX','BSZFC','BSZXDF','BSZFCDF'])
+            ->get();
+        return $oracleDocOrderHeaders->map(function ($item){
+            return [
+                'type' => 'JD',
+                'is_process' => true,
+                'task_id' => Str::uuid(),
+                'data' => '',
+                'component_type' => 'JD',
+                'owner_code' => $item->customerid ?? '',
+                'logistic_code' => $item->userdefine1 ?? '',
+                'logistic_number' => $item['deliveryno'],
+                'delivery' => null,
+                'base64' => $this->getBase64($item),
+                'printerName' => '',
+            ];
+        })->toArray();
+    }
+
+    public function getBase64($item): string
+    {
+        $printTemplate = OwnerLogisticPrintTemplate::query()->with('printTemplate')->where('owner_id', function(Builder $query)use($item){
+            $query->from("owners")->selectRaw('id')->where("code",$item['customerid']);
+        })->where('logistic_id', function(Builder $query)use($item){
+            $query->from("logistics")->selectRaw('id')->where("code",$item['userdefine1']);
+        })->first();
+
+        $delivery = $this->getDelivery($item);
+
+        $image = $this->draw($delivery,$printTemplate->printTemplate ?? null,null);
+
+        if (!$image) return '';
+
+        $path = '';
+
+        $this->saveImage($image,$path);
+
+        return $this->readImageBase64($path);
+    }
+
+    /*
+     * 订单号
+     * 波次号
+     * 运单号
+     * 店铺
+     * 商家联系号
+     * 商家订单号
+     * 发货地址,省,市区
+     *
+     * 发货分拣中心
+     * 发货人
+     * 发货人号码
+     * 收货地址
+     * 收货分拣中心
+     * 收件人
+     * 商品
+     * 备注
+     *
+     */
+
+    function getDelivery($item): array
+    {
+        $delivery = $this->getDocOrderInfo($item);
+        $delivery['b_addresss1'] = $item['MJ-ZP'];
+        $delivery['d_addresss1'] = $item['BB-Y3-21'];
+        return $delivery;
+    }
+
+    function processing(&$params)
+    {
+        return null;
+    }
+}

+ 36 - 0
app/Services/OrderPackageExpressBillPrintRecordService.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Services;
+
+use App\OrderPackage;
+use App\Traits\ServiceAppAop;
+use App\OrderPackageExpressBillPrintRecord;
+use Illuminate\Support\Facades\Auth;
+
+class OrderPackageExpressBillPrintRecordService
+{
+    use ServiceAppAop;
+    protected $modelClass=OrderPackageExpressBillPrintRecord::class;
+
+    // 添加 快递面单打印记录
+    public function expressBillPrintRecord($logisticNumber,$task_id): array
+    {
+        $orderPackage =  OrderPackage::query()->with('order')->where('logistic_number',$logisticNumber)->first();
+        $data = ['logistic_number' => $logisticNumber];
+        if ($orderPackage){
+            $data['order_id'] = $orderPackage['order']['id'];
+            $data['order_package_id'] = $orderPackage['id'];
+        }
+
+        $printCount = OrderPackageExpressBillPrintRecord::query()->where('logistic_number',$logisticNumber)->max("print_count");
+        if ($printCount == 0) $printCount = 1;
+        else $printCount ++;
+        $data['print_count'] = $printCount;
+        $data['task_id'] = $task_id;
+
+        $data['process_id'] = Auth::user()['id'];
+        OrderPackageExpressBillPrintRecord::query()->create($data);
+        return ['success' => true];
+    }
+
+}

+ 72 - 0
app/Services/PDDDeliveryService.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Services;
+
+use App\OwnerLogisticPrintTemplate;
+use App\Services\Interfaces\DeliveryInterface;
+use App\Traits\DeliveryProcess;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Support\Facades\File;
+use Illuminate\Support\Str;
+
+class PDDDeliveryService implements DeliveryInterface
+{
+    use DeliveryProcess;
+
+    function getDeliveryInfo($OracleDocOrderDeliveryInfos): ?array
+    {
+        $items = $OracleDocOrderDeliveryInfos->filter(function($item){
+            return $item->docOrderHeader->consigneeid == 'PDD' || $item->docOrderHeader['h_edi_01'] == 'PDD';
+        });
+
+        return $items->map(function($item) {
+            return [
+                // TODO 需要动态获取面单获取的组件 TYPE
+                'type' => 'PDD',
+                'task_id' => Str::uuid(),
+                'is_process' => false,
+                'data' => $item['userdefine1'],
+                'component_type' => 'PDD',
+                'owner_code' => $item->docOrderHeader->customerid ?? '',
+                'logistic_code' => $item->docOrderHeader->userdefine1 ?? '',
+                'logistic_number' => $item['trackingno'],
+                'delivery' => $this->getDelivery($item),
+                'base64' => null,
+            ];
+        })->toArray();
+    }
+
+    public function getDelivery($item): array
+    {
+        return $this->getDocOrderDeliveryInfo($item);
+    }
+
+    public function getBase64($img = null, $item = null): ?string
+    {
+        if (!$img) return null;
+        $imgName = 'PDD-' . $item['logistic_code'] . 'png';
+        $path = storage_path(['app/public/files/' . $imgName]);
+        $img->save($path);
+        return File::get($path);
+    }
+
+    function processing(&$item)
+    {
+        $item['is_process'] = true;
+        $PrintTemplate = OwnerLogisticPrintTemplate::query()->with('printTemplate')->where('owner_id', function (Builder $query) use ($item) {
+            $query->from('owners')->selectRaw('id')->where("code", $item['owner_code']);
+        })->where('logistic_id', function (Builder $query) use ($item) {
+            $query->from('logistics')->selectRaw('id')->where("code", $item['logistic_code']);
+        })->first();
+
+        if (!$item) return $item;
+        $path = '';
+        $img = $this->getImage($item['base64'], $path);
+        app(DeliveryService::class)->pushClearCache($path);
+        $image = $this->draw($item['delivery'], $PrintTemplate, $img);
+        $this->saveImage($image, $path);
+        $item['base64'] = $this->readImageBase64($path);
+        app(DeliveryService::class)->pushClearCache($path);
+        return $item;
+    }
+}

+ 75 - 0
app/Services/PrintPartImageService.php

@@ -0,0 +1,75 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\PrintPartImage;
+use App\UploadFile;
+use Ramsey\Uuid\Uuid;
+
+class PrintPartImageService
+{
+    use ServiceAppAop;
+
+    protected $modelClass = PrintPartImage::class;
+
+    /**
+     * 保存上传文件
+     * @param PrintPartImage $model
+     * @param $file
+     * @return array
+     */
+    public function saveFile(PrintPartImage $model, $file): array
+    {
+        $result = $this->saveImage($file,$model);
+        if (!$result['success'])return $result;
+        $model->saveFile($file,$result['fileName']);
+        $model->load('file');
+        return ['success' => true, 'message' => '文件上传成功', 'data' => $model ];
+    }
+
+    /**
+     * 保存上传图片
+     * @param $file
+     * @param $model
+     * @return array
+     */
+    public function saveImage($file,$model): array
+    {
+        $tmpFil = $file->getRealPath();
+        if (!$file) return ['success' => false, 'message' => '上传图片为找到'];
+        if (!$file->isValid()) return ['success' => false, 'message' => '找不到上传图片'];
+        if (!is_uploaded_file($tmpFil)) return ['success' => false, 'message' => '文件错误'];
+        if ($file->getSize() > 5 * 1024 * 1024) return ['success' => false, 'message' => '文件不能大于5MB'];
+
+        $path = ['app', 'public', 'files'];
+        $path = join(DIRECTORY_SEPARATOR, $path);
+        $dirPath = storage_path($path);
+        if (!file_exists($dirPath)) {
+            mkdir($dirPath);
+        }
+        $fileSuffix = $file->getClientOriginalExtension();
+        $fileName = $model['name'].date('ymd').'-'.Uuid::uuid1();//thumbnail common bulky
+        if (!in_array($fileSuffix, ['jpeg', 'jpg', 'png', 'svg']))
+            return ['success' => false, 'message' => '上传的文件格式应为jpeg,jpg,png,svg'];
+        $filePath = storage_path($path.DIRECTORY_SEPARATOR . $fileName .'.'. $fileSuffix);
+        $result = move_uploaded_file($tmpFil, $filePath);
+        if ($result) return ['success' => true,'fileName' => $fileName];
+        else return ['success' => false, 'message' => '文件上传失败'];
+    }
+
+    /**
+     * 修改打印图片
+     * @param PrintPartImage $model
+     * @param $file
+     * @return array
+     */
+    public function updateFile(PrintPartImage $model,$file): array
+    {
+        $result = $this->saveImage($file,$model);
+        if (!$result['success'])return $result;
+        UploadFile::query()->where(['table_name' => 'print_part_images','table_id' => $model['id']])->update(['url' => '/files/'.$result['fileName'], 'type' => $file->getClientOriginalExtension()]);
+        $model->refresh();
+        return ['success' => true, 'message' => '文件上传成功', 'data' => $model ];
+    }
+}

+ 5 - 4
app/Services/PrintPartService.php

@@ -1,13 +1,14 @@
-<?php 
+<?php
 
 namespace App\Services;
 
+use App\Traits\ModelSearchWay;
 use App\Traits\ServiceAppAop;
 use App\PrintPart;
 
 class PrintPartService
 {
+    use ModelSearchWay;
     use ServiceAppAop;
-    protected $modelClass=PrintPart::class;
-
-}
+    protected $modelClass = PrintPart::class;
+}

+ 8 - 0
app/Services/PrintService.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace App\Services;
+
+class PrintService
+{
+
+}

+ 113 - 3
app/Services/PrintTemplateService.php

@@ -1,13 +1,123 @@
-<?php 
+<?php
 
 namespace App\Services;
 
+use App\OwnerLogisticPrintTemplate;
 use App\Traits\ServiceAppAop;
 use App\PrintTemplate;
 
 class PrintTemplateService
 {
     use ServiceAppAop;
-    protected $modelClass=PrintTemplate::class;
 
-}
+    protected $modelClass = PrintTemplate::class;
+
+    public function saveRelation($relations)
+    {
+        $ownerLogisticPrintTemplate = OwnerLogisticPrintTemplate::query()->where('print_template_id',$relations[0]['print_template_id'])->get()->toArray();
+
+        // 不存在的
+        $params = array_udiff_assoc($relations,$ownerLogisticPrintTemplate,function($itemA,$itemB){
+            return ($itemA['owner_id'] === $itemB['owner_id'] && $itemA['logistic_id'] === $itemB['logistic_id'] ) ? 0:1;
+        });
+        // 存在的
+        $deleteParams = array_udiff_assoc($ownerLogisticPrintTemplate,$relations,function($itemA,$itemB){
+            return ($itemA['owner_id'] === $itemB['owner_id'] && $itemA['logistic_id'] === $itemB['logistic_id'] ) ? 0:1;
+        });
+        OwnerLogisticPrintTemplate::query()->insert($params);
+        foreach ($deleteParams as $deleteParam){
+            $item = OwnerLogisticPrintTemplate::query()->where(['print_template_id'=>$deleteParam['print_template_id'],'owner_id'=>$deleteParam['owner_id'],'logistic_id'=>$deleteParam['logistic_id']])->first();
+            $item->delete();
+        }
+    }
+
+
+    public function getParts(): array
+    {
+        return [
+            [
+                'name' => '背景',
+                'value' => $this->getBg()
+            ],
+            [
+                'name' => '文本框',
+                'value' => $this->getTextBox()
+            ],
+            [
+                'name' => '条纹码',
+                'value' => $this->getStripeCode()
+            ],
+            [
+                'name' => '二维码',
+                'value' => $this->getQrCode()
+            ],
+            [
+                'name' => '图片',
+                'value' => $this->getImage()
+            ]
+        ];
+    }
+
+    private function getTextBox(): array
+    {
+        return [
+            'type' => 'textBox',
+            'border-style' => 'none',
+            'border-width' => 1,
+            'font-size' => 12,
+            'width' => 250,
+            'height' => 50,
+            'left' => '',
+            'top' => '',
+            'white-space' => 'pre',
+            'justify-content' => 'center',
+            // flex-start 开头|flex-end 结尾|center 居中|space-between|space-around|initial|inherit;
+            'align-items' => 'center'
+            // stretch 拉伸平铺|center居中|flex-start 容器开头|flex-end容器结尾|baseline容器基线|initial|inherit
+        ];
+    }
+
+    private function getStripeCode(): array
+    {
+        return [
+            'type' => 'stripeCode',
+            'width' => 404,
+            'left' => 100,
+            'top' => 0,
+            'scale' => 1,
+        ];
+    }
+
+    private function getBg(): array
+    {
+        return [
+            'type' => 'bg',
+            'width' => 600,
+            'height' => 900,
+        ];
+    }
+
+    private function getQrCode(): array
+    {
+        return [
+            'type' => 'qRCode',
+            'width' => 100,
+            'height' => 100,
+            'left' => 0,
+            'top' => 0,
+        ];
+    }
+
+    private function getImage(): array
+    {
+        return [
+            'type' => 'image',
+            'width' => 100,
+            'height' => 100,
+            'left' => 0,
+            'top' => 0,
+            'value' => 'none',
+            'scale' => 1,
+        ];
+    }
+}

+ 67 - 0
app/Services/SFDeliveryService.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace App\Services;
+
+use App\OwnerLogisticPrintTemplate;
+use App\Services\Interfaces\DeliveryInterface;
+use App\Traits\DeliveryProcess;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Support\Str;
+
+class SFDeliveryService implements DeliveryInterface
+{
+    use DeliveryProcess;
+
+    function getDeliveryInfo($OracleDocOrderDeliveryInfos): array
+    {
+        $items = $OracleDocOrderDeliveryInfos->filter(function($item){
+            $consigned = $item->docOrderHeader['carrierid'];
+            if ((strpos($consigned,'SF') != false ) && (!in_array($consigned,['SFSYQHD','SFTHQHD']))){
+                return true;
+            }
+            return false;
+        });
+        return $items->map(function($item) {
+            $delivery = $this->getDocOrderDeliveryInfo($item);
+            $item['delivery'] = $delivery;
+            return [
+                // TODO 需要动态获取面单获取的组件 TYPE
+                'type' => 'SF',
+                'task_id' => Str::uuid(),
+                'is_process' => true,
+                'data' => $item->docOrderHeader['user'],
+                'component_type' => 'SF',
+                'owner_code' => $item->docOrderHeader->customerid ?? '',
+                'logistic_code' => $item->docOrderHeader->userdefine1 ?? '',
+                'logistic_number' => $item['trackingno'],
+                'delivery' => $this->getDelivery($item),
+                'base64' => $this->getBase64($item),
+                'printerName' => '',
+            ];
+        })->toArray();
+    }
+
+    public function getBase64($item): string
+    {
+        $item = OwnerLogisticPrintTemplate::query()->with('printTemplate')->where('owner_id', function(Builder $query)use($item){
+            $query->from('owners')->selectRaw('id')->where("code",$item['customerid']);
+        })->where('logistic_id', function(Builder $query)use($item){
+            $query->from('logistics')->selectRaw('id')->where("code",$item['userdefine1']);
+        })->first();
+
+        if (!$item) return '';
+
+        $image = $this->draw($item['delivery'],$item,null);
+        $path = '';
+        $this->saveImage($image,$path);
+        app(DeliveryService::class)->pushClearCache($path);
+        return $this->readImageBase64($path);
+    }
+
+    function getDelivery($item): array
+    {
+        return $this->getDocOrderDeliveryInfo($item);
+    }
+
+
+}

+ 70 - 0
app/Services/SFQHDDeliveryService.php

@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Services;
+
+
+use App\OracleDOCOrderHeader;
+use App\OwnerLogisticPrintTemplate;
+use App\Services\Interfaces\DeliveryInterface;
+use App\Traits\DeliveryProcess;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Support\Str;
+
+class SFQHDDeliveryService implements DeliveryInterface
+{
+
+    use DeliveryProcess;
+
+    function getDeliveryInfo($logistic_number): array
+    {
+        $oracleDocOrderHeaders = OracleDOCOrderHeader::query()
+            ->whereIn('deliveryNo', $logistic_number)
+            ->whereIn('ConsigneeId', ["SFQHD",'SFSYQHD','SFTHQHD'])
+            ->get();
+
+        return$oracleDocOrderHeaders->map(function ($item) {
+                return [
+                    // TODO 需要动态获取面单获取的组件 TYPE
+                    'type' => 'SFQHD',
+                    'task_id' => Str::uuid(),
+                    'data' => '',
+                    'component_type' => 'SFQHD',
+                    'owner_code' => $item->customerid ?? '',
+                    'logistic_code' => $item->userdefine1 ?? '',
+                    'logistic_number' => $item['deliveryno'],
+                    'delivery' => $this->getDelivery($item),
+                    'base64' => $this->getBase64($item),
+                    'printerName' => '',
+                ];
+            })->toArray();
+    }
+
+    public function getBase64($item)
+    {
+        $item = OwnerLogisticPrintTemplate::query()->with('printTemplate')->where('owner_id', function (Builder $query) use ($item) {
+            $query->from("owners")->selectRaw('id')->where("code", $item['customerid']);
+        })->where('logistic_id', function (Builder $query) use ($item) {
+            $query->from("logistic")->selectRaw('id')->where("code", $item['userdefine1']);
+        })->first();
+        $image = $this->draw($item['delivery'], $item, null);
+        $path = '';
+        $this->saveImage($image, $path);
+        app(DeliveryService::class)->pushClearCache($path);
+        return $this->readImageBase64($path);
+    }
+
+    function getDelivery($item): array
+    {
+        return $this->getDocOrderInfo($item);
+    }
+
+    function processing(&$params)
+    {
+
+    }
+
+    function construct($param)
+    {
+
+    }
+}

+ 71 - 0
app/Services/TBDeliveryService.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace App\Services;
+
+use App\OwnerLogisticPrintTemplate;
+use App\Services\Interfaces\DeliveryInterface;
+use App\Traits\DeliveryProcess;
+use Illuminate\Database\Query\Builder;
+use Illuminate\Support\Str;
+
+class TBDeliveryService implements DeliveryInterface
+{
+    use DeliveryProcess;
+
+    function getDeliveryInfo($OracleDocOrderDeliveryInfos): array
+    {
+        $items = $OracleDocOrderDeliveryInfos->filter(function ($item) {
+            return in_array($item->docOrderHeader->consigneeid, ['TB', 'TMMK', 'TM']) || $item->docOrderheader['h_edi_01'] == '幼岚天猫超市';
+        });
+
+        return $items->map(function ($item) {
+            return [
+                // TODO 需要动态获取面单获取的组件 TYPE
+                'type' => 'CAINIAO',     // PDD 或 CAINIAO  动态
+                'task_id' => Str::uuid(),
+                'is_process' => false,
+                'data' => $item['userdefine1'],
+                'component_type' => 'TB',
+                'owner_code' => $item->docOrderHeader->customerid ?? '',
+                'logistic_code' => $item->docOrderHeader->userdefine1 ?? '',
+                'logistic_number' => $item['trackingno'],
+                'delivery' => $this->getDelivery($item),
+                'base64' => null,
+                'printerName' => '',
+            ];
+        })->toArray();
+    }
+
+    public function getBase64($item)
+    {
+
+    }
+
+
+    function getDelivery($item): array
+    {
+        return $this->getDocOrderDeliveryInfo($item);
+    }
+
+    function processing(array &$item)
+    {
+        $item['is_process'] = true;
+
+        $PrintTemplate = OwnerLogisticPrintTemplate::query()->with('printTemplate')->where('owner_id', function (Builder $query) use ($item) {
+            $query->from('owners')->selectRaw('id')->where("code", $item['owner_code']);
+        })->where('logistic_id', function (Builder $query) use ($item) {
+            $query->from('logistics')->selectRaw('id')->where("code", $item['logistic_code']);
+        })->first();
+
+        if (!$item) return $item;
+        $path = '';
+        $img = $this->getImage($item['base64'], $path);
+        app(DeliveryService::class)->pushClearCache($path);
+        $image = $this->draw($item['delivery'], $PrintTemplate->printTemplate, $img);
+        $this->saveImage($image, $path);
+        $item['base64'] = $this->readImageBase64($path);
+        app(DeliveryService::class)->pushClearCache($path);
+        return $item;
+    }
+
+}

+ 37 - 0
app/Services/TerminalPrinterLogisticService.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Services;
+
+use App\Terminal;
+use App\TerminalPrinter;
+use App\Traits\ServiceAppAop;
+use App\TerminalPrinterLogistic;
+use Illuminate\Support\Facades\Request;
+
+class TerminalPrinterLogisticService
+{
+    use ServiceAppAop;
+    protected $modelClass=TerminalPrinterLogistic::class;
+
+    // 匹配设置好的打印机
+    // $results 集合 DeliveryService->getDelivery 返回面单信息
+    public function setPrinterName(&$results)
+    {
+        $ip = Request::ip();
+        $terminalQuery = Terminal::query()->select('id')->where('ip', $ip);
+        $terminalPrinterQuery = TerminalPrinter::query()->select('id')->whereIn('terminal_id',$terminalQuery);
+        $terminalPrinterLogistics = TerminalPrinterLogistic::query()->with('terminalPrinter','logistic')->whereIn('terminal_printer_id',$terminalPrinterQuery)->get();
+
+        foreach ($terminalPrinterLogistics as &$item) {
+            $logisticCode = $item['logistic']['code'];
+            $terminalPrinterName = $item['terminalPrinter']['printer_name'];
+            $data[$logisticCode] = $terminalPrinterName;
+        }
+
+        foreach ($results as &$result){
+            $result['printerName'] = $data[$result['logistic_code']] ?? '';
+        }
+
+        return $results;
+    }
+}

+ 13 - 0
app/Services/TerminalPrinterService.php

@@ -0,0 +1,13 @@
+<?php 
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\TerminalPrinter;
+
+class TerminalPrinterService
+{
+    use ServiceAppAop;
+    protected $modelClass=TerminalPrinter::class;
+
+}

+ 12 - 0
app/Services/TerminalService.php

@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Services;
+
+use App\Traits\ServiceAppAop;
+use App\Terminal;
+
+class TerminalService
+{
+    use ServiceAppAop;
+    protected $modelClass=Terminal::class;
+}

+ 21 - 0
app/Terminal.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+
+class Terminal extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $fillable = ['name', 'ip'];
+
+    public function printers()
+    {
+        return $this->hasMany(TerminalPrinter::class);
+    }
+}

+ 29 - 0
app/TerminalPrinter.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App;
+
+use App\Traits\ModelTimeFormat;
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+
+class TerminalPrinter extends Model
+{
+    use ModelLogChanging;
+    use ModelTimeFormat;
+
+    protected $fillable = ['terminal_id', 'printer_name', 'alias_name'];
+
+
+    public function terminal(): BelongsTo
+    {
+        return $this->belongsTo(Terminal::class);
+    }
+
+    public function logistics(): BelongsToMany
+    {
+        return $this->belongsToMany(Logistic::class,TerminalPrinterLogistic::class);
+    }
+}

+ 25 - 0
app/TerminalPrinterLogistic.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+use App\Traits\ModelLogChanging;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class TerminalPrinterLogistic extends Model
+{
+    use ModelLogChanging;
+
+    protected $fillable = ['terminal_printer_id','logistic_id'];
+
+    public function logistic(): BelongsTo
+    {
+        return $this->belongsTo(Logistic::class);
+    }
+
+    public function terminalPrinter(): BelongsTo
+    {
+        return $this->belongsTo(TerminalPrinter::class);
+    }
+}

+ 295 - 0
app/Traits/DeliveryProcess.php

@@ -0,0 +1,295 @@
+<?php
+
+
+namespace App\Traits;
+
+
+use App\Logistic;
+use App\OracleDOCOrderDetail;
+use App\OracleDOCWaveDetails;
+use App\Owner;
+use App\OwnerLogisticPrintTemplate;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Cache;
+
+trait DeliveryProcess
+{
+    use DrawImage;
+
+    // 将 base64编码 转 图片 进行修改并保存,后把修改保存的图片转为 base64编码 返回
+    function processing(&$params)
+    {
+        // TODO 获取模板
+        $owner_query = Owner::query()->selectRaw('id')->where('code', $params['owner_code']);
+        $logistic_query = Logistic::query()->selectRaw('id')->where('code', $params['logistic_code']);
+        $item = OwnerLogisticPrintTemplate::query()->with('printTemplate')->where('owner_id', $owner_query)->where('logistic_id', $logistic_query)->first();
+
+        $path = '';
+        $img = $this->getImage($params['base64'], $path);
+
+        $img = $this->draw($params['delivery'] ?? [], $item, $img);
+
+        $img->save($path);
+
+        $arr = [];
+        if (Cache::has('print-template-file-list')) {
+            $arr = Cache::get('print-template-file-list');
+        }
+
+        $arr[] = ['time' => Carbon::now(), 'path' => $path];
+        Cache::put('print-template-file-list', $arr);
+
+        // TODO 将文件进行base64编码
+        if ($fp = fopen($path, "rp", 0)) {
+            $gambar = fread($fp, filesize($path));
+            fclose($fp);
+            $params['base64'] = chunk_split(base64_encode($gambar));
+        }
+        $params['is_process'] = true;
+        return $params;
+    }
+
+    // $item
+    // 将按条件转化为
+    function getDeliveryInfo($item, $type): ?array
+    {
+        if (in_array($type, ['TB', 'PDD', 'SF'])) return $this->getDocOrderDeliveryInfo($item);
+        else if (in_array($type, ['JD', 'SFQHD'])) return $this->getDocOrderInfo($item);
+        return [];
+    }
+
+    // 菜鸟面单和拼多多面单 TB PDD
+    function getDocOrderDeliveryInfo($docOrderDelivery): array
+    {
+        // 波次订单总数
+        $wave_count = (function () use ($docOrderDelivery) {
+            $count = OracleDOCWaveDetails::query()->where('waveno', $docOrderDelivery->docOrderHeader['waveno'])->count();
+            return $count ? strval($count) : '0';
+        })();
+        // 备注
+        list($remark1, $remark2, $remark3) = (function () use ($docOrderDelivery) {
+            switch ($docOrderDelivery->docOrderHeader['customerid'] ?? '') {
+                case 'BOAO':
+                    return ['易碎、易融', '不可放置自提柜!', '需当面签收!'];
+                case 'LINGFEI':
+                    return ['快递小哥送货路远,风吹日晒注意安全,', '见到小主代问声好,你我共建文明家园!', ''];
+                case 'WEIXI1':
+                    return ['售后电话13816946227', '', ''];
+                default:
+                    return ['', '', ''];
+            }
+        })();
+        // 仓库
+        $wh = (function () use ($docOrderDelivery) {
+            $customerid = $docOrderDelivery->docOrderHeader['customerid'] ?? '';
+            if (strstr('泗砖', $customerid)) return '泗砖宝石仓';
+            if (strstr('武乡', $customerid)) return '武乡宝石仓';
+            if (strstr('九干', $customerid)) return '九干宝石仓';
+            return '宝时仓';
+        })();
+
+        // whaddr 仓库地址
+        $whaddr = (function () use ($docOrderDelivery) {
+            $peo = $docOrderDelivery->docOrderHeader->oracleBASCustomer['address1'];
+            if (strstr($peo, '九干')) return '上海上海市松江区九干路1300号';
+            if (strstr($peo, '武乡')) return '上海嘉定区徐行镇武乡路9号';
+            if (strstr($peo, '泗砖')) return '上海嘉定区徐行镇武乡路9号';
+            return '宝时仓';
+        })();
+        // 城镇 收货
+        $raddr = (function () use ($docOrderDelivery) {
+            $docOrderHeader  = $docOrderDelivery['doc_order_header'];
+            return ($docOrderHeader['c_province'] ?? '') . '  ' . ($docOrderHeader['c_city'] ?? '') . '  ' . ($docOrderHeader['c_address2'] ?? '');
+        })();
+        // 韵达特殊处理
+        $yd = (function () use ($docOrderDelivery) {
+            $carrierid = $docOrderDelivery->docOrderHeader['carrierid'];
+            if ($carrierid !== "YUNDA") return '';
+            if (in_array($carrierid, ['上海', '上海市'])) return 'A';
+            if (in_array($carrierid, ['江苏', '江苏省', '浙江', '浙江省'])) return 'B';
+            if (in_array($carrierid, ['北京', '北京市', '安徽', '安徽省', '山东', '河南', '江西', '湖南', '湖北', '山东省', '河南省', '江西省', '湖南省', '湖北省'])) return 'C';
+            if (in_array($carrierid, [
+                '上海', '上海市', '江苏', '江苏省', '浙江', '浙江省', '北京', '北京市', '安徽', '安徽省', '山东', '河南', '江西', '湖南', '湖北', '山东省', '河南省', '江西省', '湖南省', '湖北省'
+            ])) return 'D';
+            return '';
+        })();
+        return [
+            'waveno' => $docOrderDelivery->docOrderHeader['waveno'] ?? '',          // 波次
+            'print_sum' => '总打印数' . ($wave_count ?? 0) . '行号#' . $docOrderDelivery->docOrderHeader->oracleDOCWaveDetail['seqno'], //  订单所处波次的序号
+            'wave_count' => $wave_count,
+            // 订单总数
+            'count' => (function () use ($docOrderDelivery) {
+                $count = OracleDOCOrderDetail::query()->where('orderno', $docOrderDelivery->docOrderHeader['ordenro'])->sum('qtyAllocated_each') ;
+                return $count ? strval($count) : "0";
+            })(),                         // 订单商品总数
+            'remark1' => $remark1,
+            'remark2' => $remark2,
+            'remark3' => $remark3,
+            'wh' => $wh,
+            'whaddr' => $whaddr,                    // 仓库地址
+            'deliveryno' => $docOrderDelivery->docOrderHeader['deliveryno'] ?? $docOrderDelivery->docOrderHeader['soreference5'],  // 快递单号
+            'c_contact' => $docOrderDelivery->docOrderHeader['c_contact'] ?? '',
+            'c_province' => $docOrderDelivery->docOrderHeader['c_province'] ?? '',
+            'c_city' => $docOrderDelivery->docOrderHeader['c_city'] ?? '',
+            'c_address2' => $docOrderDelivery->docOrderHeader['c_address2'] ?? $docOrderDelivery->docOrderHeader['c_district'],
+            'c_tel1' => $docOrderDelivery->docOrderHeader['c_tel1'] ?? $docOrderDelivery->docOrderHeader['c_tel2'],
+            'c_tel2' => $docOrderDelivery->docOrderHeader['c_tel2'],
+            'issuepartyname' => $docOrderDelivery->docOrderHeader['issuepartyname'] ?? '宝时云仓',
+            'logistic_number' => $docOrderDelivery['delivery_no'],
+            'raddr' => $raddr,                                              // 收货地址
+            'YD' => $yd,                                                    // 韵达自定义
+            'H_EDI_15' => $docOrderDelivery->docOrderHeader['H_EDI_15'] ?? '',     // 大头笔
+            'logistic_name' => (function () use ($docOrderDelivery) {
+                $logistic = Logistic::query()->where('code', $docOrderDelivery->docOrderHeader['carrierid'])->first();
+                return $logistic['name'] ?? '';
+            })(),      // 承运商
+            'consigneeid' => $docOrderDelivery->docOrderHeader['consigneeid'] ?? '',
+            'qrcode' => $docOrderDelivery['qrcode'] ?? '',
+            'date' => Carbon::now()->format(Carbon::DEFAULT_TO_STRING_FORMAT)
+        ];
+    }
+
+
+    //  非菜鸟面单 SF SFQHD JD
+    function getDocOrderInfo($docOrderHeader): array
+    {
+        // 联系号码
+        $tel = (function () use ($docOrderHeader) {
+            if (in_array($docOrderHeader['customerid'], ['HANDA', 'BAIPING', 'JINBAOBEI', 'DUOCHOU', 'YUNKAI']) &&
+                in_array($docOrderHeader['issuepartyname'], ['私密渠道A', '私密渠道BP', '金宝贝启蒙', 'ATSUGI厚木', '初品公司'])) {
+                return '18616839461';
+            }
+            if (in_array($docOrderHeader['customerid'], ['HEDI']) &&
+                in_array($docOrderHeader['issuepartyname'], ['马丁'])) {
+                return '18811144744';
+            }
+            return '13761413262';
+        })();
+
+        // whaddr 仓库地址
+        $whaddr = (function () use ($docOrderHeader) {
+            $address = $docOrderHeader->oracleBASCustomer['address1'] ?? '';
+            if (strstr($address, '泗砖')) return '上海上海市松江区泗砖公路351号';
+            if (strstr($address, '武乡')) return '上海嘉定区徐行镇武乡路9号';
+            if (strstr($address, '九干')) return '上海上海市松江区九干路1300号';
+            return '';
+        })();
+
+        // $ordercount 订单波次的订单总数
+        $owner_count = (function () use ($docOrderHeader) {
+            $count =  OracleDOCWaveDetails::query()->where('waveno', $docOrderHeader['waveno'])->count();
+            return $count ? strval($count) : "0";
+        })();
+
+        // orderseq 订单在波次里的序号
+        $orderseq = (function () use ($docOrderHeader) {
+            return $docOrderHeader->oracleDOCWaveDetail->seqno ?? '';
+        })();
+
+        // seqno
+        $seqno = (function () use ($docOrderHeader, $orderseq) {
+            $count = OracleDOCWaveDetails::query()->where('waveno', $docOrderHeader['waveno'])->count();
+            return '总打印数' . ($count) . " 行号#" . $orderseq;
+        })();
+
+        // 备注
+        list($remark1, $remark2, $remark3) = (function () use ($docOrderHeader) {
+            switch ($docOrderHeader['customerid'] ?? '') {
+                case 'BOAO':
+                    return ['易碎、易融', '不可放置自提柜!', '需当面签收!'];
+                case 'LINGFEI':
+                    return ['快递小哥送货路远,风吹日晒注意安全,', '见到小主代问声好,你我共建文明家园!', ''];
+                case 'WEIXI1':
+                    return ['售后电话13816946227', '', ''];
+                default:
+                    return ['', '', ''];
+            }
+        })();
+
+        $number = (function () use ($docOrderHeader) {
+            if (in_array($docOrderHeader['userdefine1'], ['SFJR', 'SFJRDF'])) return '1';
+            if (in_array($docOrderHeader['userdefine1'], ['SFSY', 'SFSYDF', 'SFSYQHD'])) return '4';
+            if (in_array($docOrderHeader['userdefine1'], ['SFTH', 'SFTHQHD'])){
+                if (in_array($docOrderHeader['c_province'], ['西藏', '西藏自治区'])) return '4';
+                else return '68';
+            }
+            if ($docOrderHeader['userdefine1'] == 'SFTHDF'){
+                if (in_array($docOrderHeader['c_province'], ['上海', '上海市', '江苏', '江苏省', '浙江', '浙江省', '安徽省', '安徽', '西藏', '西藏自治区'])) return '4';
+                else return '6';
+            }
+            return '';
+        })();
+
+        // js 结算方式
+        $js = (function () use ($docOrderHeader) {
+            if (in_array($docOrderHeader['userdefine1'], ['SFSYDF', 'SFTHDF'])) return "到付";
+            else return '季付月结';
+        })();
+
+        // 寄付月结
+        $jfyj = (function () use ($docOrderHeader) {
+            $base_customer = $docOrderHeader['oracleBASCustomer'];
+            if (in_array($docOrderHeader['userdefine1'], ['SFSY', 'SFSYQHD', 'SFJR'])) return '寄付月结:0210188294';
+
+            if (in_array($docOrderHeader['userdefine1'], ['SFTH', 'SFTHQHD'])) {
+                if ($base_customer['udf3'] == '货安') return '寄付月结:0210321137';
+                if ($base_customer['udf3'] != '货安') return '寄付月结:0218877996';
+            }
+
+            if (in_array($docOrderHeader['userdefine1'], ['SFTH', 'SFTHQHD'])) return '到付';
+            return '寄付月结:0210188294';
+        })();
+
+
+        // 仓库
+        $wh = (function () use ($docOrderHeader) {
+            $customerid = $docOrderHeader['customerid'] ?? '';
+            if (strstr($customerid,'泗砖' )) return '泗砖宝石仓';
+            if (strstr($customerid,'武乡' )) return '武乡宝石仓';
+            if (strstr($customerid,'九干' )) return '九干宝石仓';
+            return '宝时仓';
+        })();
+
+        $yd = (function () use ($docOrderHeader) {
+            if ($docOrderHeader['carrierid'] == 'YUNDA') {
+                if (in_array($docOrderHeader['c_province'], ['上海', '上海市'])) return 'A';
+                if (in_array($docOrderHeader['c_province'], ['江苏', '江苏省', '浙江', '浙江省'])) return 'B';
+                if (in_array($docOrderHeader['c_province'], ['北京', '北京市', '安徽', '安徽省', '山东', '河南', '江西', '湖南', '湖北', '山东省', '河南省', '江西省', '湖南省', '湖北省'])) return 'C';
+                if (!in_array($docOrderHeader['c_province'],['上海','上海市','江苏','江苏省','浙江','浙江省','北京','北京市','安徽','安徽省','山东','河南','江西','湖南','湖北','山东省','河南省','江西省','湖南省','湖北省']))
+                    return 'D';
+            }
+            return '';
+        })();
+        return [
+            'waveno' => $docOrderHeader['waveno'] ?? '',
+            'print_sum' => '总打印数' . ($owner_count ?? 0) . '行号#' .$docOrderHeader->oracleDOCWaveDetail['seqno'] ?? '', //  订单所处波次的序号,
+            'wave_count' => $owner_count,
+            'orderno' => $docOrderHeader['orderno'] ?? '',
+            'logistic_number' => $docOrderHeader['deliveryno'] ?? $docOrderHeader['soreference5'],
+            'count' => $owner_count,  // 订单所属波次订单总数
+            'orderseq' => $orderseq, // 订单序号
+            'seqno' => $seqno,       // 打印序号
+            'remark1' => $remark1,   // 备注1
+            'remark2' => $remark2,   // 备注2
+            'remark3' => $remark3,   // 备注3
+            'grossweight' => $docOrderHeader->oracleDOCWaveDetail['grossweight'] ?? '',       // 重量
+            'wh' => $wh,                // 仓库
+            'whaddr' => $whaddr,        // 仓库地址
+            'deliveryno' => '',         // 快递单号
+            'raddr' =>  ($docOrderHeader['c_province'] ?? '') . '  ' . ($docOrderHeader['c_city'] ?? '') . '  ' . ($docOrderHeader['c_address2'] ?? ''), // 发货地址
+            'c_contact' => $docOrderHeader['c_contact'],
+            'c_province' => $docOrderHeader['c_province'],
+            'c_city'=> $docOrderHeader['c_city'],
+            'c_address2' => $docOrderHeader['c_address2'] ?? $docOrderHeader['c_district'],
+            'c_tel1' => $docOrderHeader['c_tel1'] ?? $docOrderHeader['c_tel2'],
+            'c_tel2' => $docOrderHeader['c_tel2'] ,
+            'issuepartyname' => $docOrderHeader['issuepartyname'] ?? '宝时云仓',
+            'tel' => $tel,          // 售后电话
+            'number' => $number,    //
+            'js' => $js,            // 结算方式
+            'jfyj' => $jfyj,        // 季付 月结
+            'YD' => $yd,            // 韵达自定义
+            'date' => Carbon::now()->format(Carbon::DEFAULT_TO_STRING_FORMAT)
+        ];
+    }
+}

+ 254 - 0
app/Traits/DrawImage.php

@@ -0,0 +1,254 @@
+<?php
+
+
+namespace App\Traits;
+
+
+use App\PrintPartImage;
+use Endroid\QrCode\QrCode;
+use Illuminate\Support\Facades\File;
+use Illuminate\Support\Str;
+use Intervention\Image\Facades\Image;
+use Picqer\Barcode\BarcodeGeneratorJPG;
+
+trait DrawImage
+{
+    public function draw($orderPackage, $template, $img = null)
+    {
+        if (!$img) $img = $this->getBgImg($template);
+        if (!$template) return $img;
+
+        $items = array_filter($template->value, function ($item) {
+            return $item['type'] != 'bg';
+        });
+
+        $array = array_filter($template->value, function ($item) {
+            return $item['type'] == 'bg';
+        });
+        $array = array_shift($array);
+
+        // 按照模型比列修改image
+        $items = (function () use ($items, $img, $array) {
+            $heightScale = $img->getHeight() / $array['height'];
+            $weightScale = $img->getWidth() / $array['width'];
+//            foreach ($items as $key => &$item) {
+            foreach ($items as  &$item) {
+                if (array_key_exists('width', $item)) $item['width'] = intval($weightScale * floatval($item['width']));
+                if (array_key_exists('height', $item)) $item['height'] = intval($heightScale * floatval($item['height']));
+                if (array_key_exists('left', $item)) $item['left'] = intval($weightScale * floatval($item['left']));
+                if (array_key_exists('top', $item)) $item['top'] = intval($heightScale * floatval($item['top']));
+                if (array_key_exists('font-size', $item)) $item['font-size'] = intval($heightScale * floatval($item['font-size']));
+                if (array_key_exists('border-width', $item)) $item['border-width'] = intval($heightScale * floatval($item['border-width']));
+                $item['heightScale'] = $heightScale;
+                $item['weightScale'] = $weightScale;
+            }
+            return $items;
+        })();
+
+        // 根据下标排序
+        usort($items, function ($a, $b) {
+            return $a['z_index'] > $b['z_index'] ? 1 : -1;
+        });
+
+        foreach ($items as $item) {
+            switch ($item['type']) {
+                case 'textBox':
+                    $this->setTextBox($img, $item, $orderPackage);
+                    break;
+                case 'image':
+                    $this->setImage($img, $item);
+                    break;
+                case 'qRCode':
+                    $this->setQRCode($img, $item, $orderPackage);
+                    break;
+                case 'stripeCode':
+                    $this->setStripeCode($img, $item, $orderPackage);
+                    break;
+                default:
+                    break;
+            }
+        }
+        return $img;
+    }
+
+    private function getBgImg($template): \Intervention\Image\Image
+    {
+        $bg = array_filter($template->value, function ($item) {
+            return $item['type'] == 'bg';
+        });
+
+        $values = array_shift($bg);
+
+        return Image::canvas($values['width'], $values['height'], '#fff');
+    }
+
+    public function setTextBox($image, $params, $orderPackages): \Intervention\Image\Image
+    {
+        $text = $this->getValue($params, $orderPackages);
+        $bgImage = Image::canvas($params['width'], $params['height'], '#000');
+
+        $img = $this->getTextBox($params, $text);
+        $bgImage = $bgImage->insert($img, 'top-left', $params['border-width'], $params['border-width']);
+
+        return $image->insert($bgImage, 'top-left', $params['left'] ?? 0, $params['top'] ?? 0);
+    }
+
+    public function setStripeCode($img, $params, $orderPackages)
+    {
+        $code = $this->getValue($params, $orderPackages);
+
+        $bgImage = Image::canvas(404, 92, '#fff');
+
+        $item = [
+            'width' => 404, 'height' => 20,
+            'justify-content' => 'center', 'align-items' => 'center',
+            'text' => $code, 'border-width' => 0,
+            'top' => 58, 'left' => 0, 'font-size' => 20
+        ];
+
+        $textBox = $this->getTextBox($item, $code);
+
+        $bgImage->insert($textBox, 'top-left', 0, 68);
+
+        $generatorPng = new BarcodeGeneratorJPG();
+
+        $barCode = $generatorPng->getBarcode($code, $generatorPng::TYPE_CODE_128_A, 1, 50);
+
+        $stripeCode = Image::make($barCode)->resize(382, 50);
+
+        $stripeCode_weight = 404 * $params['scale'] * $params['weightScale'];
+        $stripeCode_height = 92 * $params['scale'] * $params['heightScale'];
+
+        $bgImage->insert($stripeCode, 'top-left', 6, 9)->resize($stripeCode_weight, $stripeCode_height);
+
+        $left = $params['left'] + ((404 * (1- $params['scale'])) * $params['weightScale']) / 2;
+        $top = $params['top'] + ((92 * (1-$params['scale']))  * $params['heightScale']) / 2;
+
+        return $img->insert($bgImage, 'top-left', intval($left), intval($top));
+    }
+
+    public function setImage($img, $params)
+    {
+        $printPartImage = PrintPartImage::query()->where('name', $params['text'])->first();
+        $path = ['app', 'public'];
+        $path = join(DIRECTORY_SEPARATOR, $path);
+        $dirPath = storage_path($path);
+        $uri = $printPartImage->file['url'];
+        $uri = str_replace('/', DIRECTORY_SEPARATOR, $uri);
+        $path = $dirPath . $uri . '.' . $printPartImage->file['type'];
+        $image = Image::make(File::get($path));
+        $image->resize($params['width'], $params['height']);
+        $img->insert($image, 'top-left', $params['left'], $params['top']);
+        return $img;
+    }
+
+    public function getTextBox($params, $text): \Intervention\Image\Image
+    {
+
+        $bgImg = Image::canvas($params['width'], $params['height'], '#fff');
+
+        if ($params['border-width'] !== 0) {
+            $borderWidth = $params['border-width'] * 2;
+            $paddingBgImage = Image::canvas($params['width'], $params['height'], '#000');
+            $paddingImage = Image::canvas($params['width'] - $borderWidth, $params['height'] - $borderWidth, '#fff');
+            $paddingBgImage->insert($paddingImage, 'top-left', intval($params['border-width'] / 2), intval($params['border-width'] / 2));
+            $bgImg->insert($paddingBgImage);
+        }
+        $path_arr = ['fonts', 'simsun.ttc'];
+        $x = $params['width'] / 2;
+        if ($params['justify-content'] == 'center') $x = $params['width'] / 2;
+        if ($params['justify-content'] == 'flex-start') $x = $params['width'] / 2;
+        $y = $params['height'] / 2;
+        if ($params['align-items'] == 'center') $y = $params['height'] / 2;
+        if ($params['align-items'] == 'flex-start') $y = 0;
+        if ($params['align-items'] == 'flex-end') $y = ($params['height'] - 22) / 2;
+
+        // 字体路径
+        $path = app('path.public') . (DIRECTORY_SEPARATOR . join(DIRECTORY_SEPARATOR, $path_arr));
+        $aligns = [
+            'flex-start' => 'right',
+            'center' => 'center'
+        ];
+        $vAligns = [
+            'center' => 'center',
+            'flex-start' => 'top',
+            'flex-end' => 'bottom',
+        ];
+        return $bgImg->text($text, intval($x), intval($y), function ($font) use ($params, $path, $aligns, $vAligns) {
+            $font->file($path);
+            $font->size($params['font-size'] ?? 24);
+            $font->align($aligns[$params['justify-content']]); // 水平对齐方式
+            // center
+            // right
+            $font->valign($vAligns[$params['align-items']]);    // 垂直对齐方式
+            // center
+            // middle
+            // top
+            // bottom
+        });
+    }
+
+    public function setQRCode($img, $params, $orderPackages)
+    {
+
+        $text = $this->getValue($params, $orderPackages);
+        $qrCode = new QrCode($text);
+        $qrCode->setSize(100);
+        $qrCode->setMargin(0);
+        $scale = $params['scale'] ?? 1;
+        return $img->insert(Image::make($qrCode->writeString())->resize(100 * $scale * $params['weightScale'], 100 * $scale * $params['heightScale']), 'top-left', $params['left'], $params['top']);
+    }
+
+    // 获取图片
+    public function getImage($base64, &$path): \Intervention\Image\Image
+    {
+        $path = $this->getImagePath();
+        $img = Image::make($base64);
+        $img->save($path);          // 图片保存
+        return $img;
+    }
+
+    // 获取打印图片的路径
+    private function getImagePath(): string
+    {
+        $img_name = Str::uuid() . '.jpg';
+        $path_arr = ['app', 'public', 'print'];
+        $dir_path = implode(DIRECTORY_SEPARATOR, $path_arr);
+        if (!file_exists(storage_path($dir_path))) mkdir(storage_path($dir_path));
+
+        $path_arr[] = 'image';
+        $dir_path = implode(DIRECTORY_SEPARATOR, $path_arr);
+        if (!file_exists(storage_path($dir_path))) mkdir(storage_path($dir_path));
+
+        $path_arr[] = $img_name;
+
+        return storage_path(implode(DIRECTORY_SEPARATOR, $path_arr));
+    }
+
+    // 保存至保存图片路径下
+    public function saveImage($image, &$path)
+    {
+        $path = $this->getImagePath();
+        $image->save($path);          // 图片保存
+    }
+
+    // 根据图片路径读取而base64编码的图片
+    public function readImageBase64($path): string
+    {
+        if ($fp = fopen($path, "rp", 0)) {
+            $gambar = fread($fp, filesize($path));
+            fclose($fp);
+            return chunk_split(base64_encode($gambar));
+        }
+        return "";
+    }
+
+    // 通过设置获取所需参数
+    private function getValue($params, $orderPackage = [], $k = 'text'): string
+    {
+        $key = strtolower($params[$k]);
+        if ($value = strstr($key, '$')) return $orderPackage[ltrim($value, '$')] ?? '';
+        else return $params[$k];
+    }
+
+}

+ 0 - 4
composer.lock

@@ -3922,10 +3922,6 @@
                 "svg",
                 "upc"
             ],
-            "support": {
-                "issues": "https://github.com/picqer/php-barcode-generator/issues",
-                "source": "https://github.com/picqer/php-barcode-generator/tree/v2.2.0"
-            },
             "funding": [
                 {
                     "url": "https://github.com/casperbakker",

+ 16 - 0
database/factories/OrderPackageExpressBillPrintRecordFactory.php

@@ -0,0 +1,16 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\OrderPackageExpressBillPrintRecord;
+use Faker\Generator as Faker;
+
+$factory->define(OrderPackageExpressBillPrintRecord::class, function (Faker $faker) {
+    return [
+        'order_id' => $faker->numberBetween(0,10),
+        'order_package_id' => $faker->numberBetween(0,10),
+        'process_id' => $faker->numberBetween(0,10),
+        'logistic_number' => $faker->text(12),
+        'print_count' => $faker->numberBetween(0,10),
+    ];
+});

+ 19 - 0
database/factories/OwnerLogisticPrintTemplateFactory.php

@@ -0,0 +1,19 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\OwnerLogisticPrintTemplate;
+use Faker\Generator as Faker;
+
+$factory->define(OwnerLogisticPrintTemplate::class, function (Faker $faker) {
+    $owner = \App\Owner::query()->get();
+    $logistics = \App\Logistic::query()->get();
+    $delivery_interfaces = ['TB','PDD','JD','SF','SFQHD'];
+
+    return [
+        'owner_id' => $owner->random(1)->first()['id'],
+        'logistic_id' => $logistics->random(1)->first()['id'],
+        'print_template_id' => \App\PrintTemplate::query()->first(),
+        'delivery_interface' => $delivery_interfaces[rand(0,count($delivery_interfaces)-1)]
+    ];
+});

+ 13 - 0
database/factories/TerminalFactory.php

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

+ 16 - 0
database/factories/TerminalPrinterFactory.php

@@ -0,0 +1,16 @@
+<?php
+
+/** @var \Illuminate\Database\Eloquent\Factory $factory */
+
+use App\TerminalPrinter;
+use Faker\Generator as Faker;
+
+$factory->define(TerminalPrinter::class, function (Faker $faker) {
+    $types = ['菜鸟','拼多多','顺丰','京东'];
+    return [
+        'terminal_id' => rand(1,10),
+        'printer_name' => $faker->name,
+        'alias_name' => $faker->lastName,
+        'print_type' => $types[rand(0,3)]
+    ];
+});

+ 41 - 0
database/migrations/2021_06_18_150046_create_terminals_table.php

@@ -0,0 +1,41 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateTerminalsTable extends Migration
+{
+//    private $parent_name = '基础设置-快递打印-终端';
+//
+//    private $names = [
+//        '添加' => '基础设置-快递打印-终端-添加',
+//        '编辑' => '基础设置-快递打印-终端-编辑',
+//        '删除' => '基础设置-快递打印-终端-删除',
+//    ];
+
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('terminals', function (Blueprint $table) {
+            $table->id();
+            $table->string('name')->comment('计算机别名');
+            $table->string('ip')->comment('计算机ip地址');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('terminals');
+    }
+}

+ 42 - 0
database/migrations/2021_06_18_150129_create_terminal_printers_table.php

@@ -0,0 +1,42 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateTerminalPrintersTable extends Migration
+{
+//    private $parent_name = '打印机';
+//
+//    private $names = [
+//        '打印机-添加' => '基础设置-快递打印-打印机-添加',
+//        '打印机-编辑' => '基础设置-快递打印-打印机-编辑',
+//        '打印机-删除' => '基础设置-快递打印-打印机-删除',
+//    ];
+
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('terminal_printers', function (Blueprint $table) {
+            $table->id();
+            $table->string('terminal_id')->comment('终端id');
+            $table->string('printer_name')->comment('打印机名称');
+            $table->string('alias_name')->comment('打印机别名');
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('terminal_printers');
+    }
+}

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

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOwnerLogisticPrintTemplatesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('owner_logistic_print_templates', function (Blueprint $table) {
+            $table->id();
+            $table->integer('owner_id')->comment('货主');
+            $table->integer('logistic_id')->comment('快递');
+            $table->integer('print_template_id')->comment('模板编码');
+            $table->integer('delivery_interface')->comment('下发接口');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('owner_logistic_print_templates');
+    }
+}

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

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreatePrintPartImagesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('print_part_images', function (Blueprint $table) {
+            $table->id();
+            $table->string('name')->unique();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('print_part_images');
+    }
+}

+ 37 - 0
database/migrations/2021_07_29_114758_create_order_package_express_bill_print_records_table.php

@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateOrderPackageExpressBillPrintRecordsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('order_package_express_bill_print_records', function (Blueprint $table) {
+            $table->id();
+            $table->integer('order_id')->comment('订单');
+            $table->integer('order_package_id')->comment('快递');
+            $table->integer('process_id')->comment('打印账号');
+            $table->integer('logistic_number')->comment("面单号");
+            $table->integer('print_count')->comment('第几次打印');
+            $table->string("task_id")->comment("打印任务编号");
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('order_package_express_bill_print_records');
+    }
+}

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

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateTerminalPrinterLogisticsTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('terminal_printer_logistics', function (Blueprint $table) {
+            $table->id();
+            $table->integer("terminal_printer_id")->comment("终端打印机");
+            $table->integer('logistic_id')->comment("承运商");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('terminal_printer_logistics');
+    }
+}

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

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class ChangeTerminalsIpUnique extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('terminals', function (Blueprint $table) {
+            $table->string('ip')->unique()->change();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('terminals', function (Blueprint $table) {
+            $table->dropUnique(['ip']);
+        });
+    }
+}

+ 16 - 0
database/seeds/OrderPackageExpressBillPrintRecordSeeder.php

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

+ 19 - 0
database/seeds/OwnerLogisticPrintTemplateSeeder.php

@@ -0,0 +1,19 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class OwnerLogisticPrintTemplateSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        $params = factory(\App\OwnerLogisticPrintTemplate::class)->times(20)->make()->toArray();
+        foreach ($params as $param) {
+            \App\OwnerLogisticPrintTemplate::query()->firstOrCreate($param);
+        }
+    }
+}

+ 16 - 0
database/seeds/TerminalPrinterSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class TerminalPrinterSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        factory(\App\TerminalPrinter::class)->times(30)->create();
+    }
+}

+ 16 - 0
database/seeds/TerminalSeeder.php

@@ -0,0 +1,16 @@
+<?php
+
+use Illuminate\Database\Seeder;
+
+class TerminalSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+        factory(\App\Terminal::class)->times(10)->create();
+    }
+}

+ 1 - 0
package.json

@@ -39,6 +39,7 @@
     "md5": "^2.3.0",
     "moment": "^2.28.0",
     "pusher-js": "^5.1.1",
+    "qrcode": "^1.4.4",
     "socket.io-client": "^2.3.0",
     "yarn": "^1.22.10"
   },

+ 34 - 0
resources/views/maintenance/expressPrinting/image/_create.blade.php

@@ -0,0 +1,34 @@
+<div class="modal " id="imageModal" tabindex="-1">
+    <div class="modal-dialog modal-lg modal-dialog-centered">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title text-center" v-if="isCreated">图片编辑</h5>
+                <h5 class="modal-title text-center" v-else>图片编辑</h5>
+                <button type="button" class="close" data-dismiss="modal">
+                    <span>&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <form class="form">
+                    <div class="form-group row">
+                        <label for="image-name" class="col-sm-3 col-form-label text-right">图片名</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="image-name" name="imageName" class="form-control col-9" v-model="editItem.name" placeholder="图片名"></input>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="image-file" class="col-sm-3 col-form-label text-right">所属图片</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="file" id="image-file" class="form-control col-9" ref="image" accept="image/jpeg,image/jpg,image/png,image/svg"></input>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" @click="save">提交</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 30 - 0
resources/views/maintenance/expressPrinting/image/_table.blade.php

@@ -0,0 +1,30 @@
+<table class="table table-striped table-sm table-hover" id="table">
+    <thead>
+    <tr>
+        <td>序号</td>
+        <td>名称</td>
+        <td>图片</td>
+        <td>操作</td>
+    </tr>
+    </thead>
+    <tbody>
+    <template v-for="(item,i) in images">
+        <tr @click="selectTr===i+1?selectTr=0:selectTr=i+1" :class="selectTr===i+1?'focusing' : ''">
+            <td>@{{ i+1 }}</td>
+            <td>@{{ item.name }}</td>
+            <td>
+                <div style="width: 100px" class="position-relative">
+                    <img :src="imgPrefix+item.file.url+'.'+item.file.type" :alt="item.name" :id="'img'+item.id"  :onload="setImageStyle(item)"  style="width: 100px">
+                    <img :src="imgPrefix+item.file.url+'.'+item.file.type" :alt="item.name" :id="'img'+item.id"  :onload="setImageStyle(item)" style="width:200px" class="position-absolute">
+                </div>
+            </td>
+            <td>
+                <button class="btn btn-sm btn-primary" @click="edit(item,i)">编辑</button>
+                <button class="btn btn-sm btn-danger " style="opacity: 0.9" @click="destroy(item.id,i)">删除</button>
+            </td>
+        </tr>
+    </template>
+
+    </tbody>
+</table>
+{{ $printPartImages->links() }}

+ 97 - 0
resources/views/maintenance/expressPrinting/image/index.blade.php

@@ -0,0 +1,97 @@
+@extends("layouts.app")
+@section("title","图片")
+
+@section("content")
+    <div class="container-fluid d-noe" id="list">
+        <div class="card">
+            @include('maintenance.expressPrinting.image._create')
+            <div class="card-body">
+                <div class="row pull-left m-1">
+                    <button class="btn btn-outline-info mb-1 mr-3" @click="showCreatedModel"><span
+                            class="fa fa-plus"></span>&nbsp;新&nbsp;&nbsp;增
+                    </button>
+                </div>
+                @include('maintenance.expressPrinting.image._table')
+            </div>
+        </div>
+    </div>
+@endsection
+
+@section('lastScript')
+    <script>
+        let list = new Vue({
+            el:'#list',
+            data:{
+                images:{!! $printPartImages->toJson() !!}['data'],
+                notFindImgUrl:'{!! url('/icon/img404-thumbnail.jpg') !!}',
+                imgPrefix:"{{asset("/storage")}}",
+                selectTr:null,
+                isCreated:null,
+                editItem:{},
+                index:null,
+            },
+            mounted(){
+                $("#list").removeClass("d-none");
+            },
+            methods:{
+                showCreatedModel(){
+                    this.isCreated = true;
+                    $("#imageModal").modal("show");
+                },
+                edit(item){
+                    this.isCreated = false;
+                    this.editItem = JSON.parse(JSON.stringify(item))
+                    $("#imageModal").modal("show");
+                },
+                save(){
+                    let url = '{{url("apiLocal/maintenance/expressPrinting/part/image/saveFile")}}';
+                    let data = new FormData();
+                    data.set("name",this.editItem.name);
+                    let file= this.$refs.image.files[0];
+                    data.set("file",file);
+                    if (!this.isCreated)  {
+                        url = '{{url("apiLocal/maintenance/expressPrinting/part/image/update")}}'
+                        data.set("id",this.editItem.id);
+                    }
+
+                    window.tempTip.setIndex(2000);
+                    window.axios.post(url,data,{
+                        'Content-Type': 'multipart/form-data'
+                    }).then(res=>{
+                        if(res.data.success){
+                            window.tempTip.showSuccess("文件保存成功");
+                            if (this.isCreated){
+                                this.images.unshift(res.data.data);
+                            } else {
+                                this.$set(this.images,this.index,res.data.data);
+                                document.getElementById('img'+this.editItem.id).setAttribute('src',this.imgPrefix+res.data.data.file.url+'.'+res.data.data.file.type);
+                            }
+                            this.$forceUpdate();
+                            $('#imageModal').modal('hide');
+                            return
+                        }
+                        window.tempTip.show(res.data.message);
+                    }).catch(err=>{
+                        window.tempTip.show(err)
+                    });
+                },
+                destroy(id,index){
+                    let url = '{{url("apiLocal/maintenance/expressPrinting/part/image/destroy")}}'+"?id"+id;
+                    window.axios.delete(url).then(res=>{
+                        if(res.data.success){
+                            window.tempTip.showSuccess("文件删除成功");
+                            this.$delete(this.images,index)
+                            this.$forceUpdate()
+                            return
+                        }
+                        window.tempTip.show(res.data.message);
+                    }).catch(err=>{
+                        window.tempTip.show(err)
+                    });
+                },
+                setImageStyle(item){
+                }
+            }
+        })
+    </script>
+@endsection

+ 199 - 94
resources/views/maintenance/expressPrinting/part/create.blade.php

@@ -16,7 +16,8 @@
                         </div>
                         <div class="form-group">
                             <label for=template-height>高</label>
-                            <input type="number" id="template-height" v-model="template.height" class="form form-control">
+                            <input type="number" id="template-height" v-model="template.height"
+                                   class="form form-control">
                         </div>
                         <div class="form-group">
                             <label for="template-border-type">边框类型</label>
@@ -26,7 +27,8 @@
                         </div>
                         <div class="form-group">
                             <label for="template-border-weight">边框大小</label>
-                            <input type="number" id="template-border-weight" v-model="template.borderWidth" class="form form-control">
+                            <input type="number" id="template-border-weight" v-model="template.borderWidth"
+                                   class="form form-control">
                         </div>
                     </div>
                     <div class="card-footer">
@@ -34,14 +36,25 @@
                     </div>
                 </div>
             </div>
-            <div class="col-6 justify-content-center align-items-center flex-fill" >
+            <div class="col-6 justify-content-center align-items-center flex-fill">
                 {{-- 模板展示--}}
                 <div v-if="template.type==='text'" :style="getStyle()">
-                    <span >@{{ template.text }}</span>
+                    <span>@{{ template.text }}</span>
                 </div>
-                {{-- 二维码展示 --}}
-                <svg v-show="template.type!=='text'" :style="{width:template.width,height:template.height}" id="barcodeDiv1">
+                {{-- 条纹码展示 --}}
+                <svg v-show="template.type === 'StripeCode'" :style="{width:template.width,height:template.height}"
+                     id="barcodeDiv">
                 </svg>
+
+                {{-- 二维码展示 --}}
+                <div v-show="template.type ==='QrCode'" :style="{width:template.width,height:template.height}"
+                     id="qrCodeDiv">
+                </div>
+
+                {{-- 图片展示 --}}
+                <div v-show="template.type ==='QrCode'" :style="{width:template.width,height:template.height}"
+                     id="ImageDiv">
+                </div>
             </div>
             <div class="col-3">
                 {{-- 属性 --}}
@@ -52,46 +65,59 @@
                     <div class="card-body">
                         <div class="form-group">
                             <label for="template-type">组件类型</label>
-                            <select class="form-control" v-model="template.type" @change="setContent">
+                            <select class="form-control" id="template-type" v-model="template.type" @change="setContent">
                                 <option v-for="type in types" :value="type.value">@{{ type.name }}</option>
                             </select>
                         </div>
-                        <div class="form-group">
-                            <label for="template-text">内容</label>
-                            <textarea id="template-text" class="form-text form-control" cols="5" rows="5" v-model="template.text"></textarea>
-                        </div>
-                        <div class="form-group">
-                            <label for="template-text-align">文本对齐方式</label>
-                            <select name="" id="template-text-align" v-model="template.justifyContent" class="form form-control">
-                                <option v-for="justifyContent in justifyContents" :value="justifyContent.value">@{{ justifyContent.name }}</option>
-                            </select>
-                        </div>
-                        <div class="form-group">
-                            <label for="template-text-align">文本垂直对齐</label>
-                            <select name="" id="template-text-align" v-model="template.alignItems" class="form form-control">
-                                <option v-for="alignItem in alignItems" :value="alignItem.value">@{{  alignItem.name }}</option>
-                            </select>
-                        </div>
-                        <div class="form-group">
-                            <label for="template-font-size">字体大小</label>
-                            <input type="text" id="template-font-size" v-model="template.fontSize" class="form form-control">
-                        </div>
-                        <div class="form-group">
-                            <label for="template-font-weight">字体宽</label>
-                            <select id="template-font-weight" v-model="template.fontWeight" class="form-control">
-                                <option v-for="fontWeight in fontWeights" :value="fontWeight.value" >@{{ fontWeight.name }}</option>
-                            </select>
+                        <div v-if="template.type === 'Image'">
+                            <button @click="showUploadModal" class="btn btn-sm btn-primary">上传文件</button>
                         </div>
+{{--                        <div class="form-group">--}}
+{{--                            <label for="template-text">内容</label>--}}
+{{--                            <textarea id="template-text" class="form-text form-control" cols="5" rows="5"--}}
+{{--                                      v-model="template.text"></textarea>--}}
+{{--                        </div>--}}
+{{--                        <div class="form-group">--}}
+{{--                            <label for="template-text-align">文本对齐方式</label>--}}
+{{--                            <select name="" id="template-text-align" v-model="template.justifyContent"--}}
+{{--                                    class="form form-control">--}}
+{{--                                <option v-for="justifyContent in justifyContents" :value="justifyContent.value">--}}
+{{--                                    @{{ justifyContent.name }}--}}
+{{--                                </option>--}}
+{{--                            </select>--}}
+{{--                        </div>--}}
+{{--                        <div class="form-group">--}}
+{{--                            <label for="template-text-align">文本垂直对齐</label>--}}
+{{--                            <select name="" id="template-text-align" v-model="template.alignItems"--}}
+{{--                                    class="form form-control">--}}
+{{--                                <option v-for="alignItem in alignItems" :value="alignItem.value">--}}
+{{--                                    @{{ alignItem.name }}--}}
+{{--                                </option>--}}
+{{--                            </select>--}}
+{{--                        </div>--}}
+{{--                        <div class="form-group">--}}
+{{--                            <label for="template-font-size">字体大小</label>--}}
+{{--                            <input type="text" id="template-font-size" v-model="template.fontSize"--}}
+{{--                                   class="form form-control">--}}
+{{--                        </div>--}}
+{{--                        <div class="form-group">--}}
+{{--                            <label for="template-font-weight">字体宽</label>--}}
+{{--                            <select id="template-font-weight" v-model="template.fontWeight" class="form-control">--}}
+{{--                                <option v-for="fontWeight in fontWeights" :value="fontWeight.value">--}}
+{{--                                    @{{ fontWeight.name }}--}}
+{{--                                </option>--}}
+{{--                            </select>--}}
+{{--                        </div>--}}
                     </div>
                 </div>
             </div>
         </div>
-        <div class="modal fade" id="saveModal" tabindex="-1"  aria-hidden="true">
-            <div class="modal-dialog">
+        <div class="modal fade" id="saveModal" tabindex="-1" aria-hidden="true">
+            <div class="modal-dialog modal-dialog-centered">
                 <div class="modal-content">
                     <div class="modal-header">
                         <h5 class="modal-title" id="saveModalLabel">保存组件</h5>
-                        <button type="button" class="close" data-dismiss="modal" aria-label="Close" >
+                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                             <span aria-hidden="true">&times;</span>
                         </button>
                     </div>
@@ -104,104 +130,183 @@
                         </form>
                     </div>
                     <div class="modal-footer">
-                        <button type="button" class="btn btn-secondary" data-dismiss="modal" >关闭</button>
+                        <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
                         <button type="button" class="btn btn-success" @click="create">保存</button>
                     </div>
                 </div>
             </div>
         </div>
+        <div v-if="template.type === 'Image'">
+            <template v-for="value in printImages.values">
+                <div class="list-group">
+                    @{{ value }}
+                </div>
+            </template>
+        </div>
+        <div class="modal" tabindex="-1" id="uploadFileModel">
+            <div class="modal-dialog modal-dialog-centered">
+                <div class="modal-content">
+                    <div class="modal-header">
+                        <h5 class="modal-title">上传图片</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-inline form-group">
+                            <label for="fileName" class="col-sm-3 col-form-label text-right ">图片名称</label>
+                            <input type="name"  class="form-control" id="fileName" placeholder="图片名">
+                        </div>
+
+                        <div class="form-inline form-group">
+                            <label for="uploadfile" class="col-sm-3 col-form-label text-right ">图片</label>
+                            <input type="file" id="uploadfile" class="form-control" name="image" accept="image/*" ref="upload">
+                        </div>
+                    </div>
+                    <div class="modal-footer">
+                        <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                        <button type="button" class="btn btn-primary" @click="pushImage">上传</button>
+                    </div>
+                </div>
+            </div>
+        </div>
     </div>
+
 @endsection
 
 @section('lastScript')
     <script type="text/javascript" src="{{mix('js/utilities/barcode.js')}}"></script>
+    <script type="text/javascript" src="{{mix('/js/utilities/qrcode.js')}}"></script>
 
     <script>
         let vue = new Vue({
-            el:"#create-print-part",
-            data:{
-                printPart:{name:'',value:''},
-                template:{
-                    type:'text',
-                    width:200,
-                    height:100,
-                    borderStyle:'solid',
-                    borderWidth:1,
-                    fontSize:12,
-                    text:'',
-                    fontWeight:'normal',   //normal lighter bold
-                    justifyContent:'',     //文字水平对齐方式
-                    alignItems:'center',         //文字垂直对齐
+            el: "#create-print-part",
+            data: {
+                printPart: {name: '', value: ''},
+                printImages:{!! $imgPrintPart->toJson() !!},
+                template: {
+                    type: 'text',
+                    width: 200,
+                    height: 100,
+                    borderStyle: 'solid',
+                    borderWidth: 1,
+                    fontSize: 12,
+                    text: '',
+                    fontWeight: 'normal',   //normal lighter bold
+                    justifyContent: '',     //文字水平对齐方式
+                    alignItems: 'center',         //文字垂直对齐
                 },
-                types:[
-                    {name:"文本框",value:"text"},
-                    {name:"条纹码",value:"StripeCode"},
+                types: [
+                    {name: "文本框", value: "text"},
+                    {name: "条纹码", value: "StripeCode"},
+                    {name: "二维码", value: "QrCode"},
+                    {name: "图片", value: "Image"},
                 ],
-                fontWeights:[
-                    {name:"正常",value:"normal"},
-                    {name:"细体",value:"lighter"},
-                    {name:"粗体",value:"bold"},
+                fontWeights: [
+                    {name: "正常", value: "normal"},
+                    {name: "细体", value: "lighter"},
+                    {name: "粗体", value: "bold"},
                 ],
-                justifyContents:[
-                    {name:"左对齐",value:""},
-                    {name:"居中",value:"center"},
-                    {name:"右对齐",value:"flex-end"},
+                justifyContents: [
+                    {name: "左对齐", value: ""},
+                    {name: "居中", value: "center"},
+                    {name: "右对齐", value: "flex-end"},
                 ],
-                alignItems:[
-                    {name:"顶部",value:""},
-                    {name:"居中",value:"center"},
-                    {name:"底部",value:"flex-end"},
+                alignItems: [
+                    {name: "顶部", value: ""},
+                    {name: "居中", value: "center"},
+                    {name: "底部", value: "flex-end"},
                 ],
-                borderStyles:[
-                    {name:"默认边框",value:"solid"},
-                    {name:"无边框",value:"none"},
-                    {name:"虚线边框",value:"dashed"},
+                borderStyles: [
+                    {name: "默认边框", value: "solid"},
+                    {name: "无边框", value: "none"},
+                    {name: "虚线边框", value: "dashed"},
                 ]
             },
             mounted() {
                 $("#create-print-part").removeClass('d-none');
             },
-            methods:{
-                show(){
+            methods: {
+                show() {
                     $('#saveModal').modal('show');
                 },
-                getStyle(){
+                showUploadModal(){
+                    $('#uploadFileModel').modal('show');
+                },
+                getStyle() {
                     return {
-                        'display':'flex',
-                        'width':this.template.width+'px',
-                        'height':this.template.height+'px',
-                        'border-style':this.template.borderStyle,
-                        'border-width':this.template.borderWidth+'px',
-                        'font-size':this.template.fontSize+'px',
-                        'font-weight':this.template.fontWeight,
-                        'justify-content':this.template.justifyContent,
-                        'align-items':this.template.alignItems,
+                        'display': 'flex',
+                        'width': this.template.width + 'px',
+                        'height': this.template.height + 'px',
+                        'border-style': this.template.borderStyle,
+                        'border-width': this.template.borderWidth + 'px',
+                        'font-size': this.template.fontSize + 'px',
+                        'font-weight': this.template.fontWeight,
+                        'justify-content': this.template.justifyContent,
+                        'align-items': this.template.alignItems,
                     };
                 },
-                create(){
+                create() {
                     tempTip.setDuration(3000);
                     tempTip.setIndex(2000);
                     let value = JSON.stringify(this.template);
-                    let data = {name:this.printPart.name,value:value};
-                    window.axios.post('{{url('apiLocal/maintenance/expressPrinting/part/create')}}',data).then(res=>{
-                        if(res.data.success){
+                    let data = {name: this.printPart.name, value: value};
+                    window.axios.post('{{url('apiLocal/maintenance/expressPrinting/part/create')}}', data).then(res => {
+                        if (res.data.success) {
                             tempTip.showSuccess(res.data.data);
                             $('#saveModal').modal('hide');
                             return;
                         }
                         tempTip.show(res.data.message);
+                    }).catch(err => {
+                        tempTip.show('网络异常:' + err);
+                    });
+                },
+                setContent() {
+                    if (this.template.type === "text") return;
+
+                    if (this.template.type === "StripeCode") {
+                        let div = $("#barcodeDiv");
+                        this.template.borderStyle = 'none';
+                        window.setBarcode("0123456789", "#barcodeDiv1", 2, 50, true)
+                        this.template.width = div.width();
+                        this.template.height = div.height();
+                    } else if (this.template.type === "QrCode") {
+                        $("#qrCodeDiv").html("");
+                        new QRCode(document.getElementById("qrCodeDiv"), {
+                            text: "占位二维码",
+                            width: 128,
+                            height: 128,
+                            colorDark: "#000000",
+                            colorLight: "#fff",
+                            correctLevel: QRCode.CorrectLevel.H,
+                        });
+                    } else if (this.template.type === "Image") {
+                        $("#qrCodeDiv").html("#ImageDiv");
+                    }
+                },
+                pushImage(){
+                    let url = '{{url("apiLocal/maintenance/expressPrinting/part/pushImage")}}'
+                    let data = new FormData();
+                    data.set("name",$("#fileName").val());
+                    let file= this.$refs.upload.files[0];
+                    data.set("file",file);
+                    window.tempTip.setIndex(2000);
+                    window.axios.post(url,data,{
+                        'Content-Type': 'multipart/form-data'
+                    }).then(res=>{
+                        if(res.data.success){
+                            window.tempTip.showSuccess("文件添加成功");
+                            $('#uploadFileModel').modal('hide');
+                            return
+                        }
+                        window.tempTip.show(res.data.message);
                     }).catch(err=>{
-                        tempTip.show('网络异常:'+err);
+                        window.tempTip.show(err)
                     });
+
                 },
-                setContent(){
-                    if(this.template.type === "text")return;
-                    this.template.borderStyle = 'none';
-                    let div = $("#barcodeDiv1");
-                    window.setBarcode("0123456789","#barcodeDiv1",2,50,true)
-                    this.template.width = div.width();
-                    this.template.height = div.height();
-                }
+
             }
         });
     </script>

+ 358 - 0
resources/views/maintenance/expressPrinting/print/template.blade.php

@@ -0,0 +1,358 @@
+@extends('layouts.app')
+
+@section('title','面单打印')
+
+@section('content')
+    <div class="container  " id="list">
+        <div class="card">
+            <div class="card-header">
+                <div class="form-group">
+                    <label class="form-inline" for="printStr"></label>
+                    <input type="text" name="printStr" class="form-control" ref="printStr" id="printStr"
+                           placeholder="输入波次号,订单号,">
+                </div>
+            </div>
+            <div class="card-body">
+                <button class="btn btn-primary" @click="getPrintItems">获取</button>
+{{--                <button class="btn btn-outline-info" @click="testPreview"> 预览</button>--}}
+                <button class="btn btn-outline-primary" @click="uploadPrintData">WAS加工</button>
+{{--                <button class="btn btn-success" @click="wsPrintImage">ClientWebSocket 打印</button>--}}
+                <button class="btn btn-outline-warning" @click="initClientWebSocket">ClientWebSocket 重置</button>
+
+
+                <button class="btn btn-dark" @click="printImage">打印图片</button>
+
+                <button class="btn btn-dark" @click="getPrinters">获取打印机</button>
+            </div>
+
+            <div class="container">
+                <p>打印步骤</p>
+                <p>1:输入单号-获取</p>
+                <p>2:预览</p>
+                <p>3:加工</p>
+                <p>4:打印</p>
+            </div>
+            <div class="alert alert-success" v-if="ClientWebSocketStatus === 1">
+                链接成功
+            </div>
+            <div class="alert alert-primary" v-if="ClientWebSocketStatus === 2" @click="initClientWebSocket">
+                链接失败
+            </div>
+            <div class="alert alert-primary" v-if="ClientWebSocketStatus === 3" @click="initClientWebSocket">
+                链接错误
+            </div>
+
+        </div>
+        <div class="card">
+            <ul class="list-group">
+                <template v-for="item in printItems">
+                    <li class="list-group-item">
+                        <p> 编号:@{{ item.task_id }}</p>
+                        <p> 快递单号 :@{{ item.logistic_number }}</p>
+                        <div v-if="item.base64" class="position-relative">
+                            <img :src="'data:image/jpeg;base64,'+item.base64" alt="" style="width: 50%;height: 50%"
+                                 class="position-relative">
+                        </div>
+                    </li>
+                </template>
+            </ul>
+
+            <img src="" alt="" id="cImage">
+        </div>
+    </div>
+
+    </div>
+@endsection
+
+
+@section('lastScript')
+    <script type="text/javascript" src='http://localhost:8000/CLodopfuncs.js?name=CLODOPA'></script>
+
+    <script>
+        let WebSocketStatus = {
+            Close: 0,
+            Open: 1,
+            Error: 2,
+        }
+        let vue = new Vue({
+            el: '#list',
+            data: {
+                printItems: [],
+                ClientWebSocketPath: "ws://127.0.0.1:11011/printer",
+                ClientWebSocket: null,
+                ClientWebSocketStatus: 0,
+                cnWebSocket: null,
+                cnWebSocketPath: "ws://127.0.0.1:13528",
+                cnWebSocketStatus: null,
+                pddWebSocket: null,
+                pddWebSocketPath: "ws://127.0.0.1:5000",
+                pddWebSocketStatus: null,
+                clientPrinters: {
+                    defaultPrinter: "",
+                    printers: [],
+                },
+            },
+            created() {
+
+            },
+            mounted() {
+                // 初始化 go 链接
+                this.initClientWebSocket();
+                this.initPddWebSocket();
+                this.initCnWebSocket();
+            },
+            methods: {
+                initPddWebSocket() {
+                    if (this.pddWebSocket && this.pddWebSocket.readyState === 2) return
+                    this.pddWebSocket = new window.WebSocket(this.pddWebSocketPath)
+                    let self = this;
+                    this.pddWebSocket.onclose = function () {
+                        self.pddWebSocketStatus = WebSocketStatus.Close
+                    }
+                    this.pddWebSocket.onopen = function () {
+                        self.pddWebSocketStatus = WebSocketStatus.Open
+                    }
+                    this.pddWebSocket.onerror = function () {
+                        self.pddWebSocketStatus = WebSocketStatus.Error
+                    }
+                    this.pddWebSocket.onmessage = this.handlerPddWebsocket
+                },
+                initCnWebSocket() {
+                    if (this.cnWebSocket && this.cnWebSocket.readyState === 2) return
+                    this.cnWebSocket = new window.WebSocket(this.cnWebSocketPath)
+                    let self = this;
+                    this.cnWebSocket.onclose = function () {
+                        self.cnWebSocketStatus = WebSocketStatus.Close
+                    }
+                    this.cnWebSocket.onopen = function () {
+                        self.cnWebSocketStatus = WebSocketStatus.Open
+                    }
+                    this.cnWebSocket.onerror = function () {
+                        self.cnWebSocketStatus = WebSocketStatus.Error
+                    }
+                    this.cnWebSocket.onmessage = this.handlerCnWebsocket
+                },
+                // TODO 获取打印参数
+                getPrintItems() {
+                    let url = '{{url('apiLocal/maintenance/print/getData')}}'
+                    let data = {
+                        printStr: this.$refs.printStr.value,
+                    };
+                    if (data.printStr.trim().length === 0) {
+                        window.tempTip.show('输入后在获取')
+                    }
+                    let _this =this;
+                    window.axios.post(url, data).then(res => {
+                        this.printItems =this.printItems.concat(res.data.data);
+                         res.data.data.forEach((item,i,array)=>{
+                             _this.previewLogisticFace(item);
+                        })
+                        this.$forceUpdate();
+                    }).catch(err => {
+                        console.log(err);
+                    });
+                },
+                // TODO ClientWs 返回信息
+                receiveClientWebSocketMessage(meg) {
+                    let data = JSON.parse(meg.data);
+                    if (data['Status'] !== true){return ;}
+                    let _this = this;
+                    switch (data["ProcessType"]) {
+                        case "getBase64Image":
+                            this.printItems.forEach((item)=>{
+                                if(item['task_id'] === data["task_id"]){
+                                    item['base64'] = data['Base64'];
+                                    _this.uploadPrintData(item);
+                                }
+                            });
+                            break;
+                        case "printImage":
+                            this.printItems.forEach((item,index,array)=>{
+                                if(item['taskID'] === data["TaskID"]){
+                                    array[index]['printed'] = true;
+                                }
+                            });
+                            break;
+                        case "getPrinters":
+                            this.clientPrinters.printers = data["printes"];
+                            this.clientPrinters.defaultPrinter = (this.clientPrinters.printers).filter((item,i,array)=>{
+                               return item["IsDefault"] === true;
+                            })
+                            break
+                        default:
+                            break;
+                    }
+
+                },
+                uploadPrintData(item) {
+                    let url = "{{url("apiLocal/maintenance/print/uploadPrintData")}}";
+                    let data = {printData: [item]};
+                    let _this = this;
+                    window.axios.post(url, data).then(res => {
+                        if (res.data.success){
+                             this.printItems.forEach((item)=>{
+                                res.data.data.forEach(node=>{
+                                    if(node['task_id'] === item["task_id"]){
+                                        item['base64'] = node['base64'];
+                                        _this.printImage(item);
+                                        //_this.LodopPrint(item);
+                                    }
+                                })
+
+                            });
+                        }
+                    }).catch(err => {
+                        alert(err);
+                    });
+                },
+                initClientWebSocket() {
+                    if (this.ClientWebSocket && this.ClientWebSocket.readyState === 1) return this.ClientWebSocket;
+                    this.ClientWebSocketStatus = 2;
+                    let self = this;
+                    this.ClientWebSocket = new window.WebSocket(this.ClientWebSocketPath);
+                    this.ClientWebSocket.onmessage = this.receiveClientWebSocketMessage;
+                    this.ClientWebSocket.onopen = function () {
+                        self.ClientWebSocketStatus = 1
+                    };
+                    this.ClientWebSocket.onclose = function () {
+                        self.ClientWebSocketStatus = 2
+                    };
+
+                    this.ClientWebSocket.onerror = function () {
+                        self.ClientWebSocketStatus = 3
+                    }
+                },
+                // TODO ClientWebSocket 链接成功
+                ClientWebSocketOpen() {
+                    this.ClientWebSocketStatus = 0;
+                },
+                // TODO ClientWebSocket 链接关闭
+                ClientWebSocketClose() {
+                    this.ClientWebSocketStatus = 1;
+                },
+                // TODO  ClientWebSocket 关闭
+                closeClientWebSocket() {
+                    this.ClientWebSocketStatus = 2;
+                    this.ClientWebSocket.close();
+                },
+                // TODO 获取预览面单
+                previewLogisticFace(data) {
+                    if (data.type === "CAINIAO") {
+                        let json = this.getCnPreviewTaskJson(data)
+                        if (this.cnWebSocket && this.cnWebSocket.readyState === 1) {
+                             this.cnWebSocket.send(json)
+                        }
+                    } else if (data.type === "PDD") {
+                        let json = this.getPddPreviewTaskJson(data)
+                        if (this.pddWebSocket && this.pddWebSocket.readyState === 1) {
+                            this.pddWebSocket.send(json)
+                        }
+                    }
+                },
+                handlerCnWebsocket(message) {
+                    let data = JSON.parse(message.data);
+                    if (data.status === 'failed'){
+                        window.tempTip.show(data.msg);
+                        return ;
+                    }
+                    if (data.cmd === 'print') {
+                        let url = data['previewImage'][0];
+                        this.printItems.forEach((item)=>{
+                            if (item['task_id'] === data['taskID']){
+                                item.url = url;
+                                this.getBase64Image(item);
+                            }
+                        })
+                    }
+                },
+                handlerPddWebsocket(message) {
+                    let data = JSON.parse(message.data);
+                    // TODO 获取图片链接
+                    if (data.cmd === "PrintResultNotify") {
+                        this.getBase64Image(data['previewURL'], data);
+                    }
+                },
+                // 发送 预览面单信息
+                getPddPreviewTaskJson(item) {
+                    return JSON.stringify({
+                        cmd: "print",
+                        requestID: item['task_id'],
+                        version: "1.0",
+                        task: {
+                            taskID: item['task_id'],
+                            previewType: "jpg",
+                            notifyType: null,
+                            preview: true,
+                            printer: null,
+                            firstDocumentNumber: 0,
+                            totalDocumentCount: 1,
+                            documents: [{
+                                documentID: '11111',
+                                contents: [{
+                                    encryptedData: item['data']['encryptedData'],
+                                    signature: item['data']['signature'],
+                                    templateUrl: item['data']['templateUrl'],
+                                    ver: item['data']['ver'],
+                                }]
+                            }],
+                        }
+                    })
+                },
+                // 发送 菜鸟 预览信息
+                getCnPreviewTaskJson(item) {
+                    return JSON.stringify({
+                        cmd: 'print',
+                        request_id: item['task_id'],
+                        version: '1.0',
+                        task: {
+                            preview: true,
+                            previewType: 'image',
+                            printer: item['printerName'],
+                            taskID: item['task_id'],
+                            firstDocumentNumber: 0,
+                            totalDocumentCount: 1,
+                            documents: [{
+                                documentID: "11111",
+                                contents: [{
+                                    encryptedData: item['data']['encryptedData'],
+                                    signature: item['data']['signature'],
+                                    templateURL: item['data']['templateURL'],
+                                    ver: item['data']['ver'],
+                                }],
+                            }],
+                        },
+                    });
+                },
+                getPrinters() {
+                    let data = {ProcessType:"getPrinters"};
+                    this.ClientWebSocket.send(JSON.stringify(data));
+                },
+                setClientPrinters(data) {
+                    this.clientPrinters.defaultPrinter = data['defaultPrinter'];
+                    this.clientPrinters.printers = data['printers'];
+                },
+                // 获取图片
+                getBase64Image(item){
+                    let data = JSON.parse(JSON.stringify(item));
+                    data.ProcessType = "getBase64Image";
+                    this.ClientWebSocket.send(JSON.stringify(data));
+                },
+                // 打印图片
+                printImage(item){
+                    let data = JSON.parse(JSON.stringify(item));
+                    data.processType = "printImage";
+                    data.p
+                    this.ClientWebSocket.send(JSON.stringify(data));
+                },
+                LodopPrint(item){
+                    let LODOP = getCLodop();
+                    LODOP.PRINT_INIT("打印插件功能演示_Lodop功能_BASE64编码串打印图片");
+                    LODOP.ADD_PRINT_IMAGE(100,100,"100%","100%",'data:image/jpeg;base64,'+item.base64);
+                    LODOP.ADD_PRINT_RECT("0%","0%","100%","100%",0,1)
+                    LODOP.PREVIEW();
+                }
+            }
+        });
+
+    </script>
+@endsection

+ 109 - 0
resources/views/maintenance/expressPrinting/setting/printer/_create.blade.php

@@ -0,0 +1,109 @@
+<div class="modal " id="create-printer" tabindex="-1">
+    <div class="modal-dialog modal-xl">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title text-center">终端-打印机添加</h5>
+                <button type="button" class="close" data-dismiss="modal">
+                    <span>&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <form class="form">
+                    <div class="form-group row">
+                        <label for="create_printer_terminal_id" class="col-sm-3 col-form-label text-right">所属终端</label>
+                        <div class="col-sm-9 form-inline">
+                            <select name="printer_terminal_id" id="create_printer_terminal_id"
+                                    class="form-control col-sm-5" v-model="printer.terminal_id">
+                                <option value=""></option>
+                                <template v-for="terminal in filterTerminals">
+                                    <option :value="terminal.id">
+                                        <span v-text="terminal.name"></span>
+                                    </option>
+                                </template>
+                            </select>
+                            <label class="col-sm-2 offset-sm-1">
+                                <input type="text" class="form-control offset-sm-1" v-model="terminalFilter"
+                                       placeholder="终端筛选">
+                            </label>
+                            <div class="invalid-feedback" v-if="printerErrors.terminal_id">
+                                @{{ printerErrors.terminal_id[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="create_printer_printer_name"
+                               class="col-sm-3 col-form-label text-right">打印机名称</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="create_printer_printer_name" class="form-control col-9"
+                                   v-model="printer.printer_name"
+                                   placeholder="打印机名称"
+                                   :class="printerErrors.printer_name?'is-invalid':''"
+                                   @focus="printerErrors.printer_name= null">
+                            <div class="invalid-feedback" v-if="printerErrors.printer_name">
+                                @{{ printerErrors.printer_name[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="create_printer_alias_name" class="col-sm-3 col-form-label text-right">名称</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="create_printer_alias_name" class="form-control col-9"
+                                   v-model="printer.alias_name"
+                                   placeholder="名称"
+                                   :class="printerErrors.alias_name?'is-invalid':''"
+                                   @focus="printerErrors.alias_name= null">
+                            <div class="invalid-feedback" v-if="printerErrors.alias_name">
+                                @{{ printerErrors.alias_name[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="create_printer_print_type" class="col-sm-3 col-form-label text-right">关联</label>
+                        <div class="col-sm-9 form-inline">
+                            <div class="col-9 form-inline">
+                                <div class="card col-6 p-0">
+                                    <div class="card-header">
+                                        <input class="form-control" type="text" placeholder="筛选" v-model="filter['logistic']">
+                                    </div>
+                                    <div class="card-body overflow-scrollbar-200 p-0 m-0" style="max-height: 300px">
+                                        <ul class="list-group">
+                                            <template v-for="logistic in logistics">
+                                                <li class="list-group-item"  @dblclick="addToLogistics(logistic)" v-if="includeLogistics(logistic)">
+                                                    @{{ logistic.name }}
+                                                </li>
+                                            </template>
+                                        </ul>
+                                    </div>
+                                </div>
+                                <div class="card col-6 p-0">
+                                    <div class="card-header" style="max-height: 300px">
+                                        <input class="form-control" type="text" placeholder="筛选" v-model="filter['printer_logistic']">
+                                    </div>
+                                    <div class="card-body overflow-scrollbar-200 p-0 m-0" style="max-height: 300px">
+                                        <ul class="list-group">
+                                            <template v-for="logistic in logistics">
+                                                <li class="list-group-item"  @dblclick="removeToLogistics(logistic)" v-if="includePrintLogistic(logistic)">
+                                                    @{{ logistic.name }}
+                                                </li>
+                                            </template>
+                                        </ul>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="invalid-feedback" v-if="printerErrors.print_type">
+                                @{{ printerErrors.print_type[0] }}
+                            </div>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" @click="createPrinter">提交</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 104 - 0
resources/views/maintenance/expressPrinting/setting/printer/_edit.blade.php

@@ -0,0 +1,104 @@
+<div class="modal " id="edit-printer" tabindex="-1">
+    <div class="modal-dialog modal-lg modal-dialog-centered">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title text-center">终端-打印机编辑</h5>
+                <button type="button" class="close" data-dismiss="modal">
+                    <span>&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <form class="form">
+                    <div class="form-group row">
+                        <label for="edit_printer_terminal_id" class="col-sm-3 col-form-label text-right">所属终端</label>
+                        <div class="col-sm-9 form-inline">
+                            <select name="printer_terminal_id" id="edit_printer_terminal_id" class="form-control col-sm-5" v-model="printer.terminal_id">
+                                <option value=""></option>
+                                <template v-for="terminal in filterTerminals">
+                                    <option :value="terminal.id">
+                                        <span v-text="terminal.name"></span>
+                                    </option>
+                                </template>
+                            </select>
+                            <label class="col-sm-2 offset-sm-1">
+                                <input type="text" class="form-control offset-sm-1"  v-model="terminalFilter" placeholder="终端筛选">
+                            </label>
+                            <div class="invalid-feedback" v-if="printerErrors.terminal_id">
+                                @{{ printerErrors.terminal_id[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="edit_printer_printer_name" class="col-sm-3 col-form-label text-right">打印机名称</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="edit_printer_printer_name" class="form-control col-9" v-model="printer.printer_name"
+                                   placeholder="打印机名称"
+                                   :class="printerErrors.printer_name?'is-invalid':''"
+                                   @focus="printerErrors.printer_name= null">
+                            <div class="invalid-feedback" v-if="printerErrors.printer_name">
+                                @{{ printerErrors.printer_name[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="edit_printer_alias_name" class="col-sm-3 col-form-label text-right">名称</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="edit_printer_alias_name" class="form-control col-9" v-model="printer.alias_name"
+                                   placeholder="名称"
+                                   :class="printerErrors.alias_name?'is-invalid':''"
+                                   @focus="printerErrors.alias_name= null">
+                            <div class="invalid-feedback" v-if="printerErrors.alias_name">
+                                @{{ printerErrors.alias_name[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="edit_printer_print_type" class="col-sm-3 col-form-label text-right">打印机类型</label>
+                        <div class="col-sm-9 form-inline">
+                           <div class="col-9 form-inline">
+                                <div class="card col-6 p-0">
+                                    <div class="card-header">
+                                        <input class="form-control" type="text" placeholder="筛选" v-model="filter['logistic']">
+                                    </div>
+                                    <div class="card-body overflow-scrollbar-200 p-0 m-0" style="max-height: 300px">
+                                        <ul class="list-group">
+                                            <template v-for="logistic in logistics">
+                                                <li class="list-group-item"  @dblclick="addToLogistics(logistic)" v-if="includeLogistics(logistic)">
+                                                    @{{ logistic.name }}
+                                                </li>
+                                            </template>
+                                        </ul>
+                                    </div>
+                                </div>
+                                <div class="card col-6 p-0">
+                                    <div class="card-header" style="max-height: 300px">
+                                        <input class="form-control" type="text" placeholder="筛选" v-model="filter['printer_logistic']">
+                                    </div>
+                                    <div class="card-body overflow-scrollbar-200 p-0 m-0" style="max-height: 300px">
+                                        <ul class="list-group">
+                                            <template v-for="logistic in logistics">
+                                                <li class="list-group-item"  @dblclick="removeToLogistics(logistic)" v-if="includePrintLogistic(logistic)">
+                                                    @{{ logistic.name }}
+                                                </li>
+                                            </template>
+                                        </ul>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="invalid-feedback" v-if="printerErrors.print_type">
+                                @{{ printerErrors.print_type[0] }}
+                            </div>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" @click="editPrinter">提交</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 25 - 0
resources/views/maintenance/expressPrinting/setting/printer/_table.blade.php

@@ -0,0 +1,25 @@
+<table class="table table-striped table-sm table-hover" id="table">
+    <thead>
+    <tr>
+        <td>序号</td>
+        <td>打印机名</td>
+        <td>名称</td>
+        <td>终端名称</td>
+        <td>操作</td>
+    </tr>
+    </thead>
+    <tbody>
+    <tr v-for="(printer,i) in printers" @click="selectTr===i+1?selectTr=0:selectTr=i+1"
+        :class="selectTr===i+1?'focusing' : ''">
+        <td>@{{ i+1 }}</td>
+        <td>@{{ printer.printer_name }}</td>
+        <td>@{{ printer.alias_name }}</td>
+        <td>@{{ printer.terminal ? printer.terminal.name : '' }}</td>
+        <td>
+            <button class="btn btn-sm btn-primary" @click="showEditModel(printer,i)">编辑</button>
+            <button class="btn btn-sm btn-danger " style="opacity: 0.9" @click="destroyPrinter(printer,i)">删除</button>
+        </td>
+    </tr>
+    </tbody>
+</table>
+{{ $terminalPrinters->links()}}

+ 178 - 0
resources/views/maintenance/expressPrinting/setting/printer/index.blade.php

@@ -0,0 +1,178 @@
+@extends('layouts.app')
+
+@section('title','打印终端')
+
+@section('content')
+    <div class="container-fluid " id="list">
+        <div class="card">
+{{--            @can('基础设置-快递打印-打印机-添加')--}}
+                @include('maintenance.expressPrinting.setting.printer._create')
+{{--            @endcan--}}
+{{--            @can('基础设置-快递打印-打印机-编辑')--}}
+                @include('maintenance.expressPrinting.setting.printer._edit')
+{{--            @endcan--}}
+            <div class="card-body">
+                <div class="row pull-left m-1">
+{{--                    @can('基础设置-快递打印-打印机-添加')--}}
+                        <button class="btn btn-outline-info mb-1 mr-3" @click="showCreatedModel"><span
+                                class="fa fa-plus"></span>&nbsp;新&nbsp;&nbsp;增
+                        </button>
+{{--                    @endcan--}}
+                </div>
+                @include('maintenance.expressPrinting.setting.printer._table')
+            </div>
+        </div>
+    </div>
+@endsection
+
+@section('lastScript')
+    <script>
+        var list = new Vue({
+            el: "#list",
+            data: {
+                printers: {!! $terminalPrinters->toJson() !!}['data'],
+                terminals: [
+                    @foreach($terminals as $terminal)
+                    {id:'{{$terminal->id}}',name:'{{$terminal->name}}'},
+                    @endforeach
+                ],
+                logistics:[
+                    @foreach($logistics as $logistic)
+                    {id:'{{$logistic->id}}',name:'{{$logistic->name}}'},
+                    @endforeach
+                ],
+                printer: {
+                    logistic_ids:[],
+                },
+                printerErrors: {},
+                terminalFilter:null,
+                filter:{
+                    logistic:null,      // 删选 logistic
+                    printer_logistic:null, // 删选 printer->logistic
+                },
+                index: null,
+                selectTr: null,
+            },
+            mounted() {
+                $("#list").removeClass('d-none');
+            },
+            computed:{
+                filterTerminals(){
+                    let terminals = JSON.parse(JSON.stringify(this.terminals));
+                    let self = this;
+                    if(this.terminalFilter === null){
+                        return terminals;
+                    }
+                    let terminalFilter = terminals.filter(item=>{
+                        return item.name.indexOf(self.terminalFilter) >= 0;
+                    });
+                    if (terminalFilter.length < this.terminals.length && terminalFilter.length>0)
+                        this.printer.terminal_id = terminalFilter[0]['id'];
+                    return terminalFilter;
+                },
+
+            },
+            methods: {
+                includeLogistics(item){
+                    let bool = !this.printer.logistic_ids.includes(item.id);
+                    if (bool && this.filter.logistic !== null && this.filter.logistic.length > 0)
+                        return item.name.includes(this.filter.logistic)
+                    return bool;
+                },
+                includePrintLogistic(item){
+                    if (this.printer.logistic_ids.length === 0) return false;
+                    let bool = this.printer.logistic_ids.includes(item.id);
+                    if (bool && this.filter.printer_logistic !== null  && this.filter.printer_logistic.length > 0)
+                        return item.name.includes(this.filter.printer_logistic)
+                    return bool;
+                },
+                showCreatedModel() {
+                    this.printer.logistic_ids = [];
+                    this.printerErrors = {};
+                    this.filter.logistic = null;
+                    this.filter.printer_logistic = null;
+                    $('#create-printer').modal('show');
+                },
+                showEditModel(printer, index) {
+                    let data = JSON.parse(JSON.stringify(printer));
+                    data.logistics = null;
+                    data.logistic_ids = printer.logistics.map(item=>{
+                        return ""+item.id;
+                    });
+                    this.printer = data;
+                    this.$forceUpdate();
+                    this.index = index;
+                    this.printerErrors = {};
+                    this.filter.logistic = null;
+                    this.filter.printer_logistic = null;
+                    $('#edit-printer').modal('show');
+                },
+                createPrinter() {
+                    let url = '{{url('apiLocal/maintenance/expressPrinting/setting/printer')}}';
+                    let data = this.printer;
+                    window.tempTip.setIndex(1999)
+                    window.axios.post(url, data).then(res => {
+                        if (res.data['success']) {
+                            this.printers.unshift(res.data['data']);
+                            this.$forceUpdate();
+                            $('#create-printer').modal('hide');
+                            window.tempTip.showSuccess('添加成功!');
+                            return;
+                        } else if (res.data['errors']) {
+                            this.printerErrors = res.data['errors'];
+                            return;
+                        }
+                        window.tempTip.show(res.data['message']);
+                    }).catch(err => {
+                        window.tempTip.show('网络异常!' + err);
+                    })
+
+                },
+                editPrinter() {
+                    let url = '{{url('apiLocal/maintenance/expressPrinting/setting/printer')}}';
+                    let data = this.printer;
+                    window.tempTip.setIndex(1999)
+                    window.axios.put(url, data).then(res => {
+                        if (res.data['success']) {
+                            this.$set(this.printers, this.index, res.data['data']);
+                            window.tempTip.showSuccess('编辑成功!');
+                            $('#edit-printer').modal('hide');
+                            return;
+                        } else if (res.data['errors']) {
+                            this.printerErrors = res.data['errors'];
+                            return;
+                        }
+                        window.tempTip.show(res.data['message'])
+                    }).catch(err => {
+                        window.tempTip.show('网络异常!' + err)
+                    });
+                },
+                destroyPrinter(printer, index) {
+                    let url = '{{url('apiLocal/maintenance/expressPrinting/setting/printer')}}' + '/' + printer['id'];
+                    if (!confirm('是否删除当前终端')) return;
+                    window.axios.delete(url).then(res => {
+                        if (res.data['success']) {
+                            this.$delete(this.printers, index);
+                            window.tempTip.showSuccess('删除成功!');
+                            return;
+                        } else if (res.data['errors']) {
+                            this.printerErrors = res.data['errors'];
+                            return;
+                        }
+                        window.tempTip.show(res.data['message'])
+                    }).catch(err => {
+                        window.tempTip.show('网络异常!' + err)
+                    });
+                },
+                addToLogistics(logistic){
+                    this.printer.logistic_ids.push(logistic.id)
+                },
+                removeToLogistics(logistic){
+                    this.printer.logistic_ids = this.printer.logistic_ids.filter(item=>{
+                        return item !== logistic.id;
+                    });
+                }
+            }
+        })
+    </script>
+@endsection

+ 44 - 0
resources/views/maintenance/expressPrinting/setting/terminal/_create.blade.php

@@ -0,0 +1,44 @@
+<div class="modal " id="create-terminal" tabindex="-1">
+    <div class="modal-dialog modal-lg modal-dialog-centered">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title text-center">终端添加</h5>
+                <button type="button" class="close" data-dismiss="modal">
+                    <span>&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <form class="form">
+                    <div class="form-group row">
+                        <label for="add-name" class="col-sm-3 col-form-label text-right">名称</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="add-name" class="form-control col-9" v-model="terminal.name"
+                                   placeholder="终端名称"
+                                   :class="terminalErrors.name?'is-invalid':''"
+                                   @focus="terminalErrors.name= null">
+                            <div class="invalid-feedback" v-if="terminalErrors.name">
+                                @{{ terminalErrors.name[0] }}
+                            </div>
+                        </div>
+                    </div>
+                    <div class="form-group row">
+                        <label for="add-name" class="col-sm-3 col-form-label text-right">IP</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="add-name" class="form-control col-9" v-model="terminal.ip"
+                                   placeholder="终端IP"
+                                   :class="terminalErrors.ip?'is-invalid':''"
+                                   @focus="terminalErrors.ip= null">
+                            <div class="invalid-feedback" v-if="terminalErrors.ip">
+                                @{{ terminalErrors.ip[0] }}
+                            </div>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" @click="createTerminal">提交</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 45 - 0
resources/views/maintenance/expressPrinting/setting/terminal/_edit.blade.php

@@ -0,0 +1,45 @@
+<div class="modal " id="edit-terminal" tabindex="-1">
+    <div class="modal-dialog modal-lg modal-dialog-centered">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title text-center">终端编辑</h5>
+                <button type="button" class="close" data-dismiss="modal">
+                    <span>&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <form class="form">
+                    <div class="form-group row">
+                        <label for="add-name" class="col-sm-3 col-form-label text-right">名称</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="add-name" class="form-control col-9" v-model="terminal.name"
+                                   placeholder="终端名称"
+                                   :class="terminalErrors.name?'is-invalid':''"
+                                   @focus="terminalErrors.name= null">
+                            <div class="invalid-feedback" v-if="terminalErrors.name">
+                                @{{ supplierErrors.name[0] }}
+                            </div>
+                        </div>
+                    </div>
+                    <div class="form-group row">
+                        <label for="add-name" class="col-sm-3 col-form-label text-right">IP</label>
+                        <div class="col-sm-9 form-inline">
+                            <input type="text" id="add-name" class="form-control col-9" v-model="terminal.ip"
+                                   placeholder="终端IP"
+                                   :class="terminalErrors.ip?'is-invalid':''"
+                                   @focus="terminalErrors.ip= null">
+                            <div class="invalid-feedback" v-if="terminalErrors.ip">
+                                @{{ supplierErrors.ip[0] }}
+                            </div>
+                        </div>
+                    </div>
+
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary" @click="editTerminal">提交</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 27 - 0
resources/views/maintenance/expressPrinting/setting/terminal/_table.blade.php

@@ -0,0 +1,27 @@
+<table><table class="table table-striped table-sm table-hover" id="table">
+
+    <thead>
+    <tr>
+        <td>序号</td>
+        <td>名称</td>
+        <td>ip</td>
+        <td>打印机</td>
+        <td>操作</td>
+    </tr>
+    </thead>
+    <tbody>
+        <tr v-for="(terminal,i) in terminals" @click="selectTr===i+1?selectTr=0:selectTr=i+1" :class="selectTr===i+1?'focusing' : ''">
+            <td>@{{ i+1 }}</td>
+            <td>@{{ terminal.name }}</td>
+            <td>@{{ terminal.ip }}</td>
+            <td>
+
+            </td>
+            <td>
+                <button class="btn btn-sm btn-primary" @click="showEditModel(terminal,i)">编辑</button>
+                <button class="btn btn-sm btn-danger " style="opacity: 0.9" @click="destroyTerminal(terminal,i)">删除</button>
+            </td>
+        </tr>
+    </tbody>
+</table>
+{{$terminals->links()}}

+ 117 - 0
resources/views/maintenance/expressPrinting/setting/terminal/index.blade.php

@@ -0,0 +1,117 @@
+@extends('layouts.app')
+
+@section('title','打印机')
+
+@section('content')
+    <div class="container-fluid d-none" id="list">
+        <div class="card">
+{{--            @can('基础设置-快递打印-终端-添加')--}}
+                @include('maintenance.expressPrinting.setting.terminal._create')
+{{--            @endcan--}}
+{{--            @can('基础设置-快递打印-终端-编辑')--}}
+                @include('maintenance.expressPrinting.setting.terminal._edit')
+{{--            @endcan--}}
+            <div class="card-body">
+                <div class="row pull-left m-1">
+{{--                    @can('基础设置-快递打印-终端-添加')--}}
+                        <button class="btn btn-outline-info mb-1 mr-3" @click="showCreatedModel"><span
+                                class="fa fa-plus"></span>&nbsp;新&nbsp;&nbsp;增
+                        </button>
+{{--                    @endcan--}}
+                </div>
+                @include('maintenance.expressPrinting.setting.terminal._table')
+            </div>
+        </div>
+    </div>
+@endsection
+
+@section('lastScript')
+    <script>
+        let list = new Vue({
+            el: '#list',
+            data: {
+                terminals: {!!$terminals->toJson()!!}['data'],
+                terminal: {},
+                selectTr: null,
+                index: null,
+                terminalErrors: {},
+            },
+            mounted() {
+                $('#list').removeClass('d-none');
+            },
+            created() {
+            },
+            methods: {
+                showCreatedModel() {
+                    this.terminal = {};
+                    this.terminalErrors = {};
+                    $('#create-terminal').modal('show');
+                },
+                showEditModel(terminal,index) {
+                    this.terminal = JSON.parse(JSON.stringify(terminal));
+                    this.index = index;
+                    this.terminalErrors = {};
+                    $('#edit-terminal').modal('show');
+                },
+                createTerminal() {
+                    let url = '{{url('apiLocal/maintenance/expressPrinting/setting/terminal')}}';
+                    let data = this.terminal;
+                    window.tempTip.setIndex(1999)
+                    window.axios.post(url, data).then(res => {
+                        if (res.data['success']) {
+                            this.terminals.unshift(res.data['data']);
+                            this.$forceUpdate();
+                            $('#create-terminal').modal('hide');
+                            window.tempTip.showSuccess('添加成功!');
+                            return ;
+                        } else if (res.data['errors']){
+                            this.terminalErrors = res.data['errors'];
+                            return;
+                        }
+                        window.tempTip.show(res.data['message']);
+                    }).catch(err => {
+                        window.tempTip.show('网络异常!'+err);
+                    })
+
+                },
+                editTerminal() {
+                    let url = '{{url('apiLocal/maintenance/expressPrinting/setting/terminal')}}';
+                    let data = this.terminal;
+                    window.tempTip.setIndex(1999)
+                    window.axios.put(url, data).then(res => {
+                        if (res.data['success']) {
+                            this.$set(this.terminals,this.index,res.data['data']);
+                            window.tempTip.showSuccess('编辑成功!');
+                            $('#edit-terminal').modal('hide');
+                            return ;
+                        } else if (res.data['errors']){
+                            this.terminalErrors = res.data['errors'];
+                            return;
+                        }
+                        window.tempTip.show(res.data['message'])
+                    }).catch(err => {
+                        window.tempTip.show('网络异常!'+err)
+                    });
+                },
+                destroyTerminal(terminal,index){
+                    let url = '{{url('apiLocal/maintenance/expressPrinting/setting/terminal')}}'+'/'+terminal['id'];
+                    if (!confirm('是否删除当前终端'))return;
+                    window.axios.delete(url).then(res => {
+                        if (res.data['success']) {
+                            this.$delete(this.terminals,index);
+                            window.tempTip.showSuccess('删除成功!');
+                            return ;
+                        } else if (res.data['errors']){
+                            this.terminalErrors = res.data['errors'];
+                            return;
+                        }
+                        window.tempTip.show(res.data['message'])
+                    }).catch(err => {
+                        window.tempTip.show('网络异常!'+err)
+                    });
+                }
+            }
+        })
+    </script>
+
+@endsection

+ 29 - 25
resources/views/maintenance/expressPrinting/template/_compile.blade.php

@@ -4,23 +4,27 @@
             <div class="card-header">
                 <button type="button" class="btn btn-success"  @click="showSaveModal">保存当前打印模板</button>
             </div>
-            <div class="card-header" v-show="selectProducePanel">
+            <div class="card-header" v-if="productionParts.bg">
                 <span>背景版编辑</span>
             </div>
-            <div class="card-body" v-show="selectProducePanel">
+            <div class="card-body" v-if="productionParts.bg">
                 <div class="form-group m-0">
                     <label for="producePanel-width" class="col-form-label">宽</label>
-                    <input type="text" id="producePanel-width" class="form-control form-control-sm" v-model="producePanel.width">
+                    <input type="text" id="producePanel-width" class="form-control form-control-sm" v-model="productionParts.bg.width">
                 </div>
                 <div class="form-group m-0">
                     <label for="producePanel-height" class="col-form-label">高</label>
-                    <input type="text" id="producePanel-height" class="form-control form-control-sm" v-model="producePanel.height">
+                    <input type="text" id="producePanel-height" class="form-control form-control-sm" v-model="productionParts.bg.height">
                 </div>
             </div>
             <div class="card-header">
                 <span>编辑面板</span>
             </div>
             <div class="card-body" v-if="productionParts.editPart">
+                <div class="form-group m-0">
+                    <label for="compile-border-width" class="col-form-label">边框大小</label>
+                    <input type="text" id="compile-border-width" class="form-control form-control-sm" v-model="productionParts.editPart['border-width']">
+                </div>
                 <div class="form-group m-0">
                     <label for="compile-text" class="col-form-label">文本内容</label>
                     <textarea class="form-control" id="compile-text" cols="30" rows="5" v-model="productionParts.editPart.text"></textarea>
@@ -34,42 +38,43 @@
                     <label for="compile-top" class="col-form-label">top</label>
                     <input type="text" id="compile-top" class="form-control form-control-sm" v-model="productionParts.editPart.top">
                 </div>
-                <div class="form-group m-0">
+
+                <div class="form-group m-0"  v-if="productionParts.editPart.type === 'stripeCode'">
+                    <label for="compile-scale" class="col-form-label">比例</label>
+                    <input type="text" id="compile-scale" class="form-control form-control-sm"  v-model="productionParts.editPart.scale" @input="changeStripeCodeScale">
+                </div>
+
+                <div class="form-group m-0" v-if=" ['textBox','qRCode','stripeCode'].includes(productionParts.editPart.type) ">
                     <label for="compile-width" class="col-form-label">宽</label>
-                    <input type="text" id="compile-width" class="form-control form-control-sm" v-model="productionParts.editPart.width">
+                    <input type="text" id="compile-width" class="form-control form-control-sm"
+                           v-if="['qRCode','stripeCode'].includes(productionParts.editPart.type)"
+                           v-model="productionParts.editPart.width" @input="changeStripeCodeWidth()">
+                    <input type="text" v-else id="compile-width" class="form-control form-control-sm" v-model="productionParts.editPart.width" @input="changeStripeCodeWidth()">
                 </div>
-                <div class="form-group m-0">
+
+                <div class="form-group m-0" v-if="productionParts.editPart.type === 'textBox'">
                     <label for="compile-height" class="col-form-label text-sm-right">高</label>
                     <input type="text" id="compile-height" class="form-control form-control-sm" v-model="productionParts.editPart.height">
                 </div>
-                <div class="form-group m-0" >
+                <div class="form-group m-0"  v-if="productionParts.editPart['font-size']">
                     <label for="compile-font-size" class="col-form-label text-sm-right">字体大小</label>
-                    <input type="text" id="compile-font-size" class="form-control form-control-sm" v-model="productionParts.editPart.fontSize">
-                </div>
-                <div class="form-group m-0" >
-                    <label for="compile-font-weight" class="col-form-label text-sm-right">字体粗细</label>
-                    <select class="form-control" id="compile-font-weight" v-model="productionParts.editPart.fontWeight">
-                        <option  value="normal">正常</option>
-                        <option  value="lighter">细体</option>
-                        <option  value="bold">粗体</option>
-                    </select>
+                    <input type="text" id="compile-font-size" class="form-control form-control-sm" v-model="productionParts.editPart['font-size']">
                 </div>
                 <div class="form-group m-0" >
                     <label for="compile-z-index" class="col-form-label text-sm-right">图层</label>
                     <input type="text" id="compile-z-index" class="form-control form-control-sm" v-model="productionParts.editPart.z_index">
                 </div>
-                <div class="form-group m-0" >
+                <div class="form-group m-0" v-if="productionParts.editPart.type === 'textBox'">
                     <label for="compile-justify-content" class="col-form-label text-sm-right">文本水平对齐方式</label>
-                    <select class="form-control-sm form-control" id="compile-justify-content" v-model="productionParts.editPart.justifyContent">
-                        <option value="">左对齐</option>
+                    <select class="form-control-sm form-control" id="compile-justify-content" v-model="productionParts.editPart['justify-content']">
+                        <option value="flex-start">左对齐</option>
                         <option value="center">居中对齐</option>
-                        <option value="flex-end">右对齐</option>
                     </select>
                 </div>
-                <div class="form-group m-0" >
+                <div class="form-group m-0" v-if="productionParts.editPart.type === 'textBox'">
                     <label for="compile-text-align" class="col-form-label text-sm-right">文本垂直对齐方式</label>
-                    <select class="form-control-sm form-control " id="compile-text-align" v-model="productionParts.editPart.alignItems">
-                        <option value="">顶部</option>
+                    <select class="form-control-sm form-control " id="compile-text-align" v-model="productionParts.editPart['align-items']">
+                        <option value="flex-start">顶部</option>
                         <option value="center">垂直居中</option>
                         <option value="flex-end">顶部</option>
                     </select>
@@ -78,7 +83,6 @@
             <div class="card-footer" v-show="!selectProducePanel">
                 <button type="button" class="btn btn-outline-secondary" v-show="productionParts.editPart" @click="_removePartToProductionPanel(productionParts.editPart)">删除</button>
             </div>
-
         </form>
     </div>
 

+ 97 - 0
resources/views/maintenance/expressPrinting/template/_edit.blade.php

@@ -0,0 +1,97 @@
+<div class="modal " tabindex="-1" id="edit-template">
+    <div class="modal-dialog modal-xl">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">模板关联</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body row">
+                <div class="col">
+                    <div class="card">
+                        <div class="card-header">
+                            <input type="text" class="form-control form-inline" placeholder="已关联货主,输入关键字进行筛选">
+                        </div>
+                        <div class="card-body overflow-scrollbar-200 p-0 m-0"
+                             style="max-height: 300px;min-height: 300px">
+                            <ul class="list-group">
+                                <template v-for="item in editTemplate.data">
+                                    <li class="list-group-item" @dblclick="item.isActivation = false"
+                                        v-if="item.isActivation" @click="selectOwner(item)"
+                                        :class="{'list-group-item-success' : item.selected}">
+                                        @{{ item.name }}
+                                        <button class="btn btn-sm btn-outline-primary float-right"
+                                                @click="deactivateOwner(item)">取消关联
+                                        </button>
+                                    </li>
+                                </template>
+                            </ul>
+                        </div>
+                    </div>
+
+                    <div class="card">
+                        <div class="card-header">
+                            <input type="text" class="form-control form-inline" placeholder="未关联货主,输入关键字进行筛选">
+                        </div>
+                        <div class="card-body overflow-scrollbar-200 p-0 m-0"
+                             style="max-height: 300px;min-height: 300px">
+                            <ul class="list-group">
+                                <template v-for="item in editTemplate.data">
+                                    <li class="list-group-item"
+                                        v-if="!ownerIsAction(item)">
+                                        @{{ item.name }}
+                                        <button class="btn btn-sm btn-outline-success float-right"
+                                                @click="activateOwner(item)">关联
+                                        </button>
+                                    </li>
+                                </template>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+                <div class="col">
+                    <div class="card">
+                        <div class="card-header">
+                            <input type="text" class="form-control form-inline" placeholder="以关联的承运商">
+                        </div>
+                        <div class="card-body overflow-scrollbar-200 p-0 m-0"
+                             style="max-height: 650px;min-height: 650px">
+                            <ul class="list-group" v-if="selectOwnerLogistics">
+                                <template v-for="item in selectOwnerLogistics">
+                                   <li class="list-group-item" v-if="item.isActivation">
+                                       @{{ item.name }}
+                                       <button class="btn btn-sm btn-outline-primary float-right" @click="item.isActivation = false">取消关联</button>
+                                   </li>
+                                </template>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+                <div class="col">
+                    <div class="card">
+                        <div class="card-header">
+                            <input type="text" class="form-control form-inline" placeholder="未关联承运商">
+                        </div>
+                        <div class="card-body overflow-scrollbar-200 p-0 m-0"
+                             style="max-height: 650px;min-height: 650px">
+                            <ul class="list-group" v-if="selectOwnerLogistics">
+                                <template v-for="item in selectOwnerLogistics">
+                                    <li class="list-group-item" v-if="!item.isActivation" >
+                                        @{{ item.name }}
+                                        <button class="btn btn-sm btn-outline-success float-right" @click="item.isActivation = true">关联</button>
+                                    </li>
+                                </template>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button class="btn btn-secondary" data-dismiss="modal" >关闭</button>
+                <button class="btn btn-primary" @click="saveRelation">保存</button>
+            </div>
+        </div>
+    </div>
+
+</div>

+ 54 - 12
resources/views/maintenance/expressPrinting/template/_produce.blade.php

@@ -1,15 +1,57 @@
-<div class="position-relative border-1 row p-0 m-0 bg-white border" id="produce-panel" @click="editProducePanel" :style="getProducePanelStyle()" >  <!--position-relative--> <!--position-absolute-->
-    <div class="position-absolute print-part"
-         draggable="true"
-         v-for="(item,i) in productionParts.printParts"
-         @click.stop="productionParts.editPart = item"
-         @dragstart="_dragstart(item,$event)"             {{--开始拖动元素时触发--}}
-         @dragend="_dragend(item,$event)"                 {{--拖动结束--}}
-         :style="getPrintPartStyle(item)"
-         :key="i" >
-        <svg v-if="item.type ==='StripeCode'" :style="{width:item.width,height:item.height}" :id="item.id" @click.stop="productionParts.editPart = item"></svg>
-        <span v-else>@{{ item.text }}</span>
-    </div>
+<div class=" border-1 row p-0 m-0 border bg-light" id="produce-panel"  style="height: 1000px">  <!--position-relative--> <!--position-absolute-->
+    <template v-if="productionParts.bg">
+
+        <div class="bg-white border-dark position-relative"  :style="{height: productionParts.bg.height+'px', width:productionParts.bg.width+'px','z-index':9 }">
+
+            <template v-for="(printPart,i) in productionParts.printParts ">
+{{---文本框--}}
+                <template v-if="printPart.type === 'textBox'">
+                    <div class="position-absolute print-part bg-white"  :key="i" draggable="true"
+                         :style="getStyle(printPart)"
+                         @click.stop="productionParts.editPart = printPart"
+                         @dragstart="_dragstart(printPart,$event)"             {{--开始拖动元素时触发--}}
+                         @dragend="_dragend(printPart,$event)"                 {{--拖动结束--}}
+                    >
+                        @{{ printPart.text }}
+                    </div>
+                </template>
+{{---条纹码--}}
+                <template v-if="printPart.type === 'stripeCode'">
+                    <div class="position-absolute print-part bg-white" draggable="true"  :key="i"
+                         :style="getStyle(printPart)"
+                         @click.stop="productionParts.editPart = printPart"
+                         @dragstart="_dragstart(printPart,$event)"             {{--开始拖动元素时触发--}}
+                         @dragend="_dragend(printPart,$event)"                 {{--拖动结束--}}
+                    >
+                        <svg class="" :id="'svg'+printPart.id"   :class="{'bg-secondary':productionParts.editPart.id === printPart.id}">
+                        </svg>
+                    </div>
+
+                </template>
+{{---二维码--}}
+                <template v-if="printPart.type === 'qRCode'">
+                    <div class="position-absolute print-part bg-white"  :id="printPart.id" :key="i" draggable="true"
+                         :style="getStyle(printPart)"
+                         @click.stop="productionParts.editPart = printPart"
+                         @dragstart="_dragstart(printPart,$event)"             {{--开始拖动元素时触发--}}
+                         @dragend="_dragend(printPart,$event)"                 {{--拖动结束--}}
+                    >
+                    </div>
+                </template>
+{{---图片--}}
+                <template v-if="printPart.type === 'image'">
+                    <img  class="position-absolute print-part bg-white" src="\icon\img404-thumbnail.jpg" :alt="printPart.value" :key="i" :height="printPart.height" :width="printPart.width" :id="printPart.id"
+                         :style="getStyle(printPart)"
+                          :class="{'bg-secondary':productionParts.editPart.id === printPart.id}"
+                         @click.stop="productionParts.editPart = printPart"
+                         @dragstart="_dragstart(printPart,$event)"             {{--开始拖动元素时触发--}}
+                         @dragend="_dragend(printPart,$event)"                 {{--拖动结束--}}
+                    >
+                </template>
+            </template>
+        </div>
+    </template>
+
 </div>
 <div class="modal fade" id="saveModal" tabindex="-1"  aria-hidden="true">
     <div class="modal-dialog">

+ 27 - 9
resources/views/maintenance/expressPrinting/template/_select.blade.php

@@ -1,11 +1,29 @@
 <div class="container-fluid p-0"  id="select-panel" style="cursor: pointer; overflow-y: scroll;border-radius:5px;" >
-    <input type="text" class="form-control" placeholder="输入组件名称进行搜索" @change="_searchPrintPart($event)">
-    <ul class="list-group" >
-        <li class="list-group-item align-text-top hover"
-            v-for="(printPart,i) in filterPrintParts"
-            @dblclick="_addPartToProductionPanel(printPart)">
-            @{{ printPart.name }}
-            <span class="text-right float-right">添加</span>
-        </li>
-    </ul>
+    <div class="card">
+        <div class="card-header">组件列表</div>
+        <div class="card-body m-0 p-0">
+            <ul class="list-group" >
+                <li class="list-group-item align-text-top hover"
+                    v-for="(printPart,i) in printParts"
+                    @dblclick="_addPartToProductionPanel(printPart)">
+                    @{{ printPart.name }}
+                    <span class="text-right float-right btn btn-sm btn-outline-success" @click="_addPartToProductionPanel(printPart)">添加</span>
+                </li>
+            </ul>
+        </div>
+    </div>
+    <div class="card" v-if="productionParts.editPart && productionParts.editPart.type === 'image'">
+        <div class="card-header">图片选择</div>
+        <div class="card-body p-0">
+            <ul class="list-group">
+                <template v-for="(image,i) in images">
+                    <li class="list-group-item" :class="{'list-group-item active' : productionParts.editPart.value === 'image.name'}">
+                        <img :src="getImageSrc(image)" alt="image.name"  width="150">
+                        <button class="btn btn-sm btn-outline-primary" @click="setImageBg(image)">替换</button>
+                    </li>
+                </template>
+
+            </ul>
+        </div>
+    </div>
 </div>

+ 150 - 102
resources/views/maintenance/expressPrinting/template/create.blade.php

@@ -1,9 +1,9 @@
 @extends('layouts.app')
-@section('title')模板@endsection
+@section('title')模板-创建@endsection
 
 @section('content')
     <div class="container-fluid d-none row m-1" id="print-template">
-        <div class="col-lg-2 col-sm-3 p-0"  id="part_panel">
+        <div class="col-lg-2 col-sm-3 p-0" id="part_panel">
             {{--  组件选择面板 --}}
             @include('maintenance.expressPrinting.template._select')
         </div>
@@ -21,30 +21,46 @@
 
 @section('lastScript')
     <script type="text/javascript" src="{{mix('js/utilities/barcode.js')}}"></script>
+    <script type="text/javascript" src="{{mix('/js/utilities/qrcode.js')}}"></script>
     <script>
+
         let vue = new Vue({
-            el:"#print-template",
-            data:{
-                printParts:{!! $printParts !!},
-                filterPrintParts:[],
-                producePanel:{},              // 背景版
-                selectProducePanel:false,              // 是否选中背景版
-                productionParts:{
-                    printParts:[],                  // 组件s
-                    editPart: null,                    // 编辑中的组件
-                    editIndex:{},                   // 编辑中组件的下标
-                    addPart:{},                     // 添加模板
+            el: "#print-template",
+            data: {
+                printParts: [
+                        @foreach($printParts as $items)
+                    {
+                        name: '{{ $items['name'] }}', value: {
+                            @foreach($items['value'] as $key=>$value)
+                            '{{$key}}': '{{$value}}',
+                            @endforeach
+                        }
+                    },
+                    @endforeach
+                ],
+                imgPrefix:"{{asset("/storage")}}",
+                images:{!! $printPartImages ?? [] !!},
+                filterPrintParts: [],
+                producePanel: {},              // 背景版
+                selectProducePanel: false,              // 是否选中背景版
+                productionParts: {
+                    bg: null,
+                    printParts: [],                  // 组件
+                    editPart: {
+                        id:null
+                    },                  // 编辑中的组件
+                    editIndex: {},                   // 编辑中组件的下标
+                    addPart: {},                     // 添加模板
                 },
-                partIsSelected:false,               // 表示模块被选中
-                selectPrintPart:{},                 // 选中模块
-                selectPrintPartPoint:{x:'',y:''},   // 选中模块时模块 point
-                mousePointStart:{x:'',y:''},        // 开始拖动时的 point
-                mousePointEnd:{x:'',y:''},          // 结束拖动时的 point
-                selectPrintPartOffset:{left:'',top:''},     // 选中时的left 和 top
-                z_index:10,
+                partIsSelected: false,               // 表示模块被选中
+                selectPrintPart: {},                 // 选中模块
+                selectPrintPartPoint: {x: '', y: ''},   // 选中模块时模块 point
+                mousePointStart: {x: '', y: ''},        // 开始拖动时的 point
+                mousePointEnd: {x: '', y: ''},          // 结束拖动时的 point
+                selectPrintPartOffset: {left: '', top: ''},     // 选中时的left 和 top
+                z_index: 10,
             },
-            mounted(){
-                this.initProductionParts();
+            mounted() {
                 $('.navbar,.nav1,.nav2').hide();
                 $('.nav3').on('mouseenter', function () {
                     $('.navbar,.nav1,.nav2').show();
@@ -54,116 +70,148 @@
                 });
                 $("#print-template").removeClass('d-none');
             },
-            methods:{
-                _addPartToProductionPanel(printPart){
+            methods: {
+                // 添加组件至面板上
+                _addPartToProductionPanel(printPart) {
                     let _clone = JSON.parse(JSON.stringify(printPart));
-                    let print_part = JSON.parse(_clone.value);
-                    print_part.left = 0;
-                    print_part.top = 0;
+                    let print_part = _clone.value
+                    if (!this.productionParts.bg && print_part.type !== 'bg') {
+                        window.tempTip.show('先添加背景组件在进行其他组件的添加');
+                        return
+                    } else if (this.productionParts.bg && print_part.type === 'bg') {
+                        window.tempTip.show('背景组件请勿重复添加');
+                        return
+                    }
+                    if (print_part.type === 'bg') {
+                        this.productionParts.bg = print_part;
+                        return;
+                    }
                     print_part.index = this.productionParts.printParts.length;
-                    print_part.z_index = this.z_index++;
-                    print_part.id = "id"+this.z_index;
+                    this.z_index+=1
+                    print_part.z_index = this.z_index;
+                    print_part.id = "id" + this.z_index;
                     print_part.text = null;
-                    this.productionParts.printParts.push(print_part);
-                    if(print_part.type ==="StripeCode"){
-                        setTimeout(function(){
-                            window.setBarcode("0123456789","#"+print_part.id,2,50,true)
-                        },100);
+                    this.productionParts.printParts.push(print_part);  // 添加入组件列表中
+                    if (print_part.type === "stripeCode") {
+                        setTimeout(function () {
+                            window.setBarcode("0123456789", "#svg" + print_part.id, 2, 50, true)
+                        }, 100);
+                    } else if (print_part.type === "qRCode") {
+                        setTimeout(function () {
+                            new QRCode(document.getElementById(print_part.id), {
+                                text: "0123456789",
+                                width: print_part.width,
+                                height: print_part.height,
+                                colorDark : "#000000",
+                                colorLight : "#ffffff",
+                                correctLevel : QRCode.CorrectLevel.H
+                            });
+                        }, 1000)
+                    } else if (print_part.type === "image") {
+
                     }
                 },
-                _removePartToProductionPanel(editPart){
+                // 从面板删除组件
+                _removePartToProductionPanel(editPart) {
                     this.productionParts.editPart = null;
                     let index = this.productionParts.printParts.indexOf(editPart);
-                    this.productionParts.printParts.splice(index,1);
+                    this.productionParts.printParts.splice(index, 1);
                 },
-                _alignContentToProductionPanel(){
-                    this.productionParts.printParts.splice(this.productionParts.editIndex,1);
+                //
+                _alignContentToProductionPanel() {
+                    this.productionParts.printParts.splice(this.productionParts.editIndex, 1);
                     this.$forceUpdate();
                 },
-                _searchPrintPart(event){
-                    let value = $(event.target).val();
-                    this.filterPrintParts = this.printParts.filter(function(printPart){
-                        return printPart.name.includes(value);
-                    });
-                    if(value===null || value=== '') this.filterPrintParts = JSON.parse(JSON.stringify(this.printParts));
-                },
-                showSaveModal(){
+                // 打开保存modal
+                showSaveModal() {
                     $('#saveModal').modal('show');
                 },
-                _savePrintTemplate(){
+                // 保存modal
+                _savePrintTemplate() {
                     tempTip.setDuration(3000);
                     tempTip.setIndex(1999);
-                    let printTemplate =JSON.parse(JSON.stringify(this.productionParts.printParts));
-                    let bg = {
-                        type:'bg',
-                        'width':this.producePanel.width,
-                        'height':this.producePanel.height,
-                    }
-                    printTemplate.push(bg);
+                    let printTemplate = JSON.parse(JSON.stringify(this.productionParts.printParts));
+                    printTemplate.push(this.productionParts.bg);
                     let data = {
-                        name:this.productionParts.addPart.name,
-                        value:printTemplate
+                        name: this.productionParts.addPart.name,
+                        value: printTemplate
                     };
-                    window.axios.post("{{url('apiLocal/maintenance/expressPrinting/template/create')}}",data).then(res=>{
-                        if(res.data.success){
+                    window.axios.post("{{url('apiLocal/maintenance/expressPrinting/template/create')}}", data).then(res => {
+                        if (res.data.success) {
                             tempTip.showSuccess('保存成功!');
                             $('#saveModal').modal('hide');
-                            return ;
+                            return;
                         }
                         tempTip.show(res.data.message);
-                    }).catch(err=>{
+                    }).catch(err => {
                         tempTip.show(err);
                     });
                 },
-                initProductionParts(){
-                    this.productionParts = {printParts:[], editPart:null, addPart:{}};
-                    this.filterPrintParts=JSON.parse(JSON.stringify(this.printParts));
-                    let height = (window.innerHeight-120)+'px';
-                    $("#production_panel,#part_panel,#compile_panel,#select-panel,#produce-panel").css({height:height});
-                    let produce_panel = $("#produce-panel");
-                    produce_panel.css({height:height});
+                // 开始拖动元素时触发
+                _dragstart(item, $event) {
+                    console.log(item)
+                    this.selectPrintPartPoint = {x: parseInt(item.left), y: parseInt(item.top)};
+                    this.mousePointStart = {x: $event.offsetX, y: $event.offsetY};
                 },
-                getPrintPartStyle(item){
-                    let style = JSON.parse(JSON.stringify(item));
-                    style['border-style'] = style.borderStyle;
-                    style['border-width'] = style.borderWidth+'px';
-                    if(style.borderStyle === 'none'){
-                        style['border-style'] = 'dotted';
-                        style['border-width'] = '1px';
+                // 结束拖动元素时触发
+                _dragend(item, $event) {
+                    console.log(item)
+                    this.mousePointEnd = {x: $event.offsetX, y: $event.offsetY};
+                    let left = item.left - (this.mousePointStart.x - this.mousePointEnd.x);
+                    let top = item.top - (this.mousePointStart.y - this.mousePointEnd.y);
+                    item.left = left > 0 ? left : 0;
+                    item.top = top > 0 ? top : 0;
+                },
+                getProducePanelStyle() {
+                    return {
+                        width: this.producePanel.width + 'px',
+                        height: this.producePanel.height + 'px',
+                    }
+                },
+                getStyle(item) {
+                    let style =  {
+                        width: item.width + 'px',
+                        height: item.height + 'px',
+                        left: item.left + 'px',
+                        top: item.top + 'px',
+                        'z-index' : item['z_index'],
+                    };
+                    if (item.type === 'textBox'){
+                        style['font-size'] = item['font-size'];
+                        style['border-width'] = item['border-width']+'px';
+                        style['border-color'] = '#000';
+                        style['border-style'] = 'solid';
+                        style['display'] = 'flex'
+                        style['justify-content'] = item['justify-content']
+                        style['align-items'] = item['align-items']
+                        style['text-align'] = 'center'
+                    } else if (item.type === 'qRCode'){
+                        style.heigth = style.width;
+                        let scale = item.scale;
+                        style.transform =  `scale(${scale},${scale})`;
+                    } else if (item.type === 'stripeCode'){
+                        let scale = item.scale;
+                        style.transform =  `scale(${scale},${scale})`;
                     }
-                    style.display = 'flex';
-                    style['font-size'] = style.fontSize+'px';
-                    style['font-weight'] = style.fontWeight;
-                    style['justify-content'] = style.justifyContent;
-                    style['align-items'] = style.alignItems;
-                    style.width+='px';
-                    style.height+='px';
-                    style.left+='px';
-                    style.top+='px';
-                    style['white-space'] = 'pre';              // 字体换行
                     return style;
                 },
-                _dragstart(item,$event){
-                    {{--开始拖动元素时触发--}}
-                    this.selectPrintPartPoint  = {x:parseInt(item.left),y:parseInt(item.top)};
-                    this.mousePointStart = {x:$event.offsetX, y:$event.offsetY};
+                getImageSrc(image){
+                    return this.imgPrefix+image.file.url+'.'+image.file.type
                 },
-                _dragend(item,$event){
-                    {{--结束拖动元素时触发--}}
-                        this.mousePointEnd =  {x:$event.offsetX, y:$event.offsetY};
-                    let left  = item.left - (this.mousePointStart.x - this.mousePointEnd.x);
-                    let top = item.top - (this.mousePointStart.y - this.mousePointEnd.y);
-                    item.left = left >0 ? left:0;
-                    item.top =  top > 0 ? top:0;
+                setImageBg(image){
+                    let src =  this.getImageSrc(image);
+                    this.productionParts.editPart.text = image.name;
+                    document.getElementById(this.productionParts.editPart.id).setAttribute('src',src) ;
                 },
-                editProducePanel(){
-                    this.selectProducePanel =!this.selectProducePanel;
+                changeStripeCodeWidth(){
+                    let default_width = 404;
+                    if (this.productionParts.editPart.type === 'qRCode')default_width = 100;
+                    this.productionParts.editPart.scale = this.productionParts.editPart.width / default_width;
                 },
-                getProducePanelStyle(){
-                    return {
-                        width:this.producePanel.width+'px',
-                        height:this.producePanel.height+'px',
-                    }
+                changeStripeCodeScale(){
+                    let default_width = 404;
+                    if (this.productionParts.editPart.type === 'qRCode')default_width = 100;
+                    this.productionParts.editPart.width = default_width * this.productionParts.editPart.scale;
                 }
             }
         });

+ 277 - 0
resources/views/maintenance/expressPrinting/template/edit.blade.php

@@ -0,0 +1,277 @@
+@extends('layouts.app')
+@section('title')模板-编辑@endsection
+
+@section('content')
+    <div class="container-fluid d-none row m-1" id="print-template">
+        <div class="col-lg-2 col-sm-3 p-0" id="part_panel">
+            {{--  组件选择面板 --}}
+            @include('maintenance.expressPrinting.template._select')
+        </div>
+        <div class="col-lg-8 col-sm-6 p-0" id="production_panel">
+            {{--  模板制作面板 --}}
+            @include('maintenance.expressPrinting.template._produce')
+        </div>
+        <div class="col-lg-2 col-sm-3 p-0" id="compile_panel">
+            {{-- 模板编辑页面 --}}
+            @include('maintenance.expressPrinting.template._compile')
+        </div>
+    </div>
+@endsection
+
+
+@section('lastScript')
+    <script type="text/javascript" src="{{mix('js/utilities/barcode.js')}}"></script>
+    <script type="text/javascript" src="{{mix('/js/utilities/qrcode.js')}}"></script>
+    <script>
+
+        let list_vue = new Vue({
+            el: "#print-template",
+            data: {
+                printTemplate : '{!! $template !!}',
+                printParts: [
+                        @foreach($printParts as $items)
+                    {
+                        name: '{{ $items['name'] }}', value: {
+                            @foreach($items['value'] as $key=>$value)
+                            '{{$key}}': '{{$value}}',
+                            @endforeach
+                        }
+                    },
+                    @endforeach
+                ],
+                imgPrefix:"{{asset("/storage")}}",
+                images:{!! $printPartImages ?? [] !!},
+                filterPrintParts: [],
+                producePanel: {},              // 背景版
+                selectProducePanel: false,              // 是否选中背景版
+                productionParts: {
+                    id: null,
+                    bg: null,
+                    printParts: [],                  // 组件
+                    editPart: {
+                        id:null
+                    },                  // 编辑中的组件
+                    editIndex: {},                   // 编辑中组件的下标
+                    addPart: {},                     // 添加模板
+                },
+                partIsSelected: false,               // 表示模块被选中
+                selectPrintPart: {},                 // 选中模块
+                selectPrintPartPoint: {x: '', y: ''},   // 选中模块时模块 point
+                mousePointStart: {x: '', y: ''},        // 开始拖动时的 point
+                mousePointEnd: {x: '', y: ''},          // 结束拖动时的 point
+                selectPrintPartOffset: {left: '', top: ''},     // 选中时的left 和 top
+                z_index: 10,
+            },
+            mounted() {
+                $('.navbar,.nav1,.nav2').hide();
+                $('.nav3').on('mouseenter', function () {
+                    $('.navbar,.nav1,.nav2').show();
+                });
+                $('.body').on('mouseenter', function () {
+                    $('.navbar,.nav1,.nav2').hide();
+                });
+                $("#print-template").removeClass('d-none');
+
+                let template = JSON.parse(this.printTemplate)
+                this.productionParts.bg  = template.value.filter(function(item){
+                    return item.type === 'bg';
+                }).shift();
+                this.productionParts.printParts = template.value.filter(function(item){
+                    return item.type !== 'bg';
+                });
+                let self = this;
+                this.productionParts.printParts.forEach(function(item,index,array){
+                    if (self.z_index < item['z_index'])self.z_index = item['z_index']+1;
+                });
+
+                this.productionParts.addPart.name = template.name;
+                this.productionParts.id = template.id;
+                this.initCode();
+            },
+            methods: {
+                initCode(){
+                    let self = this;
+                    this.productionParts.printParts.forEach(function(item){
+                        if (item.type==="stripeCode"){
+                            setTimeout(function () {
+                                window.setBarcode("0123456789", "#svg" + item.id, 2, 50, true)
+                            }, 1000);
+                        }else if(item.type === 'qRCode'){
+                            setTimeout(function () {
+                                new QRCode(document.getElementById(item.id), {
+                                    text: "占位二维码",
+                                    width: item.height,
+                                    height: item.height,
+                                    colorDark : "#000000",
+                                    colorLight : "#ffffff",
+                                    correctLevel : QRCode.CorrectLevel.H
+                                });
+                            }, 1000)
+                        } else if(item.type === "image"){
+                            setTimeout(function () {
+                                let images = self.images.filter(function(img){
+                                    return img.name === item.text;
+                                })
+                                if (images.length === 0) {
+                                    window.tempTip.show("获取图片失败");
+                                    return
+                                }
+                                let img = images.shift();
+                                let element = document.getElementById(item.id);
+                                element.setAttribute('src',self.getImageSrc(img))
+                            }, 1000)
+
+                        }
+                    });
+                },
+                // 添加组件至面板上
+                // 添加组件至面板上
+                _addPartToProductionPanel(printPart) {
+                    let _clone = JSON.parse(JSON.stringify(printPart));
+                    let print_part = _clone.value
+                    if (!this.productionParts.bg && print_part.type !== 'bg') {
+                        window.tempTip.show('先添加背景组件在进行其他组件的添加');
+                        return
+                    } else if (this.productionParts.bg && print_part.type === 'bg') {
+                        window.tempTip.show('背景组件请勿重复添加');
+                        return
+                    }
+                    if (print_part.type === 'bg') {
+                        this.productionParts.bg = print_part;
+                        return;
+                    }
+                    print_part.index = this.productionParts.printParts.length;
+                    this.z_index+=1
+                    print_part.z_index = this.z_index;
+                    print_part.id = "id" + this.z_index;
+                    print_part.text = null;
+                    this.productionParts.printParts.push(print_part);  // 添加入组件列表中
+                    if (print_part.type === "stripeCode") {
+                        setTimeout(function () {
+                            window.setBarcode("0123456789", "#svg" + print_part.id, 2, 50, true)
+                        }, 100);
+                    } else if (print_part.type === "qRCode") {
+                        setTimeout(function () {
+                            new QRCode(document.getElementById(print_part.id), {
+                                text: "0123456789",
+                                width: print_part.width,
+                                height: print_part.height,
+                                colorDark : "#000000",
+                                colorLight : "#ffffff",
+                                correctLevel : QRCode.CorrectLevel.H
+                            });
+                        }, 1000)
+                    } else if (print_part.type === "image") {
+
+                    }
+                },
+                // 从面板删除组件
+                _removePartToProductionPanel(editPart) {
+                    this.productionParts.editPart = null;
+                    let index = this.productionParts.printParts.indexOf(editPart);
+                    this.productionParts.printParts.splice(index, 1);
+                },
+                //
+                _alignContentToProductionPanel() {
+                    this.productionParts.printParts.splice(this.productionParts.editIndex, 1);
+                    this.$forceUpdate();
+                },
+                // 打开保存modal
+                showSaveModal() {
+                    $('#saveModal').modal('show');
+                },
+                // 保存modal
+                _savePrintTemplate() {
+                    tempTip.setDuration(3000);
+                    tempTip.setIndex(1999);
+                    let printTemplate = JSON.parse(JSON.stringify(this.productionParts.printParts));
+                    printTemplate.push(this.productionParts.bg);
+                    let data = {
+                        id: this.productionParts.id,
+                        name: this.productionParts.addPart.name,
+                        value: printTemplate
+                    };
+                    window.axios.post("{{url('apiLocal/maintenance/expressPrinting/template/update')}}", data).then(res => {
+                        if (res.data.success) {
+                            tempTip.showSuccess('保存成功!');
+                            $('#saveModal').modal('hide');
+                            return;
+                        }
+                        tempTip.show(res.data.message);
+                    }).catch(err => {
+                        tempTip.show(err);
+                    });
+                },
+                // 开始拖动元素时触发
+                _dragstart(item, $event) {
+                    this.selectPrintPartPoint = {x: parseInt(item.left), y: parseInt(item.top)};
+                    this.mousePointStart = {x: $event.offsetX, y: $event.offsetY};
+                },
+                // 结束拖动元素时触发
+                _dragend(item, $event) {
+                    this.mousePointEnd = {x: $event.offsetX, y: $event.offsetY};
+                    let left = item.left - (this.mousePointStart.x - this.mousePointEnd.x);
+                    let top = item.top - (this.mousePointStart.y - this.mousePointEnd.y);
+                    item.left = left > 0 ? left : 0;
+                    item.top = top > 0 ? top : 0;
+                },
+                getProducePanelStyle() {
+                    return {
+                        width: this.producePanel.width + 'px',
+                        height: this.producePanel.height + 'px',
+                    }
+                },
+                getStyle(item) {
+                    let style =  {
+                        width: item.width + 'px',
+                        height: item.height + 'px',
+                        left: item.left + 'px',
+                        top: item.top + 'px',
+                        'z-index' : item['z_index'],
+                    };
+                    if (item.type === 'textBox'){
+                        style['font-size'] = item['font-size'];
+                        style['border-width'] = item['border-width']+'px';
+                        style['border-color'] = '#000';
+                        style['border-style'] = 'solid';
+                        style['display'] = 'flex'
+                        style['justify-content'] = item['justify-content']
+                        style['align-items'] = item['align-items']
+                        style['text-align'] = 'center'
+                    } else if (item.type === 'qRCode'){
+                        style.width = 100+'px';
+                        style.height = 100+'px';
+                        let scale = item.scale;
+                        if(scale){
+                            style.transform =  `scale(${scale},${scale})`;
+                        }
+                    } else if (item.type === 'stripeCode'){
+                        let scale = item.scale;
+                        style.transform =  `scale(${scale},${scale})`;
+                    }
+                    return style;
+                },
+                getImageSrc(image){
+                    return this.imgPrefix+image.file.url+'.'+image.file.type
+                },
+                setImageBg(image){
+                    let src =  this.getImageSrc(image);
+                    this.productionParts.editPart.text = image.name;
+                    document.getElementById(this.productionParts.editPart.id).setAttribute('src',src) ;
+                },
+                changeStripeCodeWidth(){
+                    let default_width = 404;
+                    if (this.productionParts.editPart.type === 'qRCode')default_width = 100;
+                    this.productionParts.editPart.scale = this.productionParts.editPart.width / default_width;
+                },
+                changeStripeCodeScale(){
+                    let default_width = 404;
+                    if (this.productionParts.editPart.type === 'qRCode')default_width = 100;
+                    this.productionParts.editPart.width = default_width * this.productionParts.editPart.scale;
+                },
+
+            }
+        });
+    </script>
+@endsection
+

+ 117 - 6
resources/views/maintenance/expressPrinting/template/index.blade.php

@@ -9,17 +9,24 @@
                 <th>序号</th>
                 <th>名称</th>
                 <th>内容</th>
+                <th>绑定</th>
                 <th>操作</th>
             </tr>
             <tr v-for="(template,i) in templates">
                 <td>@{{ i+1 }}</td>
                 <td>@{{ template.name }}</td>
-                <td>@{{ template.value }}</td>
+                <td></td>
+                <td>
+                    <button type="button" class="btn btn-sm btn-outline-primary" @click="edit(template,i)">编辑关联</button>
+                </td>
                 <td>
                     <button type="button" class="btn btn-danger" @click="destroy(template.id,i)">删除</button>
+                    <a class="btn btn-primary" :href="'{{url('maintenance/expressPrinting/template/edit')}}'+'/'+template.id" :target="'maintenance/expressPrinting/template/edit'+template.id">编辑模板</a>
                 </td>
             </tr>
+            @include('maintenance.expressPrinting.template._edit')
         </table>
+        {{ $templates->links() }}
     </div>
 @endsection
 
@@ -28,15 +35,119 @@
         let vue = new Vue({
             el:'#part-template',
             data:{
-                templates:{!! $templates !!},
+                templates:{!! $templates->toJson() !!}['data'],
+                logistics:[@foreach($logistics as $logistic ){!! $logistic !!},@endforeach],
+                owners:[@foreach($owners as $owner ){!! $owner !!},@endforeach],
+                editTemplate:{
+                    index:null,
+                    selectOwnerId:null,
+                    item:null,
+                    data:{},
+                },
+                templateMode:null,
+            },
+            mounted() {
+                this.init();
+            },
+            computed:{
+                selectOwnerLogistics(){
+                    if (this.editTemplate.selectOwnerId === null ) return [];
+                    let ownerId = this.editTemplate.selectOwnerId;
+                    return this.editTemplate.data[ownerId]['logistics'];
+                },
             },
             methods:{
+                init(){
+                    let self = this;
+                    let data = {};
+                    this.owners.forEach(function(owner){
+                        // isActivation 是否激活
+                        data[owner.id] = {isActivation:false,logistics:{},name:owner.name,selected:false,id:owner.id};
+                        self.logistics.forEach(function(logistic){
+                            // selected 是否选中
+                            data[owner.id].logistics[logistic.id] = {isActivation:false,id:logistic.id,name:logistic.name}
+                        })
+                    });
+                    this.templateMode = data;
+                },
                 destroy(id,i){
                     if (!confirm('是否删除当前模板')) return;
-                    tempTip.setDuration(3000);
-                    window.tempTip.postBasicRequest("{{url('apiLocal/maintenance/expressPrinting/template/destroy')}}",{id:id},res=>{
-                        tempTip.showSuccess('删除成功');
-                        this.$delete(this.templates,i);
+                    let url = "{{url('apiLocal/maintenance/expressPrinting/template')}}"+"/"+id;
+                    window.axios.delete(url).then(res=>{
+                        if(res.data.success){
+                            window.tempTip.showSuccess('删除成功');
+                            this.$delete(this.templates,i);
+                        }
+                    }).catch(err=>{
+                        window.tempTip.show('删除异常:'+err);
+                    })
+                },
+                edit(item,index){
+                    let data = JSON.parse(JSON.stringify(this.templateMode));
+
+                    if (!item['owner_logistic_print_template']) return
+
+                    item['owner_logistic_print_template'].forEach(function(item){
+                        data[item.owner_id].isActivation = true;
+                        data[item.owner_id].logistics[item.logistic_id].isActivation = true;
+                    });
+
+                    this.editTemplate.item = JSON.parse(JSON.stringify(item));
+                    this.editTemplate.data = data;
+                    this.editTemplate.index = index
+
+                    $("#edit-template").modal('show');
+                },
+                ownerIsAction(item){
+                    return item.isActivation;
+                },
+                activateOwner(item){
+                    item.isActivation = true;
+                },
+                deactivateOwner(item){
+                    item.isActivation = false;
+                    for (const key in this.editTemplate.data[item.id].logistics){
+                        this.editTemplate.data[item.id].logistics[key].isActivation = false;
+                    }
+                },
+                selectOwner(item){
+                    if(this.editTemplate.selectOwnerId === item.id){
+                        for (const key in this.editTemplate.data) {
+                            if (this.editTemplate.data[key].id === item.id)
+                                this.editTemplate.data[key].selected = false
+                        }
+                        this.editTemplate.selectOwnerId = null;
+                    } else {
+                        this.editTemplate.selectOwnerId = JSON.parse(JSON.stringify(item.id))
+                        for (const key in this.editTemplate.data) {
+                            this.editTemplate.data[key].selected  = false;
+                        }
+                        item.selected = true;
+                    }
+                },
+                saveRelation(){
+                    let url = "{{url("apiLocal/maintenance/expressPrinting/template/saveRelation")}}";
+                    let relations = [];
+                    for(const key in this.editTemplate.data){
+                        if (this.editTemplate.data[key].isActivation){
+                            for (const logistic_id in this.editTemplate.data[key].logistics) {
+                                if (this.editTemplate.data[key].logistics[logistic_id].isActivation){
+                                    relations.push({owner_id:key,logistic_id:logistic_id,print_template_id:this.editTemplate.item.id});
+                                }
+                            }
+                        }
+                    }
+                    window.axios.post(url,{relations:relations,template_id:this.editTemplate.item['id']}).then(res=>{
+                        $("#edit-template").modal('hide');
+                        if (res.data.success){
+                            this.$set(this.templates,this.editTemplate.index,res.data.data);
+                            window.tempTip.showSuccess("保存成功");
+                            return
+                        }
+                        window.tempTip.show(res.data.message)
+                    }).catch(err=>{
+                        $("#edit-template").modal('hide');
+                        window.tempTip.show(err)
                     });
                 }
             }

+ 34 - 5
routes/apiLocal.php

@@ -1,6 +1,5 @@
 <?php
 
-use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Route;
 
 /*
@@ -124,19 +123,49 @@ Route::group(['prefix'=>'maintenance'],function (){
     Route::group(['prefix'=>'owner'],function (){
         Route::post('getOwners','OwnerController@getOwners');
     });
+    Route::group(['prefix' => 'print'],function(){
+        Route::post('getData','PrintController@getPrintDataApi');
+        Route::post('uploadPrintData','PrintController@uploadPrintDataApi');
+        Route::post("upDatePrintCount","PrintController@updatePrintCountApi");
+    });
     Route::group(['prefix'=>'expressPrinting'],function (){
         Route::group(['prefix'=>'part'],function(){
-            Route::post('create','PrintPartController@storeApi');
-            Route::post('destroy','PrintPartController@destroyApi');
-            Route::post('printTemplate','PrintPartController@printTemplateApi');
+
+            Route::group(['prefix'=>'image'],function(){
+                Route::post('saveFile','PrintPartImageController@saveFileApi');
+                Route::post('update','PrintPartImageController@updateFileApi');
+                Route::delete('{id}','PrintPartImageController@updateFileApi');
+            });
         });
+
         Route::group(['prefix'=>'template'],function(){
             Route::post('create','PrintTemplateController@storeApi');
-            Route::post('destroy','PrintTemplateController@destroyApi');
+            Route::delete('{id}','PrintTemplateController@destroyApi');
+            Route::post('update','PrintTemplateController@updateApi');
+            Route::post('saveRelation','PrintTemplateController@saveRelationApi');
+        });
+        Route::group(['prefix' => 'setting'],function () {
+            Route::group(['prefix' => 'terminal'],function () {
+                Route::post('','TerminalController@storeApi')->name('terminal.storeApi');
+                Route::put('','TerminalController@updateApi')->name('terminal.updateApi');
+                Route::delete('{id}','TerminalController@destroyApi')->name('terminal.destroyApi');
+                Route::get('{ip}','TerminalController@getTerminalApi');
+                Route::get('/getTerminal','TerminalController@getTerminalByIPApi');
+            });
+            Route::group(['prefix' => 'printer'],function () {
+                Route::post('','TerminalPrinterController@storeApi')->name('printer.storeApi');
+                Route::put('','TerminalPrinterController@updateApi')->name('printer.updateApi');
+                Route::delete('{id}','TerminalPrinterController@destroyApi')->name('printer.destroyApi');
+            });
         });
     });
 });
 
+/** 快递打印次数记录*/
+Route::group(['prefix'=>'printRecord'],function(){
+    Route::post("/updateRecord/{logistic_number}/{task_id}","OrderPackageExpressBillPrintRecordController@updateRecordApi");
+});
+
 /** 控制台 */
 Route::group(['prefix'=>'control'],function () {
     Route::post('panel/menu/orderCountingRecordApi','ControlPanelController@orderCountingRecordsApi') ;

+ 32 - 28
routes/web.php

@@ -303,13 +303,15 @@ Route::group(['middleware'=>'auth'],function ($route){
         /** 服务商 */
         Route::resource('facilitator','FacilitatorController');
         /** 快递打印 */
+        Route::get('/print/index','PrintController@index');
         Route::group(['prefix'=>'expressPrinting'],function(){
-            Route::get('/part','PrintPartController@index');
-            Route::get('/part/create','PrintPartController@create');
-            Route::group(['prefix'=>'template'],function(){
-                Route::get('/index','PrintTemplateController@index');
-                Route::get('/create','PrintTemplateController@create');
+            Route::get("index",'PrintController@index');
+
+            Route::group(['prefix' => 'setting'] ,function (){
+                Route::get('/terminal/index','TerminalController@index');
+                Route::get('/printer/index','TerminalPrinterController@index');
             });
+            Route::get('/printing/index','PrintController@index');
         });
         Route::get('syncRedisLogs','LogController@syncRedisLogs');
         Route::get('region', 'RegionController@index');
@@ -420,30 +422,32 @@ Route::group(['middleware'=>'auth'],function ($route){
             Route::post('exportAllExcelOnParams', 'RejectedController@exportAllExcelOnParams');
         });
 
-        Route::get('relating', function () {return view('rejected.relating');});
-        Route::get('recycle', 'RejectedController@recycle');
-        Route::post('ajaxCheck', 'RejectedController@ajaxCheck');
-        Route::post('ajaxCheckAll', 'RejectedController@ajaxCheckAll');
-        Route::post('ajaxFinishAll', 'RejectedController@ajaxFinishAll');
-        Route::any('export', 'RejectedController@export');
-        Route::any('exportAnalyze', 'RejectedController@exportAnalyze');
-        Route::post('ajaxGetRejected', 'RejectedController@ajaxGetRejected');
-        Route::post('changeRejectedBillRemark', 'RejectedController@changeRejectedBillRemark');
-        Route::get('importRejectedNumber','RejectedBillController@importRejectedNumber');
+    Route::get('relating', function () {return view('rejected.relating');});
+    Route::get('recycle', 'RejectedController@recycle');
+    Route::post('ajaxCheck', 'RejectedController@ajaxCheck');
+    Route::post('ajaxCheckAll', 'RejectedController@ajaxCheckAll');
+    Route::post('ajaxFinishAll', 'RejectedController@ajaxFinishAll');
+    Route::any('export', 'RejectedController@export');
+    Route::any('exportAnalyze', 'RejectedController@exportAnalyze');
+    Route::post('ajaxGetRejected', 'RejectedController@ajaxGetRejected');
+    Route::post('changeRejectedBillRemark', 'RejectedController@changeRejectedBillRemark');
+    Route::get('importRejectedNumber','RejectedBillController@importRejectedNumber');
+});
+Route::resource('rejected', 'RejectedController');
+
+
+/** 包裹 */
+Route::group(['prefix'=>'package'],function(){
+    /** 统计 */
+    Route::group(['prefix'=>'statistics'],function(){
+        Route::any('export','WeighController@statisticsExport');
+    });
+    /** 异常 */
+    Route::group(['prefix'=>'weightExcepted'],function(){
+        Route::get('indexCreate','WeighExceptedController@indexCreate');
+        Route::get('indexIssued','WeighExceptedController@indexIssued');
+        Route::any('export/{type}','WeighExceptedController@export');
     });
-    $route->resource('rejected', 'RejectedController');
-    /** 包裹 */
-    $route->group(['prefix'=>'package'],function(){
-        /** 统计 */
-        Route::group(['prefix'=>'statistics'],function(){
-            Route::any('export','WeighController@statisticsExport');
-        });
-        /** 异常 */
-        Route::group(['prefix'=>'weightExcepted'],function(){
-            Route::get('indexCreate','WeighExceptedController@indexCreate');
-            Route::get('indexIssued','WeighExceptedController@indexIssued');
-            Route::any('export/{type}','WeighExceptedController@export');
-        });
 
         Route::any('export','WeighController@export');
         Route::get('statistics','WeighController@statistics');

+ 32 - 0
tests/Services/DeliveryService/GetDeliveryTest.php

@@ -0,0 +1,32 @@
+<?php
+
+
+namespace Tests\Services\DeliveryService;
+
+use App\Services\DeliveryService;
+
+class GetDeliveryTest extends \Tests\TestCase
+{
+    /** @var DeliveryService $service */
+    private $service;
+    private $data = [];
+    protected function setUp(): void
+    {
+        parent::setUp(); // TODO: Change the autogenerated stub
+        $this->service = app(DeliveryService::class);
+        $this->data['printStr'] = "SO210621008542";
+    }
+
+    public function testGetDeliveryTest()
+    {
+//        $params = $this->service->getDelivery($this->data['printStr']);
+
+//        dd($params);
+        $this->assertTrue(true);
+    }
+
+    protected function tearDown(): void
+    {
+        parent::tearDown(); // TODO: Change the autogenerated stub
+    }
+}

+ 21 - 0
tests/Unit/BaseTest.php

@@ -1,5 +1,7 @@
 <?php
 
+use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\Storage;
 use Tests\TestCase;
 
 class BaseTest extends TestCase
@@ -14,4 +16,23 @@ class BaseTest extends TestCase
         dump(self::CONSTANT);
         $this->assertTrue(true);
     }
+
+    public function testPrintPart(){
+        $service = app(\App\Services\PrintPartService::class);
+        $item = $service->getImagePart();
+        dd($item);
+    }
+
+    public function testPushPartiImg()
+    {
+        Storage::fake('avatars');
+
+        $file = UploadedFile::fake()->image('avatar.jpg');
+        $response = $this->json('POST', 'apiLocal/maintenance/expressPrinting/part/pushImage', [
+            'name' => 'SF',
+            'file' => $file,
+        ]);
+
+        dd($response);
+    }
 }

+ 66 - 2
yarn.lock

@@ -1334,6 +1334,11 @@ base64-js@^1.0.2:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
   integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
 
+base64-js@^1.3.1:
+  version "1.5.1"
+  resolved "https://registry.npm.taobao.org/base64-js/download/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+  integrity sha1-GxtEAWClv3rUC2UPCVljSBkDkwo=
+
 base@^0.11.1:
   version "0.11.2"
   resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
@@ -1551,7 +1556,25 @@ browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.8.5:
     escalade "^3.0.1"
     node-releases "^1.1.58"
 
-buffer-from@^1.0.0:
+buffer-alloc-unsafe@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.npm.taobao.org/buffer-alloc-unsafe/download/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
+  integrity sha1-vX3CauKXLQ7aJTvgYdupkjScGfA=
+
+buffer-alloc@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npm.taobao.org/buffer-alloc/download/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
+  integrity sha1-iQ3ZDZI6hz4I4Q5f1RpX5bfM4Ow=
+  dependencies:
+    buffer-alloc-unsafe "^1.1.0"
+    buffer-fill "^1.0.0"
+
+buffer-fill@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npm.taobao.org/buffer-fill/download/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
+  integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
+
+buffer-from@^1.0.0, buffer-from@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
   integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
@@ -1575,6 +1598,14 @@ buffer@^4.3.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
+buffer@^5.4.3:
+  version "5.7.1"
+  resolved "https://registry.npm.taobao.org/buffer/download/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha1-umLnwTEzBTWCGXFghRqPZI6Z7tA=
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
 builtin-status-codes@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -2470,6 +2501,11 @@ diffie-hellman@^5.0.0:
     miller-rabin "^4.0.0"
     randombytes "^2.0.0"
 
+dijkstrajs@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.nlark.com/dijkstrajs/download/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257"
+  integrity sha1-LkjA07glRir+datK1egpyOzjYlc=
+
 dir-glob@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
@@ -3543,6 +3579,11 @@ icss-utils@^2.1.0:
   dependencies:
     postcss "^6.0.1"
 
+ieee754@^1.1.13:
+  version "1.2.1"
+  resolved "https://registry.nlark.com/ieee754/download/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+  integrity sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I=
+
 ieee754@^1.1.4:
   version "1.1.13"
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -3949,6 +3990,11 @@ isarray@2.0.1:
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
   integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=
 
+isarray@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.npm.taobao.org/isarray/download/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+  integrity sha1-ivHkwSISRMxiRZ+vOJQNTmRKVyM=
+
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -5111,6 +5157,11 @@ pkg-dir@^3.0.0:
   dependencies:
     find-up "^3.0.0"
 
+pngjs@^3.3.0:
+  version "3.4.0"
+  resolved "https://registry.npm.taobao.org/pngjs/download/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f"
+  integrity sha1-mcp9clll+2VYFOr2XzjxK72/VV8=
+
 popper.js@^1.16.1:
   version "1.16.1"
   resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
@@ -5577,6 +5628,19 @@ q@^1.1.2:
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
 
+qrcode@^1.4.4:
+  version "1.4.4"
+  resolved "https://registry.nlark.com/qrcode/download/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83"
+  integrity sha1-8MQ1aKfnUQpV78O4jZYC9xlj6oM=
+  dependencies:
+    buffer "^5.4.3"
+    buffer-alloc "^1.2.0"
+    buffer-from "^1.1.1"
+    dijkstrajs "^1.0.1"
+    isarray "^2.0.1"
+    pngjs "^3.3.0"
+    yargs "^13.2.4"
+
 qs@6.7.0:
   version "6.7.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
@@ -7149,7 +7213,7 @@ yargs@^12.0.5:
     y18n "^3.2.1 || ^4.0.0"
     yargs-parser "^11.1.1"
 
-yargs@^13.3.2:
+yargs@^13.2.4, yargs@^13.3.2:
   version "13.3.2"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
   integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==