peng
2025-10-16 477d38740ea84af7abf3eb51a3cf98d6b1e9db40
manager/src/views/order/order/orderDetail.vue
@@ -7,6 +7,7 @@
          <Button v-if="allowOperation.editConsignee" @click="editAddress" type="primary" ghost>修改收货地址</Button>
          <Button v-if="allowOperation.cancel" @click="orderCancel" type="warning" ghost>订单取消</Button>
          <Button v-if="orderInfo.order.orderStatus === 'UNPAID'" @click="confirmPrice" type="primary">收款</Button>
          <Button v-if="allowOperation.showLogistics || orderPackage.length > 0" @click="checkLogistics" type="primary">查看物流</Button>
          <Button @click="orderLog" type="info" ghost>订单日志</Button>
          <Button @click="printOrder" type="primary" ghost style="float:right;"
            v-if="$route.query.orderType != 'VIRTUAL'">打印发货单</Button>
@@ -112,6 +113,7 @@
            <div class="div-item-right">{{ orderInfo.order.remark }}</div>
          </div>
          <!-- <div class="div-item" v-if="orderInfo.order.needReceipt == false">
            <div class="div-item-left">发票信息:</div>
            <div class="div-item-right">暂无发票信息</div>
@@ -170,6 +172,64 @@
            </div>
          </div>
        </div>
        <Col span="24">
          <!-- 外层容器:循环遍历 userCheckTemplates 集合 -->
          <div class="check-template-list">
            <div class="template-item" v-for="(item, index) in orderInfo.userCheckTemplates" :key="item.id">
              <!-- 2. 模板标题:仅第一个循环项显示 templateName(index===0 控制) -->
              <div class="div-item" > <!-- 关键:仅首项渲染 -->
                <div class="div-item-left">商品模板:</div>
                <div class="div-item-right">
                  {{ item.templateName || '无模板标题' }} <!-- 处理空值默认显示 -->
                </div>
              </div>
              <!-- 4. 选择图片:渲染 chooseImg 字段(处理 null/空值) -->
              <div class="div-item">
                <div class="div-item-left">模板图片:</div>
                <div class="div-item-right">
                  <img
                    v-if="item.chooseImg"
                    :src="item.chooseImg"
                    alt="选择的图片"
                    class="selected-img"
                    style="max-width: 200px; max-height: 150px;"
                  >
                  <span v-else>无选择图片</span> <!-- 无图片时默认文本 -->
                </div>
              </div>
              <!-- 1. 商品模板:每个循环项都显示 subName -->
              <div class="div-item">
                <div class="div-item-left">模板标题:</div>
                <div class="div-item-right">
                  {{ item.subName || '无商品模板名称' }} <!-- 处理空值默认显示 -->
                </div>
              </div>
              <!-- 3. 文本内容:判断 content 是「图片URL」还是「纯文本」 -->
              <div class="div-item">
                <div class="div-item-left">{{isUrl(item.content)? '图片:':'文本内容'}}</div>
                <div class="div-item-right">
                  <!-- 正则判断:content 以 http/https 开头 → 渲染图片;否则渲染文本 -->
                  <img
                    v-if="isUrl(item.content)"
                    :src="item.content"
                    alt="内容图片"
                    class="content-img"
                    style="max-width: 200px; max-height: 150px;"
                  >
                  <span v-else>{{ item.content || '无文本内容' }}</span> <!-- 纯文本/空值处理 -->
                </div>
              </div>
              <!-- 可选:循环项分隔线,优化视觉 -->
              <hr v-if="index !== orderInfo.userCheckTemplates.length - 1" style="margin: 15px 0; border: none; border-top: 1px solid #eee;">
            </div>
          </div>
        </Col>
      </Card>
      <Card class="mt_10">
        <Table :loading="loading" border :columns="columns" :data="data" ref="table" sortable="custom">
