<template>
  <el-dialog :visible.sync="visibleSync" title="会员画像" :width="'1500px'" append-to-body top="8vh"
    custom-class="member-portrait-dialog" :close-on-click-modal="false" @closed="handleClosed">
    <div v-loading="loading" class="portrait-wrapper">
      <!-- 中部：左右分栏（参考个人档案排版） -->
      <div class="portrait-main-panel">
        <!-- 左侧：头像 + 姓名 + 关键信息 + 操作 -->
        <div class="panel-left">
          <div class="avatar-circle">
            <i class="el-icon-user-solid"></i>
          </div>
          <h2 class="member-name">{{ baseInfo.MemberName || '—' }}</h2>
          <el-tag v-if="baseInfo.ConsumeLevel !== undefined" :type="getConsumeLevelTagType(baseInfo.ConsumeLevel)" size="small" class="level-tag">
            {{ getConsumeLevelText(baseInfo.ConsumeLevel) }}
          </el-tag>
          <div class="left-meta">
            <div class="meta-row">档案号：{{ baseInfo.MemberCode || '无' }}</div>
            <div class="meta-row phone">手机号：{{ baseInfo.Mobile || '无' }}</div>
          </div>
          <el-button type="primary" size="small" icon="el-icon-edit" class="btn-edit" @click="handleEdit">
            修改资料
          </el-button>
        </div>

        <!-- 右侧：3 列属性网格 -->
        <div class="panel-right">
          <div class="attr-grid">
            <div class="attr-item"><span class="attr-label">性别：</span><span class="attr-value">{{ baseInfo.Gender || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">年龄：</span><span class="attr-value">{{ getDisplayAge() }}</span></div>
            <div class="attr-item"><span class="attr-label">客户类型：</span><span class="attr-value">{{ baseInfo.CustomerTypeName || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">归属门店：</span><span class="attr-value">{{ baseInfo.StoreName || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">进店渠道：</span><span class="attr-value">{{ baseInfo.Channel || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">注册时间：</span><span class="attr-value">{{ formatDate(baseInfo.RegisterTime) || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">生日：</span><span class="attr-value">{{ formatBirthdayDisplay() }}</span></div>
            <div class="attr-item"><span class="attr-label">沉睡天数：</span>
              <el-tag :type="baseInfo.SleepDays > 30 ? 'warning' : 'info'" size="mini">{{ baseInfo.SleepDays || 0 }} 天</el-tag>
            </div>
            <div class="attr-item"><span class="attr-label">剩余权益：</span><span class="attr-value highlight primary">￥{{ formatMoney(behaviorSummary.RemainingRightsAmount) }}</span></div>
            <div class="attr-item"><span class="attr-label">累计开单：</span><span class="attr-value highlight">￥{{ formatMoney(behaviorSummary.TotalBillingAmount) }}</span></div>
            <div class="attr-item"><span class="attr-label">累计消耗：</span><span class="attr-value highlight">￥{{ formatMoney(behaviorSummary.TotalConsumeAmount) }}</span></div>
            <div class="attr-item"><span class="attr-label">推荐人：</span><span class="attr-value">{{ baseInfo.ReferrerName || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">拓客人员：</span><span class="attr-value">{{ baseInfo.ExpandUserName || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">主健康师：</span><span class="attr-value">{{ baseInfo.MainHealthUserName || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">负责顾问：</span><span class="attr-value">{{ baseInfo.SubHealthUserName || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">首次到店：</span><span class="attr-value">{{ formatDateTime(baseInfo.FirstVisitTime) || '无' }}</span></div>
            <div class="attr-item"><span class="attr-label">最后到店：</span><span class="attr-value">{{ formatDateTime(baseInfo.LastVisitTime) || '无' }}</span></div>
            <div class="attr-item attr-item-full"><span class="attr-label">联系地址：</span><span class="attr-value">{{ baseInfo.Address || '无' }}</span></div>
            <div class="attr-item attr-item-full"><span class="attr-label">备注：</span><span class="attr-value">{{ baseInfo.Remark || '无' }}</span></div>
          </div>
          <!-- 会员类型标签 -->
          <div v-if="baseInfo.MemberTypes && baseInfo.MemberTypes.length > 0" class="member-types-row">
            <span v-for="type in baseInfo.MemberTypes" :key="type.TypeName" class="member-type-wrap">
              <el-tag :type="getMemberTypeTagType(type.TypeName)" size="small">{{ type.TypeName }}会员</el-tag>
              <span v-if="type.BecomeTime" class="type-date">{{ formatDate(type.BecomeTime) }}</span>
            </span>
          </div>
        </div>
      </div>

      <!-- 选项卡内容 -->
      <el-tabs v-model="activeTab" class="portrait-tabs">
        <!-- 概览：消费行为、趋势、分析（个人档案已在顶部面板展示） -->
        <el-tab-pane label="概览" name="overview">
          <div class="tab-content profile-layout">
            <!-- 消费行为 -->
            <div class="detail-section">
              <div class="section-title">
                <i class="el-icon-shopping-cart-full"></i>
                <span>消费行为</span>
              </div>
              <div class="section-content">
                <div class="behavior-grid">
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-document"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">开单次数</div>
                      <div class="behavior-value">{{ behaviorSummary.BillingCount || 0 }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-goods"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">消耗次数</div>
                      <div class="behavior-value">{{ behaviorSummary.ConsumeCount || 0 }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-refresh-left"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">退卡次数</div>
                      <div class="behavior-value">{{ behaviorSummary.RefundCount || 0 }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-coin"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">平均开单金额</div>
                      <div class="behavior-value">￥{{ formatMoney(behaviorSummary.AvgBillingAmount) }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-coin"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">平均消耗金额</div>
                      <div class="behavior-value">￥{{ formatMoney(behaviorSummary.AvgConsumeAmount) }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-time"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">最近开单</div>
                      <div class="behavior-value">{{ formatDateTime(behaviorSummary.LastBillingTime) }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-time"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">最近消耗</div>
                      <div class="behavior-value">{{ formatDateTime(behaviorSummary.LastConsumeTime) }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-calendar"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">首次开单</div>
                      <div class="behavior-value">{{ formatDateTime(behaviorSummary.FirstBillingTime) }}</div>
                    </div>
                  </div>
                  <div class="behavior-item">
                    <div class="behavior-icon">
                      <i class="el-icon-calendar"></i>
                    </div>
                    <div class="behavior-content">
                      <div class="behavior-label">首次消耗</div>
                      <div class="behavior-value">{{ formatDateTime(behaviorSummary.FirstConsumeTime) }}</div>
                    </div>
                  </div>
                </div>
              </div>
            </div>

            <!-- 近12个月消费趋势 -->
            <div class="detail-section">
              <div class="section-title">
                <i class="el-icon-data-line"></i>
                <span>近12个月消费趋势</span>
              </div>
              <div class="section-content">
                <div ref="trendChart" class="trend-chart"></div>
              </div>
            </div>

            <!-- 消费分析 -->
            <div v-if="consumptionAnalysis" class="detail-section">
              <div class="section-title">
                <i class="el-icon-data-analysis"></i>
                <span>消费分析</span>
              </div>
              <div class="section-content">
                <div class="analysis-layout">
                  <div class="analysis-item">
                    <div class="analysis-icon">
                      <i class="el-icon-timer"></i>
                    </div>
                    <div class="analysis-content">
                      <div class="analysis-label">消费频率</div>
                      <div class="analysis-value">{{ formatMoney(consumptionAnalysis.ConsumeFrequency) }} 次/月</div>
                    </div>
                  </div>
                  <div class="analysis-item">
                    <div class="analysis-icon">
                      <i class="el-icon-timer"></i>
                    </div>
                    <div class="analysis-content">
                      <div class="analysis-label">开单频率</div>
                      <div class="analysis-value">{{ formatMoney(consumptionAnalysis.BillingFrequency) }} 次/月</div>
                    </div>
                  </div>
                  <div class="analysis-item">
                    <div class="analysis-icon">
                      <i class="el-icon-success"></i>
                    </div>
                    <div class="analysis-content">
                      <div class="analysis-label">消费活跃度</div>
                      <div class="analysis-value">
                        <el-tag :type="consumptionAnalysis.IsActive ? 'success' : 'info'" size="small">
                          {{ consumptionAnalysis.IsActive ? '活跃' : '不活跃' }}
                        </el-tag>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </el-tab-pane>

        <!-- 权益明细 -->
        <el-tab-pane label="权益明细" name="assets">
          <div class="tab-content tab-content-direct">
            <el-table :data="remainingItems" size="small" border stripe :default-sort="{ prop: 'RemainingValue', order: 'descending' }">
                  <el-table-column prop="ItemName" label="品项名称" min-width="140" sortable />
                  <el-table-column prop="SourceType" label="来源类型" width="90" sortable />
                  <el-table-column prop="UnitPrice" label="单价" width="90" sortable :sort-method="sortNumber('UnitPrice')">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.UnitPrice) }}</template>
                  </el-table-column>
                  <el-table-column prop="TotalQuantity" label="总数量" width="80" sortable :sort-method="sortNumber('TotalQuantity')" />
                  <el-table-column prop="ConsumedQuantity" label="已消费" width="80" sortable :sort-method="sortNumber('ConsumedQuantity')" />
                  <el-table-column prop="RefundedQuantity" label="已退款" width="80" sortable :sort-method="sortNumber('RefundedQuantity')" />
                  <el-table-column prop="DeductedQuantity" label="已扣除" width="80" sortable :sort-method="sortNumber('DeductedQuantity')" />
                  <el-table-column prop="RemainingQuantity" label="剩余" width="80" sortable :sort-method="sortNumber('RemainingQuantity')" />
                  <el-table-column prop="RemainingValue" label="剩余价值" width="120" sortable :sort-method="sortNumber('RemainingValue')">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.RemainingValue) }}</template>
                  </el-table-column>
                </el-table>
          </div>
        </el-tab-pane>

        <!-- 邀约记录 -->
        <el-tab-pane label="邀约记录" name="invite">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="inviteLoading" :data="inviteList" size="small" border stripe>
                  <el-table-column prop="InviteDate" label="邀约时间" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.InviteDate) }}</template>
                  </el-table-column>
                  <el-table-column prop="StoreName" label="门店" width="120" />
                  <el-table-column prop="InviterName" label="邀约人" width="100" />
                  <el-table-column prop="ContactTime" label="联系时间" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.ContactTime) }}</template>
                  </el-table-column>
                  <el-table-column prop="ContactRecord" label="联系记录" min-width="200" show-overflow-tooltip />
                  <el-table-column prop="PhoneValid" label="电话有效" width="90" />
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="invitePagination.total" :current-page="invitePagination.pageIndex"
                :page-size="invitePagination.pageSize" @size-change="handleInviteSizeChange"
                @current-change="handleInvitePageChange" />
            </div>
          </div>
        </el-tab-pane>

        <!-- 预约记录 -->
        <el-tab-pane label="预约记录" name="appointment">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="appointmentLoading" :data="appointmentList" size="small" border stripe>
                  <el-table-column prop="AppointmentDate" label="预约时间" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.AppointmentDate) }}</template>
                  </el-table-column>
                  <el-table-column prop="StoreName" label="门店" width="120" />
                  <el-table-column prop="InviterName" label="邀约人" width="100" />
                  <el-table-column prop="HealthCoachName" label="预约健康师" width="100" />
                  <el-table-column prop="ExperienceItem" label="体验项目" min-width="120" show-overflow-tooltip />
                  <el-table-column prop="Status" label="状态" width="80" />
                  <el-table-column prop="NoDealRemark" label="未成交说明" min-width="150" show-overflow-tooltip />
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="appointmentPagination.total" :current-page="appointmentPagination.pageIndex"
                :page-size="appointmentPagination.pageSize" @size-change="handleAppointmentSizeChange"
                @current-change="handleAppointmentPageChange" />
            </div>
          </div>
        </el-tab-pane>

        <!-- 开单记录 -->
        <el-tab-pane label="开单记录" name="billing">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="billingLoading" :data="billingList" size="small" border stripe>
                  <el-table-column prop="BillingDate" label="开单日期" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.BillingDate) }}</template>
                  </el-table-column>
                  <el-table-column prop="StoreName" label="门店" width="100" />
                  <el-table-column prop="CreatorName" label="开单人员" width="90" show-overflow-tooltip />
                  <el-table-column prop="HealthCoachNames" label="健康师" width="100" show-overflow-tooltip />
                  <el-table-column prop="TechTeacherNames" label="科技老师" width="100" show-overflow-tooltip />
                  <el-table-column prop="Items" label="开单品项" min-width="180" show-overflow-tooltip />
                  <el-table-column prop="Amount" label="实付金额" width="90">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.Amount) }}</template>
                  </el-table-column>
                  <el-table-column prop="DebtAmount" label="欠款金额" width="90">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.DebtAmount) }}</template>
                  </el-table-column>
                  <el-table-column prop="ActivityName" label="活动名称" width="100" show-overflow-tooltip />
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="billingPagination.total" :current-page="billingPagination.pageIndex"
                :page-size="billingPagination.pageSize" @size-change="handleBillingSizeChange"
                @current-change="handleBillingPageChange" />
            </div>
          </div>
        </el-tab-pane>

        <!-- 消耗记录 -->
        <el-tab-pane label="消耗记录" name="consume">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="consumeLoading" :data="consumeList" size="small" border stripe>
                  <el-table-column prop="ConsumeDate" label="消耗日期" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.ConsumeDate) }}</template>
                  </el-table-column>
                  <el-table-column prop="StoreName" label="门店" width="100" />
                  <el-table-column prop="OperatorName" label="操作人员" width="90" show-overflow-tooltip />
                  <el-table-column prop="HealthCoachNames" label="健康师" width="100" show-overflow-tooltip />
                  <el-table-column prop="TechTeacherNames" label="科技老师" width="100" show-overflow-tooltip />
                  <el-table-column prop="Items" label="消耗品项" min-width="180" show-overflow-tooltip />
                  <el-table-column prop="Amount" label="消耗金额" width="90">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.Amount) }}</template>
                  </el-table-column>
                  <el-table-column prop="LaborCost" label="手工费" width="90">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.LaborCost) }}</template>
                  </el-table-column>
                  <el-table-column label="操作" width="100" align="left" fixed="right">
                    <template slot-scope="scope">
                      <el-button v-if="scope.row.HasServiceLog" type="text" size="small" icon="el-icon-document"
                        @click="showConsumeServiceLog(scope.row.Id)">
                        查看日志
                      </el-button>
                      <span v-else class="text-muted">—</span>
                    </template>
                  </el-table-column>
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="consumePagination.total" :current-page="consumePagination.pageIndex"
                :page-size="consumePagination.pageSize" @size-change="handleConsumeSizeChange"
                @current-change="handleConsumePageChange" />
            </div>
          </div>
        </el-tab-pane>

        <!-- 服务日志 -->
        <el-tab-pane label="服务日志" name="serviceLog">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="serviceLogLoading" :data="serviceLogList" size="small" border stripe>
                  <el-table-column prop="CreateTime" label="记录时间" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.CreateTime) }}</template>
                  </el-table-column>
                  <el-table-column prop="CreatorName" label="添加人" width="100" />
                  <el-table-column prop="Remark" label="备注" min-width="200" show-overflow-tooltip />
                  <el-table-column prop="KjbRemark" label="科技部备注" min-width="120" show-overflow-tooltip />
                  <el-table-column label="操作" width="100" align="left">
                    <template slot-scope="scope">
                      <el-button v-if="hasServiceLogImages(scope.row)" type="text" size="small" icon="el-icon-picture-outline" @click="showServiceLogImages(scope.row)">
                        查看图片
                      </el-button>
                      <span v-else class="text-muted">—</span>
                    </template>
                  </el-table-column>
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="serviceLogPagination.total" :current-page="serviceLogPagination.pageIndex"
                :page-size="serviceLogPagination.pageSize" @size-change="handleServiceLogSizeChange"
                @current-change="handleServiceLogPageChange" />
            </div>
          </div>
        </el-tab-pane>

        <!-- 旧日志（历史开单记录） -->
        <el-tab-pane label="旧日志" name="oldLog">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="oldLogLoading" :data="oldLogList" size="small" border stripe>
                  <el-table-column label="记录时间" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.createTime || scope.row.CreateTime) }}</template>
                  </el-table-column>
                  <el-table-column label="开单号" width="140" show-overflow-tooltip>
                    <template slot-scope="scope">{{ scope.row.orderNo || scope.row.OrderNo || '无' }}</template>
                  </el-table-column>
                  <el-table-column label="会员名称" width="100" show-overflow-tooltip>
                    <template slot-scope="scope">{{ scope.row.memberName || scope.row.MemberName || '无' }}</template>
                  </el-table-column>
                  <el-table-column label="备注" min-width="280" show-overflow-tooltip>
                    <template slot-scope="scope">{{ scope.row.remarks || scope.row.Remarks || '无' }}</template>
                  </el-table-column>
                  <el-table-column label="操作" width="100" align="left">
                    <template slot-scope="scope">
                      <el-button v-if="hasOldLogImage(scope.row)" type="text" size="small" icon="el-icon-picture-outline" @click="showOldLogImages(scope.row)">
                        查看图片
                      </el-button>
                      <span v-else class="text-muted">—</span>
                    </template>
                  </el-table-column>
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="oldLogPagination.total" :current-page="oldLogPagination.pageIndex"
                :page-size="oldLogPagination.pageSize" @size-change="handleOldLogSizeChange"
                @current-change="handleOldLogPageChange" />
            </div>
          </div>
        </el-tab-pane>

        <!-- 退卡列表 -->
        <el-tab-pane label="退卡列表" name="refund">
          <div class="tab-content tab-content-direct">
            <el-table v-loading="refundLoading" :data="refundList" size="small" border stripe>
                  <el-table-column prop="RefundDate" label="退卡日期" width="160">
                    <template slot-scope="scope">{{ formatDateTime(scope.row.RefundDate) }}</template>
                  </el-table-column>
                  <el-table-column prop="StoreName" label="门店" width="120" />
                  <el-table-column prop="RefundAmount" label="退卡金额" width="100">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.RefundAmount) }}</template>
                  </el-table-column>
                  <el-table-column prop="ActualRefundAmount" label="实际退款" width="100">
                    <template slot-scope="scope">￥{{ formatMoney(scope.row.ActualRefundAmount) }}</template>
                  </el-table-column>
                  <el-table-column prop="RefundReason" label="退卡原因" min-width="150" show-overflow-tooltip />
                </el-table>
            <div class="pagination-bar">
              <el-pagination layout="total, sizes, prev, pager, next" :page-sizes="[10, 20, 50]"
                :total="refundPagination.total" :current-page="refundPagination.pageIndex"
                :page-size="refundPagination.pageSize" @size-change="handleRefundSizeChange"
                @current-change="handleRefundPageChange" />
            </div>
          </div>
        </el-tab-pane>
      </el-tabs>
    </div>

    <!-- 服务日志图片预览弹窗 -->
    <el-dialog title="服务日志图片" :visible.sync="serviceLogImageVisible" width="900px" append-to-body
      custom-class="service-log-image-dialog" @closed="serviceLogImageRow = null">
      <div v-if="serviceLogImageRow" class="image-preview-content">
        <div class="preview-header">
          <span class="preview-meta"><i class="el-icon-time"></i> {{ formatDateTime(serviceLogImageRow.CreateTime) }}</span>
          <span class="preview-meta"><i class="el-icon-user"></i> {{ serviceLogImageRow.CreatorName || '—' }}</span>
        </div>
        <div class="preview-row">
          <div class="image-section">
            <div class="image-section-title"><i class="el-icon-picture-outline"></i> 服务前</div>
            <div class="image-list">
              <template v-if="parseServiceLogImages(serviceLogImageRow.BeforeImage).length > 0">
                <el-image v-for="(img, idx) in parseServiceLogImages(serviceLogImageRow.BeforeImage)" :key="'b-' + idx"
                  :src="getServiceLogImageUrl(img)" :preview-src-list="getServiceLogPreviewList(serviceLogImageRow.BeforeImage)"
                  fit="contain" class="preview-img" />
              </template>
              <div v-else class="image-placeholder">
                <i class="el-icon-picture-outline"></i>
                <span>暂无图片</span>
              </div>
            </div>
          </div>
          <div class="image-section">
            <div class="image-section-title"><i class="el-icon-picture-outline"></i> 服务后</div>
            <div class="image-list">
              <template v-if="parseServiceLogImages(serviceLogImageRow.AfterImage).length > 0">
                <el-image v-for="(img, idx) in parseServiceLogImages(serviceLogImageRow.AfterImage)" :key="'a-' + idx"
                  :src="getServiceLogImageUrl(img)" :preview-src-list="getServiceLogPreviewList(serviceLogImageRow.AfterImage)"
                  fit="contain" class="preview-img" />
              </template>
              <div v-else class="image-placeholder">
                <i class="el-icon-picture-outline"></i>
                <span>暂无图片</span>
              </div>
            </div>
          </div>
        </div>
        <div v-if="serviceLogImageRow.Remark" class="preview-remark">
          <span class="remark-label">备注：</span>{{ serviceLogImageRow.Remark }}
        </div>
      </div>
    </el-dialog>

    <!-- 旧日志-图片预览弹窗 -->
    <el-dialog title="旧日志图片" :visible.sync="oldLogImageVisible" width="800px" append-to-body
      custom-class="old-log-image-dialog" @closed="oldLogImageUrls = []">
      <div v-if="oldLogImageUrls.length > 0" class="old-log-image-content">
        <el-image v-for="(url, idx) in oldLogImageUrls" :key="idx"
          :src="url" :preview-src-list="oldLogImageUrls" :initial-index="idx"
          fit="contain" class="old-log-preview-img" />
      </div>
    </el-dialog>

    <!-- 消耗记录-服务日志弹窗（与服务日志tab的查看图片弹窗同款样式） -->
    <el-dialog title="服务日志" :visible.sync="consumeServiceLogVisible" width="900px" append-to-body
      custom-class="service-log-image-dialog" @closed="consumeServiceLogList = []">
      <div v-loading="consumeServiceLogLoading" class="consume-service-log-content">
        <div v-if="consumeServiceLogList.length === 0 && !consumeServiceLogLoading" class="no-data">
          <el-empty description="暂无服务日志数据"></el-empty>
        </div>
        <div v-else class="log-list">
          <div v-for="(row, idx) in consumeServiceLogList" :key="row.Id || idx" class="image-preview-content log-item">
            <div class="preview-header">
              <span class="preview-meta"><i class="el-icon-time"></i> {{ formatDateTime(row.CreateTime) }}</span>
              <span class="preview-meta"><i class="el-icon-user"></i> {{ row.CreatorName || '—' }}</span>
            </div>
            <div class="preview-row">
              <div class="image-section">
                <div class="image-section-title"><i class="el-icon-picture-outline"></i> 服务前</div>
                <div class="image-list">
                  <template v-if="parseServiceLogImages(row.BeforeImage).length > 0">
                    <el-image v-for="(img, i) in parseServiceLogImages(row.BeforeImage)" :key="'b-' + i"
                      :src="getServiceLogImageUrl(img)" :preview-src-list="getServiceLogPreviewList(row.BeforeImage)"
                      fit="contain" class="preview-img" />
                  </template>
                  <div v-else class="image-placeholder">
                    <i class="el-icon-picture-outline"></i>
                    <span>暂无图片</span>
                  </div>
                </div>
              </div>
              <div class="image-section">
                <div class="image-section-title"><i class="el-icon-picture-outline"></i> 服务后</div>
                <div class="image-list">
                  <template v-if="parseServiceLogImages(row.AfterImage).length > 0">
                    <el-image v-for="(img, i) in parseServiceLogImages(row.AfterImage)" :key="'a-' + i"
                      :src="getServiceLogImageUrl(img)" :preview-src-list="getServiceLogPreviewList(row.AfterImage)"
                      fit="contain" class="preview-img" />
                  </template>
                  <div v-else class="image-placeholder">
                    <i class="el-icon-picture-outline"></i>
                    <span>暂无图片</span>
                  </div>
                </div>
              </div>
            </div>
            <div v-if="row.Remark" class="preview-remark">
              <span class="remark-label">备注：</span>{{ row.Remark }}
            </div>
            <div v-if="row.KjbRemark" class="preview-remark">
              <span class="remark-label">科技部备注：</span>{{ row.KjbRemark }}
            </div>
          </div>
        </div>
      </div>
    </el-dialog>
  </el-dialog>
