Commit 6aa6629d93ef5d31bc1ef40d3e4ff8b476377f36

Authored by “wangming”
1 parent d2158212

优化会员列表卡片视图样式和功能

- 优化卡片视图样式:调整字体大小匹配卡片尺寸,确保最小字体12px
- 改进卡片布局:调整间距和内边距,保持毛玻璃效果
- 修复会员沉睡天数计算:更新消费记录时正确更新LastConsumeTime
- 默认视图模式:客户页面进入时默认显示列表视图
- 添加会员沉睡天数数据修复SQL脚本
antis-ncc-admin/src/views/lqKhxx/index.vue
... ... @@ -105,6 +105,12 @@
105 105 @click="handleBatchRemoveDel()">批量删除</el-button>
106 106 </div>
107 107 <div class="NCC-common-head-right">
  108 + <div style="display: inline-block; margin-right: 15px;">
  109 + <el-button size="mini" :type="viewMode === 'list' ? 'primary' : ''" icon="el-icon-s-data"
  110 + @click="switchView('list')">列表</el-button>
  111 + <el-button size="mini" :type="viewMode === 'card' ? 'primary' : ''" icon="el-icon-menu"
  112 + @click="switchView('card')">卡片</el-button>
  113 + </div>
108 114 <el-tooltip effect="dark" content="刷新" placement="top">
109 115 <el-link icon="icon-ym icon-ym-Refresh NCC-common-head-icon" :underline="false"
110 116 @click="reset()" />
... ... @@ -112,7 +118,130 @@
112 118 <screenfull isContainer />
113 119 </div>
114 120 </div>
115   - <NCC-table v-loading="listLoading" :data="list" has-c @selection-change="handleSelectionChange"
  121 + <div v-if="viewMode === 'card'" v-loading="listLoading" class="member-card-wrapper">
  122 + <div class="member-card-scroll-container">
  123 + <el-row :gutter="16" type="flex" style="flex-wrap: wrap;" class="member-card-row">
  124 + <el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" v-for="item in list" :key="item.id">
  125 + <el-card shadow="hover" class="member-card" :class="`level-card-${item.consumeLevel}`">
  126 + <div class="card-header-wrapper">
  127 + <div class="header-top">
  128 + <div class="info-left">
  129 + <span class="member-name">{{ item.khmc || '无名氏' }}</span>
  130 + <i class="el-icon-user gender-icon" :class="getGenderClass(item.xb)"></i>
  131 + </div>
  132 + <el-tag :type="getConsumeLevelType(item.consumeLevel)" size="small"
  133 + class="level-tag" effect="dark">
  134 + {{ getConsumeLevelName(item.consumeLevel) }}
  135 + </el-tag>
  136 + </div>
  137 + <div class="header-tags">
  138 + <el-tag v-if="item.isBeautyMember === 1" type="success" size="mini"
  139 + effect="plain" class="mini-tag">生美</el-tag>
  140 + <el-tag v-if="item.isMedicalMember === 1" type="warning" size="mini"
  141 + effect="plain" class="mini-tag">医美</el-tag>
  142 + <el-tag v-if="item.isTechMember === 1" type="info" size="mini" effect="plain"
  143 + class="mini-tag">科技</el-tag>
  144 + <el-tag v-if="item.isEducationMember === 1" type="primary" size="mini"
  145 + effect="plain" class="mini-tag">教育</el-tag>
  146 + </div>
  147 + </div>
  148 +
  149 + <div class="card-content">
  150 + <div class="data-grid">
  151 + <div class="data-item">
  152 + <span class="label">
  153 + <i class="el-icon-phone icon-inline"></i>手机号
  154 + </span>
  155 + <span class="value">{{ item.sjh || '-' }}</span>
  156 + </div>
  157 + <div class="data-item">
  158 + <span class="label">
  159 + <i class="el-icon-shop icon-inline"></i>归属门店
  160 + </span>
  161 + <span class="value text-truncate" :title="item.gsmdName">{{ item.gsmdName ||
  162 + '-'
  163 + }}</span>
  164 + </div>
  165 + <div class="data-item">
  166 + <span class="label">
  167 + <i class="el-icon-user icon-inline"></i>客户类型
  168 + </span>
  169 + <span class="value">{{ item.khlxName || '-' }}</span>
  170 + </div>
  171 + <div class="data-item">
  172 + <span class="label">
  173 + <i class="el-icon-time icon-inline" :class="{ 'icon-warning': item.sleepDays > 0 }"></i>沉睡天数
  174 + </span>
  175 + <span class="value" :class="{ 'text-danger': item.sleepDays > 0 }">{{
  176 + item.sleepDays || 0
  177 + }}天</span>
  178 + </div>
  179 + <div class="data-item full-width">
  180 + <span class="label">
  181 + <i class="el-icon-calendar icon-inline"></i>最后到店
  182 + </span>
  183 + <span class="value">{{ formatDate(item.lastVisitTime) }}</span>
  184 + </div>
  185 + </div>
  186 +
  187 + <div class="amount-section">
  188 + <div class="amount-box">
  189 + <span class="sub-label">
  190 + <i class="el-icon-wallet icon-inline"></i>开卡金额
  191 + </span>
  192 + <span class="sub-value primary-color">{{
  193 + formatMoney(item.totalBillingAmount) }}</span>
  194 + </div>
  195 + <div class="divider"></div>
  196 + <div class="amount-box">
  197 + <span class="sub-label">
  198 + <i class="el-icon-coin icon-inline"></i>剩余权益
  199 + </span>
  200 + <span class="sub-value warning-color">{{
  201 + formatMoney(item.remainingRightsAmount)
  202 + }}</span>
  203 + </div>
  204 + </div>
  205 + </div>
  206 +
  207 + <div class="card-footer">
  208 + <el-tooltip content="详情" placement="top">
  209 + <el-button type="text" icon="el-icon-view" class="action-btn view-btn"
  210 + @click="openMemberPortrait(item.id)">
  211 + 详情
  212 + </el-button>
  213 + </el-tooltip>
  214 + <el-tooltip content="权益" placement="top">
  215 + <el-button type="text" icon="el-icon-folder-opened"
  216 + class="action-btn rights-btn" @click="showMemberRights(item.id)">
  217 + 权益
  218 + </el-button>
  219 + </el-tooltip>
  220 + <div class="more-actions">
  221 + <el-dropdown trigger="click" @command="handleCommand($event, item)">
  222 + <span class="el-dropdown-link">
  223 + 更多<i class="el-icon-arrow-down el-icon--right"></i>
  224 + </span>
  225 + <el-dropdown-menu slot="dropdown">
  226 + <el-dropdown-item command="edit" icon="el-icon-edit"
  227 + v-if="has('btn_edit')">编辑</el-dropdown-item>
  228 + <el-dropdown-item command="delete" icon="el-icon-delete"
  229 + v-if="has('btn_remove')"
  230 + style="color: #F56C6C;">删除</el-dropdown-item>
  231 + </el-dropdown-menu>
  232 + </el-dropdown>
  233 + </div>
  234 + </div>
  235 + </el-card>
  236 + </el-col>
  237 + </el-row>
  238 + <div v-if="list.length === 0" class="empty-data">
  239 + <el-empty description="暂无数据"></el-empty>
  240 + </div>
  241 + </div>
  242 + </div>
  243 + <NCC-table v-if="viewMode === 'list'" v-loading="listLoading" :data="list" has-c
  244 + @selection-change="handleSelectionChange"
