zhangwencui
9 天以前 fc10ba09ac226832b8dd59a11b57fc3545503d82
新增客户详情页面,回访提醒跳转客户详情页面
已添加1个文件
已修改3个文件
526 ■■■■■ 文件已修改
src/App.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cooperativeOffice/clientVisit.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/cooperativeOffice/customerManage/detail.vue 493 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue
@@ -57,7 +57,21 @@
    console.log("点击推送消息:", msg);
    console.log("解析后:", msg.payload.noticeId);
    try {
      confirmMessage(msg.payload.noticeId, 1).then(res => {
      if (msg.payload.needMarkRead) {
        confirmMessage(msg.payload.noticeId, 1).then(res => {
          if (msg.payload.url) {
            if (msg.payload.url.indexOf("/") === 0) {
              uni.navigateTo({
                url: msg.payload.url,
              });
            } else {
              uni.navigateTo({
                url: "/" + msg.payload.url,
              });
            }
          }
        });
      } else {
        if (msg.payload.url) {
          if (msg.payload.url.indexOf("/") === 0) {
            uni.navigateTo({
@@ -69,7 +83,7 @@
            });
          }
        }
      });
      }
    } catch (error) {
      uni.showToast({
        title: "路径:" + msg.payload,
src/api/cooperativeOffice/clientVisit.js
@@ -33,4 +33,12 @@
        url: '/customerVisits/'+ids,
        method: 'delete',
    })
}
// æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆè¯¦ç»†
export function getCustomer(id) {
    return request({
        url: '/basic/customer/' + id,
        method: 'get'
    })
}
src/pages.json
@@ -977,6 +977,13 @@
      }
    },
    {
      "path": "pages/cooperativeOffice/customerManage/detail",
      "style": {
        "navigationBarTitleText": "客户详情",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/message",
      "style": {
        "navigationBarTitleText": "消息中心"
src/pages/cooperativeOffice/customerManage/detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,493 @@
<template>
  <view class="customer-detail-page">
    <!-- é¡µé¢å¤´éƒ¨ -->
    <PageHeader title="客户详情"
                @back="goBack" />
    <!-- è¯¦æƒ…内容 -->
    <view class="detail-content">
      <!-- å®¢æˆ·åŸºæœ¬ä¿¡æ¯ -->
      <view class="section">
        <view class="section-title">客户基本信息</view>
        <view class="info-list">
          <view class="info-item">
            <text class="info-label">客户名称</text>
            <text class="info-value">{{ detailData.customerName || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">客户类型</text>
            <text class="info-value">{{ detailData.customerType || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">纳税人识别号</text>
            <text class="info-value">{{ detailData.taxpayerIdentificationNumber || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">公司地址</text>
            <text class="info-value">{{ detailData.companyAddress || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">公司电话</text>
            <text class="info-value">{{ detailData.companyPhone || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">联系人</text>
            <text class="info-value">{{ detailData.contactPerson || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">联系电话</text>
            <text class="info-value">{{ detailData.contactPhone || '-' }}</text>
          </view>
        </view>
      </view>
      <!-- è·Ÿè¿›è®°å½• -->
      <view class="section">
        <view class="section-title">
          <view class="title-left">
            <up-icon name="clock-fill"
                     size="16"
                     color="#409eff" />
            <text>跟进记录</text>
          </view>
          <text class="record-count"
                v-if="detailData.followUpList && detailData.followUpList.length > 0">
            å…±{{ detailData.followUpList.length }}条
          </text>
        </view>
        <view v-if="detailData.followUpList && detailData.followUpList.length > 0"
              class="follow-up-list">
          <view v-for="(item, index) in detailData.followUpList"
                :key="index"
                class="follow-up-item">
            <!-- æ—¶é—´è½´å·¦ä¾§çº¿æ¡ -->
            <view class="timeline-left">
              <view class="timeline-dot"></view>
              <view class="timeline-line"
                    v-if="index !== detailData.followUpList.length - 1"></view>
            </view>
            <!-- å†…容区域 -->
            <view class="follow-up-content-wrapper">
              <view class="follow-up-header">
                <view class="follow-up-info">
                  <view class="user-info">
                    <up-icon name="account-fill"
                             size="14"
                             color="#909399" />
                    <text class="follow-up-name">{{ item.followerUserName || '-' }}</text>
                  </view>
                  <view class="time-info">
                    <up-icon name="clock"
                             size="12"
                             color="#c0c4cc" />
                    <text class="follow-up-time">{{ formatDateTime(item.followUpTime) }}</text>
                  </view>
                </view>
                <view class="follow-up-tags">
                  <up-tag type="primary"
                          size="mini">{{ item.followUpMethod || '-' }}</up-tag>
                  <up-tag :type="getFollowUpLevelType(item.followUpLevel)"
                          size="mini">{{ item.followUpLevel || '-' }}</up-tag>
                </view>
              </view>
              <view class="follow-up-content">
                <view class="content-label">
                  <up-icon name="edit-pen-fill"
                           size="12"
                           color="#c0c4cc" />
                  <text>跟进内容</text>
                </view>
                <view class="content-box">
                  <text class="content-text">{{ item.content || '-' }}</text>
                </view>
              </view>
              <!-- <view v-if="item.fileList && item.fileList.length > 0"
                    class="follow-up-files">
                <view class="files-header">
                  <up-icon name="folder-open"
                           size="12"
                           color="#c0c4cc" />
                  <text class="files-label">附件 ({{ item.fileList.length }})</text>
                </view>
                <view class="files-list">
                  <view v-for="(file, fileIndex) in item.fileList"
                        :key="fileIndex"
                        class="file-item">
                    <up-icon name="attach"
                             size="14"
                             color="#409eff" />
                    <text class="file-name">{{ file.name || '附件' + (fileIndex + 1) }}</text>
                  </view>
                </view>
              </view> -->
            </view>
          </view>
        </view>
        <view v-else
              class="no-data">
          <up-empty mode="data"
                    text="暂无跟进记录" />
        </view>
      </view>
      <!-- é“¶è¡Œä¿¡æ¯ -->
      <view class="section">
        <view class="section-title">银行信息</view>
        <view class="info-list">
          <view class="info-item">
            <text class="info-label">基本银行账户</text>
            <text class="info-value">{{ detailData.basicBankAccount || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">开户行</text>
            <text class="info-value">{{ detailData.bankAccount || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">银行账号</text>
            <text class="info-value">{{ detailData.bankCode || '-' }}</text>
          </view>
        </view>
      </view>
      <!-- ç»´æŠ¤ä¿¡æ¯ -->
      <view class="section">
        <view class="section-title">维护信息</view>
        <view class="info-list">
          <view class="info-item">
            <text class="info-label">维护人</text>
            <text class="info-value">{{ detailData.maintainer || '-' }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">维护时间</text>
            <text class="info-value">{{ formatDateTime(detailData.maintenanceTime) }}</text>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>
<script setup>
  import { ref, onMounted } from "vue";
  import PageHeader from "@/components/PageHeader.vue";
  import { getCustomer } from "@/api/cooperativeOffice/clientVisit";
  import { onLoad, onShow } from "@dcloudio/uni-app";
  import dayjs from "dayjs";
  // èŽ·å–é¡µé¢å‚æ•°
  const getPageId = () => {
    const pages = getCurrentPages();
    const currentPage = pages[pages.length - 1];
    const options = currentPage.options || {};
    return options.id || "";
  };
  // è¯¦æƒ…数据
  const detailData = ref({});
  // æ ¼å¼åŒ–日期时间
  const formatDateTime = dateStr => {
    if (!dateStr) return "-";
    return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss");
  };
  // èŽ·å–è·Ÿè¿›çº§åˆ«æ ‡ç­¾ç±»åž‹
  const getFollowUpLevelType = level => {
    switch (level) {
      case "非常熟悉":
        return "success";
      case "熟悉":
        return "primary";
      case "一般":
        return "warning";
      case "不熟悉":
        return "error";
      default:
        return "info";
    }
  };
  // è¿”回上一页
  const goBack = () => {
    uni.navigateBack();
  };
  const customerId = ref("");
  // customerId
  onLoad(options => {
    // è§£æžcustomerId
    if (options.customerId) {
      customerId.value = options.customerId;
      getDetail();
    }
  });
  // èŽ·å–è¯¦æƒ…æ•°æ®
  const getDetail = async () => {
    if (!customerId.value) {
      uni.showToast({
        title: "参数错误",
        icon: "none",
      });
      return;
    }
    try {
      uni.showLoading({
        title: "加载中...",
      });
      const res = await getCustomer(customerId.value);
      if (res.code === 200) {
        detailData.value = res.data;
      } else {
        uni.showToast({
          title: res.msg || "获取详情失败",
          icon: "none",
        });
      }
    } catch (error) {
      console.error("加载详情数据失败:", error);
      uni.showToast({
        title: "网络错误",
        icon: "none",
      });
    } finally {
      uni.hideLoading();
    }
  };
  onMounted(() => {});
</script>
<style scoped lang="scss">
  .customer-detail-page {
    min-height: 100vh;
    background-color: #f5f5f5;
  }
  .detail-content {
    padding: 16px;
  }
  .section {
    background: #ffffff;
    border-radius: 12px;
    margin-bottom: 16px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  }
  .section-title {
    padding: 16px;
    font-size: 16px;
    font-weight: 600;
    color: #303133;
    border-bottom: 1px solid #f0f0f0;
  }
  .info-list {
    padding: 8px 0;
  }
  .info-item {
    display: flex;
    padding: 12px 16px;
    border-bottom: 1px solid #f8f8f8;
  }
  .info-item:last-child {
    border-bottom: none;
  }
  .info-label {
    width: 120px;
    font-size: 14px;
    color: #606266;
  }
  .info-value {
    flex: 1;
    font-size: 14px;
    color: #303133;
    text-align: right;
  }
  .section-title {
    padding: 16px;
    font-size: 16px;
    font-weight: 600;
    color: #303133;
    border-bottom: 1px solid #f0f0f0;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .title-left {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .record-count {
    font-size: 12px;
    color: #909399;
    font-weight: normal;
    background: #f5f7fa;
    padding: 2px 8px;
    border-radius: 10px;
  }
  .follow-up-list {
    padding: 0;
  }
  .follow-up-item {
    display: flex;
    padding: 16px;
    position: relative;
  }
  .timeline-left {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-right: 12px;
    padding-top: 4px;
  }
  .timeline-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #409eff;
    border: 2px solid #e6f2ff;
    flex-shrink: 0;
  }
  .timeline-line {
    width: 2px;
    flex: 1;
    background: #e4e7ed;
    margin-top: 4px;
    min-height: 40px;
  }
  .follow-up-content-wrapper {
    flex: 1;
    background: #f8f9fa;
    border-radius: 8px;
    padding: 12px;
  }
  .follow-up-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    margin-bottom: 12px;
  }
  .follow-up-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .user-info {
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .time-info {
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .follow-up-name {
    font-size: 14px;
    font-weight: 600;
    color: #303133;
  }
  .follow-up-time {
    font-size: 12px;
    color: #909399;
  }
  .follow-up-tags {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
  }
  .follow-up-content {
    margin-bottom: 12px;
  }
  .content-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: #909399;
    margin-bottom: 8px;
  }
  .content-box {
    background: #ffffff;
    border-radius: 6px;
    padding: 10px 12px;
    border-left: 3px solid #409eff;
  }
  .content-text {
    font-size: 14px;
    color: #303133;
    line-height: 1.6;
    word-break: break-all;
    word-wrap: break-word;
  }
  .follow-up-files {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px dashed #e4e7ed;
  }
  .files-header {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-bottom: 8px;
  }
  .files-label {
    font-size: 12px;
    color: #909399;
  }
  .files-list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }
  .file-item {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 10px;
    background: #ffffff;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
  }
  .file-name {
    font-size: 12px;
    color: #606266;
  }
  .no-data {
    padding: 40px 20px;
    text-align: center;
  }
</style>