</template>

<script>
import request from '@/utils/request'
import * as echarts from 'echarts'

export default {
  name: 'MemberPortraitDialog',
  props: {
    visible: { type: Boolean, default: false },
    memberId: { type: String, default: '' }
  },
  data() {
    return {
      visibleSync: false,
      loading: false,
      activeTab: 'overview',
      baseInfo: {},
      behaviorSummary: {},
      remainingItems: [],
      monthlyTrend: [],
      consumptionAnalysis: null,
      trendChart: null,
      // 开单列表
      billingList: [],
      billingLoading: false,
      billingPagination: {
        pageIndex: 1,
        pageSize: 10,
        total: 0
      },
      // 消耗列表
      consumeList: [],
      consumeLoading: false,
      consumePagination: {
        pageIndex: 1,
        pageSize: 10,
        total: 0
      },
      // 退卡列表
      refundList: [],
      refundLoading: false,
      refundPagination: {
        pageIndex: 1,
        pageSize: 10,
        total: 0
      },
      // 预约记录
      appointmentList: [],
      appointmentLoading: false,
      appointmentPagination: { pageIndex: 1, pageSize: 10, total: 0 },
      // 邀约记录
      inviteList: [],
      inviteLoading: false,
      invitePagination: { pageIndex: 1, pageSize: 10, total: 0 },
      // 服务日志
      serviceLogList: [],
      serviceLogLoading: false,
      serviceLogPagination: { pageIndex: 1, pageSize: 10, total: 0 },
      // 旧日志
      oldLogList: [],
      oldLogLoading: false,
      oldLogPagination: { pageIndex: 1, pageSize: 10, total: 0 },
      // 服务日志图片预览
      serviceLogImageVisible: false,
      serviceLogImageRow: null,
      // 消耗记录-服务日志弹窗（与服务日志tab同款）
      consumeServiceLogVisible: false,
      consumeServiceLogList: [],
      consumeServiceLogLoading: false,
      // 旧日志-图片预览
      oldLogImageVisible: false,
      oldLogImageUrls: []
    }
  },
  watch: {
    visible: {
      immediate: true,
      handler(v) {
        this.visibleSync = v
        if (v && this.memberId) {
          this.fetchData()
        }
      }
    },
    memberId: {
      handler(newVal) {
        if (newVal && this.visibleSync) {
          this.fetchData()
        }
      }
    },
    activeTab(newVal) {
      if (newVal === 'billing' && this.billingList.length === 0) {
        this.fetchBillingList()
      } else if (newVal === 'consume' && this.consumeList.length === 0) {
        this.fetchConsumeList()
      } else if (newVal === 'refund' && this.refundList.length === 0) {
        this.fetchRefundList()
      } else if (newVal === 'appointment' && this.appointmentList.length === 0) {
        this.fetchAppointmentList()
      } else if (newVal === 'invite' && this.inviteList.length === 0) {
        this.fetchInviteList()
      } else if (newVal === 'serviceLog' && this.serviceLogList.length === 0) {
        this.fetchServiceLogList()
      } else if (newVal === 'oldLog') {
        this.fetchOldLogList()
      }
    }
  },
  methods: {
    /** 重置所有数据，避免二次进入时显示旧数据 */
    resetData() {
      this.baseInfo = {}
      this.behaviorSummary = {}
      this.remainingItems = []
      this.monthlyTrend = []
      this.consumptionAnalysis = null
      this.billingList = []
      this.billingPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.consumeList = []
      this.consumePagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.refundList = []
      this.refundPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.appointmentList = []
      this.appointmentPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.inviteList = []
      this.invitePagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.serviceLogList = []
      this.serviceLogPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.oldLogList = []
      this.oldLogPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.activeTab = 'overview'
    },
    /** 年龄显示：优先用后端返回的 Age，否则根据阳历生日计算 */
    getDisplayAge() {
      const info = this.baseInfo
      if (!info) return '无'
      let age = info.Age
      if (age == null && info.Yanglsr) {
        const birth = new Date(info.Yanglsr)
        if (!isNaN(birth.getTime())) {
          const now = new Date()
          age = now.getFullYear() - birth.getFullYear()
          if (now.getMonth() < birth.getMonth() || (now.getMonth() === birth.getMonth() && now.getDate() < birth.getDate())) {
            age--
          }
        }
      }
      return age != null ? age + '岁' : '无'
    },
    /** 修改资料：关闭画像并触发父组件打开编辑 */
    handleEdit() {
      this.$emit('edit', this.memberId)
      this.$emit('update:visible', false)
    },
    async fetchData() {
      if (!this.memberId) {
        this.$message.warning('会员ID不能为空')
        return
      }

      // 切换会员时清空列表数据，避免显示上一次的列表
      this.billingList = []
      this.billingPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.consumeList = []
      this.consumePagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.refundList = []
      this.refundPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.appointmentList = []
      this.appointmentPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.inviteList = []
      this.invitePagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.serviceLogList = []
      this.serviceLogPagination = { pageIndex: 1, pageSize: 10, total: 0 }
      this.oldLogList = []
      this.oldLogPagination = { pageIndex: 1, pageSize: 10, total: 0 }

      this.loading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/overview',
          method: 'GET',
          data: { memberId: this.memberId }
        })

        if (res.code === 200 && res.data) {
          this.baseInfo = res.data.BaseInfo || {}
          this.behaviorSummary = res.data.BehaviorSummary || {}
          this.remainingItems = (res.data.Assets && res.data.Assets.RemainingItems) || []
          this.monthlyTrend = res.data.MonthlyTrend || []
          this.consumptionAnalysis = res.data.ConsumptionAnalysis || null

          this.$nextTick(() => {
            this.renderTrendChart()
          })
        } else {
          this.$message.error(res.msg || '获取会员画像数据失败')
        }
      } catch (error) {
        console.error('获取会员画像数据失败:', error)
        this.$message.error('获取会员画像数据失败: ' + (error.message || '未知错误'))
      } finally {
        this.loading = false
      }
    },
    async fetchBillingList() {
      if (!this.memberId) return

      this.billingLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/billing-list',
          method: 'GET',
          data: {
            memberId: this.memberId,
            pageIndex: this.billingPagination.pageIndex,
            pageSize: this.billingPagination.pageSize
          }
        })

        if (res.code === 200 && res.data) {
          this.billingList = res.data.List || []
          this.billingPagination.total = res.data.Total || 0
        } else {
          this.$message.error(res.msg || '获取开单列表失败')
        }
      } catch (error) {
        console.error('获取开单列表失败:', error)
        this.$message.error('获取开单列表失败: ' + (error.message || '未知错误'))
      } finally {
        this.billingLoading = false
      }
    },
    async fetchConsumeList() {
      if (!this.memberId) return

      this.consumeLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/consume-list',
          method: 'GET',
          data: {
            memberId: this.memberId,
            pageIndex: this.consumePagination.pageIndex,
            pageSize: this.consumePagination.pageSize
          }
        })

        if (res.code === 200 && res.data) {
          this.consumeList = res.data.List || []
          this.consumePagination.total = res.data.Total || 0
        } else {
          this.$message.error(res.msg || '获取消耗列表失败')
        }
      } catch (error) {
        console.error('获取消耗列表失败:', error)
        this.$message.error('获取消耗列表失败: ' + (error.message || '未知错误'))
      } finally {
        this.consumeLoading = false
      }
    },
    async fetchRefundList() {
      if (!this.memberId) return

      this.refundLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/refund-list',
          method: 'GET',
          data: {
            memberId: this.memberId,
            pageIndex: this.refundPagination.pageIndex,
            pageSize: this.refundPagination.pageSize
          }
        })

        if (res.code === 200 && res.data) {
          this.refundList = res.data.List || []
          this.refundPagination.total = res.data.Total || 0
        } else {
          this.$message.error(res.msg || '获取退卡列表失败')
        }
      } catch (error) {
        console.error('获取退卡列表失败:', error)
        this.$message.error('获取退卡列表失败: ' + (error.message || '未知错误'))
      } finally {
        this.refundLoading = false
      }
    },
    handleBillingPageChange(page) {
      this.billingPagination.pageIndex = page
      this.fetchBillingList()
    },
    handleBillingSizeChange(size) {
      this.billingPagination.pageSize = size
      this.billingPagination.pageIndex = 1
      this.fetchBillingList()
    },
    handleConsumePageChange(page) {
      this.consumePagination.pageIndex = page
      this.fetchConsumeList()
    },
    handleConsumeSizeChange(size) {
      this.consumePagination.pageSize = size
      this.consumePagination.pageIndex = 1
      this.fetchConsumeList()
    },
    handleRefundPageChange(page) {
      this.refundPagination.pageIndex = page
      this.fetchRefundList()
    },
    handleRefundSizeChange(size) {
      this.refundPagination.pageSize = size
      this.refundPagination.pageIndex = 1
      this.fetchRefundList()
    },
    async fetchAppointmentList() {
      if (!this.memberId) return
      this.appointmentLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/appointment-list',
          method: 'GET',
          data: {
            memberId: this.memberId,
            pageIndex: this.appointmentPagination.pageIndex,
            pageSize: this.appointmentPagination.pageSize
          }
        })
        if (res.code === 200 && res.data) {
          this.appointmentList = res.data.List || []
          this.appointmentPagination.total = res.data.Total || 0
        } else {
          this.$message.error(res.msg || '获取预约记录失败')
        }
      } catch (error) {
        console.error('获取预约记录失败:', error)
        this.$message.error('获取预约记录失败: ' + (error.message || '未知错误'))
      } finally {
        this.appointmentLoading = false
      }
    },
    handleAppointmentPageChange(page) {
      this.appointmentPagination.pageIndex = page
      this.fetchAppointmentList()
    },
    handleAppointmentSizeChange(size) {
      this.appointmentPagination.pageSize = size
      this.appointmentPagination.pageIndex = 1
      this.fetchAppointmentList()
    },
    async fetchInviteList() {
      if (!this.memberId) return
      this.inviteLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/invite-list',
          method: 'GET',
          data: {
            memberId: this.memberId,
            pageIndex: this.invitePagination.pageIndex,
            pageSize: this.invitePagination.pageSize
          }
        })
        if (res.code === 200 && res.data) {
          this.inviteList = res.data.List || []
          this.invitePagination.total = res.data.Total || 0
        } else {
          this.$message.error(res.msg || '获取邀约记录失败')
        }
      } catch (error) {
        console.error('获取邀约记录失败:', error)
        this.$message.error('获取邀约记录失败: ' + (error.message || '未知错误'))
      } finally {
        this.inviteLoading = false
      }
    },
    handleInvitePageChange(page) {
      this.invitePagination.pageIndex = page
      this.fetchInviteList()
    },
    handleInviteSizeChange(size) {
      this.invitePagination.pageSize = size
      this.invitePagination.pageIndex = 1
      this.fetchInviteList()
    },
    async fetchServiceLogList() {
      if (!this.memberId) return
      this.serviceLogLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/service-log-list',
          method: 'GET',
          data: {
            memberId: this.memberId,
            pageIndex: this.serviceLogPagination.pageIndex,
            pageSize: this.serviceLogPagination.pageSize
          }
        })
        if (res.code === 200 && res.data) {
          this.serviceLogList = res.data.List || []
          this.serviceLogPagination.total = res.data.Total || 0
        } else {
          this.$message.error(res.msg || '获取服务日志失败')
        }
      } catch (error) {
        console.error('获取服务日志失败:', error)
        this.$message.error('获取服务日志失败: ' + (error.message || '未知错误'))
      } finally {
        this.serviceLogLoading = false
      }
    },
    handleServiceLogPageChange(page) {
      this.serviceLogPagination.pageIndex = page
      this.fetchServiceLogList()
    },
    handleServiceLogSizeChange(size) {
      this.serviceLogPagination.pageSize = size
      this.serviceLogPagination.pageIndex = 1
      this.fetchServiceLogList()
    },
    async fetchOldLogList() {
      const memberCode = this.baseInfo.MemberCode || ''
      const mobile = this.baseInfo.Mobile || ''
      if (!memberCode && !mobile) {
        this.$message.warning('会员编号和手机号均无，无法查询旧日志')
        return
      }

      this.oldLogLoading = true
      try {
        const res = await request({
          url: '/api/Extend/MemberPortrait/old-log-list',
          method: 'GET',
          data: {
            memberCode,
            mobile,
            pageIndex: this.oldLogPagination.pageIndex,
            pageSize: this.oldLogPagination.pageSize
          }
        })

        if (res.code === 200 && res.data) {
          const list = res.data.List || res.data.list || []
          const total = (res.data.Total != null ? res.data.Total : res.data.total) || 0
          this.oldLogList = list
          this.oldLogPagination.total = total
          // 调试：输出接口返回数据
          console.log('[旧日志] 接口返回:', { data: res.data, list, total, firstItem: list[0] })
        } else {
          this.$message.error(res.msg || '获取旧日志失败')
        }
      } catch (error) {
        console.error('获取旧日志失败:', error)
        this.$message.error('获取旧日志失败: ' + (error.message || '未知错误'))
      } finally {
        this.oldLogLoading = false
      }
    },
    handleOldLogPageChange(page) {
      this.oldLogPagination.pageIndex = page
      this.fetchOldLogList()
    },
    handleOldLogSizeChange(size) {
      this.oldLogPagination.pageSize = size
      this.oldLogPagination.pageIndex = 1
      this.fetchOldLogList()
    },
    showServiceLogImages(row) {
      this.serviceLogImageRow = row
      this.serviceLogImageVisible = true
    },
    async showConsumeServiceLog(consumeId) {
      this.consumeServiceLogVisible = true
      this.consumeServiceLogLoading = true
      this.consumeServiceLogList = []
      try {
        const res = await request({
          url: `/api/Extend/lqxhfeedback/GetByConsumeId/${consumeId}`,
          method: 'GET'
        })
        if (res.code === 200 && res.data) {
          const list = Array.isArray(res.data) ? res.data : []
          this.consumeServiceLogList = list.map(item => ({
            Id: item.id,
            CreateTime: item.createTime,
            CreatorName: item.createUserName,
            Remark: item.remark,
            KjbRemark: item.kjbRemark,
            BeforeImage: item.beforeImage,
            AfterImage: item.afterImage
          }))
        }
      } catch (error) {
        console.error('获取服务日志失败:', error)
        this.$message.error('获取服务日志失败')
      } finally {
        this.consumeServiceLogLoading = false
      }
    },
    parseServiceLogImages(imageStr) {
      if (!imageStr) return []
      try {
        const arr = typeof imageStr === 'string' ? JSON.parse(imageStr) : imageStr
        return Array.isArray(arr) ? arr : []
      } catch {
        return []
      }
    },
    getServiceLogImageUrl(img) {
      if (!img || !img.url) return ''
      const url = img.url
      if (url.startsWith('http://') || url.startsWith('https://')) return url
      const base = process.env.VUE_APP_IMG_API || process.env.VUE_APP_BASE_API || ''
      return base ? (base.replace(/\/$/, '') + (url.startsWith('/') ? url : '/' + url)) : url
    },
    getServiceLogPreviewList(imageStr) {
      return this.parseServiceLogImages(imageStr).map(img => this.getServiceLogImageUrl(img)).filter(Boolean)
    },
    hasServiceLogImages(row) {
      const before = this.parseServiceLogImages(row && row.BeforeImage).length
      const after = this.parseServiceLogImages(row && row.AfterImage).length
      return before > 0 || after > 0
    },
    hasOldLogImage(row) {
      const name = row && (row.imageName || row.ImageName)
      if (!name || typeof name !== 'string') return false
      return name.split(';').filter(s => s && s.trim()).length > 0
    },
    parseOldLogImageUrls(imageStr) {
      if (!imageStr || typeof imageStr !== 'string') return []
      return imageStr.split(';').map(s => s.trim()).filter(Boolean)
    },
    showOldLogImages(row) {
      const urls = this.parseOldLogImageUrls(row && (row.imageName || row.ImageName))
      if (urls.length === 0) return
      this.oldLogImageUrls = urls
      this.oldLogImageVisible = true
    },
    renderTrendChart() {
      if (!this.$refs.trendChart) return

      if (this.trendChart) {
        this.trendChart.dispose()
      }

      this.trendChart = echarts.init(this.$refs.trendChart)

      const months = this.monthlyTrend.map(item => item.Month)
      const billingData = this.monthlyTrend.map(item => item.BillingAmount || 0)
      const consumeData = this.monthlyTrend.map(item => item.ConsumeAmount || 0)
      const refundData = this.monthlyTrend.map(item => item.RefundAmount || 0)

      const option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: { type: 'cross' }
        },
        legend: {
          data: ['开单金额', '消耗金额', '退卡金额'],
          bottom: 0
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '15%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          data: months,
          axisLabel: {
            rotate: 45
          }
        },
        yAxis: {
          type: 'value',
          axisLabel: {
            formatter: value => '￥' + this.formatMoney(value)
          }
        },
        series: [
          {
            name: '开单金额',
            type: 'line',
            data: billingData,
            itemStyle: { color: '#409EFF' }
          },
          {
            name: '消耗金额',
            type: 'line',
            data: consumeData,
            itemStyle: { color: '#67C23A' }
          },
          {
            name: '退卡金额',
            type: 'line',
            data: refundData,
            itemStyle: { color: '#F56C6C' }
          }
        ]
      }

      this.trendChart.setOption(option)

      // 响应式调整
      window.addEventListener('resize', this.handleChartResize)
    },
    handleChartResize() {
      if (this.trendChart) {
        this.trendChart.resize()
      }
    },
    formatMoney(amount) {
      if (amount === null || amount === undefined || amount === '') return '0.00'
      return Number(amount).toFixed(2)
    },
    sortNumber(prop) {
      return (a, b) => {
        const va = Number(a[prop]) || 0
        const vb = Number(b[prop]) || 0
        return va - vb
      }
    },
    formatDateTime(timestamp) {
      if (!timestamp) return '无'
      const date = typeof timestamp === 'number' ? new Date(timestamp) : new Date(timestamp)
      if (!isNaN(date.getTime())) {
        return date.toLocaleString('zh-CN', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit',
          hour: '2-digit',
          minute: '2-digit'
        })
      }
      return timestamp
    },
    formatDate(timestamp) {
      if (!timestamp) return ''
      const date = typeof timestamp === 'number' ? new Date(timestamp) : new Date(timestamp)
      if (!isNaN(date.getTime())) {
        return date.toLocaleDateString('zh-CN', {
          year: 'numeric',
          month: '2-digit',
          day: '2-digit'
        })
      }
      return timestamp
    },
    formatBirthdayDisplay() {
      const info = this.baseInfo
      if (!info) return '无'
      if (info.BirthdayType === 1 && info.Yinlsr) return `${info.BirthdayTypeName || '农历'} ${info.Yinlsr}`
      if (info.Yanglsr) {
        const d = new Date(info.Yanglsr)
        if (!isNaN(d.getTime())) return `${info.BirthdayTypeName || '阳历'} ${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`
      }
      if (info.Yinlsr) return `农历 ${info.Yinlsr}`
      return '无'
    },
    getConsumeLevelText(level) {
      const levelMap = {
        0: 'D',
        1: 'C',
        2: 'B',
        3: 'A',
        4: 'A+',
        5: 'A++'
      }
      return levelMap[level] || 'D'
    },
    getConsumeLevelTagType(level) {
      const typeMap = {
        0: 'info',
        1: '',
        2: 'warning',
        3: 'success',
        4: 'success',
        5: 'danger'
      }
      return typeMap[level] || 'info'
    },
    getMemberTypeTagType(typeName) {
      const typeMap = {
        '生美': 'success',
        '医美': 'warning',
        '科技部': 'primary',
        '教育部': 'info'
      }
      return typeMap[typeName] || 'info'
    },
    handleClosed() {
      if (this.trendChart) {
        window.removeEventListener('resize', this.handleChartResize)
        this.trendChart.dispose()
        this.trendChart = null
      }
      this.resetData()
      this.$emit('update:visible', false)
      this.$emit('closed')
    }
  },
  beforeDestroy() {
    if (this.trendChart) {
      window.removeEventListener('resize', this.handleChartResize)
      this.trendChart.dispose()
    }
  }
}
</script>

