Bladeren bron

快递鸟相关代码(开发中)

zyy 1 week geleden
bovenliggende
commit
ab26cebae4
3 gewijzigde bestanden met toevoegingen van 2042 en 0 verwijderingen
  1. 20 0
      src/api/kdniao/eorder.js
  2. 1085 0
      src/views/kdniao/eorder/index.vue
  3. 937 0
      src/views/kdniao/eorder/indexNew.vue

+ 20 - 0
src/api/kdniao/eorder.js

@@ -0,0 +1,20 @@
+import request from '@/utils/request'
+
+// 快递鸟电子面单下单
+export function submitKdniaoEOrder(data) {
+  return request({
+    url: '/kdniao/eorder/submit',
+    method: 'post',
+    data: data
+  })
+}
+
+
+// 快递鸟统一规则版下单
+export function submitKdniaoUniversalEOrder(data) {
+  return request({
+    url: '/kdniao/universal/eorder/submit',
+    method: 'post',
+    data: data
+  })
+}

+ 1085 - 0
src/views/kdniao/eorder/index.vue

@@ -0,0 +1,1085 @@
+<template>
+  <div class="app-container">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="110px"
+      @submit.native.prevent
+    >
+      <!-- 基础信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>电子面单下单</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="业务订单号" prop="bizOrderNo">
+              <el-input v-model="form.bizOrderNo" placeholder="请输入业务订单号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="快递公司" prop="shipperCode">
+              <el-select
+                v-model="form.shipperCode"
+                placeholder="请选择快递公司"
+                clearable
+                style="width: 100%"
+                @change="handleShipperChange"
+              >
+                <el-option label="顺丰 SF" value="SF" />
+                <el-option label="EMS" value="EMS" />
+                <el-option label="京东快运 JDKY" value="JDKY" />
+                <el-option label="京东快递 JOS" value="JOS" />
+                <el-option label="京东生鲜医药 JDSXYY" value="JDSXYY" />
+                <el-option label="中通 ZTO" value="ZTO" />
+                <el-option label="中通冷链 ZTOCOLD" value="ZTOCOLD" />
+                <el-option label="菜鸟橙运 CNCY" value="CNCY" />
+                <el-option label="菜鸟速递 CNSD" value="CNSD" />
+                <el-option label="笨鸟速运 BNSY" value="BNSY" />
+                <el-option label="丰云配 FYP" value="FYP" />
+                <el-option label="云集医药冷链 YJYYLL" value="YJYYLL" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="支付方式" prop="payType">
+              <el-select v-model="form.payType" placeholder="请选择支付方式" style="width: 100%">
+                <el-option label="现付" :value="1" />
+                <el-option label="到付" :value="2" />
+                <el-option label="月结" :value="3" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="业务类型" prop="expType">
+              <el-input v-model="form.expType" placeholder="如 1" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-alert
+          v-if="shipperTip"
+          :title="shipperTip"
+          type="info"
+          :closable="false"
+          show-icon
+        />
+      </el-card>
+
+      <!-- 收件人信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>收件人信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="收件人姓名" prop="receiverName">
+              <el-input v-model="form.receiverName" placeholder="请输入收件人姓名" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="手机号" prop="receiverMobile">
+              <el-input v-model="form.receiverMobile" placeholder="请输入手机号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="电话">
+              <el-input v-model="form.receiverTel" placeholder="请输入电话" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="邮编" :prop="needReceiverPostCode ? 'receiverPostCode' : ''">
+              <el-input
+                v-model="form.receiverPostCode"
+                :placeholder="needReceiverPostCode ? '当前快递要求填写邮编' : '非必填'"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="省" prop="receiverProvinceName">
+              <el-input v-model="form.receiverProvinceName" placeholder="如 广东省" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="市" prop="receiverCityName">
+              <el-input v-model="form.receiverCityName" placeholder="如 深圳市" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="区/县" prop="receiverExpAreaName">
+              <el-input v-model="form.receiverExpAreaName" placeholder="如 福田区" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="详细地址" prop="receiverAddress">
+              <el-input v-model="form.receiverAddress" placeholder="不要填写省市区" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 商品信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>商品与面单信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="商品名称" prop="goodsName">
+              <el-input v-model="form.goodsName" placeholder="如 文件、电子产品" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品数量">
+              <el-input-number v-model="form.goodsQuantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品价格">
+              <el-input-number v-model="form.goodsPrice" :min="0" :precision="2" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品重量">
+              <el-input-number v-model="form.goodsWeight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="包裹数量">
+              <el-input-number v-model="form.quantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总重量" :prop="needWeight ? 'weight' : ''">
+              <el-input-number v-model="form.weight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总体积" :prop="needVolume ? 'volume' : ''">
+              <el-input-number v-model="form.volume" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="模板尺寸">
+              <el-input v-model="form.templateSize" placeholder="默认130" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="备注">
+              <el-input
+                v-model="form.remark"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入备注"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="商品描述">
+              <el-input
+                v-model="form.goodsDesc"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品描述"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 快递专属字段 -->
+      <el-card shadow="never" class="mb12" v-if="showCarrierSpecific">
+        <div slot="header">
+          <span>快递专属参数</span>
+        </div>
+
+        <!-- 顺丰 -->
+        <template v-if="isSF">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="币种">
+                <el-select v-model="form.sf.currencyCode" placeholder="请选择币种" clearable style="width: 100%">
+                  <el-option label="人民币 CNY" value="CNY" />
+                  <el-option label="港币 HKD" value="HKD" />
+                  <el-option label="新台币 NTD" value="NTD" />
+                  <el-option label="澳门元 MOP" value="MOP" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- EMS -->
+        <template v-if="isEMS">
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <el-alert
+                title="EMS要求收件人邮编必填,发件人邮编需在后端配置里配置。"
+                type="warning"
+                :closable="false"
+                show-icon
+              />
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 京东快运 -->
+        <template v-if="isJDKY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="是否通知取件" prop="jdkyIsNotice">
+                <el-select v-model="form.jdky.isNotice" placeholder="请选择" clearable style="width: 100%">
+                  <el-option label="通知(0)" :value="0" />
+                  <el-option label="不通知(1)" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="配送方式">
+                <el-input-number v-model="form.jdky.deliveryMethod" :min="0" style="width: 100%" />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="上门开始" :prop="needPickupTime ? 'jdkyStartDate' : ''">
+                <el-input v-model="form.jdky.startDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="上门结束" :prop="needPickupTime ? 'jdkyEndDate' : ''">
+                <el-input v-model="form.jdky.endDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 京东快递 -->
+        <template v-if="isJOS">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.jos.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 京东生鲜医药 -->
+        <template v-if="isJDSXYY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.jdsxyy.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 中通 -->
+        <template v-if="isZTO">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="操作指令">
+                <el-input v-model="form.zto.operateDirective" placeholder="如有需要可填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 中通冷链 -->
+        <template v-if="isZTOCOLD">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="操作指令">
+                <el-input v-model="form.ztocold.operateDirective" placeholder="如有需要可填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 菜鸟橙运 -->
+        <template v-if="isCNCY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.cncy.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="是否通知取件">
+                <el-select v-model="form.cncy.isNotice" placeholder="请选择" clearable style="width: 100%">
+                  <el-option label="通知(0)" :value="0" />
+                  <el-option label="不通知(1)" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 菜鸟速递 -->
+        <template v-if="isCNSD">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.cnsd.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="是否通知取件">
+                <el-select v-model="form.cnsd.isNotice" placeholder="请选择" clearable style="width: 100%">
+                  <el-option label="通知(0)" :value="0" />
+                  <el-option label="不通知(1)" :value="1" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 笨鸟速运 -->
+        <template v-if="isBNSY">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.bnsy.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 丰云配 -->
+        <template v-if="isFYP">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.fyp.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+
+        <!-- 云集医药冷链 -->
+        <template v-if="isYJYYLL">
+          <el-row :gutter="20">
+            <el-col :span="6">
+              <el-form-item label="运输类型">
+                <el-input v-model="form.yjyyll.transType" placeholder="按业务填写" clearable />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </template>
+      </el-card>
+
+      <!-- 按钮 -->
+      <el-row :gutter="10" class="mb8">
+        <el-button type="primary" icon="el-icon-s-promotion" :loading="submitLoading" @click="handleSubmit">
+          提交下单
+        </el-button>
+
+        <el-button icon="el-icon-refresh" @click="resetFormData">
+          重置
+        </el-button>
+
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-view"
+          :disabled="!result.printTemplate"
+          @click="handlePreview"
+        >
+          查看面单
+        </el-button>
+
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-printer"
+          :disabled="!result.printTemplate"
+          @click="handlePrintDirect"
+        >
+          打印面单
+        </el-button>
+
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-document"
+          :disabled="!result.printTemplate"
+          @click="openNewWindow"
+        >
+          新窗口打开
+        </el-button>
+
+        <el-button
+          plain
+          icon="el-icon-download"
+          :disabled="!result.printTemplate"
+          @click="downloadHtml"
+        >
+          下载HTML
+        </el-button>
+      </el-row>
+    </el-form>
+
+    <!-- 返回结果 -->
+    <el-card shadow="never" v-if="result.raw">
+      <div slot="header">
+        <span>下单结果</span>
+      </div>
+
+      <el-row :gutter="20" class="mb8">
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">是否成功:</span>
+            <el-tag :type="result.success ? 'success' : 'danger'" size="mini">
+              {{ result.success ? "成功" : "失败" }}
+            </el-tag>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">返回码:</span>
+            <span>{{ result.resultCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">运单号:</span>
+            <span>{{ result.logisticCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">订单号:</span>
+            <span>{{ result.orderCode || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="24">
+          <div class="result-item">
+            <span class="label">返回信息:</span>
+            <span>{{ result.reason || serverMsg || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-divider content-position="left">原始返回</el-divider>
+
+      <el-input type="textarea" :rows="12" :value="result.raw" readonly />
+    </el-card>
+
+    <!-- 面单预览 -->
+    <el-dialog
+      title="面单预览"
+      :visible.sync="previewOpen"
+      width="1000px"
+      append-to-body
+      top="3vh"
+    >
+      <div class="preview-toolbar">
+        <el-button size="mini" type="primary" icon="el-icon-printer" @click="printIframe">
+          打印
+        </el-button>
+        <el-button size="mini" icon="el-icon-document" @click="openNewWindow">
+          新窗口打开
+        </el-button>
+        <el-button size="mini" icon="el-icon-download" @click="downloadHtml">
+          下载HTML
+        </el-button>
+      </div>
+
+      <iframe ref="previewFrame" class="preview-frame"></iframe>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { submitKdniaoUniversalEOrder } from "@/api/kdniao/eorder";
+
+export default {
+  name: "KdniaoEOrder",
+  data() {
+    return {
+      submitLoading: false,
+      previewOpen: false,
+      serverMsg: "",
+      form: this.getDefaultForm(),
+      result: {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      },
+      rules: {
+        bizOrderNo: [{ required: true, message: "业务订单号不能为空", trigger: "blur" }],
+        shipperCode: [{ required: true, message: "快递公司不能为空", trigger: "change" }],
+        payType: [{ required: true, message: "支付方式不能为空", trigger: "change" }],
+        expType: [{ required: true, message: "业务类型不能为空", trigger: "blur" }],
+        receiverName: [{ required: true, message: "收件人姓名不能为空", trigger: "blur" }],
+        receiverMobile: [{ required: true, message: "收件人手机号不能为空", trigger: "blur" }],
+        receiverProvinceName: [{ required: true, message: "省不能为空", trigger: "blur" }],
+        receiverCityName: [{ required: true, message: "市不能为空", trigger: "blur" }],
+        receiverExpAreaName: [{ required: true, message: "区/县不能为空", trigger: "blur" }],
+        receiverAddress: [{ required: true, message: "详细地址不能为空", trigger: "blur" }],
+        receiverPostCode: [{ required: true, message: "当前快递要求邮编必填", trigger: "blur" }],
+        goodsName: [{ required: true, message: "商品名称不能为空", trigger: "blur" }],
+        weight: [{ required: true, message: "当前快递要求总重量必填", trigger: "blur" }],
+        volume: [{ required: true, message: "当前快递要求总体积必填", trigger: "blur" }],
+        jdkyIsNotice: [{ required: true, message: "京东快运要求是否通知取件必填", trigger: "change" }],
+        jdkyStartDate: [{ required: true, message: "请填写上门开始时间", trigger: "blur" }],
+        jdkyEndDate: [{ required: true, message: "请填写上门结束时间", trigger: "blur" }]
+      }
+    };
+  },
+  computed: {
+    isSF() {
+      return this.form.shipperCode === "SF";
+    },
+    isEMS() {
+      return this.form.shipperCode === "EMS";
+    },
+    isJDKY() {
+      return this.form.shipperCode === "JDKY";
+    },
+    isJOS() {
+      return this.form.shipperCode === "JOS";
+    },
+    isJDSXYY() {
+      return this.form.shipperCode === "JDSXYY";
+    },
+    isZTO() {
+      return this.form.shipperCode === "ZTO";
+    },
+    isZTOCOLD() {
+      return this.form.shipperCode === "ZTOCOLD";
+    },
+    isCNCY() {
+      return this.form.shipperCode === "CNCY";
+    },
+    isCNSD() {
+      return this.form.shipperCode === "CNSD";
+    },
+    isBNSY() {
+      return this.form.shipperCode === "BNSY";
+    },
+    isFYP() {
+      return this.form.shipperCode === "FYP";
+    },
+    isYJYYLL() {
+      return this.form.shipperCode === "YJYYLL";
+    },
+    showCarrierSpecific() {
+      return true;
+    },
+    needReceiverPostCode() {
+      return this.isEMS;
+    },
+    needWeight() {
+      return this.isJDKY;
+    },
+    needVolume() {
+      return this.isJDKY;
+    },
+    needPickupTime() {
+      return this.isJDKY && this.form.jdky.isNotice === 0;
+    },
+    shipperTip() {
+      if (this.isEMS) {
+        return "EMS:收件人邮编必填。";
+      }
+      if (this.isJDKY) {
+        return "京东快运:重量、体积、是否通知取件必填;通知取件时需填写上门时间。";
+      }
+      if (this.isSF) {
+        return "顺丰:港澳台等场景可填写币种。";
+      }
+      return "";
+    }
+  },
+  methods: {
+    getDefaultForm() {
+      return {
+        bizOrderNo: "ORDER" + new Date().getTime(),
+        shipperCode: "SF",
+        payType: 1,
+        expType: "1",
+
+        receiverName: "李四",
+        receiverMobile: "13900000000",
+        receiverTel: "",
+        receiverProvinceName: "广东省",
+        receiverCityName: "深圳市",
+        receiverExpAreaName: "福田区",
+        receiverAddress: "华强北赛格广场",
+        receiverPostCode: "518000",
+
+        goodsName: "电子产品",
+        goodsQuantity: 1,
+        goodsPrice: 1000,
+        goodsWeight: 0.5,
+        goodsDesc: "测试商品",
+
+        quantity: 1,
+        weight: 0.5,
+        volume: 0.001,
+        cost: null,
+        otherCost: null,
+
+        remark: "测试单请勿接",
+        isReturnPrintTemplate: "1",
+        templateSize: "130",
+        isSubscribe: "0",
+
+        sf: {
+          currencyCode: ""
+        },
+        jdky: {
+          isNotice: null,
+          deliveryMethod: null,
+          startDate: "",
+          endDate: ""
+        },
+        jos: {
+          transType: ""
+        },
+        jdsxyy: {
+          transType: ""
+        },
+        zto: {
+          operateDirective: ""
+        },
+        ztocold: {
+          operateDirective: ""
+        },
+        cncy: {
+          transType: "",
+          isNotice: null
+        },
+        cnsd: {
+          transType: "",
+          isNotice: null
+        },
+        bnsy: {
+          transType: ""
+        },
+        fyp: {
+          transType: ""
+        },
+        yjyyll: {
+          transType: ""
+        }
+      };
+    },
+
+    handleShipperChange() {
+      // 切换快递时不重置通用字段,只重置专属字段
+      const oldCommon = {
+        bizOrderNo: this.form.bizOrderNo,
+        shipperCode: this.form.shipperCode,
+        payType: this.form.payType,
+        expType: this.form.expType,
+
+        receiverName: this.form.receiverName,
+        receiverMobile: this.form.receiverMobile,
+        receiverTel: this.form.receiverTel,
+        receiverProvinceName: this.form.receiverProvinceName,
+        receiverCityName: this.form.receiverCityName,
+        receiverExpAreaName: this.form.receiverExpAreaName,
+        receiverAddress: this.form.receiverAddress,
+        receiverPostCode: this.form.receiverPostCode,
+
+        goodsName: this.form.goodsName,
+        goodsQuantity: this.form.goodsQuantity,
+        goodsPrice: this.form.goodsPrice,
+        goodsWeight: this.form.goodsWeight,
+        goodsDesc: this.form.goodsDesc,
+
+        quantity: this.form.quantity,
+        weight: this.form.weight,
+        volume: this.form.volume,
+        cost: this.form.cost,
+        otherCost: this.form.otherCost,
+
+        remark: this.form.remark,
+        isReturnPrintTemplate: this.form.isReturnPrintTemplate,
+        templateSize: this.form.templateSize,
+        isSubscribe: this.form.isSubscribe
+      };
+
+      this.form = Object.assign(this.getDefaultForm(), oldCommon, {
+        shipperCode: this.form.shipperCode
+      });
+    },
+
+    buildSubmitParams() {
+      const carrierFields = {};
+
+      if (this.isSF && this.form.sf.currencyCode) {
+        carrierFields.CurrencyCode = this.form.sf.currencyCode;
+      }
+
+      if (this.isJDKY) {
+        if (this.form.jdky.deliveryMethod !== null && this.form.jdky.deliveryMethod !== undefined && this.form.jdky.deliveryMethod !== "") {
+          carrierFields.DeliveryMethod = this.form.jdky.deliveryMethod;
+        }
+      }
+
+      if (this.isJOS && this.form.jos.transType) {
+        carrierFields.TransType = this.form.jos.transType;
+      }
+
+      if (this.isJDSXYY && this.form.jdsxyy.transType) {
+        carrierFields.TransType = this.form.jdsxyy.transType;
+      }
+
+      if (this.isZTO && this.form.zto.operateDirective) {
+        carrierFields.OperateDirective = this.form.zto.operateDirective;
+      }
+
+      if (this.isZTOCOLD && this.form.ztocold.operateDirective) {
+        carrierFields.OperateDirective = this.form.ztocold.operateDirective;
+      }
+
+      if (this.isCNCY) {
+        if (this.form.cncy.transType) {
+          carrierFields.TransType = this.form.cncy.transType;
+        }
+      }
+
+      if (this.isCNSD) {
+        if (this.form.cnsd.transType) {
+          carrierFields.TransType = this.form.cnsd.transType;
+        }
+      }
+
+      if (this.isBNSY && this.form.bnsy.transType) {
+        carrierFields.TransType = this.form.bnsy.transType;
+      }
+
+      if (this.isFYP && this.form.fyp.transType) {
+        carrierFields.TransType = this.form.fyp.transType;
+      }
+
+      if (this.isYJYYLL && this.form.yjyyll.transType) {
+        carrierFields.TransType = this.form.yjyyll.transType;
+      }
+
+      let isNotice = null;
+      let startDate = "";
+      let endDate = "";
+
+      if (this.isJDKY) {
+        isNotice = this.form.jdky.isNotice;
+        startDate = this.form.jdky.startDate;
+        endDate = this.form.jdky.endDate;
+      } else if (this.isCNCY) {
+        isNotice = this.form.cncy.isNotice;
+      } else if (this.isCNSD) {
+        isNotice = this.form.cnsd.isNotice;
+      }
+
+      return {
+        bizOrderNo: this.form.bizOrderNo,
+        shipperCode: this.form.shipperCode,
+        payType: this.form.payType,
+        expType: this.form.expType,
+
+        receiver: {
+          name: this.form.receiverName,
+          mobile: this.form.receiverMobile,
+          tel: this.form.receiverTel,
+          provinceName: this.form.receiverProvinceName,
+          cityName: this.form.receiverCityName,
+          expAreaName: this.form.receiverExpAreaName,
+          address: this.form.receiverAddress,
+          postCode: this.form.receiverPostCode
+        },
+
+        commodity: [
+          {
+            goodsName: this.form.goodsName,
+            goodsQuantity: this.form.goodsQuantity,
+            goodsPrice: this.form.goodsPrice,
+            goodsWeight: this.form.goodsWeight,
+            goodsDesc: this.form.goodsDesc
+          }
+        ],
+
+        quantity: this.form.quantity,
+        weight: this.form.weight,
+        volume: this.form.volume,
+        cost: this.form.cost,
+        otherCost: this.form.otherCost,
+
+        remark: this.form.remark,
+        isReturnPrintTemplate: this.form.isReturnPrintTemplate,
+        templateSize: this.form.templateSize,
+        isSubscribe: this.form.isSubscribe,
+
+        isNotice: isNotice,
+        startDate: startDate,
+        endDate: endDate,
+
+        carrierFields: carrierFields
+      };
+    },
+
+    handleSubmit() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) return;
+
+        this.submitLoading = true;
+        this.serverMsg = "";
+        this.result = {
+          success: false,
+          resultCode: "",
+          reason: "",
+          logisticCode: "",
+          orderCode: "",
+          printTemplate: "",
+          raw: ""
+        };
+
+        const submitParams = this.buildSubmitParams();
+
+        submitKdniaoUniversalEOrder(submitParams).then(res => {
+          this.serverMsg = res.msg || "";
+
+          const data = res.data || {};
+          this.result.success = data.success === true || data.Success === true;
+          this.result.resultCode = data.resultCode || data.ResultCode || "";
+          this.result.reason = data.reason || data.Reason || "";
+          this.result.printTemplate = data.printTemplate || data.PrintTemplate || "";
+
+          const order = data.order || data.Order || {};
+          this.result.logisticCode = order.logisticCode || order.LogisticCode || "";
+          this.result.orderCode = order.orderCode || order.OrderCode || "";
+          this.result.raw = JSON.stringify(data, null, 2);
+
+          if (res.code === 200) {
+            this.msgSuccess(res.msg || "下单成功");
+          } else {
+            this.msgError(res.msg || "下单失败");
+          }
+        }).catch(err => {
+          this.msgError((err && err.message) || "请求失败");
+        }).finally(() => {
+          this.submitLoading = false;
+        });
+      });
+    },
+
+    handlePreview() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+      });
+    },
+
+    handlePrintDirect() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+        setTimeout(() => {
+          this.printIframe();
+        }, 200);
+      });
+    },
+
+    writePreviewFrame() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        return;
+      }
+      const doc = iframe.contentWindow.document;
+      doc.open();
+      doc.write(this.buildWaybillHtml(this.result.printTemplate));
+      doc.close();
+    },
+
+    buildWaybillHtml(templateHtml) {
+      const html = templateHtml || "";
+
+      if (/<html[\s\S]*?>/i.test(html)) {
+        return html;
+      }
+
+      return `
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8" />
+  <title>面单预览</title>
+  <style>
+    html, body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+    }
+    body {
+      overflow: auto;
+    }
+    @page {
+      margin: 0;
+    }
+  </style>
+</head>
+<body>
+${html}
+</body>
+</html>`;
+    },
+
+    openNewWindow() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      const win = window.open("", "_blank");
+      if (!win) {
+        this.msgError("浏览器拦截了新窗口,请允许弹窗后重试");
+        return;
+      }
+      win.document.open();
+      win.document.write(this.buildWaybillHtml(this.result.printTemplate));
+      win.document.close();
+    },
+
+    printIframe() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        this.msgError("打印窗口未初始化");
+        return;
+      }
+      try {
+        iframe.contentWindow.focus();
+        iframe.contentWindow.print();
+      } catch (e) {
+        this.msgError("打印失败:" + (e.message || "未知异常"));
+      }
+    },
+
+    downloadHtml() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      const html = this.buildWaybillHtml(this.result.printTemplate);
+      const blob = new Blob([html], { type: "text/html;charset=utf-8" });
+      const url = window.URL.createObjectURL(blob);
+      const a = document.createElement("a");
+
+      a.href = url;
+      a.download = "waybill_" + (this.result.orderCode || new Date().getTime()) + ".html";
+      document.body.appendChild(a);
+      a.click();
+      document.body.removeChild(a);
+      window.URL.revokeObjectURL(url);
+    },
+
+    resetFormData() {
+      this.form = this.getDefaultForm();
+      this.result = {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      };
+      this.serverMsg = "";
+      this.resetForm("form");
+    }
+  }
+};
+</script>
+
+<style scoped>
+.app-container {
+  min-height: calc(100vh - 84px);
+}
+
+.mb8 {
+  margin-bottom: 8px;
+}
+
+.mb12 {
+  margin-bottom: 12px;
+}
+
+.result-item {
+  line-height: 28px;
+  word-break: break-all;
+}
+
+.result-item .label {
+  color: #606266;
+  font-weight: 600;
+}
+
+.preview-toolbar {
+  margin-bottom: 10px;
+}
+
+.preview-frame {
+  width: 100%;
+  height: 700px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  background: #fff;
+}
+</style>

