diff --git a/admin/src/views/content/popup.vue b/admin/src/views/content/popup.vue index 0e67861..f211c2f 100644 --- a/admin/src/views/content/popup.vue +++ b/admin/src/views/content/popup.vue @@ -16,7 +16,8 @@ import type { PopupItem, UpdatePopupRequest } from '@/types/popup.d' const popupTypes = [ { type: 1, name: '每日首次弹窗', description: '用户每天首次打开小程序时展示' }, { type: 2, name: '服务号关注弹窗', description: '引导用户关注公众号' }, - { type: 3, name: '会员广告弹窗', description: '会员推广广告条,显示在首页底部', showDisplayMode: true } + { type: 3, name: '会员广告弹窗', description: '会员推广广告条,显示在首页底部', showDisplayMode: true }, + { type: 4, name: '订阅消息提醒条', description: '引导用户开启消息通知,显示在首页底部' } ] // 显示模式选项 diff --git a/admin/src/views/user/detail.vue b/admin/src/views/user/detail.vue index c344e80..797bad7 100644 --- a/admin/src/views/user/detail.vue +++ b/admin/src/views/user/detail.vue @@ -9,6 +9,7 @@ import { ElMessage, ElMessageBox } from 'element-plus' import { ArrowLeft } from '@element-plus/icons-vue' import StatusTag from '@/components/StatusTag/index.vue' import { getUserDetail, updateUserStatus, updateContactCount, updateMemberLevel, cancelRealName } from '@/api/user' +import { getMemberTierList } from '@/api/memberTier' import { getFullImageUrl } from '@/utils/image' import type { UserDetail } from '@/types/user.d' @@ -106,14 +107,26 @@ const handleEditContactCount = async () => { } } -// 会员等级选项 -const memberLevelOptions = [ - { value: 0, label: '非会员' }, - { value: 1, label: '不限时会员' }, - { value: 2, label: '诚意会员' }, - { value: 3, label: '家庭版会员' }, - { value: 4, label: '限时会员' } -] +// 会员等级选项 - 从后台配置动态获取 +const memberLevelOptions = ref([ + { value: 0, label: '非会员' } +]) + +// 加载会员等级配置 +const loadMemberTierOptions = async () => { + try { + const res = await getMemberTierList() + const tiers = res.data || res || [] + if (Array.isArray(tiers) && tiers.length > 0) { + memberLevelOptions.value = [ + { value: 0, label: '非会员' }, + ...tiers.map((t: any) => ({ value: t.level, label: t.name })) + ] + } + } catch (error) { + console.error('加载会员等级配置失败:', error) + } +} // 修改会员等级对话框 const memberLevelDialogVisible = ref(false) @@ -257,6 +270,7 @@ const formatIncome = (min: number | undefined, max: number | undefined) => { onMounted(() => { fetchUserDetail() + loadMemberTierOptions() }) diff --git a/admin/src/views/user/list.vue b/admin/src/views/user/list.vue index dd4c4ba..4586661 100644 --- a/admin/src/views/user/list.vue +++ b/admin/src/views/user/list.vue @@ -11,6 +11,7 @@ import SearchForm from '@/components/SearchForm/index.vue' import Pagination from '@/components/Pagination/index.vue' import StatusTag from '@/components/StatusTag/index.vue' import { getUserList, updateUserStatus, createTestUsers, deleteUser } from '@/api/user' +import { getMemberTierList } from '@/api/memberTier' import { getFullImageUrl } from '@/utils/image' import type { UserListItem, UserQueryParams } from '@/types/user.d' @@ -54,15 +55,28 @@ const memberOptions = [ { label: '否', value: false } ] -// 会员等级选项 -const memberLevelOptions = [ +// 会员等级选项 - 从后台配置动态获取 +const memberLevelOptions = ref([ { label: '全部', value: '' }, - { label: '非会员', value: 0 }, - { label: '不限时', value: 1 }, - { label: '诚意', value: 2 }, - { label: '家庭版', value: 3 }, - { label: '限时', value: 4 } -] + { label: '非会员', value: 0 } +]) + +// 加载会员等级配置 +const loadMemberTierOptions = async () => { + try { + const res = await getMemberTierList() + const tiers = res.data || res || [] + if (Array.isArray(tiers) && tiers.length > 0) { + memberLevelOptions.value = [ + { label: '全部', value: '' }, + { label: '非会员', value: 0 }, + ...tiers.map((t: any) => ({ label: t.name, value: t.level })) + ] + } + } catch (error) { + console.error('加载会员等级配置失败:', error) + } +} // 实名认证选项 const realNameOptions = [ @@ -213,8 +227,8 @@ const handleOpenTestUserDialog = () => { // 创建测试用户 const handleCreateTestUsers = async () => { - if (testUserForm.count < 1 || testUserForm.count > 50) { - ElMessage.warning('创建数量必须在1-50之间') + if (testUserForm.count < 1 || testUserForm.count > 100) { + ElMessage.warning('创建数量必须在1-100之间') return } @@ -233,6 +247,7 @@ const handleCreateTestUsers = async () => { onMounted(() => { fetchUserList() + loadMemberTierOptions() }) @@ -559,9 +574,10 @@ onMounted(() => { +
单次最多创建100个
diff --git a/miniapp/components/VoiceRecorder/index.vue b/miniapp/components/VoiceRecorder/index.vue index e69ee16..d6dc9a9 100644 --- a/miniapp/components/VoiceRecorder/index.vue +++ b/miniapp/components/VoiceRecorder/index.vue @@ -4,107 +4,137 @@ - {{ isRecording ? '松开发送' : '按住说话' }} + {{ btnText }} - - + + - - 🎤 + + {{ isCancelling ? '↩' : '🎤' }} {{ recordingTip }} - {{ recordingTime }}s + {{ recordingTime }}s diff --git a/miniapp/config/index.js b/miniapp/config/index.js index 7794e45..6bdbd8d 100644 --- a/miniapp/config/index.js +++ b/miniapp/config/index.js @@ -23,7 +23,7 @@ const ENV = { } // 当前环境 - 开发时使用 development,打包时改为 production -const CURRENT_ENV = 'development' +const CURRENT_ENV = 'production' // 导出配置 export const config = { diff --git a/miniapp/pages/chat/index.vue b/miniapp/pages/chat/index.vue index 9986813..c8f84c5 100644 --- a/miniapp/pages/chat/index.vue +++ b/miniapp/pages/chat/index.vue @@ -39,7 +39,7 @@ scroll-y scroll-with-animation :scroll-into-view="scrollToId" - :style="{ paddingTop: (statusBarHeight + 44) + 'px' }" + :style="{ paddingTop: (statusBarHeight + 44) + 'px', paddingBottom: (140 + keyboardHeight) + 'px' }" @scrolltoupper="loadMoreMessages" > @@ -270,7 +270,7 @@ - + + @@ -388,6 +390,9 @@ const hasMore = ref(true) const pageIndex = ref(1) const isInputFocused = ref(false) +// 键盘高度 +const keyboardHeight = ref(0) + // 交换状态 - 记录是否已成功交换过微信/照片 const hasExchangedWeChat = ref(false) const hasExchangedPhoto = ref(false) @@ -1045,10 +1050,7 @@ const handleInputFocus = () => { } const handleInputBlur = () => { - // 延迟隐藏,避免点击发送按钮时按钮消失 - setTimeout(() => { - isInputFocused.value = false - }, 100) + isInputFocused.value = false } const shouldShowTime = (message, index) => { @@ -1100,6 +1102,14 @@ const handleMore = () => { onMounted(async () => { getSystemInfo() + // 监听键盘高度变化 + uni.onKeyboardHeightChange((res) => { + keyboardHeight.value = res.height || 0 + if (res.height > 0) { + nextTick(() => scrollToBottom()) + } + }) + const pages = getCurrentPages() const currentPage = pages[pages.length - 1] const options = currentPage?.options || {} @@ -1373,6 +1383,11 @@ const handleMessagesRead = (data) => { onUnmounted(() => { chatStore.clearCurrentSession() + // 移除键盘高度监听 + // #ifdef MP-WEIXIN + uni.offKeyboardHeightChange() + // #endif + // 停止语音播放 stopVoice() if (innerAudioContext.value) { @@ -1455,7 +1470,6 @@ onUnmounted(() => { .content-scroll { flex: 1; height: 0; - padding-bottom: 280rpx; background-color: #f8f8f8; } @@ -1877,6 +1891,8 @@ onUnmounted(() => { right: 0; background: #f5f6fa; padding-bottom: env(safe-area-inset-bottom); + transition: bottom 0.15s ease-out; + z-index: 100; .action-buttons { display: flex; @@ -2004,9 +2020,10 @@ onUnmounted(() => { color: #fff; border: none; flex-shrink: 0; + text-align: center; - &::after { - border: none; + &:active { + opacity: 0.8; } &.active { diff --git a/miniapp/pages/index/index.vue b/miniapp/pages/index/index.vue index 4ac1037..53dcade 100644 --- a/miniapp/pages/index/index.vue +++ b/miniapp/pages/index/index.vue @@ -66,12 +66,8 @@