From 6d04ddc3a381a3695ee9f1472f082f53d0aa2435 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Thu, 27 Feb 2025 22:40:57 +0000 Subject: [PATCH] scanner: add chat page --- scanner/app.json | 3 +- .../components/contentstream/contentstream.js | 80 +++++++++++++++ .../contentstream/contentstream.json | 6 ++ .../contentstream/contentstream.wxml | 13 +++ .../contentstream/contentstream.wxss | 31 ++++++ .../components/scanguidecss/scanguidecss.js | 5 +- .../components/scanguidecss/scanguidecss.wxml | 4 +- .../components/scanguidecss/scanguidecss.wxss | 3 + scanner/pages/chat/chat.js | 92 ++++++++++++++++++ scanner/pages/chat/chat.json | 6 ++ scanner/pages/chat/chat.wxml | 13 +++ scanner/pages/chat/chat.wxss | 45 +++++++++ scanner/project.private.config.json | 7 ++ scanner/static/up-button.png | Bin 0 -> 18773 bytes 14 files changed, 304 insertions(+), 4 deletions(-) create mode 100644 scanner/components/contentstream/contentstream.js create mode 100644 scanner/components/contentstream/contentstream.json create mode 100644 scanner/components/contentstream/contentstream.wxml create mode 100644 scanner/components/contentstream/contentstream.wxss create mode 100644 scanner/pages/chat/chat.js create mode 100644 scanner/pages/chat/chat.json create mode 100644 scanner/pages/chat/chat.wxml create mode 100644 scanner/pages/chat/chat.wxss create mode 100644 scanner/static/up-button.png diff --git a/scanner/app.json b/scanner/app.json index f4592b1..a16941b 100644 --- a/scanner/app.json +++ b/scanner/app.json @@ -9,7 +9,8 @@ "pages/productinfo/productinfo", "pages/test/test", "pages/article/article", - "pages/nav/nav" + "pages/nav/nav", + "pages/chat/chat" ], "window": { "backgroundTextStyle": "light", diff --git a/scanner/components/contentstream/contentstream.js b/scanner/components/contentstream/contentstream.js new file mode 100644 index 0000000..f797d86 --- /dev/null +++ b/scanner/components/contentstream/contentstream.js @@ -0,0 +1,80 @@ +// components/contentstream/contentstream.js +Component({ + + /** + * Component properties + */ + properties: { + content: { + type: Array, + value: [] + } + }, + + /** + * Component initial data + */ + data: { + visible_content: [], + next_index: 0, + next_offset: 0, + }, + + /** + * Component methods + */ + methods: { + update_visible_content() { + if (this.data.next_index >= this.properties.content.length) { + clearInterval(this.update_interval); + return; + } + var next_entry = this.properties.content[this.data.next_index]; + if (next_entry.type != 'text') { + // just add the next entry to the visible content + this.setData({ + visible_content: this.data.visible_content.concat(next_entry), + next_index: this.data.next_index + 1, + next_offset: 0 + }); + return; + } + if (this.data.next_offset >= next_entry.content.length) { + // end of current entry, move to the next entry + this.setData({ + next_index: this.data.next_index + 1, + next_offset: 0, + }) + return; + } + // here we have some remaining text to add to the last visible content + if (this.data.next_offset == 0) { + this.setData({ + visible_content: this.data.visible_content.concat({ + type: 'text', + content: "", + }) + }); + } + var last_visible_entry = this.data.visible_content[this.data.visible_content.length - 1]; + var new_text = next_entry.content.slice(this.data.next_offset, this.data.next_offset + 1); + last_visible_entry.content = last_visible_entry.content + new_text; + this.setData({ + visible_content: this.data.visible_content.slice(0, this.data.visible_content.length - 1).concat(last_visible_entry), + next_offset: this.data.next_offset + 1, + }); + } + + }, + ready() { + console.log('onShow', this.data.next_index, this.data.next_offset); + setTimeout(() => { + this.update_interval = setInterval(() => { + this.update_visible_content(); + }, 50); + }, 500); + }, + detached() { + clearInterval(this.update_interval); + } +}) \ No newline at end of file diff --git a/scanner/components/contentstream/contentstream.json b/scanner/components/contentstream/contentstream.json new file mode 100644 index 0000000..cebc0b6 --- /dev/null +++ b/scanner/components/contentstream/contentstream.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "scanguidecss": "/components/scanguidecss/scanguidecss" + } +} \ No newline at end of file diff --git a/scanner/components/contentstream/contentstream.wxml b/scanner/components/contentstream/contentstream.wxml new file mode 100644 index 0000000..22b77a2 --- /dev/null +++ b/scanner/components/contentstream/contentstream.wxml @@ -0,0 +1,13 @@ + + + + + + + {{ item.content }} + + + + + + \ No newline at end of file diff --git a/scanner/components/contentstream/contentstream.wxss b/scanner/components/contentstream/contentstream.wxss new file mode 100644 index 0000000..1698589 --- /dev/null +++ b/scanner/components/contentstream/contentstream.wxss @@ -0,0 +1,31 @@ + +.item { + display: flex; + margin: 30rpx; +} + +.item .icon { + display: inline-block; + margin-right: 30rpx; +} + +.icon image { + width: 80rpx; + height: 80rpx; +} + +.item .content { + display: inline-block; + min-height: 80rpx; +} + +.content text { + display: block; + margin-bottom: 40rpx; + font-size: 35rpx; + margin-right: 20rpx; +} + +.content view.media { + margin-bottom: 40rpx; +} \ No newline at end of file diff --git a/scanner/components/scanguidecss/scanguidecss.js b/scanner/components/scanguidecss/scanguidecss.js index 6ad2e47..12cf6c7 100644 --- a/scanner/components/scanguidecss/scanguidecss.js +++ b/scanner/components/scanguidecss/scanguidecss.js @@ -5,7 +5,10 @@ Component({ * Component properties */ properties: { - + hide_title: { + type: Boolean, + value: false + } }, /** diff --git a/scanner/components/scanguidecss/scanguidecss.wxml b/scanner/components/scanguidecss/scanguidecss.wxml index af54f53..946d61a 100644 --- a/scanner/components/scanguidecss/scanguidecss.wxml +++ b/scanner/components/scanguidecss/scanguidecss.wxml @@ -1,5 +1,5 @@ - + 移近一点 @@ -13,7 +13,7 @@ 采集完成 - + diff --git a/scanner/components/scanguidecss/scanguidecss.wxss b/scanner/components/scanguidecss/scanguidecss.wxss index 6af64c1..0fd0021 100644 --- a/scanner/components/scanguidecss/scanguidecss.wxss +++ b/scanner/components/scanguidecss/scanguidecss.wxss @@ -66,6 +66,9 @@ width: 100%; box-sizing: border-box; height: 400rpx; +} + +.scan.with-title { top: 80rpx; } diff --git a/scanner/pages/chat/chat.js b/scanner/pages/chat/chat.js new file mode 100644 index 0000000..1712ebb --- /dev/null +++ b/scanner/pages/chat/chat.js @@ -0,0 +1,92 @@ +// pages/chat/chat.js +Page({ + + /** + * Page initial data + */ + data: { + initial_message: [ + { + type: 'text', + content: '欢迎使用徵象AI,让每一次验证都成为与品牌的深度对话。' + }, + { + type: 'text', + content: '为保障您的权益,本系统采用多模态AI深度鉴真引擎,通过多重校验机制实现智能二维码防伪核验。' + }, + { + type: 'text', + content: '请将手机摄像头对准产品智能二维码,' + }, + { + type: 'scanguidecss', + }, + { + type: 'text', + content: '保持画面完整覆盖定位点(图示区域), 系统将自动触发高精度图像分割算法完成验证。 ' + }, + ] + }, + + /** + * Lifecycle function--Called when page load + */ + onLoad(options) { + + }, + + /** + * Lifecycle function--Called when page is initially rendered + */ + onReady() { + + }, + + /** + * Lifecycle function--Called when page show + */ + onShow() { + + }, + + /** + * Lifecycle function--Called when page hide + */ + onHide() { + + }, + + /** + * Lifecycle function--Called when page unload + */ + onUnload() { + + }, + + /** + * Page event handler function--Called when user drop down + */ + onPullDownRefresh() { + + }, + + /** + * Called when page reach bottom + */ + onReachBottom() { + + }, + + /** + * Called when user click on the top right corner to share + */ + onShareAppMessage() { + + }, + + goto_camera() { + wx.navigateTo({ + url: '/pages/camera/camera', + }) + } +}) \ No newline at end of file diff --git a/scanner/pages/chat/chat.json b/scanner/pages/chat/chat.json new file mode 100644 index 0000000..a197071 --- /dev/null +++ b/scanner/pages/chat/chat.json @@ -0,0 +1,6 @@ +{ + "usingComponents": { + "contentstream": "/components/contentstream/contentstream" + }, + "navigationBarTitleText": "徵象AI" +} \ No newline at end of file diff --git a/scanner/pages/chat/chat.wxml b/scanner/pages/chat/chat.wxml new file mode 100644 index 0000000..27971d2 --- /dev/null +++ b/scanner/pages/chat/chat.wxml @@ -0,0 +1,13 @@ + + + + + + + 开启AI验证 + + + + + + \ No newline at end of file diff --git a/scanner/pages/chat/chat.wxss b/scanner/pages/chat/chat.wxss new file mode 100644 index 0000000..5798a97 --- /dev/null +++ b/scanner/pages/chat/chat.wxss @@ -0,0 +1,45 @@ +.chatlog { + width: 750rpx; + height: calc(100vh - 200rpx); + margin-top: 40rpx; +} + +.chatinput { + position: fixed; + bottom: 0; + width: 750rpx; + height: 200rpx; +} + +.chatinput .inputbox { + display: flex; + margin: 30rpx; + height: 120rpx; + border-radius: 60rpx; + background: #eee; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 0 20rpx; +} + +.chatinput .placeholder { + font-size: 38rpx; + flex: 1; + display: flex; + align-items: center; + padding-left: 30rpx; +} + +.chatinput .icon { + width: 100rpx; + height: 100rpx; + display: flex; + align-items: center; + justify-content: center; +} + +.chatinput .icon .up { + width: 80rpx; + height: 80rpx; +} \ No newline at end of file diff --git a/scanner/project.private.config.json b/scanner/project.private.config.json index 67f8ad4..b14be75 100644 --- a/scanner/project.private.config.json +++ b/scanner/project.private.config.json @@ -2,6 +2,13 @@ "condition": { "miniprogram": { "list": [ + { + "name": "chat", + "pathName": "pages/chat/chat", + "query": "", + "launchMode": "default", + "scene": null + }, { "name": "camera (dev)", "pathName": "pages/camera/camera", diff --git a/scanner/static/up-button.png b/scanner/static/up-button.png new file mode 100644 index 0000000000000000000000000000000000000000..06f078e9ae7b7f65aa7a4f22994e03beba5ba22c GIT binary patch literal 18773 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