diff --git a/antis-ncc-admin/src/views/lqKhxx/index.vue b/antis-ncc-admin/src/views/lqKhxx/index.vue index 2ea5936..9d00cbc 100644 --- a/antis-ncc-admin/src/views/lqKhxx/index.vue +++ b/antis-ncc-admin/src/views/lqKhxx/index.vue @@ -105,6 +105,12 @@ @click="handleBatchRemoveDel()">批量删除
+
+ 列表 + 卡片 +
@@ -112,7 +118,130 @@
- +
+ + + +
+
+
+ {{ item.khmc || '无名氏' }} + +
+ + {{ getConsumeLevelName(item.consumeLevel) }} + +
+
+ 生美 + 医美 + 科技 + 教育 +
+
+ +
+
+
+ + 手机号 + + {{ item.sjh || '-' }} +
+
+ + 归属门店 + + {{ item.gsmdName || + '-' + }} +
+
+ + 客户类型 + + {{ item.khlxName || '-' }} +
+
+ + 沉睡天数 + + {{ + item.sleepDays || 0 + }}天 +
+
+ + 最后到店 + + {{ formatDate(item.lastVisitTime) }} +
+
+ +
+
+ + 开卡金额 + + {{ + formatMoney(item.totalBillingAmount) }} +
+
+
+ + 剩余权益 + + {{ + formatMoney(item.remainingRightsAmount) + }} +
+
+
+ + +
+
+
+
+ +
+
+ + @@ -320,7 +449,7 @@ - + - + @@ -368,6 +501,7 @@ export default { components: { NCCForm, ExportBox, MemberRightsDialog, DetailDialog, MemberPortraitDialog }, data() { return { + viewMode: 'list', // list or card showAll: false, kdhyLoading: false, memberRightsDialogVisible: false, @@ -475,12 +609,52 @@ export default { }, computed: {}, created() { + // 默认使用列表视图,不恢复本地存储的视图模式 + this.viewMode = 'list'; this.initData(); this.getgsmdOptions(); this.getkhlxOptions(); this.getkdhyOptions(); }, methods: { + // 权限检查方法 + has(enCode) { + try { + const permissionList = this.$store.getters && this.$store.getters.permissionList + if (!permissionList || !permissionList.length) return false + const modelId = this.$route.meta.modelId || '' + if (!modelId) return false + const list = permissionList.filter(o => o.modelId === modelId) + if (!list.length) return false + const btnList = list[0] && list[0].button ? list[0].button : [] + if (!btnList.length) return false + const hasPermission = btnList.some(btn => btn.enCode === enCode) + return hasPermission + } catch (e) { + console.warn('Permission check error:', e) + return false + } + }, + switchView(mode) { + console.log('Switching view to:', mode); + if (mode !== 'list' && mode !== 'card') { + console.error('Invalid view mode:', mode); + return; + } + this.viewMode = mode; + // 保存视图模式到本地存储 + try { + localStorage.setItem('memberListViewMode', mode); + } catch (e) { + console.warn('Failed to save view mode to localStorage:', e); + } + // 强制更新视图 + this.$forceUpdate(); + // 触发窗口大小变化事件,确保布局正确 + this.$nextTick(() => { + window.dispatchEvent(new Event('resize')); + }); + }, getgsmdOptions() { previewDataInterface('730960205902251269').then(res => { this.gsmdOptions = res.data @@ -800,6 +974,13 @@ export default { 5: 'danger' } return typeMap[level] || 'info' + }, + handleCommand(command, item) { + if (command === 'edit') { + this.addOrUpdateHandle(item.id); + } else if (command === 'delete') { + this.handleDel(item.id); + } } } } @@ -1376,4 +1557,655 @@ export default { } } } + +// 卡片视图样式 +.member-card-wrapper { + display: flex; + flex-direction: column; + flex: 1; + height: 100%; + min-height: 0; // 重要:允许 flex 子元素收缩 + box-sizing: border-box; + overflow: hidden; // 防止内容溢出 +} + +.member-card-scroll-container { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding: 16px 20px; // 合理的容器内边距 + box-sizing: border-box; + min-height: 0; // 重要:允许 flex 子元素收缩 + // 使用渐变背景(为毛玻璃效果提供视觉基础) + background: linear-gradient(135deg, #F8FAFC 0%, #F1F5F9 50%, #E2E8F0 100%); + + // 自定义滚动条样式 + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.05); + border-radius: 4px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + transition: background 0.2s ease; + + &:hover { + background: rgba(0, 0, 0, 0.3); + } + } + + .member-card-row { + .el-col { + margin-bottom: 16px; // 合理的卡片间距 + display: flex; + } + } + + .empty-data { + display: flex; + justify-content: center; + align-items: center; + height: 400px; + } + + .member-card { + width: 100%; + display: flex; + flex-direction: column; + border-radius: 12px; // 符合规范:12px圆角 + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); // 更有弹性的动画 + // 精致的毛玻璃效果边框(双层边框) + border: 1px solid rgba(255, 255, 255, 0.4); + // 增强的毛玻璃背景 + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.9) 0%, + rgba(255, 255, 255, 0.8) 50%, + rgba(255, 255, 255, 0.85) 100% + ); + backdrop-filter: blur(24px) saturate(190%); + -webkit-backdrop-filter: blur(24px) saturate(190%); + // 精致的多层次阴影系统(5层阴影,创造更强的深度) + box-shadow: + 0 2px 4px rgba(0, 0, 0, 0.04), + 0 4px 8px rgba(0, 0, 0, 0.03), + 0 8px 16px rgba(0, 0, 0, 0.02), + 0 16px 32px rgba(0, 0, 0, 0.015), + inset 0 1px 1px rgba(255, 255, 255, 0.95), // 顶部高光 + inset 0 -1px 1px rgba(0, 0, 0, 0.02); // 底部阴影 + overflow: hidden; + position: relative; + cursor: pointer; + // 添加微妙的渐变遮罩层 + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.1) 0%, + transparent 50%, + rgba(0, 0, 0, 0.02) 100% + ); + pointer-events: none; + opacity: 0; + transition: opacity 0.3s ease; + } + + // 顶部装饰条(根据等级变化)- 更精致的渐变效果 + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient( + 90deg, + #2563EB 0%, + #3B82F6 50%, + #60A5FA 100% + ); + opacity: 1; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3); // 添加发光效果 + } + + // 等级差异化样式(A++ - 5级)- 更丰富的渐变和发光效果 + &.level-card-5::before { + background: linear-gradient(90deg, #DC2626 0%, #EF4444 50%, #F87171 100%); + height: 5px; + box-shadow: 0 2px 12px rgba(220, 38, 38, 0.4); + } + + // A++ - 4级 + &.level-card-4::before { + background: linear-gradient(90deg, #F97316 0%, #FB923C 50%, #FDBA74 100%); + height: 5px; + box-shadow: 0 2px 12px rgba(249, 115, 22, 0.4); + } + + // A+ - 3级 + &.level-card-3::before { + background: linear-gradient(90deg, #F59E0B 0%, #FBBF24 50%, #FCD34D 100%); + height: 4px; + box-shadow: 0 2px 10px rgba(245, 158, 11, 0.35); + } + + // A - 2级 + &.level-card-2::before { + background: linear-gradient(90deg, #10B981 0%, #34D399 50%, #6EE7B7 100%); + box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3); + } + + // C - 1级 + &.level-card-1::before { + background: linear-gradient(90deg, #3B82F6 0%, #60A5FA 50%, #93C5FD 100%); + box-shadow: 0 2px 8px rgba(59, 130, 246, 0.3); + } + + // D (普通) - 0级 + &.level-card-0::before { + background: linear-gradient(90deg, #94A3B8 0%, #CBD5E1 50%, #E2E8F0 100%); + box-shadow: 0 1px 4px rgba(148, 163, 184, 0.2); + } + + // Hover 效果 - 精致的提升动画 + &:hover { + transform: translateY(-8px) scale(1.02); + // 增强毛玻璃效果 + background: linear-gradient( + 135deg, + rgba(255, 255, 255, 0.95) 0%, + rgba(255, 255, 255, 0.9) 50%, + rgba(255, 255, 255, 0.92) 100% + ); + backdrop-filter: blur(30px) saturate(200%); + -webkit-backdrop-filter: blur(30px) saturate(200%); + // 增强的悬浮阴影(创造浮动感) + box-shadow: + 0 8px 16px rgba(0, 0, 0, 0.08), + 0 16px 32px rgba(0, 0, 0, 0.06), + 0 32px 64px rgba(0, 0, 0, 0.04), + 0 0 0 1px rgba(255, 255, 255, 0.6), + inset 0 2px 2px rgba(255, 255, 255, 0.98), + inset 0 -1px 2px rgba(0, 0, 0, 0.03); + border-color: rgba(255, 255, 255, 0.6); + z-index: 10; + + &::before { + height: 6px; + box-shadow: 0 4px 16px rgba(37, 99, 235, 0.4); + } + + &::after { + opacity: 1; // 显示渐变遮罩 + } + } + + // 响应减少动画偏好 + @media (prefers-reduced-motion: reduce) { + transition: none; + + &:hover { + transform: none; + } + } + + ::v-deep .el-card__body { + padding: 0; // 自定义padding + flex: 1; + display: flex; + flex-direction: column; + } + + .card-header-wrapper { + padding: 10px 12px 8px; // 缩小卡片头部内边距 + border-bottom: 1px solid rgba(255, 255, 255, 0.3); + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0.4) 0%, + rgba(255, 255, 255, 0.2) 50%, + rgba(255, 255, 255, 0) 100% + ); + position: relative; + // 添加微妙的光影效果 + &::after { + content: ''; + position: absolute; + bottom: 0; + left: 16px; + right: 16px; + height: 1px; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.5) 50%, + transparent 100% + ); + } + + .header-top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 6px; // 缩小间距 + gap: 4px; + + .info-left { + display: flex; + align-items: center; + flex: 1; + min-width: 0; + + .member-name { + font-size: 16px; // 增大标题字体,匹配卡片大小 + font-weight: 700; + color: #0F172A; + margin-right: 6px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + letter-spacing: -0.02em; + line-height: 1.4; // 合理的行高 + text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8); + } + + .gender-icon { + font-size: 14px; // 增大图标 + + &.gender-male { + color: #409EFF; + } + + &.gender-female { + color: #F56C6C; + } + + &.gender-unknown { + color: #909399; + } + } + } + } + + .header-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; // 缩小标签间距 + min-height: 24px; + + .mini-tag { + border-radius: 6px; + padding: 3px 8px; + height: 22px; + line-height: 18px; + font-size: 13px; // 增大标签字体 + font-weight: 600; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + border: 0.5px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); + + &:hover { + transform: translateY(-1px) scale(1.05); + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12); + } + } + } + + .level-tag { + font-weight: 700; + border-radius: 8px; + padding: 5px 12px; + font-size: 13px; // 增大等级标签字体 + letter-spacing: 0.03em; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + border: 0.5px solid rgba(255, 255, 255, 0.3); + transition: all 0.25s ease; + + &:hover { + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); + } + } + } + + .card-content { + flex: 1; + padding: 10px 12px; // 缩小内容区内边距 + display: flex; + flex-direction: column; + gap: 8px; // 缩小间距 + + .data-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 6px 8px; // 缩小网格间距 + + .data-item { + display: flex; + flex-direction: column; + padding: 3px 0; // 缩小内边距 + + &.full-width { + grid-column: span 2; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 6px 0 3px; // 缩小内边距 + border-top: 1px dashed rgba(255, 255, 255, 0.3); + margin-top: 3px; + + .value { + text-align: right; + font-weight: 600; + } + } + + .label { + font-size: 13px; // 增大标签字体,匹配卡片大小 + color: #475569; + margin-bottom: 4px; // 合理的间距 + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + line-height: 1.4; // 合理的行高 + display: flex; + align-items: center; + gap: 5px; // 合理的图标间距 + + .icon-inline { + font-size: 13px; // 增大图标 + opacity: 0.75; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + filter: drop-shadow(0 1px 1px rgba(255, 255, 255, 0.5)); + } + + &:hover .icon-inline { + opacity: 1; + transform: scale(1.15) rotate(5deg); + } + } + + .icon-warning { + color: #EF4444; + opacity: 1 !important; + } + + .value { + font-size: 14px; // 增大数据值字体,匹配卡片大小 + color: #0F172A; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.5; // 合理的行高 + letter-spacing: -0.01em; + + &.text-truncate { + max-width: 100%; + } + + &.text-danger { + color: #EF4444; + font-weight: 700; + position: relative; + + &::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, rgba(239, 68, 68, 0.3), transparent); + } + } + } + } + } + + .amount-section { + margin-top: auto; + // 更精致的渐变背景 + background: linear-gradient( + 135deg, + rgba(37, 99, 235, 0.12) 0%, + rgba(59, 130, 246, 0.08) 50%, + rgba(96, 165, 250, 0.06) 100% + ); + backdrop-filter: blur(12px) saturate(150%); + -webkit-backdrop-filter: blur(12px) saturate(150%); + border: 1px solid rgba(255, 255, 255, 0.5); + border-radius: 10px; // 缩小圆角 + padding: 8px 10px; // 缩小内边距 + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 6px; // 缩小上边距 + position: relative; + overflow: hidden; + box-shadow: + inset 0 1px 2px rgba(255, 255, 255, 0.6), + inset 0 -1px 1px rgba(37, 99, 235, 0.1), + 0 2px 8px rgba(37, 99, 235, 0.1); + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(37, 99, 235, 0.4) 50%, + transparent 100% + ); + box-shadow: 0 2px 4px rgba(37, 99, 235, 0.2); + } + + .amount-box { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 3px; // 缩小间距 + + .sub-label { + font-size: 13px; // 增大金额标签字体 + color: #475569; + margin-bottom: 4px; // 合理的间距 + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + display: flex; + align-items: center; + justify-content: center; + gap: 5px; // 合理的图标间距 + + .icon-inline { + font-size: 13px; // 增大图标 + opacity: 0.8; + transition: all 0.2s ease; + } + + &:hover .icon-inline { + opacity: 1; + transform: scale(1.1); + } + } + + .sub-value { + font-size: 18px; // 增大金额字体,匹配卡片大小 + font-weight: 700; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'SF Pro Display', 'Helvetica Neue', 'Inter', sans-serif; + letter-spacing: -0.03em; + line-height: 1.3; // 合理的行高 + transition: all 0.2s ease; + + &.primary-color { + color: #2563EB; + background: linear-gradient(135deg, #2563EB, #3B82F6); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + &.warning-color { + color: #F97316; + background: linear-gradient(135deg, #F97316, #FB923C); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + } + } + + .divider { + width: 1px; + height: 32px; // 合理的高度 + background: linear-gradient(180deg, transparent, rgba(226, 232, 240, 0.8), transparent); + margin: 0 12px; // 合理的边距 + } + } + } + + .card-footer { + margin-top: 0; + padding: 12px 14px; // 符合规范:12px内边距 + border-top: 1px solid rgba(255, 255, 255, 0.3); + display: flex; + justify-content: space-between; + align-items: center; + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.2) 50%, + rgba(248, 250, 252, 0.4) 100% + ); + gap: 8px; // 合理的间距 + position: relative; + + // 顶部高光线条 + &::before { + content: ''; + position: absolute; + top: 0; + left: 16px; + right: 16px; + height: 1px; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.6) 50%, + transparent 100% + ); + } + + .action-btn { + padding: 6px 12px; // 合理的内边距 + font-size: 13px; // 增大按钮字体 + display: flex; + align-items: center; + gap: 6px; // 合理的间距 + border-radius: 8px; + transition: all 0.2s ease; + font-weight: 500; + cursor: pointer; + + &.view-btn { + color: #475569; + background: transparent; + + &:hover { + color: #2563EB; + background: rgba(37, 99, 235, 0.08); + transform: translateY(-1px); + } + } + + &.rights-btn { + color: #475569; + background: transparent; + + &:hover { + color: #F97316; + background: rgba(249, 115, 22, 0.08); + transform: translateY(-1px); + } + } + + i { + font-size: 15px; // 增大按钮图标 + transition: transform 0.2s ease; + } + + &:hover i { + transform: scale(1.1); + } + } + + .more-actions { + .el-dropdown-link { + cursor: pointer; + color: #64748B; + font-size: 13px; // 增大更多按钮字体 + padding: 6px 12px; // 合理的内边距 + border-radius: 8px; + transition: all 0.2s ease; + font-weight: 500; + display: flex; + align-items: center; + gap: 4px; // 合理的间距 + + &:hover { + color: #2563EB; + background: rgba(37, 99, 235, 0.08); + } + + i { + font-size: 13px; // 增大图标 + transition: transform 0.2s ease; + } + } + } + } + } + + .empty-data { + display: flex; + justify-content: center; + align-items: center; + height: 400px; + } +} + +// 分页组件样式(卡片模式) +::v-deep .pagination-card-mode { + flex-shrink: 0; // 防止分页被压缩 + position: relative; + z-index: 20; // 确保在卡片之上 + margin-top: 16px; + padding: 12px 0; + background: transparent; + // 毛玻璃效果的分页背景(可选) + // background: rgba(255, 255, 255, 0.85); + // backdrop-filter: blur(10px); + // border-radius: 8px; + // border-top: 1px solid rgba(226, 232, 240, 0.5); +} \ No newline at end of file diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs index 502f9ee..b4dc49a 100644 --- a/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs +++ b/netcore/src/Modularity/Extend/NCC.Extend/LqXhHyhkService.cs @@ -900,10 +900,11 @@ namespace NCC.Extend.LqXhHyhk { memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); } - //判断消费金额是否大于0 - if (entity.Sgfy > 0) + // 只要有耗卡记录且消费金额大于0,就更新最后消费时间(用于沉睡天数计算) + // 使用耗卡时间而不是当前时间,确保准确性 + if (entity.Xfje > 0) { - memberInfo.LastConsumeTime = DateTime.Now; + memberInfo.LastConsumeTime = entity.Hksj; } //保存会员信息 await _db.Updateable(memberInfo).ExecuteCommandAsync(); @@ -1282,6 +1283,11 @@ namespace NCC.Extend.LqXhHyhk { memberInfo.Khlx = MemberTypeEnum.新客.GetHashCode().ToString(); } + // 如果消费金额大于0,更新最后消费时间(用于沉睡天数计算) + if (entity.Xfje > 0) + { + memberInfo.LastConsumeTime = entity.Hksj; + } //保存会员信息 await _db.Updateable(memberInfo).ExecuteCommandAsync(); diff --git a/sql/修复会员沉睡天数数据.sql b/sql/修复会员沉睡天数数据.sql new file mode 100644 index 0000000..9a70bd9 --- /dev/null +++ b/sql/修复会员沉睡天数数据.sql @@ -0,0 +1,44 @@ +-- 修复会员沉睡天数数据 +-- 问题:耗卡记录的手工费为0时,LastConsumeTime未更新,导致沉睡天数计算错误 +-- 解决方案:根据最新的有效耗卡记录更新LastConsumeTime + +-- 1. 修复单个会员(陈秋竹) +UPDATE lq_khxx kh +INNER JOIN ( + SELECT + hy, + MAX(hksj) as last_consume_time + FROM lq_xh_hyhk + WHERE hymc = '陈秋竹' + AND F_IsEffective = 1 + AND xfje > 0 + GROUP BY hy +) consume ON kh.F_Id = consume.hy +SET + kh.F_LastConsumeTime = consume.last_consume_time, + kh.F_SleepDays = GREATEST(0, DATEDIFF(CURDATE(), consume.last_consume_time)) +WHERE kh.khmc = '陈秋竹' + AND kh.F_IsEffective = 1; + +-- 2. 批量修复所有会员(根据最新的有效耗卡记录更新LastConsumeTime) +-- 注意:执行此SQL前请先备份数据库 +UPDATE lq_khxx kh +INNER JOIN ( + SELECT + hy, + MAX(hksj) as last_consume_time + FROM lq_xh_hyhk + WHERE F_IsEffective = 1 + AND xfje > 0 + GROUP BY hy +) consume ON kh.F_Id = consume.hy +SET + kh.F_LastConsumeTime = consume.last_consume_time, + kh.F_SleepDays = GREATEST(0, DATEDIFF(CURDATE(), consume.last_consume_time)) +WHERE kh.F_IsEffective = 1 + AND ( + -- 只更新需要修复的记录:LastConsumeTime为空或LastConsumeTime小于最新的耗卡时间 + kh.F_LastConsumeTime IS NULL + OR kh.F_LastConsumeTime < consume.last_consume_time + ); +