<style lang="scss" scoped>
::v-deep .el-dialog {
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15), 0 8px 24px rgba(0, 0, 0, 0.1);
}

.member-portrait-dialog {
  .el-dialog {
    border-radius: 20px;
    overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.2);
  }

  .el-dialog__header {
    padding: 18px 24px;
    border-bottom: 1px solid #e4e7ed;
    background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%);
    color: #fff;
    position: relative;

    &::after {
      content: '';
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 3px;
      background: linear-gradient(90deg, #409EFF 0%, #66b1ff 100%);
    }

    .el-dialog__title {
      color: #fff;
      font-size: 18px;
      font-weight: 600;
      letter-spacing: 0;
    }

    .el-dialog__close {
      color: rgba(255, 255, 255, 0.8);
      font-size: 20px;
      transition: all 0.2s ease;

      &:hover {
        color: #fff;
      }
    }
  }

  .el-dialog__body {
    padding: 20px;
    background: #f5f7fa;
    color: #303133;
    overflow: hidden;
    height: calc(90vh - 120px);
    display: flex;
    flex-direction: column;
  }
}

.portrait-wrapper {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow: hidden;

  // 中部左右分栏
  .portrait-main-panel {
    flex-shrink: 0;
    display: flex;
    gap: 24px;
    margin-bottom: 16px;
    background: linear-gradient(180deg, #fff 0%, #fafbfc 100%);
    border-radius: 12px;
    padding: 24px;
    border: 1px solid #e4e7ed;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06), 0 0 0 1px rgba(64, 158, 255, 0.04);
    min-height: 180px;

    .panel-left {
      flex-shrink: 0;
      width: 220px;
      min-width: 220px;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding-right: 24px;
      border-right: 1px solid #ebeef5;

      .avatar-circle {
        width: 64px;
        height: 64px;
        border-radius: 12px;
        background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;
        font-size: 28px;
        margin-bottom: 12px;
        box-shadow: 0 4px 12px rgba(64, 158, 255, 0.35);
      }

      .member-name {
        font-size: 18px;
        font-weight: 600;
        color: #303133;
        margin: 0 0 8px 0;
        line-height: 1.3;
        text-align: center;
        width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      .level-tag {
        margin-bottom: 12px;
      }

      .left-meta {
        width: 100%;
        font-size: 13px;
        color: #606266;
        margin-bottom: 12px;

        .meta-row {
          padding: 5px 10px;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          border-radius: 6px;
          background: #f8f9fa;

          &.phone {
            color: #67C23A;
            font-weight: 500;
          }
        }
      }

      .btn-edit {
        width: 100%;
      }
    }

    .panel-right {
      flex: 1;
      min-width: 0;
      overflow: hidden;

      .attr-grid {
        display: grid;
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: 10px 20px;

        .attr-item {
          display: flex;
          align-items: center;
          font-size: 13px;
          min-height: 28px;
          min-width: 0;
          padding: 4px 10px;
          border-radius: 6px;
          background: #f8f9fa;
          transition: background 0.2s ease;

          &:hover {
            background: #f0f2f5;
          }

          &.attr-item-full {
            grid-column: 1 / -1;
            white-space: normal;
            word-break: break-all;
            padding: 8px 12px;
          }

          &:not(.attr-item-full) {
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }

          .attr-label {
            color: #909399;
            flex-shrink: 0;
            margin-right: 8px;
            font-size: 12px;
          }

          .attr-value {
            color: #303133;
            overflow: hidden;
            text-overflow: ellipsis;

            &.highlight {
              font-weight: 600;
              color: #303133;
            }

            &.primary {
              color: #409EFF;
              font-weight: 600;
            }
          }

          .el-tag {
            flex-shrink: 0;
          }
        }
      }

      .member-types-row {
        margin-top: 14px;
        padding-top: 14px;
        border-top: 1px dashed #e4e7ed;
        display: flex;
        flex-wrap: wrap;
        gap: 8px 16px;

        .member-type-wrap {
          display: inline-flex;
          align-items: center;
          gap: 6px;
          padding: 4px 10px;
          background: #f8f9fa;
          border-radius: 6px;
          transition: background 0.2s ease;

          &:hover {
            background: #f0f2f5;
          }

          .type-date {
            font-size: 12px;
            color: #909399;
          }
        }
      }
    }
  }

  // 个人档案样式（与 detail-dialog 一致）
  .profile-layout {
    .detail-section {
      margin-bottom: 20px;
      background: #fff;
      border-radius: 8px;
      overflow: hidden;
      border: 1px solid #ebeef5;
      box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);

      .section-title {
        background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
        color: #fff;
        padding: 12px 20px;
        font-size: 15px;
        font-weight: 600;
        display: flex;
        align-items: center;
        gap: 8px;

        i {
          font-size: 18px;
        }
      }

      .section-content {
        padding: 20px;

        .info-row {
          display: flex;
          flex-wrap: wrap;
          gap: 20px 40px;

          .info-item {
            flex: 0 0 calc(33.333% - 27px);
            min-width: 220px;
            display: flex;
            align-items: center;
            padding: 6px 0;

            &.info-item-full {
              flex: 1 1 100%;
              min-width: 100%;
            }

            .label {
              color: #606266;
              font-weight: 500;
              margin-right: 8px;
              flex-shrink: 0;
            }

            .value {
              color: #303133;

              &.highlight {
                font-weight: 600;
                color: #303133;
              }

              &.primary {
                color: #409EFF;
              }
            }
          }
        }
      }
    }
  }


  // 选项卡样式
  .portrait-tabs {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;

    ::v-deep .el-tabs__content {
      flex: 1;
      min-height: 0;
      overflow: hidden;
      display: flex;
      flex-direction: column;
    }

    ::v-deep .el-tab-pane {
      flex: 1;
      min-height: 0;
      overflow: hidden;
      display: flex;
      flex-direction: column;
    }

    ::v-deep .el-tabs__header {
      margin-bottom: 16px;
      background: #ffffff;
      padding: 0 20px;
      border-radius: 8px;
      border: 1px solid #e4e7ed;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
    }

    ::v-deep .el-tabs__nav-wrap::after {
      display: none;
    }

    ::v-deep .el-tabs__item {
      font-size: 14px;
      font-weight: 500;
      padding: 0 24px;
      height: 48px;
      line-height: 48px;
      color: #606266;
      transition: all 0.2s ease;

      &:hover {
        color: #409EFF;
      }

      &.is-active {
        color: #409EFF;
        font-weight: 600;
      }
    }

    ::v-deep .el-tabs__active-bar {
      background: linear-gradient(90deg, #409EFF 0%, #66b1ff 100%);
      height: 3px;
    }

    .tab-content {
      flex: 1;
      min-height: 0;
      overflow-y: auto;
      overflow-x: hidden;

      &.tab-content-direct {
        padding: 18px;
        background: #fff;
        border-radius: 8px;
        border: 1px solid #e4e7ed;

        .pagination-bar {
          padding: 12px 0 0;
          margin-top: 12px;
          border-top: 1px solid #e4e7ed;
        }
      }
      .content-card {
        background: #ffffff;
        border-radius: 8px;
        border: 1px solid #e4e7ed;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
        margin-bottom: 16px;
        overflow: hidden;
        transition: all 0.2s ease;

        &:hover {
          box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }

        .card-header {
          padding: 14px 18px;
          background: #f8f9fa;
          border-bottom: 1px solid #e4e7ed;
          display: flex;
          align-items: center;
          gap: 8px;

          i {
            font-size: 16px;
            color: #409EFF;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
          }

          &:hover i {
            transform: scale(1.1) rotate(5deg);
            color: #66b1ff;
          }

          .card-title {
            font-size: 15px;
            font-weight: 600;
            color: #303133;
          }
        }

        .card-body {
          padding: 18px;
        }
      }
    }
  }

  // 消费行为网格
  .behavior-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;

    .behavior-item {
      background: #f8f9fa;
      border-radius: 8px;
      border: 1px solid #e4e7ed;
      padding: 14px;
      display: flex;
      align-items: center;
      gap: 12px;
      transition: all 0.2s ease;

      &:hover {
        background: #ffffff;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
        border-color: #c0c4cc;
      }

      .behavior-icon {
        width: 40px;
        height: 40px;
        border-radius: 6px;
        background: linear-gradient(135deg, #409EFF 0%, #66b1ff 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;
        font-size: 16px;
        flex-shrink: 0;
        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        box-shadow: 0 2px 6px rgba(64, 158, 255, 0.3);
      }

      &:hover .behavior-icon {
        transform: scale(1.1) rotate(5deg);
        background: linear-gradient(135deg, #66b1ff 0%, #409EFF 100%);
        box-shadow: 0 4px 10px rgba(64, 158, 255, 0.4);
      }

      .behavior-content {
        flex: 1;
        min-width: 0;

        .behavior-label {
          font-size: 13px;
          color: #909399;
          margin-bottom: 8px;
          font-weight: 500;
        }

        .behavior-value {
          font-size: 16px;
          color: #303133;
          font-weight: 600;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
      }
    }
  }

  // 消费分析布局
  .analysis-layout {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;

    .analysis-item {
      flex: 1;
      min-width: 200px;
      background: #f8f9fa;
      border-radius: 8px;
      border: 1px solid #e4e7ed;
      padding: 14px;
      display: flex;
      align-items: center;
      gap: 12px;
      transition: all 0.2s ease;

      &:hover {
        background: #ffffff;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
        border-color: #c0c4cc;
      }

      .analysis-icon {
        width: 40px;
        height: 40px;
        border-radius: 6px;
        background: linear-gradient(135deg, #67C23A 0%, #85ce61 100%);
        display: flex;
        align-items: center;
        justify-content: center;
        color: #fff;
        font-size: 16px;
        flex-shrink: 0;
        transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        box-shadow: 0 2px 6px rgba(103, 194, 58, 0.3);
      }

      &:hover .analysis-icon {
        transform: scale(1.1) rotate(5deg);
        background: linear-gradient(135deg, #85ce61 0%, #67C23A 100%);
        box-shadow: 0 4px 10px rgba(103, 194, 58, 0.4);
      }

      .analysis-content {
        flex: 1;
        min-width: 0;

        .analysis-label {
          font-size: 13px;
          color: #909399;
          margin-bottom: 8px;
          font-weight: 500;
        }

        .analysis-value {
          font-size: 16px;
          color: #303133;
          font-weight: 600;
        }
      }
    }
  }

  // 趋势图
  .trend-chart {
    width: 100%;
    height: 360px;
    border-radius: 6px;
    background: #fafbfc;
    padding: 8px;
  }

  // 分页
  .pagination-bar {
    display: flex;
    justify-content: flex-end;
    padding: 14px 0 0 0;
    margin-top: 14px;
    border-top: 1px solid #e4e7ed;
  }

  // 表格样式优化
  ::v-deep .el-table {
    border-radius: 6px;
    overflow: hidden;

    .el-table__header {
      background: linear-gradient(135deg, rgba(64, 158, 255, 0.08) 0%, rgba(102, 177, 255, 0.05) 100%);

      th {
        background: transparent !important;
        border-bottom: 1px solid rgba(64, 158, 255, 0.2);
        color: #303133;
        font-weight: 600;
        font-size: 13px;
        padding: 12px 0;
      }
    }

    .el-table__body {
      tr {
        transition: all 0.2s ease;

        &:hover {
          background: linear-gradient(90deg, rgba(64, 158, 255, 0.06) 0%, rgba(102, 177, 255, 0.04) 100%) !important;
        }

        td {
          border-bottom: 1px solid #f0f2f5;
          padding: 12px 0;
          color: #606266;
          font-size: 13px;
        }
      }

      tr.el-table__row--striped {
        background: #fafbfc;
      }
    }
  }

  // 滚动条美化
  ::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }

  ::-webkit-scrollbar-track {
    background: rgba(240, 242, 245, 0.5);
    border-radius: 4px;
  }

  ::-webkit-scrollbar-thumb {
    background: linear-gradient(135deg, #c0c4cc 0%, #909399 100%);
    border-radius: 4px;
    transition: background 0.3s;

    &:hover {
      background: linear-gradient(135deg, #909399 0%, #606266 100%);
    }
  }

  .text-muted {
    color: #909399;
    font-size: 13px;
  }
}
</style>

<style lang="scss">
/* 旧日志图片预览弹窗 */
.old-log-image-dialog {
  .el-dialog__body {
    padding: 16px 24px 24px;
  }

  .old-log-image-content {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 12px;
  }

  .old-log-preview-img {
    width: 100%;
    aspect-ratio: 1;
    max-height: 200px;
    border-radius: 8px;
    border: 1px solid #e4e7ed;
    cursor: pointer;
  }
}

/* 服务日志图片预览弹窗（弹窗 append-to-body，需全局样式） */
.service-log-image-dialog {
  .el-dialog__body {
    padding: 16px 24px 24px;
  }

  .consume-service-log-content {
    max-height: 70vh;
    overflow-y: auto;
  }

  .log-list .log-item {
    margin-bottom: 24px;

    &:last-child {
      margin-bottom: 0;
    }
  }

  .no-data {
    padding: 40px 0;
  }

  .image-preview-content {
    min-height: 80px;
  }

  .preview-header {
    display: flex;
    align-items: center;
    gap: 24px;
    padding: 12px 16px;
    margin-bottom: 16px;
    background: linear-gradient(135deg, #f8f9fc 0%, #f0f2f7 100%);
    border-radius: 8px;
    border-left: 4px solid #409EFF;
  }

  .preview-meta {
    font-size: 13px;
    color: #606266;

    i {
      margin-right: 6px;
      color: #909399;
    }
  }

  .preview-row {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;

    .image-section {
      flex: 1;
      min-width: 280px;
    }
  }

  .image-section {
    padding: 16px;
    background: #fafbfc;
    border-radius: 8px;
    border: 1px solid #e4e7ed;
  }

  .image-section-title {
    font-size: 14px;
    font-weight: 600;
    color: #303133;
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid #e4e7ed;

    i {
      margin-right: 6px;
      color: #409EFF;
    }
  }

  .image-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
  }

  .preview-img {
    width: 100%;
    aspect-ratio: 1;
    max-height: 160px;
    border-radius: 8px;
    border: 1px solid #e4e7ed;
    cursor: pointer;
    transition: border-color 0.2s, box-shadow 0.2s;

    &:hover {
      border-color: #409EFF;
      box-shadow: 0 2px 12px rgba(64, 158, 255, 0.2);
    }
  }

  .image-placeholder {
    grid-column: 1 / 2;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    aspect-ratio: 1;
    max-height: 160px;
    background: #f5f7fa;
    border: 1px dashed #dcdfe6;
    border-radius: 8px;
    color: #c0c4cc;
    font-size: 13px;

    i {
      font-size: 40px;
    }
  }

  .preview-remark {
    margin-top: 16px;
    padding: 12px 16px;
    font-size: 13px;
    color: #606266;
    background: #fafbfc;
    border-radius: 8px;
    border: 1px solid #e4e7ed;
    line-height: 1.6;

    .remark-label {
      font-weight: 600;
      color: #303133;
      margin-right: 4px;
    }
  }
}
</style>
