Compare commits
2 Commits
c93aac0aa9
...
87890a10ed
| Author | SHA1 | Date | |
|---|---|---|---|
| 87890a10ed | |||
| d6861ab8c4 |
@@ -10,8 +10,8 @@ module.exports = {
|
||||
|
||||
// 生产环境
|
||||
serverUrl: 'https://server.websoft.top/api',
|
||||
// apiUrl: 'http://127.0.0.1:9200/api',
|
||||
apiUrl: 'https://cms-api.websoft.top/api',
|
||||
apiUrl: 'http://127.0.0.1:9200/api',
|
||||
// apiUrl: 'https://cms-api.websoft.top/api',
|
||||
socketUrl: 'wss://server.websoft.top',
|
||||
fileUrl: 'https://file.wsdns.cn',
|
||||
|
||||
|
||||
+178
-83
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="page-toolbar">
|
||||
<view class="page-toolbar-title">AI找房助手</view>
|
||||
</view>
|
||||
<view class="message-scroll">
|
||||
<view class="message-content">
|
||||
<view class="intro-card">
|
||||
@@ -30,22 +33,19 @@
|
||||
></image>
|
||||
<view :class="['message-bubble', message.role === 'user' ? 'bubble-user' : 'bubble-ai']">
|
||||
<view v-if="message.type === 'form'" class="form-message">
|
||||
<view class="form-title">您想要找什么样的房型?</view>
|
||||
<!-- <view class="form-title">{{ message.content || '您好,请填写您的找房需求' }}</view> -->
|
||||
<view class="form-desc">填写楼盘、面积和预算,我来帮您筛选合适的房源。</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">多少平方</text>
|
||||
<input v-model="form.extent" class="form-input" type="text" placeholder="请输入面积" :disabled="loading" />
|
||||
<text class="form-label">楼盘</text>
|
||||
<input v-model="form.building" class="form-input" type="text" placeholder="请输入楼盘名称" :disabled="loading" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">多少楼层</text>
|
||||
<input v-model="form.floor" class="form-input" type="text" placeholder="请输入楼层" :disabled="loading" />
|
||||
<text class="form-label">面积</text>
|
||||
<input v-model="form.area" class="form-input" type="text" placeholder="请输入面积" :disabled="loading" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">多少租金</text>
|
||||
<input v-model="form.rent" class="form-input" type="text" placeholder="请输入租金" :disabled="loading" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">什么地段</text>
|
||||
<input v-model="form.location" class="form-input" type="text" placeholder="请输入地段" :disabled="loading" />
|
||||
<text class="form-label">预算</text>
|
||||
<input v-model="form.budget" class="form-input" type="text" placeholder="请输入预算" :disabled="loading" />
|
||||
</view>
|
||||
<view
|
||||
class="finder-submit"
|
||||
@@ -110,7 +110,12 @@
|
||||
</view>
|
||||
</view>
|
||||
<view v-else-if="message.type === 'text'" class="text-message">
|
||||
<view user-select>{{ message.content }}</view>
|
||||
<mp-html
|
||||
v-if="message.role === 'ai'"
|
||||
:content="message.content"
|
||||
markdown
|
||||
/>
|
||||
<view v-else user-select>{{ message.content }}</view>
|
||||
<!-- <view-->
|
||||
<!-- v-if="message.content"-->
|
||||
<!-- class="copy-btn"-->
|
||||
@@ -182,7 +187,6 @@
|
||||
|
||||
<view class="input-panel">
|
||||
<view class="quick-question-row">
|
||||
<view class="finder-tag" :class="{ active: showFinderForm }" @click="toggleFinderForm">找房</view>
|
||||
<view
|
||||
v-for="item in quickQuestions"
|
||||
:key="item.label"
|
||||
@@ -192,6 +196,14 @@
|
||||
>
|
||||
{{ item.label }}
|
||||
</view>
|
||||
<view
|
||||
class="clear-session-btn clear-session-btn-inline"
|
||||
:class="{ 'clear-session-btn-disabled': loading }"
|
||||
@click="confirmClearSession"
|
||||
>
|
||||
<u-icon name="trash" size="18" color="#667085"></u-icon>
|
||||
<text class="clear-session-text">清空</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="input-row">
|
||||
<input
|
||||
@@ -234,27 +246,25 @@
|
||||
quickQuestions: [
|
||||
{
|
||||
label: '面积和价格',
|
||||
question: '这套房的面积和价格是多少?'
|
||||
question: '介绍一下所有楼盘的面积和价格。'
|
||||
},
|
||||
{
|
||||
label: '物业和水电',
|
||||
question: '这套房的物业费和水电怎么收费?'
|
||||
question: '介绍一下所有楼盘的物业费和水电收费情况。'
|
||||
},
|
||||
{
|
||||
label: '空调和停车',
|
||||
question: '这套房有空调和停车位吗?'
|
||||
question: '介绍一下所有楼盘是否配备空调和停车位。'
|
||||
}
|
||||
],
|
||||
inputText: '',
|
||||
loading: false,
|
||||
progressText: '',
|
||||
messages: [],
|
||||
showFinderForm: false,
|
||||
form: {
|
||||
extent: '',
|
||||
floor: '',
|
||||
rent: '',
|
||||
location: ''
|
||||
building: '',
|
||||
area: '',
|
||||
budget: ''
|
||||
},
|
||||
messageFormOpened: false,
|
||||
messageSubmitting: false,
|
||||
@@ -274,7 +284,8 @@
|
||||
socketConnected: false,
|
||||
heartbeatTimer: null,
|
||||
loginUserId: null,
|
||||
nextMessageId: 0
|
||||
nextMessageId: 0,
|
||||
sessionRestored: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -282,7 +293,7 @@
|
||||
if (!this.aiConfigLoaded) {
|
||||
return '正在加载AI助手配置...'
|
||||
}
|
||||
return this.aiConfig.welcomeMessage || '告诉我面积、楼层、预算和地段,我来帮您筛选更合适的房源。'
|
||||
return this.aiConfig.welcomeMessage || '填写楼盘、面积和预算,我来帮您筛选更合适的房源。'
|
||||
},
|
||||
resolvedAiAvatar() {
|
||||
return this.aiConfig.aiAvatar || 'https://oss.wsdns.cn/20260601/0d53634419a448919c90c99ab9779d8a.png?x-oss-process=image/resize,w_750/quality,Q_90'
|
||||
@@ -302,44 +313,97 @@
|
||||
}, 300)
|
||||
return
|
||||
}
|
||||
this.conversationId = `house-ai-${this.loginUserId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||
this.conversationId = this.createConversationId()
|
||||
this.sessionRestored = this.restoreSession()
|
||||
this.connectWebSocket()
|
||||
this.loadAiConfig()
|
||||
},
|
||||
onUnload() {
|
||||
if (this.conversationId) {
|
||||
HouseAiChatApi.clearHouseAiSession({
|
||||
conversationId: this.conversationId
|
||||
}).catch(() => {})
|
||||
}
|
||||
this.persistSession()
|
||||
this.closeWebSocket()
|
||||
},
|
||||
methods: {
|
||||
getSessionStorageKey(userId = this.loginUserId) {
|
||||
return userId ? `house-ai-session-${userId}` : ''
|
||||
},
|
||||
createConversationId() {
|
||||
return `house-ai-${this.loginUserId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||
},
|
||||
restoreSession() {
|
||||
const storageKey = this.getSessionStorageKey()
|
||||
if (!storageKey || !storage || typeof storage.get !== 'function') {
|
||||
return false
|
||||
}
|
||||
const savedSession = storage.get(storageKey)
|
||||
if (!savedSession || typeof savedSession !== 'object' || !savedSession.conversationId) {
|
||||
return false
|
||||
}
|
||||
this.conversationId = savedSession.conversationId
|
||||
this.messages = Array.isArray(savedSession.messages) ? savedSession.messages : []
|
||||
this.messageSubmitted = Boolean(savedSession.messageSubmitted)
|
||||
this.nextMessageId = this.messages.reduce((nextId, message) => {
|
||||
const match = String(message?.id || '').match(/^msg-(\d+)$/)
|
||||
return match ? Math.max(nextId, Number(match[1]) + 1) : nextId
|
||||
}, 0)
|
||||
return true
|
||||
},
|
||||
persistSession() {
|
||||
const storageKey = this.getSessionStorageKey()
|
||||
if (!storageKey || !this.conversationId || !storage || typeof storage.set !== 'function') {
|
||||
return
|
||||
}
|
||||
storage.set(storageKey, {
|
||||
conversationId: this.conversationId,
|
||||
messages: JSON.parse(JSON.stringify(this.messages)),
|
||||
messageSubmitted: this.messageSubmitted
|
||||
})
|
||||
},
|
||||
confirmClearSession() {
|
||||
if (this.loading) {
|
||||
return
|
||||
}
|
||||
uni.showModal({
|
||||
title: '清空会话',
|
||||
content: '确定清空当前聊天记录吗?',
|
||||
confirmText: '清空',
|
||||
cancelText: '取消',
|
||||
success: (result) => {
|
||||
if (result.confirm) {
|
||||
this.clearSession()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
async clearSession() {
|
||||
if (this.loading) {
|
||||
return
|
||||
}
|
||||
const previousConversationId = this.conversationId
|
||||
this.startNewChat()
|
||||
this.conversationId = this.createConversationId()
|
||||
this.appendWelcomeMessage()
|
||||
try {
|
||||
await HouseAiChatApi.clearHouseAiSession({
|
||||
conversationId: previousConversationId
|
||||
})
|
||||
uni.showToast({
|
||||
title: '会话已清空',
|
||||
icon: 'none'
|
||||
})
|
||||
} catch (error) {
|
||||
uni.showToast({
|
||||
title: '本地已清空,服务端同步失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
askQuickQuestion(question) {
|
||||
if (this.loading) {
|
||||
return
|
||||
}
|
||||
this.showFinderForm = false
|
||||
this.inputText = question
|
||||
this.sendMessage()
|
||||
},
|
||||
toggleFinderForm() {
|
||||
if (this.loading) {
|
||||
return
|
||||
}
|
||||
this.showFinderForm = !this.showFinderForm
|
||||
if (this.showFinderForm) {
|
||||
this.messages = [
|
||||
this.createMessage('ai', 'text', {
|
||||
content: this.aiConfig.welcomeMessage || '您好,我是AI找房助手,请告诉我您的找房需求。'
|
||||
}),
|
||||
this.createMessage('ai', 'form')
|
||||
]
|
||||
} else {
|
||||
this.appendWelcomeMessage()
|
||||
}
|
||||
this.scrollToBottom()
|
||||
},
|
||||
submitFinderForm() {
|
||||
if (this.loading) {
|
||||
return
|
||||
@@ -367,32 +431,30 @@
|
||||
startNewChat() {
|
||||
this.loading = false
|
||||
this.progressText = ''
|
||||
this.showFinderForm = false
|
||||
this.messageFormOpened = false
|
||||
this.messageSubmitting = false
|
||||
this.messageSubmitted = false
|
||||
this.inputText = ''
|
||||
this.form = {
|
||||
extent: '',
|
||||
floor: '',
|
||||
rent: '',
|
||||
location: ''
|
||||
building: '',
|
||||
area: '',
|
||||
budget: ''
|
||||
}
|
||||
this.messageForm = {
|
||||
realName: '',
|
||||
phone: '',
|
||||
wechat: ''
|
||||
}
|
||||
this.nextMessageId = 0
|
||||
this.messages = []
|
||||
this.scrollToBottom()
|
||||
},
|
||||
appendWelcomeMessage() {
|
||||
const welcomeMessage = this.aiConfig.welcomeMessage || '您好,我是AI找房助手,请告诉我面积、楼层、预算和地段,我来帮您筛选更合适的房源。'
|
||||
this.messages = [
|
||||
this.createMessage('ai', 'text', {
|
||||
content: welcomeMessage
|
||||
})
|
||||
]
|
||||
const welcomeMessage = this.aiConfig.welcomeMessage || '您好,我是AI找房助手,请填写楼盘、面积和预算,我来帮您筛选更合适的房源。'
|
||||
this.messages = [this.createMessage('ai', 'form', {
|
||||
content: welcomeMessage
|
||||
})]
|
||||
this.persistSession()
|
||||
},
|
||||
async loadAiConfig() {
|
||||
this.aiConfigLoaded = false
|
||||
@@ -410,8 +472,12 @@
|
||||
}
|
||||
}
|
||||
this.aiConfigLoaded = true
|
||||
this.startNewChat()
|
||||
this.appendWelcomeMessage()
|
||||
if (!this.sessionRestored) {
|
||||
this.startNewChat()
|
||||
this.appendWelcomeMessage()
|
||||
} else if (!this.messages.length) {
|
||||
this.appendWelcomeMessage()
|
||||
}
|
||||
this.scrollToBottom()
|
||||
},
|
||||
openMessageForm() {
|
||||
@@ -419,6 +485,7 @@
|
||||
return
|
||||
}
|
||||
this.messageFormOpened = true
|
||||
this.persistSession()
|
||||
this.scrollToBottom()
|
||||
},
|
||||
validateMessageForm() {
|
||||
@@ -466,6 +533,7 @@
|
||||
wechat: (this.messageForm.wechat || '').trim()
|
||||
})
|
||||
this.messageSubmitted = true
|
||||
this.persistSession()
|
||||
uni.showToast({
|
||||
title: '提交成功',
|
||||
icon: 'none'
|
||||
@@ -500,6 +568,7 @@
|
||||
content: question
|
||||
})
|
||||
)
|
||||
this.persistSession()
|
||||
this.inputText = ''
|
||||
this.loading = true
|
||||
this.progressText = '正在分析您的需求'
|
||||
@@ -523,6 +592,7 @@
|
||||
content: error.message || 'AI服务暂时不可用,请稍后再试。'
|
||||
})
|
||||
)
|
||||
this.persistSession()
|
||||
this.scrollToBottom()
|
||||
}
|
||||
},
|
||||
@@ -530,10 +600,10 @@
|
||||
return (this.inputText || '').trim()
|
||||
},
|
||||
buildFormQuestion() {
|
||||
if (!this.form.extent || !this.form.floor || !this.form.rent || !this.form.location) {
|
||||
if (!this.form.building || !this.form.area || !this.form.budget) {
|
||||
return ''
|
||||
}
|
||||
return `帮我找房,想要${this.form.location}的房源,面积在${this.form.extent}左右,楼层要求${this.form.floor},预算租金控制在${this.form.rent}以内`
|
||||
return `帮我找房,想要${this.form.building}的房源,面积在${this.form.area}左右,预算控制在${this.form.budget}以内`
|
||||
},
|
||||
copyText(text) {
|
||||
if (!text) {
|
||||
@@ -641,6 +711,7 @@
|
||||
content: raw
|
||||
})
|
||||
)
|
||||
this.persistSession()
|
||||
this.scrollToBottom()
|
||||
return
|
||||
}
|
||||
@@ -687,6 +758,7 @@
|
||||
if (data.showContactForm) {
|
||||
this.messages.push(this.createMessage('ai', 'messageForm'))
|
||||
}
|
||||
this.persistSession()
|
||||
this.scrollToBottom()
|
||||
},
|
||||
goHouseDetail(item) {
|
||||
@@ -719,6 +791,42 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.page-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 24rpx 0;
|
||||
}
|
||||
|
||||
.page-toolbar-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #344054;
|
||||
}
|
||||
|
||||
.clear-session-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 10rpx 14rpx;
|
||||
border: 1rpx solid #d0d5dd;
|
||||
border-radius: 8rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.clear-session-text {
|
||||
font-size: 24rpx;
|
||||
color: #667085;
|
||||
}
|
||||
|
||||
.clear-session-btn-disabled {
|
||||
opacity: 0.45;
|
||||
}
|
||||
|
||||
.clear-session-btn-inline {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.message-scroll {
|
||||
padding: 24rpx 24rpx 0;
|
||||
box-sizing: border-box;
|
||||
@@ -917,6 +1025,14 @@
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.form-desc {
|
||||
margin-top: -8rpx;
|
||||
margin-bottom: 10rpx;
|
||||
font-size: 25rpx;
|
||||
line-height: 1.6;
|
||||
color: #667085;
|
||||
}
|
||||
|
||||
.form-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -1162,27 +1278,6 @@
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.finder-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12rpx 26rpx;
|
||||
border-radius: 999rpx;
|
||||
background: rgba(79, 124, 255, 0.1);
|
||||
color: #3557d6;
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.finder-tag.active {
|
||||
background: rgba(79, 124, 255, 0.16);
|
||||
color: #274ed8;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
|
||||
const pagePath = path.resolve(__dirname, '../pages/ai-house/index.vue')
|
||||
const source = fs.readFileSync(pagePath, 'utf8')
|
||||
const scriptMatch = source.match(/<script>([\s\S]*?)<\/script>/)
|
||||
|
||||
assert.ok(scriptMatch, '未找到 AI 找房页面脚本')
|
||||
|
||||
const componentScript = scriptMatch[1]
|
||||
.replace(/^\s*import .*$/gm, '')
|
||||
.replace('export default {', 'return {')
|
||||
const component = new Function(
|
||||
'HouseAiConfigApi',
|
||||
'HouseAiChatApi',
|
||||
'storage',
|
||||
'USER_ID',
|
||||
'apiUrl',
|
||||
componentScript
|
||||
)({}, {}, {}, 'userId', 'http://localhost')
|
||||
|
||||
function createPageVm() {
|
||||
const vm = component.data()
|
||||
Object.entries(component.methods).forEach(([name, method]) => {
|
||||
vm[name] = method.bind(vm)
|
||||
})
|
||||
vm.scrollToBottom = () => {}
|
||||
return vm
|
||||
}
|
||||
|
||||
const vm = createPageVm()
|
||||
vm.handleSocketMessage(JSON.stringify({
|
||||
type: 'house_ai_result',
|
||||
answer: '航洋城附近通勤方便,停车以现场政策为准。',
|
||||
locationCards: [{
|
||||
locationId: 101,
|
||||
locationName: '航洋城',
|
||||
knowledgeItems: [{ title: '停车' }]
|
||||
}],
|
||||
houses: [{
|
||||
houseId: 201,
|
||||
houseTitle: '航洋城附近两居室',
|
||||
files: []
|
||||
}]
|
||||
}))
|
||||
|
||||
assert.equal(vm.messages.filter((message) => message.type === 'location').length, 0,
|
||||
'地点资料不应在小程序中创建卡片消息')
|
||||
assert.equal(vm.messages.filter((message) => message.type === 'text').length, 1,
|
||||
'地点资料应由 AI 直接组织为文本回答')
|
||||
assert.equal(vm.messages.find((message) => message.type === 'text').content,
|
||||
'航洋城附近通勤方便,停车以现场政策为准。')
|
||||
assert.equal(vm.messages.filter((message) => message.type === 'list').length, 1,
|
||||
'房源结果仍应创建房源卡片消息')
|
||||
|
||||
console.log('地点资料仅由 AI 组织为文本,页面不渲染地点卡片')
|
||||
@@ -0,0 +1,24 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
|
||||
const root = path.resolve(__dirname, '..')
|
||||
const pagePath = path.join(root, 'pages/ai-house/index.vue')
|
||||
const componentPath = path.join(root, 'uni_modules/mp-html/components/mp-html/mp-html.vue')
|
||||
const markdownPluginPath = path.join(root, 'uni_modules/mp-html/components/mp-html/markdown/index.js')
|
||||
|
||||
const page = fs.readFileSync(pagePath, 'utf8')
|
||||
const component = fs.readFileSync(componentPath, 'utf8')
|
||||
|
||||
assert.match(page, /v-if="message\.role === 'ai'"[\s\S]*:content="message\.content"[\s\S]*markdown/)
|
||||
assert.match(component, /markdown:\s*Boolean/)
|
||||
assert.match(component, /require\('\.\/markdown\/index\.js'\)/)
|
||||
|
||||
const Markdown = require(markdownPluginPath)
|
||||
const html = new Markdown({ markdown: true }).onUpdate('# 标题\n\n**重点**\n\n- 房源 A')
|
||||
|
||||
assert.match(html, /<h1[^>]*>标题<\/h1>/)
|
||||
assert.match(html, /<strong>重点<\/strong>/)
|
||||
assert.match(html, /<li>房源 A<\/li>/)
|
||||
|
||||
console.log('AI 找房 Markdown 渲染配置检查通过')
|
||||
@@ -0,0 +1,24 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
|
||||
const pagePath = path.resolve(__dirname, '../pages/ai-house/index.vue')
|
||||
const page = fs.readFileSync(pagePath, 'utf8')
|
||||
|
||||
for (const label of ['面积和价格', '物业和水电', '空调和停车']) {
|
||||
assert.match(page, new RegExp(`label: '${label}'`))
|
||||
}
|
||||
|
||||
for (const question of [
|
||||
'介绍一下所有楼盘的面积和价格。',
|
||||
'介绍一下所有楼盘的物业费和水电收费情况。',
|
||||
'介绍一下所有楼盘是否配备空调和停车位。'
|
||||
]) {
|
||||
assert.ok(page.includes(`question: '${question}'`))
|
||||
}
|
||||
|
||||
assert.ok(!page.includes('这套房的面积和价格是多少?'))
|
||||
assert.ok(!page.includes('这套房的物业费和水电怎么收费?'))
|
||||
assert.ok(!page.includes('这套房有空调和停车位吗?'))
|
||||
|
||||
console.log('AI 找房全量楼盘快捷提问检查通过')
|
||||
@@ -0,0 +1,98 @@
|
||||
const assert = require('node:assert/strict')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
|
||||
const pagePath = path.resolve(__dirname, '../pages/ai-house/index.vue')
|
||||
const source = fs.readFileSync(pagePath, 'utf8')
|
||||
const scriptMatch = source.match(/<script>([\s\S]*?)<\/script>/)
|
||||
|
||||
assert.ok(scriptMatch, 'AI house page script is missing')
|
||||
|
||||
const savedValues = new Map()
|
||||
const storage = {
|
||||
get(key) {
|
||||
return savedValues.get(key) || false
|
||||
},
|
||||
set(key, value) {
|
||||
savedValues.set(key, value)
|
||||
}
|
||||
}
|
||||
const clearCalls = []
|
||||
const componentScript = scriptMatch[1]
|
||||
.replace(/^\s*import .*$/gm, '')
|
||||
.replace('export default {', 'return {')
|
||||
const component = new Function(
|
||||
'HouseAiConfigApi',
|
||||
'HouseAiChatApi',
|
||||
'storage',
|
||||
'USER_ID',
|
||||
'apiUrl',
|
||||
componentScript
|
||||
)(
|
||||
{},
|
||||
{
|
||||
clearHouseAiSession(payload) {
|
||||
clearCalls.push(payload)
|
||||
return Promise.resolve()
|
||||
}
|
||||
},
|
||||
storage,
|
||||
'userId',
|
||||
'http://localhost'
|
||||
)
|
||||
|
||||
global.uni = {
|
||||
showToast() {}
|
||||
}
|
||||
|
||||
function createPageVm() {
|
||||
const vm = component.data()
|
||||
Object.entries(component.methods).forEach(([name, method]) => {
|
||||
vm[name] = method.bind(vm)
|
||||
})
|
||||
vm.scrollToBottom = () => {}
|
||||
return vm
|
||||
}
|
||||
|
||||
const firstPage = createPageVm()
|
||||
firstPage.loginUserId = 1001
|
||||
firstPage.conversationId = 'house-ai-1001-existing'
|
||||
firstPage.messages = [firstPage.createMessage('user', 'text', { content: 'Need a two-bedroom home' })]
|
||||
firstPage.messageSubmitted = true
|
||||
firstPage.form = {
|
||||
building: '阳光小区',
|
||||
area: '90',
|
||||
budget: '3000'
|
||||
}
|
||||
const finderQuestion = firstPage.buildFormQuestion()
|
||||
assert.ok(finderQuestion.includes('阳光小区'))
|
||||
assert.ok(finderQuestion.includes('90'))
|
||||
assert.ok(finderQuestion.includes('3000'))
|
||||
firstPage.persistSession()
|
||||
|
||||
const restoredPage = createPageVm()
|
||||
restoredPage.loginUserId = 1001
|
||||
assert.equal(restoredPage.restoreSession(), true)
|
||||
assert.equal(restoredPage.conversationId, 'house-ai-1001-existing')
|
||||
assert.equal(restoredPage.messages[0].content, 'Need a two-bedroom home')
|
||||
assert.equal(restoredPage.messageSubmitted, true)
|
||||
assert.equal(restoredPage.createMessage('ai', 'text').id, 'msg-1')
|
||||
|
||||
restoredPage.aiConfig.welcomeMessage = 'Welcome back'
|
||||
restoredPage.clearSession().then(() => {
|
||||
assert.deepEqual(clearCalls, [{ conversationId: 'house-ai-1001-existing' }])
|
||||
assert.notEqual(restoredPage.conversationId, 'house-ai-1001-existing')
|
||||
assert.equal(restoredPage.messages.length, 1)
|
||||
assert.equal(restoredPage.messages[0].type, 'form')
|
||||
assert.equal(restoredPage.messages[0].content, 'Welcome back')
|
||||
assert.equal(restoredPage.messageSubmitted, false)
|
||||
|
||||
const storedSession = storage.get('house-ai-session-1001')
|
||||
assert.equal(storedSession.conversationId, restoredPage.conversationId)
|
||||
assert.equal(storedSession.messages.length, 1)
|
||||
|
||||
console.log('AI house session persistence and clearing checks passed')
|
||||
}).catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @fileoverview markdown 插件
|
||||
* Include marked (https://github.com/markedjs/marked)
|
||||
* Include github-markdown-css (https://github.com/sindresorhus/github-markdown-css)
|
||||
*/
|
||||
const marked = require('./marked.min')
|
||||
let index = 0
|
||||
|
||||
function Markdown (vm) {
|
||||
this.vm = vm
|
||||
vm._ids = {}
|
||||
}
|
||||
|
||||
Markdown.prototype.onUpdate = function (content) {
|
||||
if (this.vm.markdown) {
|
||||
return marked(content)
|
||||
}
|
||||
}
|
||||
|
||||
Markdown.prototype.onParse = function (node, vm) {
|
||||
if (vm.options.markdown) {
|
||||
// 中文 id 需要转换,否则无法跳转
|
||||
if (vm.options.useAnchor && node.attrs && /[\u4e00-\u9fa5]/.test(node.attrs.id)) {
|
||||
const id = 't' + index++
|
||||
this.vm._ids[node.attrs.id] = id
|
||||
node.attrs.id = id
|
||||
}
|
||||
if (node.name === 'p' || node.name === 'table' || node.name === 'tr' || node.name === 'th' || node.name === 'td' || node.name === 'blockquote' || node.name === 'pre' || node.name === 'code') {
|
||||
node.attrs.class = `md-${node.name} ${node.attrs.class || ''}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Markdown
|
||||
File diff suppressed because one or more lines are too long
@@ -39,7 +39,7 @@
|
||||
// #ifndef APP-PLUS-NVUE
|
||||
import node from './node/node'
|
||||
// #endif
|
||||
const plugins=[]
|
||||
const plugins=[require('./markdown/index.js')]
|
||||
const Parser = require('./parser')
|
||||
// #ifdef APP-PLUS-NVUE
|
||||
const dom = weex.requireModule('dom')
|
||||
@@ -55,6 +55,7 @@ export default {
|
||||
}
|
||||
},
|
||||
props: {
|
||||
markdown: Boolean,
|
||||
containerStyle: {
|
||||
type: String,
|
||||
default: ''
|
||||
@@ -152,6 +153,7 @@ export default {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
navigateTo (id, offset) {
|
||||
id = this._ids[decodeURI(id)] || id
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.useAnchor) {
|
||||
reject(Error('Anchor is disabled'))
|
||||
|
||||
Reference in New Issue
Block a user