+ 937 - 0
src/views/kdniao/eorder/indexNew.vue

@@ -0,0 +1,937 @@
+<template>
+  <div class="app-container">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      label-width="110px"
+      @submit.native.prevent
+    >
+      <!-- 基础信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>电子面单下单</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="业务订单号" prop="bizOrderNo">
+              <el-input v-model="form.bizOrderNo" placeholder="请输入业务订单号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="快递公司" prop="shipperCode">
+              <el-select
+                v-model="form.shipperCode"
+                placeholder="请选择快递公司"
+                clearable
+                style="width: 100%"
+                @change="handleShipperChange"
+              >
+                <el-option label="顺丰 SF" value="SF" />
+                <el-option label="邮政 EMS" value="EMS" />
+                <el-option label="京东快运 JDKY" value="JDKY" />
+                <el-option label="京东快递 JOS" value="JOS" />
+                <el-option label="京东生鲜医药 JDSXYY" value="JDSXYY" />
+                <el-option label="中通 ZTO" value="ZTO" />
+                <el-option label="中通冷链 ZTOCOLD" value="ZTOCOLD" />
+                <el-option label="菜鸟橙运 CNCY" value="CNCY" />
+                <el-option label="菜鸟速递 CNSD" value="CNSD" />
+                <el-option label="笨鸟速运 BNSY" value="BNSY" />
+                <el-option label="丰云配 FYP" value="FYP" />
+                <el-option label="云集医药冷链 YJYYLL" value="YJYYLL" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="支付方式" prop="payType">
+              <el-select v-model="form.payType" placeholder="请选择支付方式" style="width: 100%">
+                <el-option label="现付" :value="1" />
+                <el-option label="到付" :value="2" />
+                <el-option label="月结" :value="3" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="业务类型" prop="expType">
+              <el-input v-model="form.expType" placeholder="如 1" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-alert
+          v-if="shipperTip"
+          :title="shipperTip"
+          type="info"
+          :closable="false"
+          show-icon
+        />
+      </el-card>
+
+      <!-- 收件人 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>收件人信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="收件人姓名" prop="receiverName">
+              <el-input v-model="form.receiverName" placeholder="请输入收件人姓名" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="手机号" prop="receiverMobile">
+              <el-input v-model="form.receiverMobile" placeholder="请输入手机号" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="电话">
+              <el-input v-model="form.receiverTel" placeholder="请输入电话" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="邮编" :prop="needReceiverPostCode ? 'receiverPostCode' : ''">
+              <el-input
+                v-model="form.receiverPostCode"
+                :placeholder="needReceiverPostCode ? '当前快递要求填写邮编' : '非必填'"
+                clearable
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="省" prop="receiverProvinceName">
+              <el-input v-model="form.receiverProvinceName" placeholder="如 广东省" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="市" prop="receiverCityName">
+              <el-input v-model="form.receiverCityName" placeholder="如 深圳市" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="区/县" prop="receiverExpAreaName">
+              <el-input v-model="form.receiverExpAreaName" placeholder="如 福田区" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="详细地址" prop="receiverAddress">
+              <el-input v-model="form.receiverAddress" placeholder="不要填写省市区" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 商品信息 -->
+      <el-card shadow="never" class="mb12">
+        <div slot="header">
+          <span>商品与面单信息</span>
+        </div>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="商品名称" prop="goodsName">
+              <el-input v-model="form.goodsName" placeholder="如 文件、电子产品" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品数量">
+              <el-input-number v-model="form.goodsQuantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品价格">
+              <el-input-number v-model="form.goodsPrice" :min="0" :precision="2" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="商品重量">
+              <el-input-number v-model="form.goodsWeight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="包裹数量">
+              <el-input-number v-model="form.quantity" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总重量" :prop="needWeight ? 'weight' : ''">
+              <el-input-number v-model="form.weight" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="总体积" :prop="needVolume ? 'volume' : ''">
+              <el-input-number v-model="form.volume" :min="0" :precision="3" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="模板尺寸">
+              <el-input v-model="form.templateSize" placeholder="默认130" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="备注">
+              <el-input
+                v-model="form.remark"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入备注"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="商品描述">
+              <el-input
+                v-model="form.goodsDesc"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品描述"
+                maxlength="100"
+                show-word-limit
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 动态差异字段 -->
+      <el-card shadow="never" class="mb12" v-if="showCarrierFields">
+        <div slot="header">
+          <span>快递差异字段</span>
+        </div>
+
+        <!-- 顺丰 -->
+        <el-row :gutter="20" v-if="isSF">
+          <el-col :span="6">
+            <el-form-item label="币种">
+              <el-select v-model="form.currencyCode" placeholder="请选择币种" clearable style="width: 100%">
+                <el-option label="人民币 CNY" value="CNY" />
+                <el-option label="港币 HKD" value="HKD" />
+                <el-option label="新台币 NTD" value="NTD" />
+                <el-option label="澳门元 MOP" value="MOP" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- EMS -->
+        <el-row :gutter="20" v-if="isEMS">
+          <el-col :span="12">
+            <el-alert
+              title="EMS/邮政要求收件人邮编必填,发件人邮编需在后端配置中填写。"
+              type="warning"
+              :closable="false"
+              show-icon
+            />
+          </el-col>
+        </el-row>
+
+        <!-- 京东快运 -->
+        <el-row :gutter="20" v-if="isJDKY">
+          <el-col :span="6">
+            <el-form-item label="是否通知取件" prop="isNotice">
+              <el-select v-model="form.isNotice" placeholder="请选择" clearable style="width: 100%">
+                <el-option label="通知(0)" :value="0" />
+                <el-option label="不通知(1)" :value="1" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="配送方式">
+              <el-input-number v-model="form.deliveryMethod" :min="0" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="上门开始" :prop="needPickupTime ? 'startDate' : ''">
+              <el-input v-model="form.startDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="6">
+            <el-form-item label="上门结束" :prop="needPickupTime ? 'endDate' : ''">
+              <el-input v-model="form.endDate" placeholder="yyyy-MM-dd HH:mm:ss" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 京东快递 / 京东生鲜医药 / 部分快运 -->
+        <el-row :gutter="20" v-if="isJOS || isJDSXYY">
+          <el-col :span="6">
+            <el-form-item label="运输类型">
+              <el-input v-model="form.transType" placeholder="如 1" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 中通 / 中通冷链 -->
+        <el-row :gutter="20" v-if="isZTO || isZTOCOLD">
+          <el-col :span="6">
+            <el-form-item label="操作指令">
+              <el-input v-model="form.operateDirective" placeholder="可按业务填写" clearable />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <!-- 菜鸟/笨鸟/丰云配/云集 -->
+        <el-row :gutter="20" v-if="isCNCY || isCNSD || isBNSY || isFYP || isYJYYLL">
+          <el-col :span="6">
+            <el-form-item label="运输类型">
+              <el-input v-model="form.transType" placeholder="按业务填写" clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="是否通知取件">
+              <el-select v-model="form.isNotice" placeholder="请选择" clearable style="width: 100%">
+                <el-option label="通知(0)" :value="0" />
+                <el-option label="不通知(1)" :value="1" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-card>
+
+      <!-- 操作按钮 -->
+      <el-row :gutter="10" class="mb8">
+        <el-button type="primary" icon="el-icon-s-promotion" :loading="submitLoading" @click="handleSubmit">
+          提交下单
+        </el-button>
+
+        <el-button icon="el-icon-refresh" @click="resetFormData">
+          重置
+        </el-button>
+
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-view"
+          :disabled="!result.printTemplate"
+          @click="handlePreview"
+        >
+          查看面单
+        </el-button>
+
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-printer"
+          :disabled="!result.printTemplate"
+          @click="handlePrintDirect"
+        >
+          打印面单
+        </el-button>
+
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-document"
+          :disabled="!result.printTemplate"
+          @click="openNewWindow"
+        >
+          新窗口打开
+        </el-button>
+
+        <el-button
+          plain
+          icon="el-icon-download"
+          :disabled="!result.printTemplate"
+          @click="downloadHtml"
+        >
+          下载HTML
+        </el-button>
+      </el-row>
+    </el-form>
+
+    <!-- 返回结果 -->
+    <el-card shadow="never" v-if="result.raw">
+      <div slot="header">
+        <span>下单结果</span>
+      </div>
+
+      <el-row :gutter="20" class="mb8">
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">是否成功:</span>
+            <el-tag :type="result.success ? 'success' : 'danger'" size="mini">
+              {{ result.success ? "成功" : "失败" }}
+            </el-tag>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">返回码:</span>
+            <span>{{ result.resultCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">运单号:</span>
+            <span>{{ result.logisticCode || "-" }}</span>
+          </div>
+        </el-col>
+
+        <el-col :span="6">
+          <div class="result-item">
+            <span class="label">订单号:</span>
+            <span>{{ result.orderCode || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-row>
+        <el-col :span="24">
+          <div class="result-item">
+            <span class="label">返回信息:</span>
+            <span>{{ result.reason || serverMsg || "-" }}</span>
+          </div>
+        </el-col>
+      </el-row>
+
+      <el-divider content-position="left">原始返回</el-divider>
+
+      <el-input
+        type="textarea"
+        :rows="12"
+        :value="result.raw"
+        readonly
+      />
+    </el-card>
+
+    <!-- 面单预览 -->
+    <el-dialog
+      title="面单预览"
+      :visible.sync="previewOpen"
+      width="1000px"
+      append-to-body
+      top="3vh"
+    >
+      <div class="preview-toolbar">
+        <el-button size="mini" type="primary" icon="el-icon-printer" @click="printIframe">
+          打印
+        </el-button>
+        <el-button size="mini" icon="el-icon-document" @click="openNewWindow">
+          新窗口打开
+        </el-button>
+        <el-button size="mini" icon="el-icon-download" @click="downloadHtml">
+          下载HTML
+        </el-button>
+      </div>
+
+      <iframe ref="previewFrame" class="preview-frame"></iframe>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { submitKdniaoUniversalEOrder } from "@/api/kdniao/eorder";
+
+export default {
+  name: "KdniaoEOrder",
+  data() {
+    return {
+      submitLoading: false,
+      previewOpen: false,
+      serverMsg: "",
+      form: {
+        bizOrderNo: "ORDER" + new Date().getTime(),
+        shipperCode: "SF",
+        payType: 1,
+        expType: "1",
+
+        receiverName: "李四",
+        receiverMobile: "13900000000",
+        receiverTel: "",
+        receiverProvinceName: "广东省",
+        receiverCityName: "深圳市",
+        receiverExpAreaName: "福田区",
+        receiverAddress: "华强北赛格广场",
+        receiverPostCode: "518000",
+
+        goodsName: "电子产品",
+        goodsQuantity: 1,
+        goodsPrice: 1000,
+        goodsWeight: 0.5,
+        goodsDesc: "测试商品",
+
+        quantity: 1,
+        weight: 0.5,
+        volume: 0.001,
+        cost: null,
+        otherCost: null,
+
+        remark: "测试单请勿接",
+        isReturnPrintTemplate: "1",
+        templateSize: "130",
+        isSubscribe: "0",
+
+        isNotice: null,
+        startDate: "",
+        endDate: "",
+
+        currencyCode: "",
+        deliveryMethod: null,
+        transType: "",
+        operateDirective: ""
+      },
+      result: {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      },
+      rules: {
+        bizOrderNo: [{ required: true, message: "业务订单号不能为空", trigger: "blur" }],
+        shipperCode: [{ required: true, message: "快递公司不能为空", trigger: "change" }],
+        payType: [{ required: true, message: "支付方式不能为空", trigger: "change" }],
+        expType: [{ required: true, message: "业务类型不能为空", trigger: "blur" }],
+        receiverName: [{ required: true, message: "收件人姓名不能为空", trigger: "blur" }],
+        receiverMobile: [{ required: true, message: "收件人手机号不能为空", trigger: "blur" }],
+        receiverProvinceName: [{ required: true, message: "省不能为空", trigger: "blur" }],
+        receiverCityName: [{ required: true, message: "市不能为空", trigger: "blur" }],
+        receiverExpAreaName: [{ required: true, message: "区/县不能为空", trigger: "blur" }],
+        receiverAddress: [{ required: true, message: "详细地址不能为空", trigger: "blur" }],
+        receiverPostCode: [{ required: true, message: "当前快递要求邮编必填", trigger: "blur" }],
+        goodsName: [{ required: true, message: "商品名称不能为空", trigger: "blur" }],
+        weight: [{ required: true, message: "当前快递要求总重量必填", trigger: "blur" }],
+        volume: [{ required: true, message: "当前快递要求总体积必填", trigger: "blur" }],
+        isNotice: [{ required: true, message: "当前快递要求是否通知取件必填", trigger: "change" }],
+        startDate: [{ required: true, message: "当前快递要求上门开始时间必填", trigger: "blur" }],
+        endDate: [{ required: true, message: "当前快递要求上门结束时间必填", trigger: "blur" }]
+      }
+    };
+  },
+  computed: {
+    isSF() {
+      return this.form.shipperCode === "SF";
+    },
+    isEMS() {
+      return this.form.shipperCode === "EMS";
+    },
+    isJDKY() {
+      return this.form.shipperCode === "JDKY";
+    },
+    isJOS() {
+      return this.form.shipperCode === "JOS";
+    },
+    isJDSXYY() {
+      return this.form.shipperCode === "JDSXYY";
+    },
+    isZTO() {
+      return this.form.shipperCode === "ZTO";
+    },
+    isZTOCOLD() {
+      return this.form.shipperCode === "ZTOCOLD";
+    },
+    isCNCY() {
+      return this.form.shipperCode === "CNCY";
+    },
+    isCNSD() {
+      return this.form.shipperCode === "CNSD";
+    },
+    isBNSY() {
+      return this.form.shipperCode === "BNSY";
+    },
+    isFYP() {
+      return this.form.shipperCode === "FYP";
+    },
+    isYJYYLL() {
+      return this.form.shipperCode === "YJYYLL";
+    },
+    showCarrierFields() {
+      return true;
+    },
+    needReceiverPostCode() {
+      return this.isEMS;
+    },
+    needWeight() {
+      return this.isJDKY;
+    },
+    needVolume() {
+      return this.isJDKY;
+    },
+    needPickupTime() {
+      return this.isJDKY && this.form.isNotice === 0;
+    },
+    shipperTip() {
+      if (this.isEMS) {
+        return "EMS/邮政:收件人邮编必填,发件人邮编需后端配置。";
+      }
+      if (this.isJDKY) {
+        return "京东快运:总重量、总体积、是否通知取件必填;通知取件时需填写上门时间。";
+      }
+      if (this.isSF) {
+        return "顺丰:如港澳台场景,可填写币种。";
+      }
+      if (this.isZTO || this.isZTOCOLD) {
+        return "中通/中通冷链:如业务需要可填写操作指令。";
+      }
+      return "";
+    }
+  },
+  methods: {
+    handleShipperChange() {
+      // 按快递切换时清理差异字段,避免串值
+      this.form.currencyCode = "";
+      this.form.deliveryMethod = null;
+      this.form.transType = "";
+      this.form.operateDirective = "";
+      this.form.isNotice = null;
+      this.form.startDate = "";
+      this.form.endDate = "";
+
+      if (!this.isEMS) {
+        // 非EMS时邮编不强制,但保留用户已填内容,不强清
+      }
+    },
+
+    buildSubmitParams() {
+      const carrierFields = {};
+
+      if (this.form.currencyCode) {
+        carrierFields.CurrencyCode = this.form.currencyCode;
+      }
+      if (this.form.deliveryMethod !== null && this.form.deliveryMethod !== "" && this.form.deliveryMethod !== undefined) {
+        carrierFields.DeliveryMethod = this.form.deliveryMethod;
+      }
+      if (this.form.transType) {
+        carrierFields.TransType = this.form.transType;
+      }
+      if (this.form.operateDirective) {
+        carrierFields.OperateDirective = this.form.operateDirective;
+      }
+
+      return {
+        bizOrderNo: this.form.bizOrderNo,
+        shipperCode: this.form.shipperCode,
+        payType: this.form.payType,
+        expType: this.form.expType,
+
+        receiver: {
+          name: this.form.receiverName,
+          mobile: this.form.receiverMobile,
+          tel: this.form.receiverTel,
+          provinceName: this.form.receiverProvinceName,
+          cityName: this.form.receiverCityName,
+          expAreaName: this.form.receiverExpAreaName,
+          address: this.form.receiverAddress,
+          postCode: this.form.receiverPostCode
+        },
+
+        commodity: [
+          {
+            goodsName: this.form.goodsName,
+            goodsQuantity: this.form.goodsQuantity,
+            goodsPrice: this.form.goodsPrice,
+            goodsWeight: this.form.goodsWeight,
+            goodsDesc: this.form.goodsDesc
+          }
+        ],
+
+        quantity: this.form.quantity,
+        weight: this.form.weight,
+        volume: this.form.volume,
+        cost: this.form.cost,
+        otherCost: this.form.otherCost,
+
+        remark: this.form.remark,
+        isReturnPrintTemplate: this.form.isReturnPrintTemplate,
+        templateSize: this.form.templateSize,
+        isSubscribe: this.form.isSubscribe,
+
+        isNotice: this.form.isNotice,
+        startDate: this.form.startDate,
+        endDate: this.form.endDate,
+
+        carrierFields: carrierFields
+      };
+    },
+
+    handleSubmit() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) return;
+
+        this.submitLoading = true;
+        this.serverMsg = "";
+        this.result = {
+          success: false,
+          resultCode: "",
+          reason: "",
+          logisticCode: "",
+          orderCode: "",
+          printTemplate: "",
+          raw: ""
+        };
+
+        const submitParams = this.buildSubmitParams();
+
+        submitKdniaoUniversalEOrder(submitParams).then(res => {
+          this.serverMsg = res.msg || "";
+
+          const data = res.data || {};
+          this.result.success = data.success === true || data.Success === true;
+          this.result.resultCode = data.resultCode || data.ResultCode || "";
+          this.result.reason = data.reason || data.Reason || "";
+          this.result.printTemplate = data.printTemplate || data.PrintTemplate || "";
+
+          const order = data.order || data.Order || {};
+          this.result.logisticCode = order.logisticCode || order.LogisticCode || "";
+          this.result.orderCode = order.orderCode || order.OrderCode || "";
+          this.result.raw = JSON.stringify(data, null, 2);
+
+          if (res.code === 200) {
+            this.msgSuccess(res.msg || "下单成功");
+          } else {
+            this.msgError(res.msg || "下单失败");
+          }
+        }).catch(err => {
+          this.msgError((err && err.message) || "请求失败");
+        }).finally(() => {
+          this.submitLoading = false;
+        });
+      });
+    },
+
+    handlePreview() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+      });
+    },
+
+    handlePrintDirect() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      this.previewOpen = true;
+      this.$nextTick(() => {
+        this.writePreviewFrame();
+        setTimeout(() => {
+          this.printIframe();
+        }, 200);
+      });
+    },
+
+    writePreviewFrame() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        return;
+      }
+      const doc = iframe.contentWindow.document;
+      doc.open();
+      doc.write(this.buildWaybillHtml(this.result.printTemplate));
+      doc.close();
+    },
+
+    buildWaybillHtml(templateHtml) {
+      const html = templateHtml || "";
+
+      if (/<html[\s\S]*?>/i.test(html)) {
+        return html;
+      }
+
+      return `
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="UTF-8" />
+  <title>面单预览</title>
+  <style>
+    html, body {
+      margin: 0;
+      padding: 0;
+      background: #fff;
+    }
+    body {
+      overflow: auto;
+    }
+    @page {
+      margin: 0;
+    }
+  </style>
+</head>
+<body>
+${html}
+</body>
+</html>`;
+    },
+
+    openNewWindow() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+      const win = window.open("", "_blank");
+      if (!win) {
+        this.msgError("浏览器拦截了新窗口,请允许弹窗后重试");
+        return;
+      }
+      win.document.open();
+      win.document.write(this.buildWaybillHtml(this.result.printTemplate));
+      win.document.close();
+    },
+
+    printIframe() {
+      const iframe = this.$refs.previewFrame;
+      if (!iframe || !iframe.contentWindow) {
+        this.msgError("打印窗口未初始化");
+        return;
+      }
+      try {
+        iframe.contentWindow.focus();
+        iframe.contentWindow.print();
+      } catch (e) {
+        this.msgError("打印失败:" + (e.message || "未知异常"));
+      }
+    },
+
+    downloadHtml() {
+      if (!this.result.printTemplate) {
+        this.msgError("暂无面单模板");
+        return;
+      }
+
+      const html = this.buildWaybillHtml(this.result.printTemplate);
+      const blob = new Blob([html], { type: "text/html;charset=utf-8" });
+      const url = window.URL.createObjectURL(blob);
+      const a = document.createElement("a");
+
+      a.href = url;
+      a.download = "waybill_" + (this.result.orderCode || new Date().getTime()) + ".html";
+      document.body.appendChild(a);
+      a.click();
+      document.body.removeChild(a);
+      window.URL.revokeObjectURL(url);
+    },
+
+    resetFormData() {
+      this.form = {
+        bizOrderNo: "ORDER" + new Date().getTime(),
+        shipperCode: "SF",
+        payType: 1,
+        expType: "1",
+
+        receiverName: "",
+        receiverMobile: "",
+        receiverTel: "",
+        receiverProvinceName: "",
+        receiverCityName: "",
+        receiverExpAreaName: "",
+        receiverAddress: "",
+        receiverPostCode: "",
+
+        goodsName: "",
+        goodsQuantity: 1,
+        goodsPrice: null,
+        goodsWeight: null,
+        goodsDesc: "",
+
+        quantity: 1,
+        weight: null,
+        volume: null,
+        cost: null,
+        otherCost: null,
+
+        remark: "",
+        isReturnPrintTemplate: "1",
+        templateSize: "130",
+        isSubscribe: "0",
+
+        isNotice: null,
+        startDate: "",
+        endDate: "",
+
+        currencyCode: "",
+        deliveryMethod: null,
+        transType: "",
+        operateDirective: ""
+      };
+      this.result = {
+        success: false,
+        resultCode: "",
+        reason: "",
+        logisticCode: "",
+        orderCode: "",
+        printTemplate: "",
+        raw: ""
+      };
+      this.serverMsg = "";
+      this.resetForm("form");
+    }
+  }
+};
+</script>
+
+<style scoped>
+.app-container {
+  min-height: calc(100vh - 84px);
+}
+
+.mb8 {
+  margin-bottom: 8px;
+}
+
+.mb12 {
+  margin-bottom: 12px;
+}
+
+.result-item {
+  line-height: 28px;
+  word-break: break-all;
+}
+
+.result-item .label {
+  color: #606266;
+  font-weight: 600;
+}
+
+.preview-toolbar {
+  margin-bottom: 10px;
+}
+
+.preview-frame {
+  width: 100%;
+  height: 700px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  background: #fff;
+}
+</style>