| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- <template>
- <view class="page-chart">
- <!-- Triangle chart -->
- <view class="chart-section">
- <TriangleChart
- :chartData="store.currentChart"
- :birthYear="store.currentBirthYear"
- :birthMonth="store.currentBirthMonth"
- :birthDay="store.currentBirthDay"
- />
- </view>
- <!-- AI Chat -->
- <view class="chat-section">
- <view class="section-title">
- <text class="title-icon">◈</text>
- AI 解读
- <text v-if="!userStore.isVipActive && maxChats > 0" class="quota-badge">
- 今日 {{ usedChats }}/{{ maxChats }} 轮
- </text>
- <text v-if="userStore.isVipActive" class="quota-badge vip">无限次</text>
- </view>
- <scroll-view class="chat-box" scroll-y :scroll-top="scrollTop">
- <view v-for="(msg, idx) in messages" :key="idx" class="msg-row" :class="msg.role">
- <view class="msg-bubble">
- <text>{{ msg.content }}</text>
- </view>
- </view>
- </scroll-view>
- <!-- Quota exceeded prompt for non-VIP -->
- <view v-if="quotaExceeded" class="upgrade-prompt">
- <text class="upgrade-text">今日 AI 解读次数已用完</text>
- <text class="upgrade-desc">开通能量师会员享无限次解读</text>
- <button class="upgrade-btn" @click="goSubscribe">立即开通 →</button>
- </view>
- <!-- Input area (disabled when quota exceeded) -->
- <template v-if="!quotaExceeded">
- <view class="input-bar">
- <input
- class="chat-input"
- v-model="chatInput"
- placeholder="输入您的问题..."
- @confirm="sendQuestion(chatInput)"
- :disabled="loading"
- />
- <button class="send-btn" @click="sendQuestion(chatInput)" :disabled="loading">
- {{ loading ? '···' : '发送' }}
- </button>
- </view>
- </template>
- </view>
- </view>
- </template>
- <script setup>
- import { ref, computed, onMounted } from 'vue'
- import { useChartStore } from '@/stores/chart'
- import { useUserStore } from '@/stores/user'
- import TriangleChart from '@/components/TriangleChart.vue'
- import { chatApi } from '@/utils/api'
- const store = useChartStore()
- const userStore = useUserStore()
- const chatInput = ref('')
- const scrollTop = ref(0)
- const messages = ref([])
- const loading = ref(false)
- const usedChats = ref(0)
- const maxChats = ref(0)
- const quotaExceeded = computed(() => {
- if (userStore.isVipActive) return false
- return maxChats.value > 0 && usedChats.value >= maxChats.value
- })
- onMounted(async () => {
- try {
- await userStore.fetchQuota()
- } catch (e) { /* ignore */ }
- // URL shareability — load from recordId if no store state (US-1.1 §14)
- const pages = getCurrentPages()
- const currentPage = pages[pages.length - 1]
- const recordId = currentPage?.options?.recordId
- if (!store.currentChart && recordId) {
- try {
- await store.loadChart(Number(recordId))
- } catch (e) {
- // fall through to "no data" message
- }
- }
- if (store.currentChart) {
- maxChats.value = userStore.maxChats || 3
- usedChats.value = userStore.usedChats || 0
- // Use consultationMessages from store (set during startConsultation) if available
- if (store.consultationMessages && store.consultationMessages.length > 0) {
- messages.value = store.consultationMessages.map(m => ({
- role: m.role === 'ai' ? 'ai' : 'user',
- content: m.content
- }))
- } else if (store.currentRecordId) {
- try {
- const history = await chatApi.history(store.currentRecordId)
- messages.value = history.map(m => ({
- role: m.role === 'ai' ? 'ai' : 'user',
- content: m.content
- }))
- const userMsgCount = history.filter(m => m.role === 'user').length
- usedChats.value = Math.max(usedChats.value, userMsgCount)
- } catch (e) {
- // fallback to generic greeting
- }
- }
- // Ensure at least a greeting message if still empty
- if (messages.value.length === 0) {
- messages.value = [{
- role: 'ai',
- content: `已为您生成命盘。主性格数字为${store.currentChart.O}。请点击下方问题或输入您想了解的内容。`
- }]
- }
- } else {
- messages.value = [{
- role: 'ai',
- content: '暂无命盘数据,请先返回首页输入出生信息。'
- }]
- }
- })
- async function sendQuestion(q) {
- if (!q.trim() || loading.value) return
- if (quotaExceeded.value) return
- messages.value.push({ role: 'user', content: q })
- chatInput.value = ''
- loading.value = true
- try {
- if (store.currentRecordId) {
- const res = await chatApi.send({ chartRecordId: store.currentRecordId, query: q })
- messages.value.push({ role: 'ai', content: res.reply })
- usedChats.value++
- } else {
- messages.value.push({
- role: 'ai',
- content: `数字能量分析显示,${q}方面您的能量组合表明有良好的发展潜力,请结合自身情况理性决策。`
- })
- }
- } catch (e) {
- if (e.code === 1008) {
- usedChats.value = maxChats.value
- messages.value.push({
- role: 'ai',
- content: '今日解读次数已用完,开通会员可享无限次解读。'
- })
- } else {
- messages.value.push({
- role: 'ai',
- content: e.message || '解读服务暂时不可用,请稍后再试。'
- })
- }
- }
- loading.value = false
- scrollTop.value = 99999
- }
- function goSubscribe() {
- uni.navigateTo({ url: '/pages/subscribe/index' })
- }
- </script>
- <style scoped lang="scss">
- .page-chart {
- height: 100vh;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- background: linear-gradient(180deg, #0c0a1a 0%, #120d28 50%, #0f0c1e 100%);
- }
- .chart-section {
- flex-shrink: 0;
- margin: 0 14px;
- }
- .chat-section {
- flex: 1;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- min-height: 0;
- margin: 0 14px;
- }
- .section-title {
- font-size: 17px;
- font-weight: 600;
- color: rgba(255, 255, 255, 0.9);
- margin: 20px 0 14px;
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .title-icon {
- font-size: 14px;
- color: #fbbf24;
- opacity: 0.7;
- }
- .quota-badge {
- font-size: 11px;
- font-weight: 400;
- color: #fbbf24;
- background: rgba(251, 191, 36, 0.12);
- padding: 2px 10px;
- border-radius: 10px;
- border: 1px solid rgba(251, 191, 36, 0.15);
- }
- .quota-badge.vip {
- color: #34d399;
- background: rgba(52, 211, 153, 0.1);
- border-color: rgba(52, 211, 153, 0.15);
- }
- .chat-box {
- flex: 1;
- height: auto;
- overflow-y: auto;
- background: rgba(255, 255, 255, 0.04);
- border: 1px solid rgba(255, 255, 255, 0.06);
- border-radius: 14px;
- padding: 14px;
- margin-bottom: 10px;
- }
- .msg-row {
- margin-bottom: 14px;
- display: flex;
- }
- .msg-row.user {
- justify-content: flex-end;
- }
- .msg-bubble {
- max-width: 80%;
- padding: 10px 16px;
- border-radius: 14px;
- font-size: 14px;
- line-height: 1.5;
- color: rgba(255, 255, 255, 0.85);
- background: rgba(255, 255, 255, 0.06);
- border: 1px solid rgba(255, 255, 255, 0.06);
- }
- .msg-row.user .msg-bubble {
- background: rgba(251, 191, 36, 0.12);
- border-color: rgba(251, 191, 36, 0.15);
- color: #fbbf24;
- }
- .input-bar {
- display: flex;
- gap: 8px;
- }
- .chat-input {
- flex: 1;
- height: 42px;
- border: 1px solid rgba(255, 255, 255, 0.1);
- border-radius: 10px;
- padding: 0 14px;
- font-size: 14px;
- color: rgba(255, 255, 255, 0.85);
- background: rgba(255, 255, 255, 0.04);
- }
- .send-btn {
- height: 42px;
- padding: 0 24px;
- background: linear-gradient(135deg, #f59e0b, #d97706);
- color: #fff;
- border: none;
- border-radius: 10px;
- line-height: 42px;
- font-size: 14px;
- font-weight: 500;
- }
- .send-btn[disabled] {
- opacity: 0.4;
- }
- /* Upgrade prompt */
- .upgrade-prompt {
- background: linear-gradient(135deg, rgba(251, 191, 36, 0.08), rgba(217, 119, 6, 0.06));
- border: 1px solid rgba(251, 191, 36, 0.12);
- border-radius: 14px;
- padding: 22px;
- text-align: center;
- margin-bottom: 12px;
- }
- .upgrade-text {
- display: block;
- font-size: 16px;
- font-weight: 600;
- color: #fbbf24;
- }
- .upgrade-desc {
- display: block;
- font-size: 13px;
- color: rgba(255, 255, 255, 0.45);
- margin: 8px 0 16px;
- }
- .upgrade-btn {
- background: linear-gradient(135deg, #f59e0b, #d97706);
- color: #fff;
- border: none;
- border-radius: 10px;
- padding: 10px 36px;
- font-size: 15px;
- font-weight: 500;
- }
- </style>
|