@@ -308,6 +368,74 @@
      <div slot="footer" style="text-align: right">
        <Button @click="modal = false">关闭</Button>
        <Button type="primary" @click="modifyPriceSubmit">调整</Button>
      </div>
    </Modal>
    <!-- 查询物流 -->
    <Modal v-model="logisticsModal" width="40">
      <p slot="header">
        <span>查询物流</span>
      </p>
      <div class="layui-layer-wrap">
        <dl>
          <dt>订单号:</dt>
          <dd>
            <div class="text-box">{{ sn }}</div>
          </dd>
        </dl>
      </div>
      <div v-if="packageTraceList.length > 0" v-for="(packageItem, packageIndex) in packageTraceList" :key="packageIndex">
        <div class="layui-layer-wrap">
          <dl>
            <dt>物流公司:</dt>
            <dd><div class="text-box">{{ packageItem.logisticsName }}</div></dd>
          </dl>
          <dl>
            <dt>快递单号:</dt>
            <dd><div nctype="ordersSn" class="text-box">{{ packageItem.logisticsNo }}</div></dd>
          </dl>
          <div class="div-express-log">
            <ul class="express-log express-log-name">
              <li v-for="(item, index) in packageItem.orderPackageItemList" :key="index">
                <span class="time" style="width: 50%;"><span>商品名称:</span><span>{{ item.goodsName }}</span></span>
                <span class="time" style="width: 30%;"><span>发货时间:</span><span>{{ item.logisticsTime }}</span></span>
                <span class="time" style="width: 20%;"><span>发货数量:</span><span>{{ item.deliverNumber }}</span></span>
              </li>
            </ul>
          </div>
          <div class="div-express-log">
            <ul class="express-log" v-if="packageItem.traces && packageItem.traces.traces">
              <li v-for="(item, index) in packageItem.traces.traces" :key="index">
                <span class="time">{{ item.AcceptTime || item.acceptTime }}</span>
                <span class="detail">{{ item.AcceptStation || item.remark }}</span>
              </li>
            </ul>
            <ul class="express-log" v-else><li>暂无物流信息</li></ul>
          </div>
        </div>
      </div>
      <div v-if = "packageTraceList.length == 0 && logisticsInfo">
        <div class="layui-layer-wrap">
          <dl>
            <dt>物流公司:</dt>
            <dd><div class="text-box">{{ logisticsInfo.shipper }}</div></dd>
          </dl>
          <dl>
            <dt>快递单号:</dt>
            <dd><div nctype="ordersSn" class="text-box">{{ logisticsInfo.logisticCode }}</div></dd>
          </dl>
          <div class="div-express-log">
            <ul class="express-log" v-if="logisticsInfo && logisticsInfo.traces">
              <li v-for="(item, index) in logisticsInfo.traces" :key="index">
                <span class="time">{{ item.AcceptTime }}</span>
                <span class="detail">{{ item.AcceptStation }}</span>
              </li>
            </ul>
            <ul class="express-log" v-else><li>暂无物流信息</li></ul>
          </div>
        </div>
      </div>
      <div slot="footer" style="text-align: right">
        <Button @click="logisticsModal = false">取消</Button>
      </div>
    </Modal>
    <!-- 订单取消模态框 -->