116 245 :header-cell-style="{ background: '#f5f7fa', color: '#606266' }">
117 246 <!-- 客户名称 -->
118 247 <el-table-column label="客户名称" align="center" width="120px" fixed="left">
... ... @@ -320,7 +449,7 @@
320 449 </template>
321 450 </el-table-column>
322 451  
323   - <!-- 操作 -->
  452 + <!-- 操作 -->
324 453 <el-table-column label="操作" width="280" align="left" fixed="right">
325 454 <template slot-scope="scope">
326 455 <div class="action-buttons">
... ... @@ -343,8 +472,12 @@
343 472 </template>
344 473 </el-table-column>
345 474 </NCC-table>
346   - <pagination :total="total" :page.sync="listQuery.currentPage" :limit.sync="listQuery.pageSize"
347   - @pagination="initData" />
  475 + <pagination
  476 + :total="total"
  477 + :page.sync="listQuery.currentPage"
  478 + :limit.sync="listQuery.pageSize"
  479 + @pagination="initData"
  480 + :class="{ 'pagination-card-mode': viewMode === 'card' }" />
348 481 </div>
349 482 </div>
350 483 <NCC-Form v-if="formVisible" ref="NCCForm" @refresh="refresh" />
... ... @@ -368,6 +501,7 @@ export default {
368 501 components: { NCCForm, ExportBox, MemberRightsDialog, DetailDialog, MemberPortraitDialog },
369 502 data() {
370 503 return {
  504 + viewMode: 'list', // list or card
371 505 showAll: false,
372 506 kdhyLoading: false,
373 507 memberRightsDialogVisible: false,
... ... @@ -475,12 +609,52 @@ export default {
475 609 },
476 610 computed: {},
477 611 created() {
  612 + // 默认使用列表视图,不恢复本地存储的视图模式
  613 + this.viewMode = 'list';
478 614 this.initData();
479 615 this.getgsmdOptions();
480 616 this.getkhlxOptions();
481 617 this.getkdhyOptions();
482 618 },
483 619 methods: {
  620 + // 权限检查方法
  621 + has(enCode) {
  622 + try {
  623 + const permissionList = this.$store.getters && this.$store.getters.permissionList
  624 + if (!permissionList || !permissionList.length) return false
  625 + const modelId = this.$route.meta.modelId || ''
  626 + if (!modelId) return false
  627 + const list = permissionList.filter(o => o.modelId === modelId)
  628 + if (!list.length) return false
  629 + const btnList = list[0] && list[0].button ? list[0].button : []
  630 + if (!btnList.length) return false
  631 + const hasPermission = btnList.some(btn => btn.enCode === enCode)
  632 + return hasPermission
  633 + } catch (e) {
  634 + console.warn('Permission check error:', e)
  635 + return false
  636 + }
  637 + },
  638 + switchView(mode) {
  639 + console.log('Switching view to:', mode);
  640 + if (mode !== 'list' && mode !== 'card') {
  641 + console.error('Invalid view mode:', mode);
  642 + return;
  643 + }
  644 + this.viewMode = mode;
  645 + // 保存视图模式到本地存储
  646 + try {
  647 + localStorage.setItem('memberListViewMode', mode);
  648 + } catch (e) {
  649 + console.warn('Failed to save view mode to localStorage:', e);
  650 + }
  651 + // 强制更新视图
  652 + this.$forceUpdate();
  653 + // 触发窗口大小变化事件,确保布局正确
  654 + this.$nextTick(() => {
  655 + window.dispatchEvent(new Event('resize'));
  656 + });
  657 + },
484 658 getgsmdOptions() {
485 659 previewDataInterface('730960205902251269').then(res => {
486 660 this.gsmdOptions = res.data
... ... @@ -800,6 +974,13 @@ export default {
800 974 5: 'danger'
801 975 }
802 976 return typeMap[level] || 'info'
  977 + },
  978 + handleCommand(command, item) {
  979 + if (command === 'edit') {
  980 + this.addOrUpdateHandle(item.id);
  981 + } else if (command === 'delete') {
  982 + this.handleDel(item.id);
  983 + }
803 984 }
804 985 }
805 986 }
... ... @@ -1376,4 +1557,655 @@ export default {
1376 1557 }
1377 1558 }
1378 1559 }
  1560 +
  1561 +// 卡片视图样式
  1562 +.member-card-wrapper {
  1563 + display: flex;
  1564 + flex-direction: column;
  1565 + flex: 1;
  1566 + height: 100%;
  1567 + min-height: 0; // 重要:允许 flex 子元素收缩
  1568 + box-sizing: border-box;
  1569 + overflow: hidden; // 防止内容溢出
  1570 +}
  1571 +
  1572 +.member-card-scroll-container {
  1573 + flex: 1;
  1574 + overflow-y: auto;
  1575 + overflow-x: hidden;
  1576 + padding: 16px 20px; // 合理的容器内边距
  1577 + box-sizing: border-box;
  1578 + min-height: 0; // 重要:允许 flex 子元素收缩
  1579 + // 使用渐变背景(为毛玻璃效果提供视觉基础)
  1580 + background: linear-gradient(135deg, #F8FAFC 0%, #F1F5F9 50%, #E2E8F0 100%);
  1581 +
  1582 + // 自定义滚动条样式
  1583 + &::-webkit-scrollbar {
  1584 + width: 8px;
  1585 + }
  1586 +
  1587 + &::-webkit-scrollbar-track {
  1588 + background: rgba(0, 0, 0, 0.05);
  1589 + border-radius: 4px;
  1590 + }
  1591 +
  1592 + &::-webkit-scrollbar-thumb {
  1593 + background: rgba(0, 0, 0, 0.2);
  1594 + border-radius: 4px;
  1595 + transition: background 0.2s ease;
  1596 +
  1597 + &:hover {
  1598 + background: rgba(0, 0, 0, 0.3);
  1599 + }
  1600 + }
  1601 +
  1602 + .member-card-row {
  1603 + .el-col {
  1604 + margin-bottom: 16px; // 合理的卡片间距
  1605 + display: flex;
  1606 + }
  1607 + }
  1608 +
  1609 + .empty-data {
  1610 + display: flex;
  1611 + justify-content: center;
  1612 + align-items: center;
  1613 + height: 400px;
  1614 + }
  1615 +
  1616 + .member-card {
  1617 + width: 100%;
  1618 + display: flex;
  1619 + flex-direction: column;
  1620 + border-radius: 12px; // 符合规范:12px圆角
  1621 + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); // 更有弹性的动画
  1622 + // 精致的毛玻璃效果边框(双层边框)
  1623 + border: 1px solid rgba(255, 255, 255, 0.4);
  1624 + // 增强的毛玻璃背景
  1625 + background: linear-gradient(
  1626 + 135deg,
  1627 + rgba(255, 255, 255, 0.9) 0%,
  1628 + rgba(255, 255, 255, 0.8) 50%,
  1629 + rgba(255, 255, 255, 0.85) 100%
  1630 + );
  1631 + backdrop-filter: blur(24px) saturate(190%);
  1632 + -webkit-backdrop-filter: blur(24px) saturate(190%);
  1633 + // 精致的多层次阴影系统(5层阴影,创造更强的深度)
  1634 + box-shadow:
  1635 + 0 2px 4px rgba(0, 0, 0, 0.04),
  1636 + 0 4px 8px rgba(0, 0, 0, 0.03),
  1637 + 0 8px 16px rgba(0, 0, 0, 0.02),
  1638 + 0 16px 32px rgba(0, 0, 0, 0.015),
  1639 + inset 0 1px 1px rgba(255, 255, 255, 0.95), // 顶部高光
  1640 + inset 0 -1px 1px rgba(0, 0, 0, 0.02); // 底部阴影
  1641 + overflow: hidden;
  1642 + position: relative;
  1643 + cursor: pointer;
  1644 + // 添加微妙的渐变遮罩层
  1645 + &::after {
  1646 + content: '';
  1647 + position: absolute;
  1648 + top: 0;
  1649 + left: 0;
  1650 + right: 0;
  1651 + bottom: 0;
  1652 + background: linear-gradient(
  1653 + 135deg,
  1654 + rgba(255, 255, 255, 0.1) 0%,
  1655 + transparent 50%,
  1656 + rgba(0, 0, 0, 0.02) 100%
  1657 + );
  1658 + pointer-events: none;
  1659 + opacity: 0;
  1660 + transition: opacity 0.3s ease;
  1661 + }
  1662 +
  1663 + // 顶部装饰条(根据等级变化)- 更精致的渐变效果
  1664 + &::before {
  1665 + content: '';
  1666 + position: absolute;
  1667 + top: 0;
  1668 + left: 0;
  1669 + right: 0;
  1670 + height: 4px;
  1671 + background: linear-gradient(
  1672 + 90deg,
  1673 + #2563EB 0%,
  1674 + #3B82F6 50%,
  1675 + #60A5FA 100%
  1676 + );
  1677 + opacity: 1;
  1678 + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  1679 + box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3); // 添加发光效果
  1680 + }
  1681 +
  1682 + // 等级差异化样式(A++ - 5级)- 更丰富的渐变和发光效果
  1683 + &.level-card-5::before {
  1684 + background: linear-gradient(90deg, #DC2626 0%, #EF4444 50%, #F87171 100%);
  1685 + height: 5px;
  1686 + box-shadow: 0 2px 12px rgba(220, 38, 38, 0.4);
  1687 + }
  1688 +
  1689 + // A++ - 4级
  1690 + &.level-card-4::before {
  1691 + background: linear-gradient(90deg, #F97316 0%, #FB923C 50%, #FDBA74 100%);
  1692 + height: 5px;
  1693 + box-shadow: 0 2px 12px rgba(249, 115, 22, 0.4);
  1694 + }
  1695 +
  1696 + // A+ - 3级
  1697 + &.level-card-3::before {
  1698 + background: linear-gradient(90deg, #F59E0B 0%, #FBBF24 50%, #FCD34D 100%);
  1699 + height: 4px;
  1700 + box-shadow: 0 2px 10px rgba(245, 158, 11, 0.35);
  1701 + }
  1702 +
  1703 + // A - 2级
  1704 + &.level-card-2::before {
  1705 + background: linear-gradient(90deg, #10B981 0%, #34D399 50%, #6EE7B7 100%);
  1706 + box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
  1707 + }
  1708 +
  1709 + // C - 1级
  1710 + &.level-card-1::before {
  1711 + background: linear-gradient(90deg, #3B82F6 0%, #60A5FA 50%, #93C5FD 100%);
  1712 + box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3);
  1713 + }
  1714 +
  1715 + // D (普通) - 0级
  1716 + &.level-card-0::before {
  1717 + background: linear-gradient(90deg, #94A3B8 0%, #CBD5E1 50%, #E2E8F0 100%);
  1718 + box-shadow: 0 1px 4px rgba(148, 163, 184, 0.2);
  1719 + }
  1720 +
  1721 + // Hover 效果 - 精致的提升动画
  1722 + &:hover {
  1723 + transform: translateY(-8px) scale(1.02);
  1724 + // 增强毛玻璃效果
  1725 + background: linear-gradient(
  1726 + 135deg,
  1727 + rgba(255, 255, 255, 0.95) 0%,
  1728 + rgba(255, 255, 255, 0.9) 50%,
  1729 + rgba(255, 255, 255, 0.92) 100%
  1730 + );
  1731 + backdrop-filter: blur(30px) saturate(200%);
  1732 + -webkit-backdrop-filter: blur(30px) saturate(200%);
  1733 + // 增强的悬浮阴影(创造浮动感)
  1734 + box-shadow:
  1735 + 0 8px 16px rgba(0, 0, 0, 0.08),
  1736 + 0 16px 32px rgba(0, 0, 0, 0.06),
  1737 + 0 32px 64px rgba(0, 0, 0, 0.04),
  1738 + 0 0 0 1px rgba(255, 255, 255, 0.6),
  1739 + inset 0 2px 2px rgba(255, 255, 255, 0.98),
  1740 + inset 0 -1px 2px rgba(0, 0, 0, 0.03);
  1741 + border-color: rgba(255, 255, 255, 0.6);
  1742 + z-index: 10;
  1743 +
  1744 + &::before {
  1745 + height: 6px;
  1746 + box-shadow: 0 4px 16px rgba(37, 99, 235, 0.4);
  1747 + }
  1748 +
  1749 + &::after {
  1750 + opacity: 1; // 显示渐变遮罩
  1751 + }
  1752 + }
  1753 +
  1754 + // 响应减少动画偏好
  1755 + @media (prefers-reduced-motion: reduce) {
  1756 + transition: none;
  1757 +
  1758 + &:hover {
  1759 + transform: none;
  1760 + }
  1761 + }
  1762 +
  1763 + ::v-deep .el-card__body {
  1764 + padding: 0; // 自定义padding
  1765 + flex: 1;
  1766 + display: flex;
  1767 + flex-direction: column;
  1768 + }
  1769 +
  1770 + .card-header-wrapper {
  1771 + padding: 10px 12px 8px; // 缩小卡片头部内边距
  1772 + border-bottom: 1px solid rgba(255, 255, 255, 0.3);
  1773 + background: linear-gradient(
  1774 + 180deg,
  1775 + rgba(255, 255, 255, 0.4) 0%,
  1776 + rgba(255, 255, 255, 0.2) 50%,
  1777 + rgba(255, 255, 255, 0) 100%
  1778 + );
  1779 + position: relative;
  1780 + // 添加微妙的光影效果
  1781 + &::after {
  1782 + content: '';
  1783 + position: absolute;
  1784 + bottom: 0;
  1785 + left: 16px;
  1786 + right: 16px;
  1787 + height: 1px;
  1788 + background: linear-gradient(
  1789 + 90deg,
  1790 + transparent 0%,
  1791 + rgba(255, 255, 255, 0.5) 50%,
  1792 + transparent 100%
  1793 + );
  1794 + }
  1795 +
  1796 + .header-top {
  1797 + display: flex;
  1798 + justify-content: space-between;
  1799 + align-items: center;
  1800 + margin-bottom: 6px; // 缩小间距
  1801 + gap: 4px;
  1802 +
  1803 + .info-left {
  1804 + display: flex;
  1805 + align-items: center;
  1806 + flex: 1;
  1807 + min-width: 0;
  1808 +
  1809 + .member-name {
  1810 + font-size: 16px; // 增大标题字体,匹配卡片大小
  1811 + font-weight: 700;
  1812 + color: #0F172A;
  1813 + margin-right: 6px;
  1814 + white-space: nowrap;
  1815 + overflow: hidden;
  1816 + text-overflow: ellipsis;
  1817 + letter-spacing: -0.02em;
  1818 + line-height: 1.4; // 合理的行高
  1819 + text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
  1820 + }
  1821 +
  1822 + .gender-icon {
  1823 + font-size: 14px; // 增大图标
  1824 +
  1825 + &.gender-male {
  1826 + color: #409EFF;
  1827 + }
  1828 +
  1829 + &.gender-female {
  1830 + color: #F56C6C;
  1831 + }
  1832 +
  1833 + &.gender-unknown {
  1834 + color: #909399;
  1835 + }
  1836 + }
  1837 + }
  1838 + }
  1839 +
  1840 + .header-tags {
  1841 + display: flex;
  1842 + flex-wrap: wrap;
  1843 + gap: 4px; // 缩小标签间距
  1844 + min-height: 24px;
  1845 +
  1846 + .mini-tag {
  1847 + border-radius: 6px;
  1848 + padding: 3px 8px;
  1849 + height: 22px;
  1850 + line-height: 18px;
  1851 + font-size: 13px; // 增大标签字体
  1852 + font-weight: 600;
  1853 + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  1854 + backdrop-filter: blur(4px);
  1855 + -webkit-backdrop-filter: blur(4px);
  1856 + border: 0.5px solid rgba(255, 255, 255, 0.3);
  1857 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  1858 +
  1859 + &:hover {
  1860 + transform: translateY(-1px) scale(1.05);
  1861 + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
  1862 + }
  1863 + }
  1864 + }
  1865 +
  1866 + .level-tag {
  1867 + font-weight: 700;
  1868 + border-radius: 8px;
  1869 + padding: 5px 12px;
  1870 + font-size: 13px; // 增大等级标签字体
  1871 + letter-spacing: 0.03em;
  1872 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  1873 + backdrop-filter: blur(4px);
  1874 + -webkit-backdrop-filter: blur(4px);
  1875 + border: 0.5px solid rgba(255, 255, 255, 0.3);
  1876 + transition: all 0.25s ease;
  1877 +
  1878 + &:hover {
  1879 + transform: translateY(-1px);
  1880 + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
  1881 + }
  1882 + }
  1883 + }
  1884 +
  1885 + .card-content {
  1886 + flex: 1;
  1887 + padding: 10px 12px; // 缩小内容区内边距
  1888 + display: flex;
  1889 + flex-direction: column;
  1890 + gap: 8px; // 缩小间距
  1891 +
  1892 + .data-grid {
  1893 + display: grid;
  1894 + grid-template-columns: 1fr 1fr;
  1895 + gap: 6px 8px; // 缩小网格间距
  1896 +
  1897 + .data-item {
  1898 + display: flex;
  1899 + flex-direction: column;
  1900 + padding: 3px 0; // 缩小内边距
  1901 +
  1902 + &.full-width {
  1903 + grid-column: span 2;
  1904 + flex-direction: row;
  1905 + justify-content: space-between;
  1906 + align-items: center;
  1907 + padding: 6px 0 3px; // 缩小内边距
  1908 + border-top: 1px dashed rgba(255, 255, 255, 0.3);
  1909 + margin-top: 3px;
  1910 +
  1911 + .value {
  1912 + text-align: right;
  1913 + font-weight: 600;
  1914 + }
  1915 + }
  1916 +
  1917 + .label {
  1918 + font-size: 13px; // 增大标签字体,匹配卡片大小
  1919 + color: #475569;
  1920 + margin-bottom: 4px; // 合理的间距
  1921 + font-weight: 600;
  1922 + text-transform: uppercase;
  1923 + letter-spacing: 0.05em;
  1924 + line-height: 1.4; // 合理的行高
  1925 + display: flex;
  1926 + align-items: center;
  1927 + gap: 5px; // 合理的图标间距
  1928 +
  1929 + .icon-inline {
  1930 + font-size: 13px; // 增大图标
  1931 + opacity: 0.75;
  1932 + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
  1933 + filter: drop-shadow(0 1px 1px rgba(255, 255, 255, 0.5));
  1934 + }
  1935 +
  1936 + &:hover .icon-inline {
  1937 + opacity: 1;
  1938 + transform: scale(1.15) rotate(5deg);
  1939 + }
  1940 + }
  1941 +
  1942 + .icon-warning {
  1943 + color: #EF4444;
  1944 + opacity: 1 !important;
  1945 + }
  1946 +
  1947 + .value {
  1948 + font-size: 14px; // 增大数据值字体,匹配卡片大小
  1949 + color: #0F172A;
  1950 + font-weight: 600;
  1951 + white-space: nowrap;
  1952 + overflow: hidden;
  1953 + text-overflow: ellipsis;
  1954 + line-height: 1.5; // 合理的行高
  1955 + letter-spacing: -0.01em;
  1956 +
  1957 + &.text-truncate {
  1958 + max-width: 100%;
  1959 + }
  1960 +
  1961 + &.text-danger {
  1962 + color: #EF4444;
  1963 + font-weight: 700;
  1964 + position: relative;
  1965 +
  1966 + &::after {
  1967 + content: '';
  1968 + position: absolute;
  1969 + bottom: -2px;
  1970 + left: 0;
  1971 + right: 0;
  1972 + height: 2px;
  1973 + background: linear-gradient(90deg, rgba(239, 68, 68, 0.3), transparent);
  1974 + }
  1975 + }
  1976 + }
  1977 + }
  1978 + }
  1979 +
  1980 + .amount-section {
  1981 + margin-top: auto;
  1982 + // 更精致的渐变背景
  1983 + background: linear-gradient(
  1984 + 135deg,
  1985 + rgba(37, 99, 235, 0.12) 0%,
  1986 + rgba(59, 130, 246, 0.08) 50%,
  1987 + rgba(96, 165, 250, 0.06) 100%
  1988 + );
  1989 + backdrop-filter: blur(12px) saturate(150%);
  1990 + -webkit-backdrop-filter: blur(12px) saturate(150%);
  1991 + border: 1px solid rgba(255, 255, 255, 0.5);
  1992 + border-radius: 10px; // 缩小圆角
  1993 + padding: 8px 10px; // 缩小内边距
  1994 + display: flex;
  1995 + justify-content: space-between;
  1996 + align-items: center;
  1997 + margin-top: 6px; // 缩小上边距
  1998 + position: relative;
  1999 + overflow: hidden;
  2000 + box-shadow:
  2001 + inset 0 1px 2px rgba(255, 255, 255, 0.6),
  2002 + inset 0 -1px 1px rgba(37, 99, 235, 0.1),
  2003 + 0 2px 8px rgba(37, 99, 235, 0.1);
  2004 +
  2005 + &::before {
  2006 + content: '';
  2007 + position: absolute;
  2008 + top: 0;
  2009 + left: 0;
  2010 + right: 0;
  2011 + height: 2px;
  2012 + background: linear-gradient(
  2013 + 90deg,
  2014 + transparent 0%,
  2015 + rgba(37, 99, 235, 0.4) 50%,
  2016 + transparent 100%
  2017 + );
  2018 + box-shadow: 0 2px 4px rgba(37, 99, 235, 0.2);
  2019 + }
  2020 +
  2021 + .amount-box {
  2022 + flex: 1;
  2023 + display: flex;
  2024 + flex-direction: column;
  2025 + align-items: center;
  2026 + gap: 3px; // 缩小间距
  2027 +
  2028 + .sub-label {
  2029 + font-size: 13px; // 增大金额标签字体
  2030 + color: #475569;
  2031 + margin-bottom: 4px; // 合理的间距
  2032 + font-weight: 600;
  2033 + text-transform: uppercase;
  2034 + letter-spacing: 0.06em;
  2035 + display: flex;
  2036 + align-items: center;
  2037 + justify-content: center;
  2038 + gap: 5px; // 合理的图标间距
  2039 +
  2040 + .icon-inline {
  2041 + font-size: 13px; // 增大图标
  2042 + opacity: 0.8;
  2043 + transition: all 0.2s ease;
  2044 + }
  2045 +
  2046 + &:hover .icon-inline {
  2047 + opacity: 1;
  2048 + transform: scale(1.1);
  2049 + }
  2050 + }
  2051 +
  2052 + .sub-value {
  2053 + font-size: 18px; // 增大金额字体,匹配卡片大小
  2054 + font-weight: 700;
  2055 + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'SF Pro Display', 'Helvetica Neue', 'Inter', sans-serif;
  2056 + letter-spacing: -0.03em;
  2057 + line-height: 1.3; // 合理的行高
  2058 + transition: all 0.2s ease;
  2059 +
  2060 + &.primary-color {
  2061 + color: #2563EB;
  2062 + background: linear-gradient(135deg, #2563EB, #3B82F6);
  2063 + -webkit-background-clip: text;
  2064 + -webkit-text-fill-color: transparent;
  2065 + background-clip: text;
  2066 + }
  2067 +
  2068 + &.warning-color {
  2069 + color: #F97316;
  2070 + background: linear-gradient(135deg, #F97316, #FB923C);
  2071 + -webkit-background-clip: text;
  2072 + -webkit-text-fill-color: transparent;
  2073 + background-clip: text;
  2074 + }
  2075 + }
  2076 + }
  2077 +
  2078 + .divider {
  2079 + width: 1px;
  2080 + height: 32px; // 合理的高度
  2081 + background: linear-gradient(180deg, transparent, rgba(226, 232, 240, 0.8), transparent);
  2082 + margin: 0 12px; // 合理的边距
  2083 + }
  2084 + }
  2085 + }
  2086 +
  2087 + .card-footer {
  2088 + margin-top: 0;
  2089 + padding: 12px 14px; // 符合规范:12px内边距
  2090 + border-top: 1px solid rgba(255, 255, 255, 0.3);
  2091 + display: flex;
  2092 + justify-content: space-between;
  2093 + align-items: center;
  2094 + background: linear-gradient(
  2095 + 180deg,
  2096 + rgba(255, 255, 255, 0) 0%,
  2097 + rgba(255, 255, 255, 0.2) 50%,
  2098 + rgba(248, 250, 252, 0.4) 100%
  2099 + );
  2100 + gap: 8px; // 合理的间距
  2101 + position: relative;
  2102 +
  2103 + // 顶部高光线条
  2104 + &::before {
  2105 + content: '';
  2106 + position: absolute;
  2107 + top: 0;
  2108 + left: 16px;
  2109 + right: 16px;
  2110 + height: 1px;
  2111 + background: linear-gradient(
  2112 + 90deg,
  2113 + transparent 0%,
  2114 + rgba(255, 255, 255, 0.6) 50%,
  2115 + transparent 100%
  2116 + );
  2117 + }
  2118 +
  2119 + .action-btn {
  2120 + padding: 6px 12px; // 合理的内边距
  2121 + font-size: 13px; // 增大按钮字体
  2122 + display: flex;
  2123 + align-items: center;
  2124 + gap: 6px; // 合理的间距
  2125 + border-radius: 8px;
  2126 + transition: all 0.2s ease;
  2127 + font-weight: 500;
  2128 + cursor: pointer;
  2129 +
  2130 + &.view-btn {
  2131 + color: #475569;
  2132 + background: transparent;
  2133 +
  2134 + &:hover {
  2135 + color: #2563EB;
  2136 + background: rgba(37, 99, 235, 0.08);
  2137 + transform: translateY(-1px);
  2138 + }
  2139 + }
  2140 +
  2141 + &.rights-btn {
  2142 + color: #475569;
  2143 + background: transparent;
  2144 +
  2145 + &:hover {
  2146 + color: #F97316;
  2147 + background: rgba(249, 115, 22, 0.08);
  2148 + transform: translateY(-1px);
  2149 + }
  2150 + }
  2151 +
  2152 + i {
  2153 + font-size: 15px; // 增大按钮图标
  2154 + transition: transform 0.2s ease;
  2155 + }
  2156 +
  2157 + &:hover i {
  2158 + transform: scale(1.1);
  2159 + }
  2160 + }
  2161 +
  2162 + .more-actions {
  2163 + .el-dropdown-link {
  2164 + cursor: pointer;
  2165 + color: #64748B;
  2166 + font-size: 13px; // 增大更多按钮字体
  2167 + padding: 6px 12px; // 合理的内边距
  2168 + border-radius: 8px;
  2169 + transition: all 0.2s ease;
  2170 + font-weight: 500;
  2171 + display: flex;
  2172 + align-items: center;
  2173 + gap: 4px; // 合理的间距
  2174 +
  2175 + &:hover {
  2176 + color: #2563EB;
  2177 + background: rgba(37, 99, 235, 0.08);
  2178 + }
  2179 +
  2180 + i {
  2181 + font-size: 13px; // 增大图标
  2182 + transition: transform 0.2s ease;
  2183 + }
  2184 + }
  2185 + }
  2186 + }
  2187 + }
  2188 +
  2189 + .empty-data {
  2190 + display: flex;
  2191 + justify-content: center;
  2192 + align-items: center;
  2193 + height: 400px;
  2194 + }
  2195 +}
  2196 +
  2197 +// 分页组件样式(卡片模式)
  2198 +::v-deep .pagination-card-mode {
  2199 + flex-shrink: 0; // 防止分页被压缩
  2200 + position: relative;
  2201 + z-index: 20; // 确保在卡片之上
  2202 + margin-top: 16px;
  2203 + padding: 12px 0;
  2204 + background: transparent;
  2205 + // 毛玻璃效果的分页背景(可选)
  2206 + // background: rgba(255, 255, 255, 0.85);
  2207 + // backdrop-filter: blur(10px);
  2208 + // border-radius: 8px;
  2209 + // border-top: 1px solid rgba(226, 232, 240, 0.5);
  2210 +}
1379 2211 </style>
1380 2212 \ No newline at end of file
... ...
netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs
... ... @@ -900,10 +900,11 @@ namespace NCC.Extend.LqXhHyhk
900 900 {
901 901 memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString();
902 902 }
903   - //判断消费金额是否大于0
904   - if (entity.Sgfy > 0)
  903 + // 只要有耗卡记录且消费金额大于0,就更新最后消费时间(用于沉睡天数计算)
  904 + // 使用耗卡时间而不是当前时间,确保准确性
  905 + if (entity.Xfje > 0)
905 906 {
906   - memberInfo.LastConsumeTime = DateTime.Now;
  907 + memberInfo.LastConsumeTime = entity.Hksj;
907 908 }
908 909 //保存会员信息
909 910 await _db.Updateable(memberInfo).ExecuteCommandAsync();
... ... @@ -1282,6 +1283,11 @@ namespace NCC.Extend.LqXhHyhk
1282 1283 {
1283 1284 memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString();
1284 1285 }
  1286 + // 如果消费金额大于0,更新最后消费时间(用于沉睡天数计算)
  1287 + if (entity.Xfje > 0)
  1288 + {
  1289 + memberInfo.LastConsumeTime = entity.Hksj;
  1290 + }
1285 1291 //保存会员信息
1286 1292 await _db.Updateable(memberInfo).ExecuteCommandAsync();
1287 1293  
... ...
sql/修复会员沉睡天数数据.sql 0 → 100644
  1 +-- 修复会员沉睡天数数据
  2 +-- 问题:耗卡记录的手工费为0时,LastConsumeTime未更新,导致沉睡天数计算错误
  3 +-- 解决方案:根据最新的有效耗卡记录更新LastConsumeTime
  4 +
  5 +-- 1. 修复单个会员(陈秋竹)
  6 +UPDATE lq_khxx kh
  7 +INNER JOIN (
  8 + SELECT
  9 + hy,
  10 + MAX(hksj) as last_consume_time
  11 + FROM lq_xh_hyhk
  12 + WHERE hymc = '陈秋竹'
  13 + AND F_IsEffective = 1
  14 + AND xfje > 0
  15 + GROUP BY hy
  16 +) consume ON kh.F_Id = consume.hy
  17 +SET
  18 + kh.F_LastConsumeTime = consume.last_consume_time,
  19 + kh.F_SleepDays = GREATEST(0, DATEDIFF(CURDATE(), consume.last_consume_time))
  20 +WHERE kh.khmc = '陈秋竹'
  21 + AND kh.F_IsEffective = 1;
  22 +
  23 +-- 2. 批量修复所有会员(根据最新的有效耗卡记录更新LastConsumeTime)
  24 +-- 注意:执行此SQL前请先备份数据库
  25 +UPDATE lq_khxx kh
  26 +INNER JOIN (
  27 + SELECT
  28 + hy,
  29 + MAX(hksj) as last_consume_time
  30 + FROM lq_xh_hyhk
  31 + WHERE F_IsEffective = 1
  32 + AND xfje > 0
  33 + GROUP BY hy
  34 +) consume ON kh.F_Id = consume.hy
  35 +SET
  36 + kh.F_LastConsumeTime = consume.last_consume_time,
  37 + kh.F_SleepDays = GREATEST(0, DATEDIFF(CURDATE(), consume.last_consume_time))
  38 +WHERE kh.F_IsEffective = 1
  39 + AND (
  40 + -- 只更新需要修复的记录:LastConsumeTime为空或LastConsumeTime小于最新的耗卡时间
  41 + kh.F_LastConsumeTime IS NULL
  42 + OR kh.F_LastConsumeTime < consume.last_consume_time
  43 + );
  44 +
... ...