@@ -451,6 +579,9 @@
  },
  data () {
    return {
      isPreviewVisible: false,
      currentPreviewImage: '',
      currentPreviewIndex: 0,
      typeList: [],
      showPrices: false,
      printHiddenFlag: false,//隐藏信息
@@ -468,6 +599,12 @@
      orderLogModal: false, //弹出调整价格框
      checkedLogistics: [], //选中的物流公司集合
      allowOperation: {}, //订单可才做选项
      logisticsModal: false, //弹出查询物流框
      packageTraceList: [],
      orderPackage: [],
      logisticsInfo: {
        shipper: "",
      }, //物流信息
      sn: "", //订单编号
      orderInfo: {
        order: {
@@ -602,6 +739,45 @@
          minWidth: 80,
        },
        {
          title: "退款状态",
          key: "isRefund",
          minWidth: 100,
          render:(h, params) => {
            if(params.row.isRefund==='NO_REFUND'){
              return h(
                "div",
                "未退款"
              );
            }else if(params.row.isRefund==='ALL_REFUND'){
              return h(
                "div",
                { style: {color:"red"} },
                "全部退款"
              );
            }else if(params.row.isRefund==='PART_REFUND'){
              return h(
                "div",
                { style: {color:"red"} },
                "部分退款"
              );
            }else if(params.row.isRefund==='REFUNDING'){
              return h(
                "div",
                { style: {color:"red"} },
                "退款中"
              );
            }
            else {
              return h(
                "div",
                { style: {color:"red"} },
                "未知状态"
              );
            }
          }
        },
        {
          title: "小计",
          key: "subTotal",
          minWidth: 100,
@@ -638,14 +814,65 @@
  },
  watch: {
    $route (to, from) {
      this.$router.go(0);
      console.log("to:",to.fullPath)
      console.log("form:",from.fullPath)
      // 正确打印路由对象的方法
      if((from.fullPath === "/orderList"|| from.fullPath === "/fictitiousOrderList" ||from.fullPath === "/orderStatistics"||from.fullPath === "/userAction"||from.fullPath.includes( "/member-detail")) && to.fullPath.includes("/order-detail")){
        this.sn = this.$route.query.sn;
        this.getDataList();
        this.getOrderPackage();
      }
    },
  },
  methods: {
    isUrl(str) {
      if (!str) return false; // 空值直接返回false
      // 正则说明:
      // 1. https?:// :支持http/https协议
      // 2. ([\w-]+\.)+ :允许子域名包含短横线(如lmk-1356772813)
      // 3. [a-zA-Z]{2,} :顶级域名(如com、cn,至少2个字母)
      // 4. (\w-./?%&=)* :URL路径/参数部分,支持常见字符
      // 5. \.(jpg|jpeg|png|gif|bmp|webp)$ :仅匹配常见图片后缀,忽略大小写(i标志)
      const imgReg = /^https?:\/\/([\w-]+\.)+[a-zA-Z]{2,}(\/[\w-./?%&=]*)*\.(jpg|jpeg|png|gif|bmp|webp)$/i;
      return imgReg.test(str);
    },
    gotoHomes () {
      return false
    },
    getOrderPackage() {
      API_Order.getPackage(this.sn).then(res => {
        if (res.success) {
          this.orderPackage = res.result;
          console.log('this.orderPackage',this.orderPackage);
        }
      })
    },
    //查询物流
    checkLogistics () {
      this.logisticsModal = true;
      if (this.orderPackage.length > 0) {
        this.logisticsList();
      } else {
        this.logistics();
      }
    },
    logisticsList () {
      this.packageTraceList = []
      this.logisticsModal = true;
      API_Order.getPackage(this.sn).then((res) => {
        if (res.success && res.result != null) {
          this.packageTraceList = res.result;
        }
      });
    },
    logistics () {
      this.logisticsModal = true;
      API_Order.getTraces(this.sn).then((res) => {
        if (res.success && res.result != null) {
          this.logisticsInfo = res.result;
        }
      });
    },
    //确认收款
    confirmPrice () {
      this.$Modal.confirm({
@@ -689,6 +916,7 @@
    // 获取订单详情
    getDataList () {
      this.loading = true;
      console.log("执行获取订单信息操作")
      API_Order.orderDetail(this.sn).then((res) => {
        this.loading = false;
        if (res.success) {
@@ -803,8 +1031,10 @@
    },
  },
  mounted () {
    console.log("加载数据")
    this.sn = this.$route.query.sn;
    this.getDataList();
    this.getOrderPackage();
  },
};
</script>
@@ -910,7 +1140,72 @@
    }
  }
}
.div-express-log {
  max-height: 300px;
  border: solid 1px #e7e7e7;
  background: #fafafa;
  overflow-y: auto;
  overflow-x: auto;
}
.layui-layer-wrap {
  dl {
    border-top: solid 1px #f5f5f5;
    margin-top: -1px;
    overflow: hidden;
    dt {
      font-size: 14px;
      line-height: 28px;
      display: inline-block;
      padding: 8px 1% 8px 0;
      color: #999;
    }
    dd {
      font-size: 14px;
      line-height: 28px;
      display: inline-block;
      padding: 8px 0 8px 8px;
      border-left: solid 1px #f5f5f5;
      .text-box {
        line-height: 40px;
        color: #333;
        word-break: break-all;
      }
    }
  }
}
.express-log {
  /*margin: 5px -10px 5px 5px;*/
  padding: 10px;
  list-style-type: none;
  .time {
    width: 30%;
    display: inline-block;
    float: left;
  }
  .detail {
    width: 60%;
    margin-left: 30px;
    display: inline-block;
  }
  li {
    line-height: 30px;
  }
}
.express-log-name {
  li {
    display: flex;
    span  {
      display: flex;
    }
  }
}
.f14 {
  font-size: 14px;
  color: #333;
@@ -978,4 +1273,30 @@
    height: inherit;
  }
}
.check-template-list {
  display: flex;
  flex-wrap: wrap; // 关键:多余子项自动换行
  gap: 15px; // 子项之间的间距(水平+垂直)
  padding: 10px 0; // 上下内边距,避免贴边
  width: 100%; // 占满父容器(Col span="12")
}
// 2. 每个模板项:控制宽度和卡片样式
.template-item {
  min-width: 280px; // 最小宽度,避免过窄
  max-width: 350px; // 最大宽度,防止过宽
  flex: 1; // 同一行子项均匀分配宽度
  padding: 12px;
  border: 1px solid #eee;
  border-radius: 6px;
  background-color: #fafafa;
  box-sizing: border-box; // 防止padding撑大宽度
}
.content-img,
.selected-img {
  max-width: 100%; // 适应子项宽度
  max-height: 150px; // 限制最大高度
  border-radius: 4px;
  object-fit: cover; // 保持图片比例,不拉伸
}
</style>