From d36faac861dbb5f1b2a83fd1cf3be297f22e0924 Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 13:37:42 +0800 Subject: [PATCH 01/23] build: support auto preview --- .github/workflows/preview.yml | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/preview.yml diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..b9979e9 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,38 @@ +name: PREVIEW + +on: + issue_comment: + types: [created] + workflow_run: + types: [completed] + +jobs: + request-preview: + runs-on: ubuntu-latest + if: github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '预览' + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '14.6.0' + - run: npm install & npm run build + shell: bash + - name: get preview qrcode + id: preview + uses: LeeJim/setup-miniprogram@main + with: + project_type: miniProgram + action_type: preview + project_path: ./_example + version: ${{ github.ref_name }} + es6: true + es7: true + minify: true + env: + MINI_APP_ID: ${{ secrets.APP_ID }} + MINI_APP_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + - name: comment-with-qrcode + uses: actions-cool/maintain-one-comment@v2.0.0 + with: + token: ${{ secrets.PAT }} + body: qrcode From 6ffa6f6aa7dc04e5f728d6cb449cc5f1280eba28 Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 13:41:12 +0800 Subject: [PATCH 02/23] build: remove workflow_run --- .github/workflows/preview.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index b9979e9..093010d 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -3,8 +3,6 @@ name: PREVIEW on: issue_comment: types: [created] - workflow_run: - types: [completed] jobs: request-preview: From 419b62484fec6023f990eda7037368ec899be395 Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 13:53:16 +0800 Subject: [PATCH 03/23] build: update preview config --- .github/workflows/preview.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 093010d..6739f36 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/setup-node@v2 with: node-version: '14.6.0' - - run: npm install & npm run build + - run: cd miniprogram & npm install shell: bash - name: get preview qrcode id: preview @@ -21,7 +21,8 @@ jobs: with: project_type: miniProgram action_type: preview - project_path: ./_example + need-pack-npm: true + project_path: ./miniprogram version: ${{ github.ref_name }} es6: true es7: true From b3ee162585afa595042c5e8082a6432853e4cb2d Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 13:57:29 +0800 Subject: [PATCH 04/23] build: update preview config --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 6739f36..3414348 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -22,7 +22,7 @@ jobs: project_type: miniProgram action_type: preview need-pack-npm: true - project_path: ./miniprogram + project_path: ./ version: ${{ github.ref_name }} es6: true es7: true From a0d288f644e9f21e6fc201a8e580d000cddf3617 Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 15:39:58 +0800 Subject: [PATCH 05/23] build: update preview config --- .github/workflows/preview.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 3414348..2491a08 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/setup-node@v2 with: node-version: '14.6.0' - - run: cd miniprogram & npm install + - run: cd miniprogram && npm install shell: bash - name: get preview qrcode id: preview @@ -22,7 +22,7 @@ jobs: project_type: miniProgram action_type: preview need-pack-npm: true - project_path: ./ + project_path: ./miniprogram version: ${{ github.ref_name }} es6: true es7: true From 09882ecbdafe4f09f10fc33e0fa97e91dcdea7e1 Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 20:01:25 +0800 Subject: [PATCH 06/23] build: change action --- .github/workflows/preview.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 2491a08..56702c4 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -30,8 +30,8 @@ jobs: env: MINI_APP_ID: ${{ secrets.APP_ID }} MINI_APP_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} - - name: comment-with-qrcode - uses: actions-cool/maintain-one-comment@v2.0.0 + - name: Comment PR + uses: thollander/actions-comment-pull-request@v1 with: - token: ${{ secrets.PAT }} - body: qrcode + message: | + qrcode From a8ce5843425d6414f2995a5defedfcd2824323af Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 20:30:44 +0800 Subject: [PATCH 07/23] build: update action --- .github/workflows/preview.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 56702c4..3affcc1 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -34,4 +34,5 @@ jobs: uses: thollander/actions-comment-pull-request@v1 with: message: | - qrcode + qrcode + --- 使用微信扫一扫 --- From 91c4d4060ddd133814aaa766842c870696d7fa4c Mon Sep 17 00:00:00 2001 From: Peter Qiu <273743552@qq.com> Date: Sun, 4 Dec 2022 22:30:09 +0800 Subject: [PATCH 08/23] build: using actino upload qrcode --- .github/workflows/preview.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 3affcc1..9f199d1 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -30,9 +30,17 @@ jobs: env: MINI_APP_ID: ${{ secrets.APP_ID }} MINI_APP_PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + - name: Upload qrcode to Tencent COS + uses: LeeJim/tencent-cos-action@main + id: cos + with: + secretId: ${{ secrets.TENCENT_COS_SECRET_ID }} + secretKey: ${{ secrets.TENCENT_COS_SECRET_KEY }} + bucket: mp-qrcode-1255404841 + region: ap-guangzhou + content: ${{ steps.preview.outputs.preview-qrcode }} - name: Comment PR uses: thollander/actions-comment-pull-request@v1 with: message: | - qrcode - --- 使用微信扫一扫 --- + qrcode From fe3824a09f29cf45a36fa0c6f5d29e8845b6216a Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Mon, 5 Dec 2022 15:25:27 +0800 Subject: [PATCH 09/23] build: add comment before preview --- .github/workflows/preview.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 9f199d1..d1db7d2 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -9,6 +9,16 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'issue_comment' && github.event.issue.pull_request && github.event.comment.body == '预览' steps: + - run: | + timestamp=$(date +%s) + echo "timestamp=${timestamp}" >> $GITHUB_OUTPUT + id: time + - name: Previous Comment + uses: thollander/actions-comment-pull-request@v1 + with: + message: | + 正在构建预览的二维码,请稍等... + - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: @@ -44,3 +54,4 @@ jobs: with: message: | qrcode + comment_tag: \ No newline at end of file From 2ba32c499365b5d79fc7934819a8befd9a1f78e5 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Mon, 5 Dec 2022 15:30:31 +0800 Subject: [PATCH 10/23] build: add comment before preview --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index d1db7d2..72dbef4 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -54,4 +54,4 @@ jobs: with: message: | qrcode - comment_tag: \ No newline at end of file + comment_tag: \ No newline at end of file From 914db95ed3a6acfb7d3b351c9740d890ca7e1435 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Wed, 7 Dec 2022 10:28:42 +0800 Subject: [PATCH 11/23] Update preview.yml --- .github/workflows/preview.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 72dbef4..fd8ca13 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -13,6 +13,12 @@ jobs: timestamp=$(date +%s) echo "timestamp=${timestamp}" >> $GITHUB_OUTPUT id: time + - name: echo event + run: | + echo ${{ github.event.pull_request.head_ref }} + echo ${{ github.event.pull_request.head.sha }} + echo ${{ github.event.pull_request.base_ref }} + echo ${{ github.event.pull_request }} - name: Previous Comment uses: thollander/actions-comment-pull-request@v1 with: @@ -54,4 +60,4 @@ jobs: with: message: | qrcode - comment_tag: \ No newline at end of file + comment_tag: From 3594e9a112afcc83f54c5d7d211e01a411a8f5f9 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Wed, 7 Dec 2022 10:33:25 +0800 Subject: [PATCH 12/23] build: update preview ci --- .github/workflows/preview.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index fd8ca13..4e6a6ff 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -15,16 +15,18 @@ jobs: id: time - name: echo event run: | - echo ${{ github.event.pull_request.head_ref }} - echo ${{ github.event.pull_request.head.sha }} - echo ${{ github.event.pull_request.base_ref }} - echo ${{ github.event.pull_request }} + headref=${{ github.event.pull_request.head_ref }} + headsha=${{ github.event.pull_request.head.sha }} + baseref=${{ github.event.pull_request.base_ref }} + echo headref + echo headsha + echo baseref - name: Previous Comment - uses: thollander/actions-comment-pull-request@v1 + uses: thollander/actions-comment-pull-request@v2 with: message: | 正在构建预览的二维码,请稍等... - + comment_tag: ${{steps.time.outputs.timestamp}} - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: @@ -56,8 +58,8 @@ jobs: region: ap-guangzhou content: ${{ steps.preview.outputs.preview-qrcode }} - name: Comment PR - uses: thollander/actions-comment-pull-request@v1 + uses: thollander/actions-comment-pull-request@v2 with: message: | qrcode - comment_tag: + comment_tag: ${{steps.time.outputs.timestamp}} From 83c1ad7a194d8a80b02899a81b754992418043b0 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Wed, 7 Dec 2022 10:36:04 +0800 Subject: [PATCH 13/23] build: add test --- .github/workflows/preview.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 4e6a6ff..a0be985 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -15,12 +15,12 @@ jobs: id: time - name: echo event run: | - headref=${{ github.event.pull_request.head_ref }} - headsha=${{ github.event.pull_request.head.sha }} - baseref=${{ github.event.pull_request.base_ref }} - echo headref - echo headsha - echo baseref + export headref=${{github.event.pull_request.head_ref}} + export headsha=${{github.event.pull_request.head.sha}} + export baseref=${{github.event.pull_request.base_ref}} + echo $headref + echo $headsha + echo $baseref - name: Previous Comment uses: thollander/actions-comment-pull-request@v2 with: From 7f304a962e65cace6651f72cfd5e750fc9ad89e6 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Wed, 7 Dec 2022 10:45:12 +0800 Subject: [PATCH 14/23] build: add test --- .github/workflows/preview.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index a0be985..a1b22a4 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -15,12 +15,13 @@ jobs: id: time - name: echo event run: | - export headref=${{github.event.pull_request.head_ref}} - export headsha=${{github.event.pull_request.head.sha}} - export baseref=${{github.event.pull_request.base_ref}} echo $headref echo $headsha echo $baseref + env: + headref: ${{github.event.pull_request.head_ref}} + headsha: ${{github.event.pull_request.head.sha}} + baseref: ${{github.event.pull_request.base_ref}} - name: Previous Comment uses: thollander/actions-comment-pull-request@v2 with: From ba036d3432400e15076b239518a0f643190396d3 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Mon, 23 Jan 2023 23:53:37 +0800 Subject: [PATCH 15/23] feat: update tdesign version --- miniprogram/app.js | 14 +++-- miniprogram/app.json | 7 ++- miniprogram/custom-tab-bar/index.json | 2 +- miniprogram/package.json | 2 +- miniprogram/pages/detail/index.js | 73 +++++++++------------------ miniprogram/pages/detail/index.less | 12 +++-- miniprogram/pages/detail/index.wxml | 36 ++++++------- miniprogram/pages/index/index.json | 2 + miniprogram/pages/index/index.less | 29 ++++------- miniprogram/pages/index/index.wxml | 18 +++---- miniprogram/pages/my/index.js | 18 ++++++- miniprogram/utils/request.js | 31 ++++++++++++ 12 files changed, 131 insertions(+), 113 deletions(-) create mode 100644 miniprogram/utils/request.js diff --git a/miniprogram/app.js b/miniprogram/app.js index 2e232ba..4552504 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -1,11 +1,19 @@ +import { post } from './utils/request'; App({ onLaunch() { - wx.cloud.init({ - env: "" - }); const updateManager = wx.getUpdateManager() + wx.login({ + success: ({ code }) => { + post('miniprogram/login', { code }).then(data => { + wx.setStorageSync('token', data.key) + }).catch(err => { + console.error(err); + }) + }, + }) + updateManager.onCheckForUpdate(function (res) { // console.log(res.hasUpdate) }) diff --git a/miniprogram/app.json b/miniprogram/app.json index 0f308c7..139f7d0 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -41,7 +41,6 @@ "usingComponents": { "list": "./components/pull-down-list/index", "t-button": "tdesign-miniprogram/button/button", - "t-button-group": "tdesign-miniprogram/button-group/button-group", "t-cell": "tdesign-miniprogram/cell/cell", "t-count-down": "tdesign-miniprogram/count-down/count-down", "t-dialog": "tdesign-miniprogram/dialog/dialog", @@ -50,9 +49,9 @@ "t-toast": "tdesign-miniprogram/toast/toast", "t-tag": "tdesign-miniprogram/tag/tag", "t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar", - "t-tab-bar-item": "tdesign-miniprogram/tab-bar/tab-bar-item", + "t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item", "t-tabs": "tdesign-miniprogram/tabs/tabs", - "t-tab-panel": "tdesign-miniprogram/tabs/tab-panel", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", "t-loading": "tdesign-miniprogram/loading/loading", "t-message": "tdesign-miniprogram/message/message", "t-navbar": "tdesign-miniprogram/navbar/navbar", @@ -61,6 +60,6 @@ "t-popup": "tdesign-miniprogram/popup/popup", "t-image": "tdesign-miniprogram/image/image", "t-steps": "tdesign-miniprogram/steps/steps", - "t-step": "tdesign-miniprogram/steps/step-item" + "t-step": "tdesign-miniprogram/step-item/step-item" } } \ No newline at end of file diff --git a/miniprogram/custom-tab-bar/index.json b/miniprogram/custom-tab-bar/index.json index df5bd1d..31f8169 100644 --- a/miniprogram/custom-tab-bar/index.json +++ b/miniprogram/custom-tab-bar/index.json @@ -2,6 +2,6 @@ "component": true, "usingComponents": { "t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar", - "t-tab-bar-item": "tdesign-miniprogram/tab-bar/tab-bar-item" + "t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item" } } \ No newline at end of file diff --git a/miniprogram/package.json b/miniprogram/package.json index b132117..e8eff26 100644 --- a/miniprogram/package.json +++ b/miniprogram/package.json @@ -14,6 +14,6 @@ "author": "leejim", "license": "MIT", "dependencies": { - "tdesign-miniprogram": "^0.9.0" + "tdesign-miniprogram": "^1.0.0-rc.1" } } diff --git a/miniprogram/pages/detail/index.js b/miniprogram/pages/detail/index.js index b6079a2..35f2bd7 100644 --- a/miniprogram/pages/detail/index.js +++ b/miniprogram/pages/detail/index.js @@ -2,6 +2,7 @@ import infos from '../../data' import tips from '../learn/data' import Toast from 'tdesign-miniprogram/toast/index'; import Message from 'tdesign-miniprogram/message/index'; +import { post } from '../../utils/request'; Page({ data: { @@ -49,36 +50,31 @@ Page({ this.setData({ done: false }) try { - const { result } = await wx.cloud.callFunction({ - name: 'getCookbook', - data: { - id - } - }) - if (result.errno == 0) { - const { likeds, liked, starreds, starred } = result.data - this.setData({ done: true }) - this.setData({ - liked, - likeds, - starreds, - starred - }) - } - } catch(e) { - console.log(e); - } finally { - this.setData({ done: true }) + const [starCount, likeCount, star, like] = await Promise.all([ + post('action/get/all', { id, type: 'star'}), + post('action/get/all', { id, type: 'like'}), + post('action/get', { id, type: 'star'}), + post('action/get', { id, type: 'like' }) + ]) + this.setData({ + done: true, + likeCount, + starCount, + star, + like + }); + } catch(err) { + console.log(err); } }, updateViews() { const { id } = this.data; - wx.cloud.callFunction({ - name: 'updateViews', - data: { id }, - type: 'cookbook' - }) + // wx.cloud.callFunction({ + // name: 'updateViews', + // data: { id }, + // type: 'cookbook' + // }) }, toMyCenter() { @@ -130,31 +126,12 @@ Page({ const { type } = dataset; try { - wx.showLoading({ - title: '加载中', - mask: true - }) - const { result } = await wx.cloud.callFunction({ - name: 'setCookbook', - data: { - id, - type, - } - }) - wx.hideLoading() - if (result.errno == 0) { - const { liked, starred, likeds } = result.data + const data = await post('action/create', { type, id }); - this.setData({ - likeds, - liked, - starred - }) - } - } catch(e) { - console.log(e); - } finally { + this.setData({ [type]: data }) wx.hideLoading() + } catch(err) { + console.log(err); } }, diff --git a/miniprogram/pages/detail/index.less b/miniprogram/pages/detail/index.less index c62bbf4..5a16bd1 100644 --- a/miniprogram/pages/detail/index.less +++ b/miniprogram/pages/detail/index.less @@ -79,11 +79,6 @@ background-color: @primary-color; border: 1px solid @primary-color; } - - &-group { - display: flex; - justify-content: flex-start; - } } .popup { @@ -125,6 +120,8 @@ box-shadow: #ccc 0 0 10px; transform: translateY(100%); transition: all .3s ease; + display: flex; + align-items: center; &__button { display: flex !important; @@ -159,3 +156,8 @@ color: #0052d9; } } + +.button-group { + display: flex; + justify-self: start; +} \ No newline at end of file diff --git a/miniprogram/pages/detail/index.wxml b/miniprogram/pages/detail/index.wxml index 086da72..211ed10 100644 --- a/miniprogram/pages/detail/index.wxml +++ b/miniprogram/pages/detail/index.wxml @@ -14,7 +14,7 @@ - + {{step}} @@ -32,17 +32,15 @@ - - - - 下一步 - - - 重新做一次 - - - 开启倒计时 - + + + 下一步 + + + 重新做一次 + + + 开启倒计时 @@ -84,14 +82,12 @@ - - - {{likeds ? likeds + ' 人' : ''}}喜欢 - - - 收藏 - - + + {{likeCount ? likeCount + ' 人' : ''}}喜欢 + + + 收藏 + diff --git a/miniprogram/pages/index/index.json b/miniprogram/pages/index/index.json index 3f34a1b..b980002 100644 --- a/miniprogram/pages/index/index.json +++ b/miniprogram/pages/index/index.json @@ -3,5 +3,7 @@ "navigationStyle": "custom", "backgroundColor": "#fff", "usingComponents": { + "t-side-bar": "tdesign-miniprogram/side-bar/side-bar", + "t-side-bar-item": "tdesign-miniprogram/side-bar-item/side-bar-item" } } \ No newline at end of file diff --git a/miniprogram/pages/index/index.less b/miniprogram/pages/index/index.less index 9fcf8f2..5743bfe 100644 --- a/miniprogram/pages/index/index.less +++ b/miniprogram/pages/index/index.less @@ -18,27 +18,12 @@ page { font-size: 28rpx; } -.header { - // background-color: #fff; - padding: 24rpx 32rpx 0; +.side-bar { + --td-side-bar-width: 256rpx; } -.search { - background: rgba(221, 221, 221, .7) !important; -} - -.container { - margin-top: 32rpx; - padding: 0 32rpx; -} - -.tabs { - height: 100%; -} - -.t-tabs__scroll { - min-height: 100vh; - background-color: #f3f3f3; +.side-bar-item { + justify-content: flex-start !important; } .card { @@ -57,4 +42,10 @@ page { scroll-view { height: 100%; +} + +.search-box { + --td-search-height: 64rpx; + margin-left: 32rpx; + width: 252px; } \ No newline at end of file diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index 8433c73..5064584 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -1,23 +1,21 @@ - - - + + + - 做饭指南 - - - - - + list="{{item.list}}" /> --> + + + { - const accept = tmplIds.some(key => res[key] === 'accept') + const accept = tmplIds.filter(key => res[key] === 'accept'); + wx.request({ + url: 'http://dev.africans.cn/miniprogram/add-subscribe', + method: 'POST', + data: { + templateIds: accept, + token: wx.getStorageSync('token') + }, + success(res) { + console.log(res); + }, + fail(err) { + console.log(err); + } + }) Toast({ context: this, selector: '#t-toast', - message: accept ? '订阅成功' : '你拒绝了订阅', + message: accept.length > 0 ? '订阅成功' : '你拒绝了订阅', }); } }) diff --git a/miniprogram/utils/request.js b/miniprogram/utils/request.js new file mode 100644 index 0000000..28db4fd --- /dev/null +++ b/miniprogram/utils/request.js @@ -0,0 +1,31 @@ +const domain = 'http://dev.africans.cn'; + +export function post(cmd, data = {}) { + const innerData = {}; + + if (cmd != 'miniprogram/login') { + innerData.token = wx.getStorageSync('token'); + } + return new Promise((resolve, reject) => { + wx.request({ + method: 'POST', + url: `${domain}/${cmd}`, + data: { + id: 'wy9HvIHSJfNYikzCR3BZ6R6VwTu4ILykYbnevVNYPBY=', + ...innerData, + ...data + }, + success({ data, statusCode}) { + if (statusCode == 200) { + resolve(data.data); + } else { + reject(data.data) + } + }, + fail(err) { + console.log('request fail: ', err); + reject(err) + } + }) + }) +} \ No newline at end of file From c730fdca933d9fbedb52b688c6a453d0979fceb1 Mon Sep 17 00:00:00 2001 From: leejimqiu Date: Tue, 24 Jan 2023 23:57:49 +0800 Subject: [PATCH 16/23] feat: update index --- miniprogram/pages/index/index.js | 11 ++++++++--- miniprogram/pages/index/index.less | 10 ++++++++++ miniprogram/pages/index/index.wxml | 11 ++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js index c1b3644..d97c7c7 100644 --- a/miniprogram/pages/index/index.js +++ b/miniprogram/pages/index/index.js @@ -10,15 +10,16 @@ Page({ list: [], searchKeyword: '', chineseMap, + categoryIndex: 0, subscribeModalVisible: false, }, onLoad() { const menu = utils.groupBy(infos, 'category'); - const list = Object.entries(menu).filter(([item]) => item !== 'template').map(([catetory, list]) => { + const list = Object.entries(menu).filter(([item]) => item !== 'template').map(([category, list]) => { return { - name: titleMap[catetory], - icon: `/assets/images/${catetory}.png`, + name: titleMap[category], + icon: `/assets/images/${category}.png`, list } }) @@ -40,6 +41,10 @@ Page({ } }, + handleChange(e) { + this.setData({ categoryIndex: e.detail.value }); + }, + handleTap(e) { const { id } = e.detail.item; diff --git a/miniprogram/pages/index/index.less b/miniprogram/pages/index/index.less index 5743bfe..7f972da 100644 --- a/miniprogram/pages/index/index.less +++ b/miniprogram/pages/index/index.less @@ -20,6 +20,7 @@ page { .side-bar { --td-side-bar-width: 256rpx; + height: 100vh; } .side-bar-item { @@ -48,4 +49,13 @@ scroll-view { --td-search-height: 64rpx; margin-left: 32rpx; width: 252px; +} + +.container { + display: flex; + height: calc(100vh - 124px); +} + +.content { + flex: 1; } \ No newline at end of file diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index 5064584..5a45452 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -5,7 +5,7 @@ - + - + - + + + + + + Date: Wed, 25 Jan 2023 19:32:00 +0800 Subject: [PATCH 17/23] feat: update index ui --- miniprogram/pages/index/index.js | 2 +- miniprogram/pages/index/index.less | 8 +++++--- miniprogram/pages/index/index.wxml | 12 ++++++------ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js index d97c7c7..4da39b3 100644 --- a/miniprogram/pages/index/index.js +++ b/miniprogram/pages/index/index.js @@ -46,7 +46,7 @@ Page({ }, handleTap(e) { - const { id } = e.detail.item; + const { id } = e.target.dataset.item; wx.navigateTo({ url: '../detail/index?id=' + id diff --git a/miniprogram/pages/index/index.less b/miniprogram/pages/index/index.less index 7f972da..bcd84fb 100644 --- a/miniprogram/pages/index/index.less +++ b/miniprogram/pages/index/index.less @@ -1,5 +1,6 @@ page { - padding-bottom: 100px; + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); } .brand { @@ -20,7 +21,7 @@ page { .side-bar { --td-side-bar-width: 256rpx; - height: 100vh; + overflow-y: scroll; } .side-bar-item { @@ -53,7 +54,8 @@ scroll-view { .container { display: flex; - height: calc(100vh - 124px); + height: calc(100vh - 362rpx); + box-sizing: border-box; } .content { diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index 5a45452..2450698 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -5,7 +5,7 @@ - + - + - - - + + + - + Date: Thu, 26 Jan 2023 23:52:15 +0800 Subject: [PATCH 18/23] fix: update title --- miniprogram/config/index.js | 22 +++++++++++----------- miniprogram/pages/index/index.wxml | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/miniprogram/config/index.js b/miniprogram/config/index.js index 512553b..8ec24b2 100644 --- a/miniprogram/config/index.js +++ b/miniprogram/config/index.js @@ -10,17 +10,17 @@ export const chineseMap = { } export const titleMap = { - breakfast: '早餐 Breakfast', - condiment: '佐料 Condiment', - dessert: '甜品 Dessert', - drink: '饮品 Drink', - 'home-cooking': '烹饪 Cooking', - 'semi-finished': '速食 FastFood', - 'soup': '汤 Soup', - 'staple': '主食 Staple', - aquatic: '水产 Aquatic Food', - 'vegetable_dish': '素菜 Vegetable', - 'meat_dish': '荤菜 Meat' + breakfast: '早餐', + condiment: '佐料', + dessert: '甜品', + drink: '饮品', + 'home-cooking': '烹饪', + 'semi-finished': '速食', + 'soup': '汤', + 'staple': '主食', + aquatic: '水产', + 'vegetable_dish': '素菜', + 'meat_dish': '荤菜' } export default { diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml index 2450698..05abb39 100644 --- a/miniprogram/pages/index/index.wxml +++ b/miniprogram/pages/index/index.wxml @@ -14,7 +14,7 @@ bind:click="handleTap" list="{{item.list}}" /> --> - + From f1e0cc205ef31e57b492cb01f9037c607782cf45 Mon Sep 17 00:00:00 2001 From: LeeJim Date: Sun, 6 Oct 2024 22:09:29 +0800 Subject: [PATCH 19/23] feat: add stock and barcode --- .editorconfig | 2 + cloudfunctionTemplate/create_code.json | 8 + cloudfunctions/barcode/index.js | 87 +++++++ cloudfunctions/barcode/package.json | 15 ++ cloudfunctions/barcode/utils/create_quota.js | 11 + cloudfunctions/barcode/utils/get_barcode.js | 58 +++++ cloudfunctions/barcode/utils/update_quota.js | 9 + cloudfunctions/quota/index.js | 37 +++ cloudfunctions/quota/package.json | 15 ++ cloudfunctions/reminders/config.json | 14 ++ cloudfunctions/reminders/index.js | 62 +++++ cloudfunctions/reminders/package.json | 15 ++ cloudfunctions/sku/index.js | 48 ++++ cloudfunctions/sku/package.json | 14 ++ cloudfunctions/userinfo/index.js | 56 +++++ cloudfunctions/userinfo/package.json | 14 ++ miniprogram/app.js | 19 +- miniprogram/app.json | 43 ++-- miniprogram/components/standard-info/index.js | 155 ++++++++++++ .../components/standard-info/index.json | 4 + .../components/standard-info/index.less | 35 +++ .../components/standard-info/index.wxml | 32 +++ miniprogram/custom-tab-bar/index.js | 10 +- miniprogram/package.json | 2 +- miniprogram/pages/stock-edit/index.js | 223 ++++++++++++++++++ miniprogram/pages/stock-edit/index.json | 5 + miniprogram/pages/stock-edit/index.less | 34 +++ miniprogram/pages/stock-edit/index.wxml | 25 ++ miniprogram/pages/stock-list/index.js | 67 ++++++ miniprogram/pages/stock-list/index.json | 4 + miniprogram/pages/stock-list/index.less | 78 ++++++ miniprogram/pages/stock-list/index.wxml | 38 +++ miniprogram/pages/stock/index.js | 77 ++++++ miniprogram/pages/stock/index.json | 3 + miniprogram/pages/stock/index.less | 20 ++ miniprogram/pages/stock/index.wxml | 6 + miniprogram/pages/user-info/index.js | 84 +++++++ miniprogram/pages/user-info/index.json | 4 + miniprogram/pages/user-info/index.less | 22 ++ miniprogram/pages/user-info/index.wxml | 8 + miniprogram/utils/scan.js | 11 + project.private.config.json | 21 ++ 42 files changed, 1466 insertions(+), 29 deletions(-) create mode 100644 .editorconfig create mode 100644 cloudfunctionTemplate/create_code.json create mode 100644 cloudfunctions/barcode/index.js create mode 100644 cloudfunctions/barcode/package.json create mode 100644 cloudfunctions/barcode/utils/create_quota.js create mode 100644 cloudfunctions/barcode/utils/get_barcode.js create mode 100644 cloudfunctions/barcode/utils/update_quota.js create mode 100644 cloudfunctions/quota/index.js create mode 100644 cloudfunctions/quota/package.json create mode 100644 cloudfunctions/reminders/config.json create mode 100644 cloudfunctions/reminders/index.js create mode 100644 cloudfunctions/reminders/package.json create mode 100644 cloudfunctions/sku/index.js create mode 100644 cloudfunctions/sku/package.json create mode 100644 cloudfunctions/userinfo/index.js create mode 100644 cloudfunctions/userinfo/package.json create mode 100644 miniprogram/components/standard-info/index.js create mode 100644 miniprogram/components/standard-info/index.json create mode 100644 miniprogram/components/standard-info/index.less create mode 100644 miniprogram/components/standard-info/index.wxml create mode 100644 miniprogram/pages/stock-edit/index.js create mode 100644 miniprogram/pages/stock-edit/index.json create mode 100644 miniprogram/pages/stock-edit/index.less create mode 100644 miniprogram/pages/stock-edit/index.wxml create mode 100644 miniprogram/pages/stock-list/index.js create mode 100644 miniprogram/pages/stock-list/index.json create mode 100644 miniprogram/pages/stock-list/index.less create mode 100644 miniprogram/pages/stock-list/index.wxml create mode 100644 miniprogram/pages/stock/index.js create mode 100644 miniprogram/pages/stock/index.json create mode 100644 miniprogram/pages/stock/index.less create mode 100644 miniprogram/pages/stock/index.wxml create mode 100644 miniprogram/pages/user-info/index.js create mode 100644 miniprogram/pages/user-info/index.json create mode 100644 miniprogram/pages/user-info/index.less create mode 100644 miniprogram/pages/user-info/index.wxml create mode 100644 miniprogram/utils/scan.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1ca109d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/cloudfunctionTemplate/create_code.json b/cloudfunctionTemplate/create_code.json new file mode 100644 index 0000000..22f6e07 --- /dev/null +++ b/cloudfunctionTemplate/create_code.json @@ -0,0 +1,8 @@ +[ + { + "name": "获取barcode", + "value": { + "barcode": "9970101181308" + } + } +] \ No newline at end of file diff --git a/cloudfunctions/barcode/index.js b/cloudfunctions/barcode/index.js new file mode 100644 index 0000000..e99744f --- /dev/null +++ b/cloudfunctions/barcode/index.js @@ -0,0 +1,87 @@ +const cloud = require('wx-server-sdk'); +const { createQuota } = require('./utils/create_quota'); +const { getBarcode } = require('./utils/get_barcode'); +const { updateQuota } = require('./utils/update_quota'); + +cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +const db = cloud.database() + +exports.main = async (event) => { + const { OPENID } = cloud.getWXContext() + const { barcode, action, quotaType = 'barcode' } = event + + if (action === 'get') { + const { list } = await db.collection('barcodes').aggregate() + .lookup({ + from: 'userinfo', + localField: 'creator', + foreignField: 'openid', + as: 'userinfo' + }) + .match({ + barcode + }) + .unwind({ path: '$userinfo', preserveNullAndEmptyArrays: true }) + .end() + + if (list.length > 0) { + return { code: 0, data: list[0] } + } + return { code: 404, data: [] } + } + + if (action === 'fetch') { + const { data: [userinfo] } = await db.collection('userinfo').where({ openid: OPENID }).get(); + if (!userinfo) { + return { code: 403, errmsg: 'userinfo is empty' } + } + const quotaRes = await db.collection('quota').where({ openid: OPENID, type: quotaType }).get() + + if (quotaRes.data.length > 0) { + const [quota] = quotaRes.data; + if (quota.times > 0) { + try { + const data = await getBarcode(db, barcode, OPENID); + await updateQuota(db, OPENID, quotaType); + return { code: 0, data: { ...data, userinfo } }; + } catch(e) { + console.error('save barcode fail: ', e.message); + return { code: 404, errmsg: 'can not get the barcode' } + } + } + return { code: 403, errmsg: 'quota is empty' } + } + + try { + await createQuota(db, OPENID); + const data = await getBarcode(db, barcode, OPENID); + return { code: 0, data: { ...data, userinfo } }; + } catch(e) { + console.error('save barcode fail: ', e.message); + return { code: 500, errmsg: e.message} + } + } + + return { code: 500, errmsg: 'unknown' } + + // if (MsgType == 'event' && Event == 'subscribe_msg_popup_event') { + // let list = [] + // if (Array.isArray(List)) { + // list = List.filter(item => item.SubscribeStatusString == 'accept') + // } else if (List.SubscribeStatusString == 'accept') { + // list = [List] + // } + + // if (list.length > 0) { + // const { result } = await db.collection('subscribe').add({ + // data: { + // status: 1, + // list, + // creator: FromUserName, + // createTime: CreateTime, + // } + // }) + // return result + // } + // } +} diff --git a/cloudfunctions/barcode/package.json b/cloudfunctions/barcode/package.json new file mode 100644 index 0000000..5c8c85e --- /dev/null +++ b/cloudfunctions/barcode/package.json @@ -0,0 +1,15 @@ +{ + "name": "barcode", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.7.7", + "wx-server-sdk": "~3.0.1" + } +} diff --git a/cloudfunctions/barcode/utils/create_quota.js b/cloudfunctions/barcode/utils/create_quota.js new file mode 100644 index 0000000..d1d3b32 --- /dev/null +++ b/cloudfunctions/barcode/utils/create_quota.js @@ -0,0 +1,11 @@ +exports.createQuota = async (db, openid) => { + return await db.collection('quota').add({ + data: { + type: 'barcode', + openid, + times: 2, + create_time: new Date(), + update_time: new Date() + } + }) +} \ No newline at end of file diff --git a/cloudfunctions/barcode/utils/get_barcode.js b/cloudfunctions/barcode/utils/get_barcode.js new file mode 100644 index 0000000..bfdebce --- /dev/null +++ b/cloudfunctions/barcode/utils/get_barcode.js @@ -0,0 +1,58 @@ +const axios = require('axios'); +const cloud = require('wx-server-sdk'); + +const appcode = '8db126b4c57b4edd8ab8b7355cb3db72'; + +exports.getBarcode = async (db, barcode, openid) => { + const res = await axios({ + url: 'https://jisutxmcx.market.alicloudapi.com/barcode2/query', + headers: { + 'Authorization': 'APPCODE ' + appcode + }, + params: { + barcode + } + }); + if (res.status === 200) { + const { data } = res; + + if (data.status == 0) { + const { result } = data; + const body = {}; + + + const params = { ...result, ...body, rawPic: data.result.pic, creator: openid, create_time: new Date() }; + const save = () => db.collection('barcodes').add({ data: params }); + try { + const { _id: docid } = await db.collection('barcodes').add({ data: params }) + if (result.pic) { + await Promise.race([async () => { + const imgRes = await axios.get(data.result.pic, { + responseType: 'stream' + }) + const filenameExtension = data.result.pic.split('.').pop(); + const filename = data.result.barcode + '_' + Date.now() + '.' + filenameExtension; + const { fileID } = await cloud.uploadFile({ + cloudPath: filename, + fileContent: imgRes.data + }) + await db.collection('barcodes').doc(docid).update({ + data: { + pic: fileID + } + }) + }, new Promise((resolve) => setTimeout(resolve, 3000))]) + } + return params; + } catch(e) { + console.error('save qrcode fail', e.message) + return {} + } + } else { + console.error('get qrcodes fail: ', data.status, '-', data.msg); + return {} + } + } + + return {} +} \ No newline at end of file diff --git a/cloudfunctions/barcode/utils/update_quota.js b/cloudfunctions/barcode/utils/update_quota.js new file mode 100644 index 0000000..093c484 --- /dev/null +++ b/cloudfunctions/barcode/utils/update_quota.js @@ -0,0 +1,9 @@ +exports.updateQuota = async (db, openid, type) => { + const _ = db.command; + return db.collection('quota').where({ openid, type }).update({ + data: { + times: _.inc(-1), + update_time: new Date() + } + }); +} \ No newline at end of file diff --git a/cloudfunctions/quota/index.js b/cloudfunctions/quota/index.js new file mode 100644 index 0000000..552b981 --- /dev/null +++ b/cloudfunctions/quota/index.js @@ -0,0 +1,37 @@ +const cloud = require('wx-server-sdk'); + +cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +const db = cloud.database() + +const extraTimes = 5 + +exports.main = async (event) => { + const { OPENID } = cloud.getWXContext() + const { action, type = 'barcode' } = event + const table = db.collection('quota'); + + try { + if (action === 'get') { + const { data } = await table.where({ openid: OPENID, type }).get(); + + if (data.length == 0) { + return { data: 3, code: 0 } + } + const [info] = data; + return { data: info.times, code: 0 } + } + + if (action === 'add') { + await table.where({ openid: OPENID, type }).update({ + data: { + times: db.command.inc(extraTimes) + } + }) + return { code: 0, data: extraTimes } + } + } catch (e) { + console.error(e.message); + return { code: 500, errmsg: 'action is not found' } + } + +}; diff --git a/cloudfunctions/quota/package.json b/cloudfunctions/quota/package.json new file mode 100644 index 0000000..b253bc4 --- /dev/null +++ b/cloudfunctions/quota/package.json @@ -0,0 +1,15 @@ +{ + "name": "quota", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.7.7", + "wx-server-sdk": "~3.0.1" + } +} diff --git a/cloudfunctions/reminders/config.json b/cloudfunctions/reminders/config.json new file mode 100644 index 0000000..1e8b7bc --- /dev/null +++ b/cloudfunctions/reminders/config.json @@ -0,0 +1,14 @@ +{ + "triggers": [ + { + "name": "expiredReminder", + "type": "timer", + "config": "0 0 10 * * * *" + } + ], + "permissions": { + "openapi": [ + "subscribeMessage.send" + ] + } +} \ No newline at end of file diff --git a/cloudfunctions/reminders/index.js b/cloudfunctions/reminders/index.js new file mode 100644 index 0000000..9a7d008 --- /dev/null +++ b/cloudfunctions/reminders/index.js @@ -0,0 +1,62 @@ + +const cloud = require('wx-server-sdk') +const dayjs = require('dayjs') + +// cloud.init({ +// env: cloud.default, +// }) +cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +const db = cloud.database() + +exports.main = async (event, context) => { + const { total } = await db.collection('userinfo').count(); + + let i = 0; + while (i < total) { + const _ = db.command; + const { data: [user] } = await db.collection('userinfo').skip(i++).limit(1).get(); + const { data: skus } = await db.collection('sku').where({ + creator: user.openid, + expiredDate: _.lte(dayjs().add(2, 'day').toDate()) + }).get(); + if (skus.length === 0) { + continue; + } + + const name = skus.length === 1 ? skus[0].name : `${skus.length}个商品` + const minExpiredDate = skus.reduce((min, sku) => + dayjs(sku.expiredDate).isBefore(min) ? dayjs(sku.expiredDate) : min, + dayjs(skus[0].expiredDate) + ).format('YYYY.MM.D') + const days = dayjs(minExpiredDate).diff(dayjs(), 'day') + + try { + const result = await cloud.openapi.subscribeMessage.send({ + "touser": user.openid, + "page": 'pages/stock-list/index', + "lang": 'zh_CN', + "data": { + "thing1": { + "value": name // 物品名称 + }, + "date2": { + "value": minExpiredDate // 到期日期 + }, + "phrase3": { + "value": '食品' // 物品类型 + }, + "number5": { + "value": days + 1 // 剩余天数 + } + }, + "templateId": 'l5caNms6dSAey7__z4JkECg4mTUSro_4aA6tyyRXZxA', + "miniprogramState": 'developer' + }) + console.log(result) + } catch (err) { + console.error(err) + } + console.log(user); + } +} + diff --git a/cloudfunctions/reminders/package.json b/cloudfunctions/reminders/package.json new file mode 100644 index 0000000..518f197 --- /dev/null +++ b/cloudfunctions/reminders/package.json @@ -0,0 +1,15 @@ +{ + "name": "reminders", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "dayjs": "^1.11.13", + "wx-server-sdk": "~3.0.1" + } +} diff --git a/cloudfunctions/sku/index.js b/cloudfunctions/sku/index.js new file mode 100644 index 0000000..e037114 --- /dev/null +++ b/cloudfunctions/sku/index.js @@ -0,0 +1,48 @@ +const cloud = require('wx-server-sdk'); + +cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +const db = cloud.database() + +exports.main = async (event) => { + const { OPENID } = cloud.getWXContext() + const { action, body, page = 0, size = 10 } = event + const table = db.collection('sku'); + const _ = db.command; + + if (action === 'get') { + const { list } = await table.aggregate() + .match({ creator: OPENID, expiredDate: _.gte(new Date()) }) + .sort({ expiredDate: 1 }) + .skip(page * size) + .limit(size) + .lookup({ + from: 'barcodes', + localField: 'barcode', + foreignField: 'barcode', + as: 'standardInfo' + }) + .unwind({ path: '$standardInfo', preserveNullAndEmptyArrays: true }) + .end(); + + return { data: list, code: 0 } + } + + if (action === 'save') { + const { barcode, name, manufactureDate, preserveDate, unit, expiredDate, pic } = body; + const docid = await table.add({ + data: { + barcode, + name, + manufactureDate: new Date(manufactureDate), + preserveDate, + unit, + expiredDate: new Date(expiredDate), + pic, + creator: OPENID, + create_time: new Date(), + update_time: new Date() + } + }) + return { code: 0, errmsg: 0, data: docid } + } +}; diff --git a/cloudfunctions/sku/package.json b/cloudfunctions/sku/package.json new file mode 100644 index 0000000..7049d66 --- /dev/null +++ b/cloudfunctions/sku/package.json @@ -0,0 +1,14 @@ +{ + "name": "sku", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "wx-server-sdk": "~3.0.1" + } +} diff --git a/cloudfunctions/userinfo/index.js b/cloudfunctions/userinfo/index.js new file mode 100644 index 0000000..e76f8e2 --- /dev/null +++ b/cloudfunctions/userinfo/index.js @@ -0,0 +1,56 @@ +const cloud = require('wx-server-sdk'); + +cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +const db = cloud.database() + +exports.main = async (event) => { + const { OPENID } = cloud.getWXContext() + const { action, body } = event + const table = db.collection('userinfo'); + + try { + if (action === 'get') { + const { data: [userinfo] } = await table.where({ openid: OPENID }).get(); + return { + code: 0, + message: '获取成功', + data: userinfo, + } + } + if (action === 'update') { + const { avatarUrl, nickName } = body; + + const { data: [userinfo] } = await table.where({ openid: OPENID }).get(); + if (!userinfo) { + await table.add({ + data: { + avatarUrl, + nickName, + openid: OPENID, + create_time: new Date(), + update_time: new Date(), + } + }) + } else { + await table.where({ openid: OPENID }).update({ + data: { + avatarUrl, + nickName, + update_time: new Date(), + } + }) + } + return { + code: 0, + message: '更新成功', + data: userinfo, + } + } + } + catch (e) { + return { + code: 500, + message: e.message, + } + } +} \ No newline at end of file diff --git a/cloudfunctions/userinfo/package.json b/cloudfunctions/userinfo/package.json new file mode 100644 index 0000000..4fc94a0 --- /dev/null +++ b/cloudfunctions/userinfo/package.json @@ -0,0 +1,14 @@ +{ + "name": "userinfo", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "wx-server-sdk": "~3.0.1" + } +} diff --git a/miniprogram/app.js b/miniprogram/app.js index 4552504..ed9dcfd 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -4,15 +4,16 @@ App({ onLaunch() { const updateManager = wx.getUpdateManager() - wx.login({ - success: ({ code }) => { - post('miniprogram/login', { code }).then(data => { - wx.setStorageSync('token', data.key) - }).catch(err => { - console.error(err); - }) - }, - }) + wx.cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) + // wx.login({ + // success: ({ code }) => { + // post('miniprogram/login', { code }).then(data => { + // wx.setStorageSync('token', data.key) + // }).catch(err => { + // console.error(err); + // }) + // }, + // }) updateManager.onCheckForUpdate(function (res) { // console.log(res.hasUpdate) diff --git a/miniprogram/app.json b/miniprogram/app.json index 139f7d0..e861dad 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -9,26 +9,35 @@ "pages/changelog/detail", "pages/learn/index", "pages/learn/detail", - "pages/search/index" + "pages/search/index", + "pages/stock/index", + "pages/stock-edit/index", + "pages/stock-list/index", + "pages/user-info/index" ], "tabBar": { "custom": true, "color": "#000", "selectedColor": "#000", "backgroundColor": "#fff", - "list": [{ - "pagePath": "pages/index/index", - "text": "首页" - }, { - "pagePath": "pages/learn/index", - "text": "技巧" - }, { - "pagePath": "pages/kitchen/index", - "text": "表情" - }, { - "pagePath": "pages/my/index", - "text": "我的" - }] + "list": [ + { + "pagePath": "pages/index/index", + "text": "首页" + }, + { + "pagePath": "pages/learn/index", + "text": "技巧" + }, + { + "pagePath": "pages/stock/index", + "text": "仓储" + }, + { + "pagePath": "pages/my/index", + "text": "我的" + } + ] }, "window": { "backgroundColor": "#f6f6f6", @@ -42,13 +51,16 @@ "list": "./components/pull-down-list/index", "t-button": "tdesign-miniprogram/button/button", "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", "t-count-down": "tdesign-miniprogram/count-down/count-down", "t-dialog": "tdesign-miniprogram/dialog/dialog", "t-empty": "tdesign-miniprogram/empty/empty", "t-icon": "tdesign-miniprogram/icon/icon", + "t-input": "tdesign-miniprogram/input/input", "t-toast": "tdesign-miniprogram/toast/toast", "t-tag": "tdesign-miniprogram/tag/tag", "t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar", + "t-date-time-picker": "tdesign-miniprogram/date-time-picker/date-time-picker", "t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item", "t-tabs": "tdesign-miniprogram/tabs/tabs", "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", @@ -59,6 +71,9 @@ "t-switch": "tdesign-miniprogram/switch/switch", "t-popup": "tdesign-miniprogram/popup/popup", "t-image": "tdesign-miniprogram/image/image", + "t-notice-bar": "tdesign-miniprogram/notice-bar/notice-bar", + "t-upload": "tdesign-miniprogram/upload/upload", + "t-avatar": "tdesign-miniprogram/avatar/avatar", "t-steps": "tdesign-miniprogram/steps/steps", "t-step": "tdesign-miniprogram/step-item/step-item" } diff --git a/miniprogram/components/standard-info/index.js b/miniprogram/components/standard-info/index.js new file mode 100644 index 0000000..3c99f77 --- /dev/null +++ b/miniprogram/components/standard-info/index.js @@ -0,0 +1,155 @@ +let videoAd = null + +Component({ + properties: { + barcode: String, + }, + + data: { + loading: false, + done: false, + quota: 0, + info: {}, + hasResult: false, + init: true, + noResult: false + }, + + lifetimes: { + attached() { + if (wx.createRewardedVideoAd) { + videoAd = wx.createRewardedVideoAd({ + adUnitId: 'adunit-a013eebf1c0ae3cf' + }) + videoAd.onLoad(() => {}) + videoAd.onError((err) => { + console.error('激励视频光告加载失败', err) + }) + videoAd.onClose((res) => {}) + } + }, + ready() { + wx.cloud.callFunction({ + name: 'barcode', + data: { + action: 'get', + barcode: this.data.barcode + } + }).then(({ result }) => { + if (result.code == 0) { + const { data } = result; + + this.setData({ + hasResult: true, + info: data + }) + this.triggerEvent('dataready', { data }) + } else { + this.setData({ + hasResult: false + }) + this.getQuota(); + } + }).finally(() => { + this.setData({ + init: false + }) + }) + } + }, + + methods: { + getQuota() { + this.setData({ + loading: true + }) + wx.cloud.callFunction({ + name: 'quota', + data: { + action: 'get' + } + }).then(({ result }) => { + console.log('quota res', result); + this.setData({ quota: result.data }) + }).finally(() => { + this.setData({ + done: true, + loading: false + }) + }) + }, + handleClick() { + if (this.data.quota <= 0) { + if (videoAd) { + videoAd.show().catch(() => { + // 失败重试 + videoAd.load() + .then(() => videoAd.show()) + .catch(err => { + console.error('激励视频 广告显示失败', err) + }) + }) + + videoAd.onClose((res) => { + if (res && res.isEnded) { + wx.cloud.callFunction({ + name: 'quota', + data: { + action: 'add' + } + }).then(({ result }) => { + if (result.code === 0) { + wx.showToast({ + title: '额度已更新,快速试试吧', + icon: 'none' + }) + this.setData({ quota: result.data }) + } + }) + } + }) + } + return; + } + wx.showLoading({ + title: '获取中...', + }) + wx.cloud.callFunction({ + name: 'barcode', + data: { + action: 'fetch', + barcode: this.data.barcode, + } + }).then(({ result }) => { + wx.hideLoading(); + if (result.code === 0) { + this.setData({ + hasResult: true, + info: result.data + }) + this.triggerEvent('dataready', { data: result.data }) + } else { + if (result.code === 403) { + wx.showToast({ + icon: "error", + title: '请先完善个人信息', + }) + setTimeout(() => { + wx.navigateTo({ + url: '/pages/user-info/index', + }) + }, 1000) + return; + } + this.setData({ noResult: true}); + wx.showToast({ + icon: "error", + title: '暂无相关信息', + }) + } + }).catch(() => { + wx.hideLoading(); + }) + } + } +}) \ No newline at end of file diff --git a/miniprogram/components/standard-info/index.json b/miniprogram/components/standard-info/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/miniprogram/components/standard-info/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/components/standard-info/index.less b/miniprogram/components/standard-info/index.less new file mode 100644 index 0000000..d561316 --- /dev/null +++ b/miniprogram/components/standard-info/index.less @@ -0,0 +1,35 @@ +.empty-action { + display: flex; + justify-content: center; +} + +.section { + margin: 16px; + padding: 16px; + background: #fff; + border-radius: var(--td-radius-large, 18rpx); +} + +.cell-title { + color: var(--td-font-gray-4, rgba(0, 0, 0, 0.26)); + font-size: 28rpx; + margin-left: 32rpx; + padding-left: var(--td-cell-vertical-padding, 32rpx); +} + +.loading-wrap { + display: flex; + justify-content: center; +} + +.empty-action-btn { + display: flex; + flex-direction: column; + align-items: center; + + .desc { + margin-top: 12px; + font-size: 14px; + color: var(--td-font-gray-4, rgba(0, 0, 0, 0.26)); + } +} \ No newline at end of file diff --git a/miniprogram/components/standard-info/index.wxml b/miniprogram/components/standard-info/index.wxml new file mode 100644 index 0000000..0f1952e --- /dev/null +++ b/miniprogram/components/standard-info/index.wxml @@ -0,0 +1,32 @@ +
+ +
+ + 商品条码信息 + + + + + + + + + + + + + + + + + + +
+ {{ done ? '一键获取(' + quota + '次机会)' : '加载中'}} + + {{ done ? (quota > 0 ? '一键获取(' + quota + '次机会)' : '额度用完,去获取') : '加载中'}} + 去看广告(6-15秒),即可获取 5 次机会 + +
+
+
\ No newline at end of file diff --git a/miniprogram/custom-tab-bar/index.js b/miniprogram/custom-tab-bar/index.js index d38e4c4..c5739c3 100644 --- a/miniprogram/custom-tab-bar/index.js +++ b/miniprogram/custom-tab-bar/index.js @@ -11,9 +11,9 @@ Component({ value: 'learn', label: '发现', },{ - icon: 'logo-android', - value: 'kitchen', - label: '表情', + icon: 'face-retouching', + value: 'stock', + label: '仓储', }, { icon: 'user', value: 'my', @@ -26,9 +26,9 @@ Component({ const curPage = pages[pages.length - 1]; if (curPage) { - const nameRe = /pages\/(\w+)\/index/.exec(curPage.route); + const nameRe = /pages\/([a-zA-Z0-9_-]+)\/index/.exec(curPage.route); - if (nameRe[1]) { + if (nameRe?.[1]) { this.setData({ value: nameRe[1] }) diff --git a/miniprogram/package.json b/miniprogram/package.json index e8eff26..442dbd2 100644 --- a/miniprogram/package.json +++ b/miniprogram/package.json @@ -14,6 +14,6 @@ "author": "leejim", "license": "MIT", "dependencies": { - "tdesign-miniprogram": "^1.0.0-rc.1" + "tdesign-miniprogram": "^1.2.9" } } diff --git a/miniprogram/pages/stock-edit/index.js b/miniprogram/pages/stock-edit/index.js new file mode 100644 index 0000000..e57cfbd --- /dev/null +++ b/miniprogram/pages/stock-edit/index.js @@ -0,0 +1,223 @@ +import dayjs from 'dayjs'; +import Message from 'tdesign-miniprogram/message/index'; + +import { toScan } from '../../utils/scan'; + +Component({ + data: { + barcode: '', + start: dayjs().subtract(3, 'year').format('YYYY-MM-DD'), + end: dayjs().format('YYYY-MM-DD'), + today: dayjs().format('YYYY-MM-DD'), + manufactureDate: '', + preserveDate: '', + unit: 'month', + expiredDate: '', + fileList: [], + loading: false, + name: '', + standardInfo: {}, + }, + + observers: { + 'manufactureDate, unit, preserveDate': function (mDate, unit, pDate) { + if (mDate && unit && pDate) { + const res = new dayjs(mDate).add(pDate, unit) + this.setData({ expiredDate: res.format('YYYY-MM-DD') }) + } else { + this.setData({ expiredDate: '' }) + } + } + }, + + methods: { + onLoad(options) { + this.setData({ barcode: options.barcode }) + }, + + handleDataready(e) { + const { data } = e.detail; + + this.setData({ name: data. name, standardInfo: data }) + }, + + showPicker(e) { + this.setData({ + dateVisible: true, + }); + }, + + onConfirm(e) { + const { value } = e.detail; + console.log('confirm', value); + + this.setData({ + date: value, + manufactureDate: value, + }); + + // this.hidePicker(); + }, + + handlePreserveDateInput(e) { + this.setData({ preserveDate: e.detail.value }) + }, + + handleUnitChange(e) { + console.log(e.detail.value); + this.setData({ unit: e.detail.value }) + }, + + handleImageAdd(e) { + // console.log(e.detail); + const { files } = e.detail; + this.setData({ fileList: files }) + }, + + toUpload(file) { + return new Promise((resolve, reject) => { + wx.cloud.uploadFile({ + cloudPath: this.data.barcode + '_' + Date.now(), // 上传至云端的路径 + filePath: file, // 小程序临时文件路径 + success: res => { + resolve(res.fileID) + }, + fail: reject + }) + }) + }, + + handleBarcodeTap() { + wx.setClipboardData({ + data: this.data.barcode, + success: () => { + wx.showToast({ + title: '已复制', + icon: 'none' + }) + } + }) + }, + + handleNameInput(e) { + this.setData({ name: e.detail.value }) + }, + + toSubscribe() { + return new Promise((resolve, reject) => { + wx.requestSubscribeMessage({ + tmplIds: ['l5caNms6dSAey7__z4JkECg4mTUSro_4aA6tyyRXZxA'], + }).then(({ l5caNms6dSAey7__z4JkECg4mTUSro_4aA6tyyRXZxA: status }) => { + if (status === 'accept') { + resolve() + } else { + reject(new Error('订阅失败')) + } + }).catch(reject) + }) + }, + + async handleSubmit() { + const { barcode, name, expiredDate, fileList, manufactureDate, unit, preserveDate } = this.data; + const isEmpty = (val) => val == null || val === ''; + if (isEmpty(name)) { + wx.showToast({ + title: '名称不能为空', + icon: 'none' + }) + return; + } + + if (isEmpty(manufactureDate)) { + wx.showToast({ + title: '请填写生产时间', + icon: 'none' + }) + return; + } + + if (isEmpty(expiredDate)) { + wx.showToast({ + title: '请填写质保时间', + icon: 'none' + }) + return; + } + + try { + await this.toSubscribe() + } catch (err) { + console.error('subscribe fail: ', err) + + Message.error({ + context: this, + duration: 3000, + offset: [0, 50], + content: '订阅失败,后续将无法收到过期提醒', + }); + await new Promise(resolve => setTimeout(resolve, 2000)) + } + this.setData({ loading: true }) + + const handleSave = (obj) => { + const data = { ...obj, barcode, name, expiredDate, manufactureDate, unit, preserveDate } + + console.log('save', data); + wx.showLoading({ + title: '保存中', + }) + wx.cloud.callFunction({ + name: 'sku', + data: { + action: 'save', + body: data + } + }).then(({ result }) => { + if (result.code == 0) { + wx.showModal({ + title: '保存成功', + content: '继续保存还是前往列表页面', + confirmText: '继续添加', + cancelText: '去列表页', + success({ confirm }) { + if (confirm) { + toScan() + } else { + wx.redirectTo({ + url: '/pages/stock-list/index', + }) + } + }, + fail: console.error + }) + } + }).finally(() => { + wx.hideLoading() + this.setData({ loading: false }) + }) + } + + if (fileList.length > 0) { + wx.showToast({ + title: '正在上传图片...', + icon: 'none' + }) + this.toUpload(fileList[0].url).then((fileId) => { + wx.hideToast() + handleSave({ pic: fileId }) + }).catch((err) => { + console.error('upload fail: ', err) + wx.hideToast() + this.setData({ loading: false }) + Message.error({ + context: this, + duration: 2000, + content: err.message, + }); + }) + } else { + handleSave({}) + } + } + } +}) \ No newline at end of file diff --git a/miniprogram/pages/stock-edit/index.json b/miniprogram/pages/stock-edit/index.json new file mode 100644 index 0000000..7ee1b28 --- /dev/null +++ b/miniprogram/pages/stock-edit/index.json @@ -0,0 +1,5 @@ +{ + "usingComponents": { + "standard-info": "../../components/standard-info" + } +} \ No newline at end of file diff --git a/miniprogram/pages/stock-edit/index.less b/miniprogram/pages/stock-edit/index.less new file mode 100644 index 0000000..7b36055 --- /dev/null +++ b/miniprogram/pages/stock-edit/index.less @@ -0,0 +1,34 @@ +/* pages/stock-edit/index.wxss */ +page { + // --td-switch-checked-color: #00A870; + --td-switch-unchecked-color: #00A870; + --td-switch-label-color: #00A870; + padding-top: 32rpx; + padding-bottom: 32rpx; +} + +.button-wrap { + margin: 32rpx; + padding-bottom: env(safe-area-inset-bottom); + padding-bottom: constant(safe-area-inset-bottom); +} + +.cell-title { + color: var(--td-font-gray-4, rgba(0, 0, 0, 0.26)); + font-size: 28rpx; + margin-left: 32rpx; + padding-left: var(--td-cell-vertical-padding, 32rpx); +} + +.section { + margin: 16px; + padding: 16px; + background: #fff; + border-radius: var(--td-radius-large, 18rpx); +} + +.tips { + font-size: 14px; + margin-top: 12px; + color: #999; +} \ No newline at end of file diff --git a/miniprogram/pages/stock-edit/index.wxml b/miniprogram/pages/stock-edit/index.wxml new file mode 100644 index 0000000..194495e --- /dev/null +++ b/miniprogram/pages/stock-edit/index.wxml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + 可选上传,默认展示条形码标准信息的图片 + + + + 保存 + + + \ No newline at end of file diff --git a/miniprogram/pages/stock-list/index.js b/miniprogram/pages/stock-list/index.js new file mode 100644 index 0000000..e86d0f1 --- /dev/null +++ b/miniprogram/pages/stock-list/index.js @@ -0,0 +1,67 @@ +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; +import updateLocale from 'dayjs/plugin/updateLocale'; +import 'dayjs/locale/zh-cn'; + +dayjs.extend(relativeTime) +dayjs.extend(updateLocale) +dayjs.locale('zh-cn') + +const size = 10; + +Component({ + data: { + data: [], + loading: false, + noMore: false, + page: 0, + }, + + methods: { + onShow() { + this.getList(); + }, + getList() { + this.setData({ loading: true }) + wx.cloud.callFunction({ + name: 'sku', + data: { + action: 'get', + page: this.data.page, + size + } + }).then(({ result }) => { + if (result.code === 0) { + const tmp = result.data.map(item => ({ + ...item, + expiredTxt: dayjs(item.expiredDate).fromNow(), + expiredDate: dayjs(item.expiredDate).format('YYYY.MM.DD') + })) + this.setData({ + data: [...this.data.data, ...tmp], + noMore: result.data.length < size, + page: this.data.page + 1, + }); + } else { + this.setData({ noMore: true }) + } + }).finally(() => { + this.setData({ loading: false }) + }) + }, + handleClick(e) { + const { pic } = e.target.dataset; + + if (pic) { + wx.previewImage({ + urls: [pic], + }) + } + }, + handleScrollToBottom() { + if (!this.data.noMore) { + this.getList(); + } + } + } +}) \ No newline at end of file diff --git a/miniprogram/pages/stock-list/index.json b/miniprogram/pages/stock-list/index.json new file mode 100644 index 0000000..64e416c --- /dev/null +++ b/miniprogram/pages/stock-list/index.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "库存列表", + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/stock-list/index.less b/miniprogram/pages/stock-list/index.less new file mode 100644 index 0000000..0fc89b4 --- /dev/null +++ b/miniprogram/pages/stock-list/index.less @@ -0,0 +1,78 @@ +page { + padding-bottom: 32rpx; +} + +.avatar { + width: 96rpx; + height: 96rpx; + border-radius: 50%; + overflow: hidden; +} + +.wrapper { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + height: 100vh; + overflow: hidden; +} + +.scroll-view { + flex-shrink: 1; + overflow: hidden; +} + +.loading-wrapper { + display: flex; + justify-content: center; + padding: 24px 0; +} + +.no-more { + padding: 24px 0; + text-align: center; + font-size: 12px; +} + +.card { + display: flex; + align-items: center; + padding: 12px; + margin: 12px; + background: #fff; + border-radius: 12px; + + .external-class-content { + color: #fff; + background-color: var(--td-brand-color, #0052d9); + font-weight: 400; + } + + .pic { + width: 120px; + height: 120px; + margin-right: 12px; + + &-wrapper { + display: flex; + align-items: center; + justify-content: center; + width: 120px; + } + } + + .desc { + margin-top: 6px; + font-size: 14px; + color: #777; + + &-item { + display: flex; + + &-label { + width: 70px; + } + } + } +} + diff --git a/miniprogram/pages/stock-list/index.wxml b/miniprogram/pages/stock-list/index.wxml new file mode 100644 index 0000000..6f69293 --- /dev/null +++ b/miniprogram/pages/stock-list/index.wxml @@ -0,0 +1,38 @@ + + + + + + + + {{item.standardInfo.brand[0] || item.name[0]}} + + + + {{item.standardInfo.brand}} + {{item.name}} + + + + 保质期: + {{item.preserveDate}}{{item.unit === 'day' ? ' 天' : ' 个月' }} + + + 到期日: + {{item.expiredDate}} + + + 到期时间: + {{item.expiredTxt}} + + + + + + + + + 没有更多数据了 + + + \ No newline at end of file diff --git a/miniprogram/pages/stock/index.js b/miniprogram/pages/stock/index.js new file mode 100644 index 0000000..e671e55 --- /dev/null +++ b/miniprogram/pages/stock/index.js @@ -0,0 +1,77 @@ +import { toScan } from '../../utils/scan'; + +Page({ + + /** + * 页面的初始数据 + */ + data: { + + }, + + handleScan() { + toScan(); + }, + + handleToList() { + wx.navigateTo({ + url: '/pages/stock-list/index', + }) + }, + + /** + * 生命周期函数--监听页面加载 + */ + onLoad(options) { + + }, + + /** + * 生命周期函数--监听页面初次渲染完成 + */ + onReady() { + + }, + + /** + * 生命周期函数--监听页面显示 + */ + onShow() { + + }, + + /** + * 生命周期函数--监听页面隐藏 + */ + onHide() { + + }, + + /** + * 生命周期函数--监听页面卸载 + */ + onUnload() { + + }, + + /** + * 页面相关事件处理函数--监听用户下拉动作 + */ + onPullDownRefresh() { + + }, + + /** + * 页面上拉触底事件的处理函数 + */ + onReachBottom() { + + }, + + /** + * 用户点击右上角分享 + */ + onShareAppMessage() { + + } +}) \ No newline at end of file diff --git a/miniprogram/pages/stock/index.json b/miniprogram/pages/stock/index.json new file mode 100644 index 0000000..8835af0 --- /dev/null +++ b/miniprogram/pages/stock/index.json @@ -0,0 +1,3 @@ +{ + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/stock/index.less b/miniprogram/pages/stock/index.less new file mode 100644 index 0000000..5d00f40 --- /dev/null +++ b/miniprogram/pages/stock/index.less @@ -0,0 +1,20 @@ +/* pages/stock/index.wxss */ +page { + display: flex; + flex-direction: column; + height: calc(100vh - 180rpx); +} + +.button-wrapper { + flex: 1; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.barcode-btn { + --td-button-large-icon-font-size: 52px; + --td-button-large-height: 110px; + --td-button-large-width: 110px; +} \ No newline at end of file diff --git a/miniprogram/pages/stock/index.wxml b/miniprogram/pages/stock/index.wxml new file mode 100644 index 0000000..9ba9b45 --- /dev/null +++ b/miniprogram/pages/stock/index.wxml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/miniprogram/pages/user-info/index.js b/miniprogram/pages/user-info/index.js new file mode 100644 index 0000000..da60e7e --- /dev/null +++ b/miniprogram/pages/user-info/index.js @@ -0,0 +1,84 @@ +const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'; + +Component({ + data: { + nickName: '', + avatarUrl: defaultAvatarUrl, + loading: false, + }, + + methods: { + onShow(){ + wx.cloud.callFunction({ + name: 'userinfo', + data: { + action: 'get', + } + }).then(({ result }) => { + if (result.code === 0) { + const { nickName, avatarUrl } = result.data; + this.setData({ + nickName, + avatarUrl, + }) + } + }) + }, + onChooseAvatar(e) { + const { avatarUrl } = e.detail + this.setData({ + avatarUrl, + }) + }, + handleNameInput(e) { + const { value } = e.detail; + this.setData({ + nickName: value, + }) + }, + handleSubmit(e) { + const { avatarUrl, nickName } = this.data; + + if (!nickName) { + wx.showToast({ + title: '请输入昵称', + icon: 'none', + }) + return; + } + + this.setData({ + loading: true, + }) + wx.cloud.callFunction({ + name: 'userinfo', + data: { + action: 'update', + body: { + avatarUrl, + nickName, + } + } + }).then(({ result }) => { + if (result.code === 0) { + wx.showToast({ + title: '保存成功', + icon: 'success', + }) + setTimeout(() => { + wx.navigateBack() + }, 1000) + } else { + wx.showToast({ + title: result.message, + icon: 'none', + }) + } + }).finally(() => { + this.setData({ + loading: false, + }) + }) + } + } +}) \ No newline at end of file diff --git a/miniprogram/pages/user-info/index.json b/miniprogram/pages/user-info/index.json new file mode 100644 index 0000000..5abf709 --- /dev/null +++ b/miniprogram/pages/user-info/index.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "个人信息", + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/user-info/index.less b/miniprogram/pages/user-info/index.less new file mode 100644 index 0000000..c7d2ff6 --- /dev/null +++ b/miniprogram/pages/user-info/index.less @@ -0,0 +1,22 @@ +page { + padding-top: 32px; + background-color: #fff; +} + +.avatar-wrapper { + background-color: transparent; + + &:before, &:after { + display: none; + } +} + +.avatar { + width: 64px; + height: 64px; + border-radius: 12px; +} + +.button-wrapper { + padding: 32px; +} \ No newline at end of file diff --git a/miniprogram/pages/user-info/index.wxml b/miniprogram/pages/user-info/index.wxml new file mode 100644 index 0000000..7000d42 --- /dev/null +++ b/miniprogram/pages/user-info/index.wxml @@ -0,0 +1,8 @@ + + + + + 保存 + \ No newline at end of file diff --git a/miniprogram/utils/scan.js b/miniprogram/utils/scan.js new file mode 100644 index 0000000..df2a64b --- /dev/null +++ b/miniprogram/utils/scan.js @@ -0,0 +1,11 @@ +export const toScan = () => { + wx.scanCode({ + scanType: ['barCode'], + success: (res) => { + console.log(res); + wx.navigateTo({ + url: '/pages/stock-edit/index?barcode=' + res.result, + }) + } + }) +} \ No newline at end of file diff --git a/project.private.config.json b/project.private.config.json index 33ce99f..a93cdc0 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -5,6 +5,27 @@ "condition": { "miniprogram": { "list": [ + { + "name": "stock-edit", + "pathName": "pages/stock-edit/index", + "query": "barcode=6920658281801", + "launchMode": "default", + "scene": null + }, + { + "name": "pages/stock-list/index", + "pathName": "pages/stock-list/index", + "query": "", + "launchMode": "default", + "scene": null + }, + { + "name": "pages/user-info/index", + "pathName": "pages/user-info/index", + "query": "", + "launchMode": "default", + "scene": null + }, { "name": "收藏页", "pathName": "pages/myStarred/index", From dfe1ce38e26ef1dbf09db84295a47006f024ebc5 Mon Sep 17 00:00:00 2001 From: LeeJim Date: Sun, 24 May 2026 15:07:31 +0800 Subject: [PATCH 20/23] feat: version 3.0 release --- .claude/mcp.json | 8 + classified-ingredients.json | 2498 ++ classified-ingredients.txt | 639 + cloudfunctions/barcode/index.js | 40 +- cloudfunctions/barcode/package.json | 1 + cloudfunctions/barcode/utils/get_barcode.js | 157 +- cloudfunctions/checkin/index.js | 96 + cloudfunctions/checkin/package.json | 9 + cloudfunctions/cookbookAction/index.js | 59 + cloudfunctions/cookbookAction/package.json | 9 + cloudfunctions/quota/index.js | 2 +- cloudfunctions/reminders/index.js | 4 +- cloudfunctions/saveUserinfo/index.js | 22 +- cloudfunctions/sku/index.js | 54 +- cloudfunctions/userinfo/index.js | 28 +- miniprogram/app.js | 2 +- miniprogram/app.json | 10 +- miniprogram/components/standard-info/index.js | 77 +- .../components/standard-info/index.less | 102 +- .../components/standard-info/index.wxml | 84 +- miniprogram/config/index.js | 17 +- miniprogram/custom-tab-bar/index.js | 6 +- miniprogram/pages/ai-cook/index.js | 60 + miniprogram/pages/ai-cook/index.json | 6 + miniprogram/pages/ai-cook/index.less | 248 + miniprogram/pages/ai-cook/index.wxml | 79 + miniprogram/pages/detail/index.js | 69 +- miniprogram/pages/detail/index.json | 4 +- miniprogram/pages/detail/index.less | 336 +- miniprogram/pages/detail/index.wxml | 173 +- miniprogram/pages/index/index.js | 103 +- miniprogram/pages/index/index.json | 7 +- miniprogram/pages/index/index.less | 283 +- miniprogram/pages/index/index.wxml | 89 +- miniprogram/pages/my/index.js | 182 +- miniprogram/pages/my/index.json | 4 +- miniprogram/pages/my/index.less | 303 +- miniprogram/pages/my/index.wxml | 128 +- miniprogram/pages/myStarred/index.js | 42 +- miniprogram/pages/myStarred/index.json | 4 +- miniprogram/pages/myStarred/index.less | 75 +- miniprogram/pages/myStarred/index.wxml | 32 +- miniprogram/pages/stock-edit/index.js | 136 +- miniprogram/pages/stock-edit/index.json | 6 +- miniprogram/pages/stock-edit/index.less | 164 +- miniprogram/pages/stock-edit/index.wxml | 110 +- miniprogram/pages/stock-list/index.js | 73 +- miniprogram/pages/stock-list/index.json | 4 +- miniprogram/pages/stock-list/index.less | 192 +- miniprogram/pages/stock-list/index.wxml | 68 +- miniprogram/pages/stock/index.js | 67 +- miniprogram/pages/stock/index.json | 3 + miniprogram/pages/stock/index.less | 54 +- miniprogram/pages/stock/index.wxml | 25 +- miniprogram/recipes-enriched.js | 26426 ++++++++++++++++ miniprogram/recipes-enriched.json | 199 + miniprogram/utils/ai-matcher.js | 92 + miniprogram/utils/common-ingredients.js | 15 + miniprogram/utils/user-level.js | 23 + project.config.json | 6 +- project.private.config.json | 9 +- script/classify-ingredients.js | 239 + script/enrich-recipes.js | 74 + 63 files changed, 33307 insertions(+), 829 deletions(-) create mode 100644 .claude/mcp.json create mode 100644 classified-ingredients.json create mode 100644 classified-ingredients.txt create mode 100644 cloudfunctions/checkin/index.js create mode 100644 cloudfunctions/checkin/package.json create mode 100644 cloudfunctions/cookbookAction/index.js create mode 100644 cloudfunctions/cookbookAction/package.json create mode 100644 miniprogram/pages/ai-cook/index.js create mode 100644 miniprogram/pages/ai-cook/index.json create mode 100644 miniprogram/pages/ai-cook/index.less create mode 100644 miniprogram/pages/ai-cook/index.wxml create mode 100644 miniprogram/recipes-enriched.js create mode 100644 miniprogram/recipes-enriched.json create mode 100644 miniprogram/utils/ai-matcher.js create mode 100644 miniprogram/utils/common-ingredients.js create mode 100644 miniprogram/utils/user-level.js create mode 100644 script/classify-ingredients.js create mode 100644 script/enrich-recipes.js diff --git a/.claude/mcp.json b/.claude/mcp.json new file mode 100644 index 0000000..4bc667d --- /dev/null +++ b/.claude/mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "tdesign-mcp-server": { + "command": "npx", + "args": ["-y", "tdesign-mcp-server@latest"] + } + } +} \ No newline at end of file diff --git a/classified-ingredients.json b/classified-ingredients.json new file mode 100644 index 0000000..e3ab3d9 --- /dev/null +++ b/classified-ingredients.json @@ -0,0 +1,2498 @@ +{ + "荤": [ + { + "name": "鸡蛋", + "count": 37 + }, + { + "name": "五花肉", + "count": 8 + }, + { + "name": "牛奶", + "count": 7 + }, + { + "name": "牛肉", + "count": 6 + }, + { + "name": "黄油", + "count": 5 + }, + { + "name": "培根", + "count": 3 + }, + { + "name": "淡奶油", + "count": 3 + }, + { + "name": "排骨", + "count": 3 + }, + { + "name": "瘦肉", + "count": 3 + }, + { + "name": "猪肉", + "count": 3 + }, + { + "name": "鸡腿", + "count": 2 + }, + { + "name": "里脊肉", + "count": 2 + }, + { + "name": "牛腩", + "count": 2 + }, + { + "name": "猪瘦肉", + "count": 2 + }, + { + "name": "肉末", + "count": 2 + }, + { + "name": "皮蛋", + "count": 2 + }, + { + "name": "肉", + "count": 2 + }, + { + "name": "火腿肠", + "count": 2 + }, + { + "name": "青蟹", + "count": 1 + }, + { + "name": "小龙虾", + "count": 1 + }, + { + "name": "黑鳕鱼,带皮", + "count": 1 + }, + { + "name": "草鱼", + "count": 1 + }, + { + "name": "生蚝", + "count": 1 + }, + { + "name": "鲈鱼", + "count": 1 + }, + { + "name": "蒸鱼豉油", + "count": 1 + }, + { + "name": "活虾", + "count": 1 + }, + { + "name": "鲤鱼", + "count": 1 + }, + { + "name": "鱼头一个", + "count": 1 + }, + { + "name": "油、盐、生抽、老抽、陈醋、蚝油、料酒、白糖", + "count": 1 + }, + { + "name": "鲤鱼、五花肉", + "count": 1 + }, + { + "name": "海虾", + "count": 1 + }, + { + "name": "蛏子", + "count": 1 + }, + { + "name": "翘嘴鱼", + "count": 1 + }, + { + "name": "鳊鱼", + "count": 1 + }, + { + "name": "鲜虾", + "count": 1 + }, + { + "name": "新鲜鸡蛋", + "count": 1 + }, + { + "name": "牛奶 50-100g,能够将燕麦搅拌粘稠即可", + "count": 1 + }, + { + "name": "全脂牛奶/奶油", + "count": 1 + }, + { + "name": "芝士", + "count": 1 + }, + { + "name": "蛋黄酱", + "count": 1 + }, + { + "name": "马斯卡彭芝士", + "count": 1 + }, + { + "name": "无菌鸡蛋", + "count": 1 + }, + { + "name": "无盐黄油", + "count": 1 + }, + { + "name": "全脂奶粉", + "count": 1 + }, + { + "name": "全脂奶粉或淡奶", + "count": 1 + }, + { + "name": "兔肉", + "count": 1 + }, + { + "name": "鸡胸肉", + "count": 1 + }, + { + "name": "半只鸡", + "count": 1 + }, + { + "name": "鸡翅中", + "count": 1 + }, + { + "name": "梅头猪肉", + "count": 1 + }, + { + "name": "鸭肉", + "count": 1 + }, + { + "name": "肋排", + "count": 1 + }, + { + "name": "鸡", + "count": 1 + }, + { + "name": "牛柳或牛肩肉", + "count": 1 + }, + { + "name": "手枪腿", + "count": 1 + }, + { + "name": "牛里脊", + "count": 1 + }, + { + "name": "鸡肉,土豆,菜椒和甜椒", + "count": 1 + }, + { + "name": "鸡爪", + "count": 1 + }, + { + "name": "猪里脊肉", + "count": 1 + }, + { + "name": "鸡蛋清", + "count": 1 + }, + { + "name": "凤尾", + "count": 1 + }, + { + "name": "猪肉片", + "count": 1 + }, + { + "name": "带皮五花肉", + "count": 1 + }, + { + "name": "猪皮", + "count": 1 + }, + { + "name": "碎牛肉", + "count": 1 + }, + { + "name": "猪蹄", + "count": 1 + }, + { + "name": "带皮羊排肉", + "count": 1 + }, + { + "name": "羊排", + "count": 1 + }, + { + "name": "五花肉薄片", + "count": 1 + }, + { + "name": "鲜仔鸭肉", + "count": 1 + }, + { + "name": "鲜鸭血", + "count": 1 + }, + { + "name": "鱼露", + "count": 1 + }, + { + "name": "鸡蛋黄", + "count": 1 + }, + { + "name": "猪里脊", + "count": 1 + }, + { + "name": "大鸡腿", + "count": 1 + }, + { + "name": "里脊肉 200g", + "count": 1 + }, + { + "name": "蛋清 1 个", + "count": 1 + }, + { + "name": "无骨肉", + "count": 1 + }, + { + "name": "盐、鸡精、蚝油、生抽、老抽、香油、香醋、芝麻酱", + "count": 1 + }, + { + "name": "牛油 4500 g", + "count": 1 + }, + { + "name": "纯猪油 500 g", + "count": 1 + }, + { + "name": "肉桂 10 g", + "count": 1 + }, + { + "name": "昂刺鱼或者沙光鱼", + "count": 1 + }, + { + "name": "前腿肉", + "count": 1 + }, + { + "name": "一个鸡蛋的鸡蛋清", + "count": 1 + }, + { + "name": "米", + "count": 1 + }, + { + "name": "虾仁", + "count": 1 + }, + { + "name": "牛肉高汤", + "count": 1 + }, + { + "name": "乳鸽", + "count": 1 + }, + { + "name": "肉沫", + "count": 1 + }, + { + "name": "腊肠 1 根", + "count": 1 + }, + { + "name": "鸡蛋 1 个", + "count": 1 + }, + { + "name": ":鸡蛋,青菜", + "count": 1 + }, + { + "name": "肥牛片", + "count": 1 + }, + { + "name": "炒河粉、猪肉/牛肉", + "count": 1 + }, + { + "name": "黄瓜、面筋块、绿豆芽、鸡蛋、蒜瓣、小葱、淀粉", + "count": 1 + }, + { + "name": "肉丁/肉末", + "count": 1 + }, + { + "name": "肉汤汁", + "count": 1 + }, + { + "name": "猪五花肉", + "count": 1 + }, + { + "name": "煮熟的腊肉", + "count": 1 + }, + { + "name": "火腿", + "count": 1 + }, + { + "name": "灯影牛肉丝", + "count": 1 + }, + { + "name": "火锅牛肉卷", + "count": 1 + }, + { + "name": "午餐肉", + "count": 1 + }, + { + "name": "虾皮", + "count": 1 + }, + { + "name": "蟹味菇", + "count": 1 + }, + { + "name": "螺丝椒", + "count": 1 + } + ], + "素": [ + { + "name": "葱", + "count": 22 + }, + { + "name": "蒜", + "count": 21 + }, + { + "name": "淀粉", + "count": 20 + }, + { + "name": "水", + "count": 15 + }, + { + "name": "洋葱", + "count": 12 + }, + { + "name": "青椒", + "count": 12 + }, + { + "name": "干辣椒", + "count": 10 + }, + { + "name": "土豆", + "count": 10 + }, + { + "name": "香菜", + "count": 9 + }, + { + "name": "小葱", + "count": 9 + }, + { + "name": "大葱", + "count": 8 + }, + { + "name": "香葱", + "count": 8 + }, + { + "name": "西红柿", + "count": 8 + }, + { + "name": "面粉", + "count": 7 + }, + { + "name": "小米椒", + "count": 7 + }, + { + "name": "小米辣", + "count": 7 + }, + { + "name": "胡萝卜", + "count": 7 + }, + { + "name": "茄子", + "count": 7 + }, + { + "name": "芝麻", + "count": 6 + }, + { + "name": "番茄酱", + "count": 6 + }, + { + "name": "生粉", + "count": 5 + }, + { + "name": "柠檬", + "count": 5 + }, + { + "name": "花生油", + "count": 4 + }, + { + "name": "白芝麻", + "count": 4 + }, + { + "name": "芹菜", + "count": 4 + }, + { + "name": "黄瓜", + "count": 4 + }, + { + "name": "芝麻油", + "count": 3 + }, + { + "name": "蒜苗", + "count": 3 + }, + { + "name": "豆芽", + "count": 3 + }, + { + "name": "土豆淀粉", + "count": 3 + }, + { + "name": "辣椒", + "count": 3 + }, + { + "name": "香菇", + "count": 3 + }, + { + "name": "红枣", + "count": 2 + }, + { + "name": "燕麦", + "count": 2 + }, + { + "name": "干辣椒面", + "count": 2 + }, + { + "name": "青红椒", + "count": 2 + }, + { + "name": "辣椒粉", + "count": 2 + }, + { + "name": "莴笋", + "count": 2 + }, + { + "name": "尖椒", + "count": 2 + }, + { + "name": "白菜", + "count": 2 + }, + { + "name": "水淀粉", + "count": 2 + }, + { + "name": "青菜", + "count": 2 + }, + { + "name": "菜椒", + "count": 2 + }, + { + "name": "生菜", + "count": 2 + }, + { + "name": "枸杞", + "count": 2 + }, + { + "name": "芝麻酱", + "count": 2 + }, + { + "name": "米粉", + "count": 2 + }, + { + "name": "西葫芦", + "count": 2 + }, + { + "name": "青辣椒", + "count": 2 + }, + { + "name": "子弹头辣椒", + "count": 1 + }, + { + "name": "干辣椒段", + "count": 1 + }, + { + "name": "千张", + "count": 1 + }, + { + "name": "绿豆芽", + "count": 1 + }, + { + "name": "芹菜段", + "count": 1 + }, + { + "name": "熟花生米", + "count": 1 + }, + { + "name": "香菜一颗", + "count": 1 + }, + { + "name": "大葱、姜、大蒜、香菜、美人椒", + "count": 1 + }, + { + "name": "八角、干辣椒", + "count": 1 + }, + { + "name": "大葱、姜、大蒜、干辣椒", + "count": 1 + }, + { + "name": "大葱葱白", + "count": 1 + }, + { + "name": "老豆腐", + "count": 1 + }, + { + "name": "米酒", + "count": 1 + }, + { + "name": "新鲜吐司", + "count": 1 + }, + { + "name": "面包机", + "count": 1 + }, + { + "name": "糯米", + "count": 1 + }, + { + "name": "桂圆", + "count": 1 + }, + { + "name": "新鲜玉米", + "count": 1 + }, + { + "name": "放得下玉米的锅", + "count": 1 + }, + { + "name": "饺子", + "count": 1 + }, + { + "name": "冷冻花卷", + "count": 1 + }, + { + "name": "可根据口味选择增加 50g 蔬菜,如菠菜。", + "count": 1 + }, + { + "name": "面包片", + "count": 1 + }, + { + "name": "手抓饼皮", + "count": 1 + }, + { + "name": "吐司", + "count": 1 + }, + { + "name": "熟白芝麻", + "count": 1 + }, + { + "name": "白醋/米醋", + "count": 1 + }, + { + "name": "草莓", + "count": 1 + }, + { + "name": "原味奥利奥", + "count": 1 + }, + { + "name": "低筋面粉", + "count": 1 + }, + { + "name": "[可选] 柠檬汁或白醋", + "count": 1 + }, + { + "name": "手指饼干", + "count": 1 + }, + { + "name": "筛网 网孔约为 1 毫米", + "count": 1 + }, + { + "name": "荔浦芋头", + "count": 1 + }, + { + "name": "紫薯粉", + "count": 1 + }, + { + "name": "糯米粉", + "count": 1 + }, + { + "name": "玉米淀粉", + "count": 1 + }, + { + "name": "混合坚果", + "count": 1 + }, + { + "name": "饼干", + "count": 1 + }, + { + "name": "魔芋粉", + "count": 1 + }, + { + "name": "水果刀", + "count": 1 + }, + { + "name": "百香果", + "count": 1 + }, + { + "name": "橙子", + "count": 1 + }, + { + "name": "耙耙柑", + "count": 1 + }, + { + "name": "乌枣", + "count": 1 + }, + { + "name": "乌梅", + "count": 1 + }, + { + "name": "山楂片", + "count": 1 + }, + { + "name": "小葱/大葱/洋葱", + "count": 1 + }, + { + "name": "花生", + "count": 1 + }, + { + "name": "罐头菠萝片", + "count": 1 + }, + { + "name": "泡椒", + "count": 1 + }, + { + "name": "熟花生", + "count": 1 + }, + { + "name": "生花生", + "count": 1 + }, + { + "name": "花椒,香叶,香果,干线椒,大蒜,大葱", + "count": 1 + }, + { + "name": "梅菜", + "count": 1 + }, + { + "name": "干辣椒粉", + "count": 1 + }, + { + "name": "红辣椒", + "count": 1 + }, + { + "name": "红泡椒", + "count": 1 + }, + { + "name": "干小米椒🌶", + "count": 1 + }, + { + "name": "土豆干粉条", + "count": 1 + }, + { + "name": "甜椒", + "count": 1 + }, + { + "name": "凤梨", + "count": 1 + }, + { + "name": "白萝卜", + "count": 1 + }, + { + "name": "蒜苔", + "count": 1 + }, + { + "name": "地瓜粉", + "count": 1 + }, + { + "name": "香干", + "count": 1 + }, + { + "name": "豆干", + "count": 1 + }, + { + "name": "香芹/芹菜", + "count": 1 + }, + { + "name": "辣椒:青椒或者红椒都可以", + "count": 1 + }, + { + "name": "干香菇", + "count": 1 + }, + { + "name": "胡萝卜 100g", + "count": 1 + }, + { + "name": "青椒 100g", + "count": 1 + }, + { + "name": "木耳 5g", + "count": 1 + }, + { + "name": "淀粉 10g", + "count": 1 + }, + { + "name": "干豆腐", + "count": 1 + }, + { + "name": "北京麻辣方便面", + "count": 1 + }, + { + "name": "凉皮、面筋", + "count": 1 + }, + { + "name": "黄瓜、大蒜、绿豆芽", + "count": 1 + }, + { + "name": "1 袋 半成品意大利面", + "count": 1 + }, + { + "name": "糍粑辣椒 3000 g", + "count": 1 + }, + { + "name": "大葱 100 g", + "count": 1 + }, + { + "name": "洋葱 100 g", + "count": 1 + }, + { + "name": "老油 ?? 干辣椒面 15 g", + "count": 1 + }, + { + "name": "未过期的一袋速冻水饺", + "count": 1 + }, + { + "name": "速冻汤圆", + "count": 1 + }, + { + "name": "未过期的一袋速冻馄饨", + "count": 1 + }, + { + "name": "香菜 1 根", + "count": 1 + }, + { + "name": "小米", + "count": 1 + }, + { + "name": "豆腐", + "count": 1 + }, + { + "name": "木耳", + "count": 1 + }, + { + "name": "小香葱", + "count": 1 + }, + { + "name": "泡好的粉丝", + "count": 1 + }, + { + "name": "大米", + "count": 1 + }, + { + "name": "紫菜", + "count": 1 + }, + { + "name": "番茄", + "count": 1 + }, + { + "name": "菌菇", + "count": 1 + }, + { + "name": "玉米", + "count": 1 + }, + { + "name": "金针菇", + "count": 1 + }, + { + "name": "银耳", + "count": 1 + }, + { + "name": "去心莲子", + "count": 1 + }, + { + "name": "豌豆淀粉", + "count": 1 + }, + { + "name": "花生碎", + "count": 1 + }, + { + "name": "米 200 ml", + "count": 1 + }, + { + "name": "香葱 1 颗", + "count": 1 + }, + { + "name": "芝麻香油", + "count": 1 + }, + { + "name": "中筋面粉", + "count": 1 + }, + { + "name": "花椰菜", + "count": 1 + }, + { + "name": "年糕/白粿", + "count": 1 + }, + { + "name": "意大利面", + "count": 1 + }, + { + "name": "番茄酱 / 黑胡椒酱", + "count": 1 + }, + { + "name": "方便面", + "count": 1 + }, + { + "name": "馒头", + "count": 1 + }, + { + "name": "挂面", + "count": 1 + }, + { + "name": "热干面特有的碱水面", + "count": 1 + }, + { + "name": "酸豆角", + "count": 1 + }, + { + "name": "萝卜干", + "count": 1 + }, + { + "name": "辣椒油", + "count": 1 + }, + { + "name": "泡面", + "count": 1 + }, + { + "name": "干红椒", + "count": 1 + }, + { + "name": "北方大米", + "count": 1 + }, + { + "name": "江南米或北方大米", + "count": 1 + }, + { + "name": "酸笋", + "count": 1 + }, + { + "name": "剁椒", + "count": 1 + }, + { + "name": "米醋", + "count": 1 + }, + { + "name": "面", + "count": 1 + }, + { + "name": "小麦粉", + "count": 1 + }, + { + "name": "冷饭", + "count": 1 + }, + { + "name": "蕨根粉", + "count": 1 + }, + { + "name": "小汤圆", + "count": 1 + }, + { + "name": "米饭", + "count": 1 + }, + { + "name": "蘑菇", + "count": 1 + }, + { + "name": "风干快熟面/任何牌子的快熟面", + "count": 1 + }, + { + "name": "原料:半干荞麦面、娃娃菜、生菜", + "count": 1 + }, + { + "name": "花生酱:pdd莺歌花生酱 24.8元两瓶,可以做68份", + "count": 1 + }, + { + "name": "娃娃菜", + "count": 1 + }, + { + "name": "油麦菜", + "count": 1 + }, + { + "name": "萝卜", + "count": 1 + }, + { + "name": "包菜", + "count": 1 + }, + { + "name": "粉丝", + "count": 1 + }, + { + "name": "葱、蒜、干辣椒", + "count": 1 + }, + { + "name": "花菜", + "count": 1 + }, + { + "name": "玉米粒", + "count": 1 + }, + { + "name": "芝麻粒", + "count": 1 + }, + { + "name": "四季豆", + "count": 1 + }, + { + "name": "橄榄菜", + "count": 1 + }, + { + "name": "叶菜类蔬菜", + "count": 1 + }, + { + "name": "菜籽油或花生油", + "count": 1 + }, + { + "name": "冬瓜", + "count": 1 + }, + { + "name": "青茄子", + "count": 1 + }, + { + "name": "茭白", + "count": 1 + }, + { + "name": "菠菜", + "count": 1 + }, + { + "name": "白豆腐", + "count": 1 + }, + { + "name": "白玉菇", + "count": 1 + }, + { + "name": "西蓝花", + "count": 1 + }, + { + "name": "毛豆", + "count": 1 + }, + { + "name": "话梅", + "count": 1 + }, + { + "name": "红椒", + "count": 1 + }, + { + "name": "豆角", + "count": 1 + }, + { + "name": "红尖椒", + "count": 1 + }, + { + "name": "嫩豆腐", + "count": 1 + } + ], + "配菜": [ + { + "name": "姜", + "count": 30 + }, + { + "name": "大蒜", + "count": 16 + }, + { + "name": "蒜头", + "count": 7 + }, + { + "name": "生姜", + "count": 7 + }, + { + "name": "姜末", + "count": 4 + }, + { + "name": "姜片", + "count": 3 + }, + { + "name": "葱姜蒜", + "count": 2 + }, + { + "name": "新鲜绿叶", + "count": 2 + }, + { + "name": "蒜蓉", + "count": 2 + }, + { + "name": "葱、姜、蒜", + "count": 2 + }, + { + "name": "蒜瓣", + "count": 2 + }, + { + "name": "葱花", + "count": 2 + }, + { + "name": "青葱", + "count": 1 + }, + { + "name": "灯笼椒", + "count": 1 + }, + { + "name": "蒜蓉酱", + "count": 1 + }, + { + "name": "薄荷叶或其他绿叶", + "count": 1 + }, + { + "name": "干桂花", + "count": 1 + }, + { + "name": "美人辣", + "count": 1 + }, + { + "name": "朝天椒", + "count": 1 + }, + { + "name": "野山椒", + "count": 1 + }, + { + "name": "蒜末", + "count": 1 + }, + { + "name": "蒜仔", + "count": 1 + }, + { + "name": "2cm 两段葱段、两片姜片,葱花、姜各 10g", + "count": 1 + }, + { + "name": "姜 20g", + "count": 1 + }, + { + "name": "葱 20g", + "count": 1 + }, + { + "name": "蒜 2 瓣", + "count": 1 + }, + { + "name": "生姜片", + "count": 1 + }, + { + "name": "盆、碗、盘子、蒜臼", + "count": 1 + }, + { + "name": "老姜 250 g", + "count": 1 + }, + { + "name": "大蒜 200 g", + "count": 1 + }, + { + "name": "葱姜花椒水", + "count": 1 + }, + { + "name": "黄花", + "count": 1 + }, + { + "name": "蒜水", + "count": 1 + }, + { + "name": "葱,姜,蒜", + "count": 1 + } + ], + "调料": [ + { + "name": "盐", + "count": 70 + }, + { + "name": "生抽", + "count": 44 + }, + { + "name": "料酒", + "count": 38 + }, + { + "name": "食用油", + "count": 36 + }, + { + "name": "白糖", + "count": 24 + }, + { + "name": "老抽", + "count": 22 + }, + { + "name": "酱油", + "count": 21 + }, + { + "name": "白砂糖", + "count": 19 + }, + { + "name": "鸡精", + "count": 18 + }, + { + "name": "食用盐", + "count": 17 + }, + { + "name": "八角", + "count": 13 + }, + { + "name": "胡椒粉", + "count": 13 + }, + { + "name": "油", + "count": 12 + }, + { + "name": "豆瓣酱", + "count": 12 + }, + { + "name": "香叶", + "count": 11 + }, + { + "name": "蚝油", + "count": 11 + }, + { + "name": "花椒", + "count": 10 + }, + { + "name": "糖", + "count": 10 + }, + { + "name": "桂皮", + "count": 9 + }, + { + "name": "冰糖", + "count": 9 + }, + { + "name": "五香粉", + "count": 8 + }, + { + "name": "香醋", + "count": 7 + }, + { + "name": "醋", + "count": 7 + }, + { + "name": "耗油", + "count": 7 + }, + { + "name": "生抽酱油", + "count": 6 + }, + { + "name": "香油", + "count": 6 + }, + { + "name": "白醋", + "count": 5 + }, + { + "name": "味精", + "count": 5 + }, + { + "name": "青花椒", + "count": 4 + }, + { + "name": "食盐", + "count": 4 + }, + { + "name": "咖喱块", + "count": 3 + }, + { + "name": "黄豆酱", + "count": 3 + }, + { + "name": "啤酒", + "count": 3 + }, + { + "name": "白胡椒粉", + "count": 3 + }, + { + "name": "十三香", + "count": 3 + }, + { + "name": "金酒", + "count": 3 + }, + { + "name": "麻油", + "count": 3 + }, + { + "name": "植物油", + "count": 3 + }, + { + "name": "豆豉", + "count": 3 + }, + { + "name": "菜籽油", + "count": 3 + }, + { + "name": "黑胡椒", + "count": 2 + }, + { + "name": "白芷", + "count": 2 + }, + { + "name": "孜然粉", + "count": 2 + }, + { + "name": "可可粉", + "count": 2 + }, + { + "name": "蜂蜜", + "count": 2 + }, + { + "name": "可乐", + "count": 2 + }, + { + "name": "孜然", + "count": 2 + }, + { + "name": "油泼辣子", + "count": 2 + }, + { + "name": "橄榄油", + "count": 2 + }, + { + "name": "藤椒油", + "count": 2 + }, + { + "name": "椰浆", + "count": 1 + }, + { + "name": "郫县豆瓣", + "count": 1 + }, + { + "name": "油、盐、鸡精、生抽、老抽、陈醋、黑胡椒粉、料酒", + "count": 1 + }, + { + "name": "黑胡椒粒", + "count": 1 + }, + { + "name": "果酱", + "count": 1 + }, + { + "name": "泡打粉", + "count": 1 + }, + { + "name": "空气炸锅或者油锅", + "count": 1 + }, + { + "name": "茴香", + "count": 1 + }, + { + "name": "红茶", + "count": 1 + }, + { + "name": "草寇", + "count": 1 + }, + { + "name": "花椒粉", + "count": 1 + }, + { + "name": "麻辣鲜", + "count": 1 + }, + { + "name": "朗姆酒", + "count": 1 + }, + { + "name": "雪花酥/牛轧糖模具", + "count": 1 + }, + { + "name": "棉花糖", + "count": 1 + }, + { + "name": "赤藓糖醇", + "count": 1 + }, + { + "name": "塔塔粉", + "count": 1 + }, + { + "name": "甘露咖啡酒", + "count": 1 + }, + { + "name": "爱尔兰百利甜酒", + "count": 1 + }, + { + "name": "蓝天原味伏特加", + "count": 1 + }, + { + "name": "波旁威士忌", + "count": 1 + }, + { + "name": "可口可乐", + "count": 1 + }, + { + "name": "袋泡红茶", + "count": 1 + }, + { + "name": "茉莉绿茶茶叶/苏打气泡水二选一", + "count": 1 + }, + { + "name": "茉莉绿茶", + "count": 1 + }, + { + "name": "酸梅晶固体饮料", + "count": 1 + }, + { + "name": "方糖", + "count": 1 + }, + { + "name": "北京二锅头酒", + "count": 1 + }, + { + "name": "黄冰糖", + "count": 1 + }, + { + "name": "甘草", + "count": 1 + }, + { + "name": "陈皮", + "count": 1 + }, + { + "name": "红豆蔻", + "count": 1 + }, + { + "name": "苏打气泡水", + "count": 1 + }, + { + "name": "龙舌兰酒", + "count": 1 + }, + { + "name": "伏特加", + "count": 1 + }, + { + "name": "白朗姆酒", + "count": 1 + }, + { + "name": "橙味甜酒", + "count": 1 + }, + { + "name": "枫糖浆", + "count": 1 + }, + { + "name": "山奈", + "count": 1 + }, + { + "name": "白蔻", + "count": 1 + }, + { + "name": "小茴香", + "count": 1 + }, + { + "name": "料酒或啤酒", + "count": 1 + }, + { + "name": "茄汁", + "count": 1 + }, + { + "name": "老抽酱油", + "count": 1 + }, + { + "name": "丁香", + "count": 1 + }, + { + "name": "郫县红油豆瓣酱", + "count": 1 + }, + { + "name": "花椒碎", + "count": 1 + }, + { + "name": "油,盐,生抽,耗油,料酒,白糖", + "count": 1 + }, + { + "name": "黑醋", + "count": 1 + }, + { + "name": "花椒油", + "count": 1 + }, + { + "name": "红油豆瓣", + "count": 1 + }, + { + "name": "蕃茄酱", + "count": 1 + }, + { + "name": "大料、花椒、白芷、桂皮、丁香、香叶、小茴香", + "count": 1 + }, + { + "name": "干罗勒或百里香", + "count": 1 + }, + { + "name": "料酒或者黄酒", + "count": 1 + }, + { + "name": "酒", + "count": 1 + }, + { + "name": "小料", + "count": 1 + }, + { + "name": "白糖 or 冰糖", + "count": 1 + }, + { + "name": "黑胡椒粉", + "count": 1 + }, + { + "name": "生抽、白胡椒粉,白糖,料/黄酒,八角三小片", + "count": 1 + }, + { + "name": "花椒:可选", + "count": 1 + }, + { + "name": "鸡精:可选", + "count": 1 + }, + { + "name": "生抽 10ml", + "count": 1 + }, + { + "name": "料酒 5ml", + "count": 1 + }, + { + "name": "醋 15ml", + "count": 1 + }, + { + "name": "白糖 10g", + "count": 1 + }, + { + "name": "盐 5g", + "count": 1 + }, + { + "name": "豆瓣酱 15g", + "count": 1 + }, + { + "name": "黑椒", + "count": 1 + }, + { + "name": "豆瓣 1000 g", + "count": 1 + }, + { + "name": "豆鼓 10 g", + "count": 1 + }, + { + "name": "红花椒 150 g", + "count": 1 + }, + { + "name": "白酒 150 ml", + "count": 1 + }, + { + "name": "山奈 5 g", + "count": 1 + }, + { + "name": "八角 10 g", + "count": 1 + }, + { + "name": "草果 10 g", + "count": 1 + }, + { + "name": "丁香 5 g", + "count": 1 + }, + { + "name": "小茴香 10 g", + "count": 1 + }, + { + "name": "白芷 10 g", + "count": 1 + }, + { + "name": "荜拨 5 g", + "count": 1 + }, + { + "name": "香草 10 g", + "count": 1 + }, + { + "name": "香果 10 g", + "count": 1 + }, + { + "name": "陈皮 5 g", + "count": 1 + }, + { + "name": "香茅 10 g", + "count": 1 + }, + { + "name": "砂仁 10 g", + "count": 1 + }, + { + "name": "白蔻 10 g", + "count": 1 + }, + { + "name": "香叶 10 g", + "count": 1 + }, + { + "name": "熟豆油", + "count": 1 + }, + { + "name": "鸡粉", + "count": 1 + }, + { + "name": "蔬菜高汤", + "count": 1 + }, + { + "name": "味素", + "count": 1 + }, + { + "name": "风味调料", + "count": 1 + }, + { + "name": "油 15 ml", + "count": 1 + }, + { + "name": "生抽 10 ml", + "count": 1 + }, + { + "name": "安琪干酵母粉", + "count": 1 + }, + { + "name": "烘焙油纸", + "count": 1 + }, + { + "name": "调味料: 酱油,盐", + "count": 1 + }, + { + "name": "炒料:盐、味精、老抽、生抽、孜然粉", + "count": 1 + }, + { + "name": "其他调味料:胡椒粉", + "count": 1 + }, + { + "name": "甜面酱", + "count": 1 + }, + { + "name": "酵母粉", + "count": 1 + }, + { + "name": "生抽,老抽,料酒,盐,五香粉", + "count": 1 + }, + { + "name": "老干妈", + "count": 1 + }, + { + "name": "醪糟", + "count": 1 + }, + { + "name": "韩式辣酱", + "count": 1 + }, + { + "name": "雪碧", + "count": 1 + }, + { + "name": "盐、生抽、老抽、耗油", + "count": 1 + }, + { + "name": "椒盐", + "count": 1 + }, + { + "name": "若干吸油纸", + "count": 1 + }, + { + "name": "蒲烧汁", + "count": 1 + }, + { + "name": "陈醋", + "count": 1 + }, + { + "name": "生抽 / 味极鲜", + "count": 1 + } + ], + "工具": [ + { + "name": "手动压汁器", + "count": 4 + }, + { + "name": "微波炉", + "count": 3 + }, + { + "name": "烤箱", + "count": 3 + }, + { + "name": "擀面杖", + "count": 3 + }, + { + "name": "保鲜膜", + "count": 2 + }, + { + "name": "电动打蛋器", + "count": 2 + }, + { + "name": "注:如果有可能,请尽量把刀磨的锋利一些。", + "count": 2 + }, + { + "name": "电饭煲", + "count": 2 + }, + { + "name": "平底锅", + "count": 2 + }, + { + "name": "炒锅", + "count": 2 + }, + { + "name": "密封袋", + "count": 1 + }, + { + "name": "火锅底料", + "count": 1 + }, + { + "name": "刷子", + "count": 1 + }, + { + "name": "盆", + "count": 1 + }, + { + "name": "菜刀一个", + "count": 1 + }, + { + "name": "笊篱一个、锅铲一个", + "count": 1 + }, + { + "name": "分可控火候微波炉或不可控火候微波炉", + "count": 1 + }, + { + "name": "筷子或牙签", + "count": 1 + }, + { + "name": "能放进微波炉的容器", + "count": 1 + }, + { + "name": "电锅", + "count": 1 + }, + { + "name": "秒表", + "count": 1 + }, + { + "name": "锡纸或保鲜膜", + "count": 1 + }, + { + "name": "圆碟子", + "count": 1 + }, + { + "name": "蒸架", + "count": 1 + }, + { + "name": "空气炸锅", + "count": 1 + }, + { + "name": "家庭小陶瓷碗", + "count": 1 + }, + { + "name": "家庭铁勺子", + "count": 1 + }, + { + "name": "蘸料碟", + "count": 1 + }, + { + "name": "可密封容器", + "count": 1 + }, + { + "name": "一个容量在 600 毫升以上且直径小深度深的容器", + "count": 1 + }, + { + "name": "小刀", + "count": 1 + }, + { + "name": "冰淇淋模具", + "count": 1 + }, + { + "name": "打蛋器或筷子", + "count": 1 + }, + { + "name": "铝合金阳极模具", + "count": 1 + }, + { + "name": "刮刀", + "count": 1 + }, + { + "name": "一个装成品的容器", + "count": 1 + }, + { + "name": "打蛋器", + "count": 1 + }, + { + "name": "烤箱 大小不限", + "count": 1 + }, + { + "name": "克数称", + "count": 1 + }, + { + "name": "搅拌器 包含且不限于筷子 打蛋器等工具", + "count": 1 + }, + { + "name": "料理搅拌机", + "count": 1 + }, + { + "name": "筛网", + "count": 1 + }, + { + "name": "不粘平底锅", + "count": 1 + }, + { + "name": "烘焙刮刀", + "count": 1 + }, + { + "name": "一次性塑料手套", + "count": 1 + }, + { + "name": "金属蛋糕模具", + "count": 1 + }, + { + "name": "烘焙纸", + "count": 1 + }, + { + "name": "餐刀", + "count": 1 + }, + { + "name": "隔热手套", + "count": 1 + }, + { + "name": "[可选] 分蛋器", + "count": 1 + }, + { + "name": "吧勺", + "count": 1 + }, + { + "name": "利口酒杯", + "count": 1 + }, + { + "name": "打火机", + "count": 1 + }, + { + "name": "压汁器", + "count": 1 + }, + { + "name": "海波杯", + "count": 1 + }, + { + "name": "研杵", + "count": 1 + }, + { + "name": "杯子,例如带刻度的杯子,陶瓷杯或保温杯", + "count": 1 + }, + { + "name": "杯", + "count": 1 + }, + { + "name": "调理机/果汁机", + "count": 1 + }, + { + "name": "搅拌机", + "count": 1 + }, + { + "name": "雪克瓶", + "count": 1 + }, + { + "name": "高球杯", + "count": 1 + }, + { + "name": "捣药罐", + "count": 1 + }, + { + "name": "燃气灶", + "count": 1 + }, + { + "name": "高压锅/砂锅/普通铝锅", + "count": 1 + }, + { + "name": "平底锅 || 微波炉", + "count": 1 + }, + { + "name": "需要烤箱 1 个", + "count": 1 + }, + { + "name": "隔热手套 1 双", + "count": 1 + }, + { + "name": "1 个空气炸锅", + "count": 1 + }, + { + "name": "粉碎机", + "count": 1 + }, + { + "name": "电饭锅", + "count": 1 + }, + { + "name": "小碗若干", + "count": 1 + }, + { + "name": "瓦罐或者高压锅", + "count": 1 + }, + { + "name": "2 个大碗", + "count": 1 + }, + { + "name": "1 个小碗", + "count": 1 + }, + { + "name": "直径 30cm 以上的盆", + "count": 1 + }, + { + "name": "披萨石", + "count": 1 + }, + { + "name": "盆、盘子", + "count": 1 + }, + { + "name": "电饼铛", + "count": 1 + }, + { + "name": "蒸锅,需带笼屉", + "count": 1 + }, + { + "name": "厚底煮锅+严丝合缝的锅盖", + "count": 1 + }, + { + "name": "一口有点深度的锅", + "count": 1 + }, + { + "name": "洗菜盆、直径 18cm 的小锅", + "count": 1 + }, + { + "name": "两个塑料簸箕", + "count": 1 + }, + { + "name": "冰箱", + "count": 1 + }, + { + "name": "砵或者有一定深度的碗", + "count": 1 + } + ], + "其他": [ + { + "name": "冰块", + "count": 7 + }, + { + "name": "热水", + "count": 2 + }, + { + "name": "水 400ml", + "count": 1 + }, + { + "name": "清水", + "count": 1 + }, + { + "name": "放凉浓缩咖啡", + "count": 1 + }, + { + "name": "蛋挞皮 品牌不限", + "count": 1 + }, + { + "name": "打碎的冰块", + "count": 1 + }, + { + "name": "冰镇苏打水", + "count": 1 + }, + { + "name": "汤力水气泡水", + "count": 1 + }, + { + "name": "凉白开水", + "count": 1 + }, + { + "name": "开水", + "count": 1 + }, + { + "name": "50 ml 清水", + "count": 1 + }, + { + "name": "网购蛋挞液 1 盒,蛋挞皮 1 盒", + "count": 1 + }, + { + "name": "1 袋半成品薯条", + "count": 1 + }, + { + "name": "1000 ml", + "count": 1 + }, + { + "name": "豆母子 140 g", + "count": 1 + }, + { + "name": "麦芽粉 12.5 g", + "count": 1 + }, + { + "name": "山楂 10 g", + "count": 1 + }, + { + "name": "饮用水", + "count": 1 + }, + { + "name": "红萝卜 1 个", + "count": 1 + }, + { + "name": "冷水", + "count": 1 + }, + { + "name": "温水", + "count": 1 + }, + { + "name": "其他:随便,不会影响到口味", + "count": 1 + } + ] +} \ No newline at end of file diff --git a/classified-ingredients.txt b/classified-ingredients.txt new file mode 100644 index 0000000..b3048cf --- /dev/null +++ b/classified-ingredients.txt @@ -0,0 +1,639 @@ + +## 荤 (107项) + + 鸡蛋 (37) + 五花肉 (8) + 牛奶 (7) + 牛肉 (6) + 黄油 (5) + 培根 (3) + 淡奶油 (3) + 排骨 (3) + 瘦肉 (3) + 猪肉 (3) + 鸡腿 (2) + 里脊肉 (2) + 牛腩 (2) + 猪瘦肉 (2) + 肉末 (2) + 皮蛋 (2) + 肉 (2) + 火腿肠 (2) + 青蟹 (1) + 小龙虾 (1) + 黑鳕鱼,带皮 (1) + 草鱼 (1) + 生蚝 (1) + 鲈鱼 (1) + 蒸鱼豉油 (1) + 活虾 (1) + 鲤鱼 (1) + 鱼头一个 (1) + 油、盐、生抽、老抽、陈醋、蚝油、料酒、白糖 (1) + 鲤鱼、五花肉 (1) + 海虾 (1) + 蛏子 (1) + 翘嘴鱼 (1) + 鳊鱼 (1) + 鲜虾 (1) + 新鲜鸡蛋 (1) + 牛奶 50-100g,能够将燕麦搅拌粘稠即可 (1) + 全脂牛奶/奶油 (1) + 芝士 (1) + 蛋黄酱 (1) + 马斯卡彭芝士 (1) + 无菌鸡蛋 (1) + 无盐黄油 (1) + 全脂奶粉 (1) + 全脂奶粉或淡奶 (1) + 兔肉 (1) + 鸡胸肉 (1) + 半只鸡 (1) + 鸡翅中 (1) + 梅头猪肉 (1) + 鸭肉 (1) + 肋排 (1) + 鸡 (1) + 牛柳或牛肩肉 (1) + 手枪腿 (1) + 牛里脊 (1) + 鸡肉,土豆,菜椒和甜椒 (1) + 鸡爪 (1) + 猪里脊肉 (1) + 鸡蛋清 (1) + 凤尾 (1) + 猪肉片 (1) + 带皮五花肉 (1) + 猪皮 (1) + 碎牛肉 (1) + 猪蹄 (1) + 带皮羊排肉 (1) + 羊排 (1) + 五花肉薄片 (1) + 鲜仔鸭肉 (1) + 鲜鸭血 (1) + 鱼露 (1) + 鸡蛋黄 (1) + 猪里脊 (1) + 大鸡腿 (1) + 里脊肉 200g (1) + 蛋清 1 个 (1) + 无骨肉 (1) + 盐、鸡精、蚝油、生抽、老抽、香油、香醋、芝麻酱 (1) + 牛油 4500 g (1) + 纯猪油 500 g (1) + 肉桂 10 g (1) + 昂刺鱼或者沙光鱼 (1) + 前腿肉 (1) + 一个鸡蛋的鸡蛋清 (1) + 米 (1) + 虾仁 (1) + 牛肉高汤 (1) + 乳鸽 (1) + 肉沫 (1) + 腊肠 1 根 (1) + 鸡蛋 1 个 (1) + :鸡蛋,青菜 (1) + 肥牛片 (1) + 炒河粉、猪肉/牛肉 (1) + 黄瓜、面筋块、绿豆芽、鸡蛋、蒜瓣、小葱、淀粉 (1) + 肉丁/肉末 (1) + 肉汤汁 (1) + 猪五花肉 (1) + 煮熟的腊肉 (1) + 火腿 (1) + 灯影牛肉丝 (1) + 火锅牛肉卷 (1) + 午餐肉 (1) + 虾皮 (1) + 蟹味菇 (1) + 螺丝椒 (1) + +## 素 (209项) + + 葱 (22) + 蒜 (21) + 淀粉 (20) + 水 (15) + 洋葱 (12) + 青椒 (12) + 干辣椒 (10) + 土豆 (10) + 香菜 (9) + 小葱 (9) + 大葱 (8) + 香葱 (8) + 西红柿 (8) + 面粉 (7) + 小米椒 (7) + 小米辣 (7) + 胡萝卜 (7) + 茄子 (7) + 芝麻 (6) + 番茄酱 (6) + 生粉 (5) + 柠檬 (5) + 花生油 (4) + 白芝麻 (4) + 芹菜 (4) + 黄瓜 (4) + 芝麻油 (3) + 蒜苗 (3) + 豆芽 (3) + 土豆淀粉 (3) + 辣椒 (3) + 香菇 (3) + 红枣 (2) + 燕麦 (2) + 干辣椒面 (2) + 青红椒 (2) + 辣椒粉 (2) + 莴笋 (2) + 尖椒 (2) + 白菜 (2) + 水淀粉 (2) + 青菜 (2) + 菜椒 (2) + 生菜 (2) + 枸杞 (2) + 芝麻酱 (2) + 米粉 (2) + 西葫芦 (2) + 青辣椒 (2) + 子弹头辣椒 (1) + 干辣椒段 (1) + 千张 (1) + 绿豆芽 (1) + 芹菜段 (1) + 熟花生米 (1) + 香菜一颗 (1) + 大葱、姜、大蒜、香菜、美人椒 (1) + 八角、干辣椒 (1) + 大葱、姜、大蒜、干辣椒 (1) + 大葱葱白 (1) + 老豆腐 (1) + 米酒 (1) + 新鲜吐司 (1) + 面包机 (1) + 糯米 (1) + 桂圆 (1) + 新鲜玉米 (1) + 放得下玉米的锅 (1) + 饺子 (1) + 冷冻花卷 (1) + 可根据口味选择增加 50g 蔬菜,如菠菜。 (1) + 面包片 (1) + 手抓饼皮 (1) + 吐司 (1) + 熟白芝麻 (1) + 白醋/米醋 (1) + 草莓 (1) + 原味奥利奥 (1) + 低筋面粉 (1) + [可选] 柠檬汁或白醋 (1) + 手指饼干 (1) + 筛网 网孔约为 1 毫米 (1) + 荔浦芋头 (1) + 紫薯粉 (1) + 糯米粉 (1) + 玉米淀粉 (1) + 混合坚果 (1) + 饼干 (1) + 魔芋粉 (1) + 水果刀 (1) + 百香果 (1) + 橙子 (1) + 耙耙柑 (1) + 乌枣 (1) + 乌梅 (1) + 山楂片 (1) + 小葱/大葱/洋葱 (1) + 花生 (1) + 罐头菠萝片 (1) + 泡椒 (1) + 熟花生 (1) + 生花生 (1) + 花椒,香叶,香果,干线椒,大蒜,大葱 (1) + 梅菜 (1) + 干辣椒粉 (1) + 红辣椒 (1) + 红泡椒 (1) + 干小米椒🌶 (1) + 土豆干粉条 (1) + 甜椒 (1) + 凤梨 (1) + 白萝卜 (1) + 蒜苔 (1) + 地瓜粉 (1) + 香干 (1) + 豆干 (1) + 香芹/芹菜 (1) + 辣椒:青椒或者红椒都可以 (1) + 干香菇 (1) + 胡萝卜 100g (1) + 青椒 100g (1) + 木耳 5g (1) + 淀粉 10g (1) + 干豆腐 (1) + 北京麻辣方便面 (1) + 凉皮、面筋 (1) + 黄瓜、大蒜、绿豆芽 (1) + 1 袋 半成品意大利面 (1) + 糍粑辣椒 3000 g (1) + 大葱 100 g (1) + 洋葱 100 g (1) + 老油 ?? 干辣椒面 15 g (1) + 未过期的一袋速冻水饺 (1) + 速冻汤圆 (1) + 未过期的一袋速冻馄饨 (1) + 香菜 1 根 (1) + 小米 (1) + 豆腐 (1) + 木耳 (1) + 小香葱 (1) + 泡好的粉丝 (1) + 大米 (1) + 紫菜 (1) + 番茄 (1) + 菌菇 (1) + 玉米 (1) + 金针菇 (1) + 银耳 (1) + 去心莲子 (1) + 豌豆淀粉 (1) + 花生碎 (1) + 米 200 ml (1) + 香葱 1 颗 (1) + 芝麻香油 (1) + 中筋面粉 (1) + 花椰菜 (1) + 年糕/白粿 (1) + 意大利面 (1) + 番茄酱 / 黑胡椒酱 (1) + 方便面 (1) + 馒头 (1) + 挂面 (1) + 热干面特有的碱水面 (1) + 酸豆角 (1) + 萝卜干 (1) + 辣椒油 (1) + 泡面 (1) + 干红椒 (1) + 北方大米 (1) + 江南米或北方大米 (1) + 酸笋 (1) + 剁椒 (1) + 米醋 (1) + 面 (1) + 小麦粉 (1) + 冷饭 (1) + 蕨根粉 (1) + 小汤圆 (1) + 米饭 (1) + 蘑菇 (1) + 风干快熟面/任何牌子的快熟面 (1) + 原料:半干荞麦面、娃娃菜、生菜 (1) + 花生酱:pdd莺歌花生酱 24.8元两瓶,可以做68份 (1) + 娃娃菜 (1) + 油麦菜 (1) + 萝卜 (1) + 包菜 (1) + 粉丝 (1) + 葱、蒜、干辣椒 (1) + 花菜 (1) + 玉米粒 (1) + 芝麻粒 (1) + 四季豆 (1) + 橄榄菜 (1) + 叶菜类蔬菜 (1) + 菜籽油或花生油 (1) + 冬瓜 (1) + 青茄子 (1) + 茭白 (1) + 菠菜 (1) + 白豆腐 (1) + 白玉菇 (1) + 西蓝花 (1) + 毛豆 (1) + 话梅 (1) + 红椒 (1) + 豆角 (1) + 红尖椒 (1) + 嫩豆腐 (1) + +## 配菜 (34项) + + 姜 (30) + 大蒜 (16) + 蒜头 (7) + 生姜 (7) + 姜末 (4) + 姜片 (3) + 葱姜蒜 (2) + 新鲜绿叶 (2) + 蒜蓉 (2) + 葱、姜、蒜 (2) + 蒜瓣 (2) + 葱花 (2) + 青葱 (1) + 灯笼椒 (1) + 蒜蓉酱 (1) + 薄荷叶或其他绿叶 (1) + 干桂花 (1) + 美人辣 (1) + 朝天椒 (1) + 野山椒 (1) + 蒜末 (1) + 蒜仔 (1) + 2cm 两段葱段、两片姜片,葱花、姜各 10g (1) + 姜 20g (1) + 葱 20g (1) + 蒜 2 瓣 (1) + 生姜片 (1) + 盆、碗、盘子、蒜臼 (1) + 老姜 250 g (1) + 大蒜 200 g (1) + 葱姜花椒水 (1) + 黄花 (1) + 蒜水 (1) + 葱,姜,蒜 (1) + +## 调料 (162项) + + 盐 (70) + 生抽 (44) + 料酒 (38) + 食用油 (36) + 白糖 (24) + 老抽 (22) + 酱油 (21) + 白砂糖 (19) + 鸡精 (18) + 食用盐 (17) + 八角 (13) + 胡椒粉 (13) + 油 (12) + 豆瓣酱 (12) + 香叶 (11) + 蚝油 (11) + 花椒 (10) + 糖 (10) + 桂皮 (9) + 冰糖 (9) + 五香粉 (8) + 香醋 (7) + 醋 (7) + 耗油 (7) + 生抽酱油 (6) + 香油 (6) + 白醋 (5) + 味精 (5) + 青花椒 (4) + 食盐 (4) + 咖喱块 (3) + 黄豆酱 (3) + 啤酒 (3) + 白胡椒粉 (3) + 十三香 (3) + 金酒 (3) + 麻油 (3) + 植物油 (3) + 豆豉 (3) + 菜籽油 (3) + 黑胡椒 (2) + 白芷 (2) + 孜然粉 (2) + 可可粉 (2) + 蜂蜜 (2) + 可乐 (2) + 孜然 (2) + 油泼辣子 (2) + 橄榄油 (2) + 藤椒油 (2) + 椰浆 (1) + 郫县豆瓣 (1) + 油、盐、鸡精、生抽、老抽、陈醋、黑胡椒粉、料酒 (1) + 黑胡椒粒 (1) + 果酱 (1) + 泡打粉 (1) + 空气炸锅或者油锅 (1) + 茴香 (1) + 红茶 (1) + 草寇 (1) + 花椒粉 (1) + 麻辣鲜 (1) + 朗姆酒 (1) + 雪花酥/牛轧糖模具 (1) + 棉花糖 (1) + 赤藓糖醇 (1) + 塔塔粉 (1) + 甘露咖啡酒 (1) + 爱尔兰百利甜酒 (1) + 蓝天原味伏特加 (1) + 波旁威士忌 (1) + 可口可乐 (1) + 袋泡红茶 (1) + 茉莉绿茶茶叶/苏打气泡水二选一 (1) + 茉莉绿茶 (1) + 酸梅晶固体饮料 (1) + 方糖 (1) + 北京二锅头酒 (1) + 黄冰糖 (1) + 甘草 (1) + 陈皮 (1) + 红豆蔻 (1) + 苏打气泡水 (1) + 龙舌兰酒 (1) + 伏特加 (1) + 白朗姆酒 (1) + 橙味甜酒 (1) + 枫糖浆 (1) + 山奈 (1) + 白蔻 (1) + 小茴香 (1) + 料酒或啤酒 (1) + 茄汁 (1) + 老抽酱油 (1) + 丁香 (1) + 郫县红油豆瓣酱 (1) + 花椒碎 (1) + 油,盐,生抽,耗油,料酒,白糖 (1) + 黑醋 (1) + 花椒油 (1) + 红油豆瓣 (1) + 蕃茄酱 (1) + 大料、花椒、白芷、桂皮、丁香、香叶、小茴香 (1) + 干罗勒或百里香 (1) + 料酒或者黄酒 (1) + 酒 (1) + 小料 (1) + 白糖 or 冰糖 (1) + 黑胡椒粉 (1) + 生抽、白胡椒粉,白糖,料/黄酒,八角三小片 (1) + 花椒:可选 (1) + 鸡精:可选 (1) + 生抽 10ml (1) + 料酒 5ml (1) + 醋 15ml (1) + 白糖 10g (1) + 盐 5g (1) + 豆瓣酱 15g (1) + 黑椒 (1) + 豆瓣 1000 g (1) + 豆鼓 10 g (1) + 红花椒 150 g (1) + 白酒 150 ml (1) + 山奈 5 g (1) + 八角 10 g (1) + 草果 10 g (1) + 丁香 5 g (1) + 小茴香 10 g (1) + 白芷 10 g (1) + 荜拨 5 g (1) + 香草 10 g (1) + 香果 10 g (1) + 陈皮 5 g (1) + 香茅 10 g (1) + 砂仁 10 g (1) + 白蔻 10 g (1) + 香叶 10 g (1) + 熟豆油 (1) + 鸡粉 (1) + 蔬菜高汤 (1) + 味素 (1) + 风味调料 (1) + 油 15 ml (1) + 生抽 10 ml (1) + 安琪干酵母粉 (1) + 烘焙油纸 (1) + 调味料: 酱油,盐 (1) + 炒料:盐、味精、老抽、生抽、孜然粉 (1) + 其他调味料:胡椒粉 (1) + 甜面酱 (1) + 酵母粉 (1) + 生抽,老抽,料酒,盐,五香粉 (1) + 老干妈 (1) + 醪糟 (1) + 韩式辣酱 (1) + 雪碧 (1) + 盐、生抽、老抽、耗油 (1) + 椒盐 (1) + 若干吸油纸 (1) + 蒲烧汁 (1) + 陈醋 (1) + 生抽 / 味极鲜 (1) + +## 工具 (86项) + + 手动压汁器 (4) + 微波炉 (3) + 烤箱 (3) + 擀面杖 (3) + 保鲜膜 (2) + 电动打蛋器 (2) + 注:如果有可能,请尽量把刀磨的锋利一些。 (2) + 电饭煲 (2) + 平底锅 (2) + 炒锅 (2) + 密封袋 (1) + 火锅底料 (1) + 刷子 (1) + 盆 (1) + 菜刀一个 (1) + 笊篱一个、锅铲一个 (1) + 分可控火候微波炉或不可控火候微波炉 (1) + 筷子或牙签 (1) + 能放进微波炉的容器 (1) + 电锅 (1) + 秒表 (1) + 锡纸或保鲜膜 (1) + 圆碟子 (1) + 蒸架 (1) + 空气炸锅 (1) + 家庭小陶瓷碗 (1) + 家庭铁勺子 (1) + 蘸料碟 (1) + 可密封容器 (1) + 一个容量在 600 毫升以上且直径小深度深的容器 (1) + 小刀 (1) + 冰淇淋模具 (1) + 打蛋器或筷子 (1) + 铝合金阳极模具 (1) + 刮刀 (1) + 一个装成品的容器 (1) + 打蛋器 (1) + 烤箱 大小不限 (1) + 克数称 (1) + 搅拌器 包含且不限于筷子 打蛋器等工具 (1) + 料理搅拌机 (1) + 筛网 (1) + 不粘平底锅 (1) + 烘焙刮刀 (1) + 一次性塑料手套 (1) + 金属蛋糕模具 (1) + 烘焙纸 (1) + 餐刀 (1) + 隔热手套 (1) + [可选] 分蛋器 (1) + 吧勺 (1) + 利口酒杯 (1) + 打火机 (1) + 压汁器 (1) + 海波杯 (1) + 研杵 (1) + 杯子,例如带刻度的杯子,陶瓷杯或保温杯 (1) + 杯 (1) + 调理机/果汁机 (1) + 搅拌机 (1) + 雪克瓶 (1) + 高球杯 (1) + 捣药罐 (1) + 燃气灶 (1) + 高压锅/砂锅/普通铝锅 (1) + 平底锅 || 微波炉 (1) + 需要烤箱 1 个 (1) + 隔热手套 1 双 (1) + 1 个空气炸锅 (1) + 粉碎机 (1) + 电饭锅 (1) + 小碗若干 (1) + 瓦罐或者高压锅 (1) + 2 个大碗 (1) + 1 个小碗 (1) + 直径 30cm 以上的盆 (1) + 披萨石 (1) + 盆、盘子 (1) + 电饼铛 (1) + 蒸锅,需带笼屉 (1) + 厚底煮锅+严丝合缝的锅盖 (1) + 一口有点深度的锅 (1) + 洗菜盆、直径 18cm 的小锅 (1) + 两个塑料簸箕 (1) + 冰箱 (1) + 砵或者有一定深度的碗 (1) + +## 其他 (23项) + + 冰块 (7) + 热水 (2) + 水 400ml (1) + 清水 (1) + 放凉浓缩咖啡 (1) + 蛋挞皮 品牌不限 (1) + 打碎的冰块 (1) + 冰镇苏打水 (1) + 汤力水气泡水 (1) + 凉白开水 (1) + 开水 (1) + 50 ml 清水 (1) + 网购蛋挞液 1 盒,蛋挞皮 1 盒 (1) + 1 袋半成品薯条 (1) + 1000 ml (1) + 豆母子 140 g (1) + 麦芽粉 12.5 g (1) + 山楂 10 g (1) + 饮用水 (1) + 红萝卜 1 个 (1) + 冷水 (1) + 温水 (1) + 其他:随便,不会影响到口味 (1) \ No newline at end of file diff --git a/cloudfunctions/barcode/index.js b/cloudfunctions/barcode/index.js index e99744f..a3200dc 100644 --- a/cloudfunctions/barcode/index.js +++ b/cloudfunctions/barcode/index.js @@ -1,15 +1,34 @@ const cloud = require('wx-server-sdk'); const { createQuota } = require('./utils/create_quota'); -const { getBarcode } = require('./utils/get_barcode'); +const { getBarcode, uploadImage } = require('./utils/get_barcode'); const { updateQuota } = require('./utils/update_quota'); -cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +cloud.init() const db = cloud.database() exports.main = async (event) => { const { OPENID } = cloud.getWXContext() const { barcode, action, quotaType = 'barcode' } = event + if (action === 'fixImage') { + const { data: docs } = await db.collection('barcodes').where({ barcode }).get(); + if (!docs.length) return { code: 404, errmsg: 'Barcode not found' }; + + const doc = docs[0]; + if (!doc.pic || !doc.pic.startsWith('http')) { + return { code: 0, data: { pic: doc.pic } }; // Already cloud URL + } + + try { + const fileID = await uploadImage(doc.pic, barcode); + await db.collection('barcodes').doc(doc._id).update({ data: { pic: fileID } }); + return { code: 0, data: { pic: fileID } }; + } catch (e) { + console.error('fixImage fail:', e.message); + return { code: 500, errmsg: e.message }; + } + } + if (action === 'get') { const { list } = await db.collection('barcodes').aggregate() .lookup({ @@ -35,8 +54,23 @@ exports.main = async (event) => { if (!userinfo) { return { code: 403, errmsg: 'userinfo is empty' } } + + // 用户保存过该条码的商品 → 免费获取 + const { data: savedSkus } = await db.collection('sku') + .where({ creator: OPENID, barcode }) + .limit(1) + .get(); + if (savedSkus.length > 0) { + try { + const data = await getBarcode(db, barcode, OPENID); + return { code: 0, data: { ...data, userinfo } }; + } catch (e) { + return { code: 404, errmsg: 'can not get the barcode' } + } + } + const quotaRes = await db.collection('quota').where({ openid: OPENID, type: quotaType }).get() - + if (quotaRes.data.length > 0) { const [quota] = quotaRes.data; if (quota.times > 0) { diff --git a/cloudfunctions/barcode/package.json b/cloudfunctions/barcode/package.json index 5c8c85e..816806f 100644 --- a/cloudfunctions/barcode/package.json +++ b/cloudfunctions/barcode/package.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.7", + "crypto-js": "^4.2.0", "wx-server-sdk": "~3.0.1" } } diff --git a/cloudfunctions/barcode/utils/get_barcode.js b/cloudfunctions/barcode/utils/get_barcode.js index bfdebce..cbd37f4 100644 --- a/cloudfunctions/barcode/utils/get_barcode.js +++ b/cloudfunctions/barcode/utils/get_barcode.js @@ -1,58 +1,127 @@ const axios = require('axios'); +const CryptoJS = require('crypto-js'); const cloud = require('wx-server-sdk'); -const appcode = '8db126b4c57b4edd8ab8b7355cb3db72'; +cloud.init() + +const SECRET_ID = 'V9PgSHCOrwi8Eil7'; +const SECRET_KEY = '6b8XMzqDu5IEt0gsUweBgu0xcUmn8nHw'; +const API_URL = 'https://ap-shanghai.cloudmarket-apigw.com/service-32z6n3ab/chkBarCode'; + +function randomUUID() { + const hex = '0123456789abcdef'; + let uuid = ''; + for (let i = 0; i < 36; i++) { + if (i === 8 || i === 13 || i === 18 || i === 23) uuid += '-'; + else if (i === 14) uuid += '4'; + else if (i === 19) uuid += hex[(Math.floor(Math.random() * 4) + 8)]; + else uuid += hex[Math.floor(Math.random() * 16)]; + } + return uuid; +} + +function signRequest() { + const datetime = new Date().toGMTString(); + const uuid = randomUUID(); + const signStr = 'x-date: ' + datetime; + const sign = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA1(signStr, SECRET_KEY)); + const auth = '{"id": "' + SECRET_ID + '", "x-date": "' + datetime + '", "signature": "' + sign + '"}'; + return { datetime, uuid, auth }; +} + +function mapResponse(body) { + const b = body.showapi_res_body; + return { + barcode: b.code || '', + name: b.goodsName || '', + brand: b.trademark || '', + company: b.manuName || '', + type: b.spec || '', + origincountry: b.ycg || '', + description: b.goodsType || '', + netcontent: b.note || '', + packagetype: '', + pic: b.img || b.sptmImg || '', + }; +} + +async function uploadImage(imageUrl, barcode) { + if (!imageUrl || !imageUrl.startsWith('http')) return imageUrl; + try { + const res = await axios({ + url: imageUrl, + responseType: 'arraybuffer', + timeout: 8000, + }); + const ext = (imageUrl.split('.').pop() || 'jpg').split('?')[0]; + const cloudPath = `barcodes/${barcode}_${Date.now()}.${ext}`; + const { fileID } = await cloud.uploadFile({ + cloudPath, + fileContent: Buffer.from(res.data), + }); + return fileID; + } catch (e) { + console.error('upload image fail:', e.message); + return imageUrl; // fallback to original URL + } +} + +exports.uploadImage = uploadImage; exports.getBarcode = async (db, barcode, openid) => { + // Check DB first + const { data: existing } = await db.collection('barcodes') + .where({ barcode }) + .limit(1) + .get(); + + if (existing.length > 0) { + const doc = existing[0]; + // 兼容旧数据:HTTP 图片转存到云存储 + if (doc.pic && doc.pic.startsWith('http')) { + const fileID = await uploadImage(doc.pic, barcode); + if (fileID && fileID.startsWith('cloud://')) { + await db.collection('barcodes').doc(doc._id).update({ data: { pic: fileID } }); + doc.pic = fileID; + } + } + return doc; + } + + // Fetch from API + const { uuid, auth } = signRequest(); + const url = API_URL + '?code=' + encodeURIComponent(barcode); + const res = await axios({ - url: 'https://jisutxmcx.market.alicloudapi.com/barcode2/query', + url, + timeout: 5000, headers: { - 'Authorization': 'APPCODE ' + appcode - }, - params: { - barcode + 'request-id': uuid, + 'Authorization': auth, } }); - if (res.status === 200) { - const { data } = res; - - if (data.status == 0) { - const { result } = data; - const body = {}; - - - const params = { ...result, ...body, rawPic: data.result.pic, creator: openid, create_time: new Date() }; - const save = () => db.collection('barcodes').add({ data: params }); - try { - const { _id: docid } = await db.collection('barcodes').add({ data: params }) - if (result.pic) { - await Promise.race([async () => { - const imgRes = await axios.get(data.result.pic, { - responseType: 'stream' - }) - const filenameExtension = data.result.pic.split('.').pop(); - const filename = data.result.barcode + '_' + Date.now() + '.' + filenameExtension; - const { fileID } = await cloud.uploadFile({ - cloudPath: filename, - fileContent: imgRes.data - }) - await db.collection('barcodes').doc(docid).update({ - data: { - pic: fileID - } - }) - }, new Promise((resolve) => setTimeout(resolve, 3000))]) - } - return params; - } catch(e) { - console.error('save qrcode fail', e.message) - return {} + + if (res.status === 200 && res.data) { + const body = res.data; + if (body.showapi_res_code === 0 && body.showapi_res_body) { + const mapped = mapResponse(body); + + // Upload image to cloud storage (WeChat blocks HTTP images) + if (mapped.pic) { + mapped.pic = await uploadImage(mapped.pic, barcode); } - } else { - console.error('get qrcodes fail: ', data.status, '-', data.msg); - return {} + + await db.collection('barcodes').add({ + data: { + ...mapped, + creator: openid, + create_time: new Date(), + } + }); + return mapped; } + throw new Error(body.showapi_res_error || 'Barcode not found'); } - return {} + throw new Error('Barcode API error: ' + res.status); } \ No newline at end of file diff --git a/cloudfunctions/checkin/index.js b/cloudfunctions/checkin/index.js new file mode 100644 index 0000000..40b25dc --- /dev/null +++ b/cloudfunctions/checkin/index.js @@ -0,0 +1,96 @@ +const cloud = require('wx-server-sdk') + +cloud.init() +const db = cloud.database() +const _ = db.command + +const LEVELS = [ + { name: '初入厨房', icon: '🥚', minStreak: 1 }, + { name: '烹饪学徒', icon: '🍳', minStreak: 7 }, + { name: '掌勺师傅', icon: '🔪', minStreak: 30 }, + { name: '私房大厨', icon: '🧑‍🍳', minStreak: 90 }, + { name: '厨神在世', icon: '👨‍🍳', minStreak: 365 }, +]; + +function getToday() { + const d = new Date(); + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, '0'); + const day = String(d.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +} + +function computeLevel(streak) { + let current = LEVELS[0]; + for (let i = LEVELS.length - 1; i >= 0; i--) { + if (streak >= LEVELS[i].minStreak) { + current = LEVELS[i]; + break; + } + } + const idx = LEVELS.indexOf(current); + const next = LEVELS[idx + 1] || null; + return { ...current, next }; +} + +exports.main = async (event) => { + const { action } = event; + const { OPENID } = cloud.getWXContext(); + const coll = db.collection('checkins'); + + try { + const { data: records } = await coll.get(); + const record = records[0] || { streak: 0, lastDate: '' }; + + if (action === 'status') { + const level = computeLevel(record.streak || 0); + const checkedToday = record.lastDate === getToday(); + const streak = record.streak || 0; + const daysUntilNext = level.next ? level.next.minStreak - streak : 0; + const progressMax = level.next ? level.next.minStreak : streak || 1; + + return { + errno: 0, + data: { + streak, + checkedToday, + level, + daysUntilNext, + progress: streak, + progressMax, + allLevels: LEVELS, + } + }; + } + + if (action === 'checkin') { + const today = getToday(); + if (record.lastDate === today) { + const level = computeLevel(record.streak || 0); + return { errno: 0, data: { alreadyCheckedIn: true, streak: record.streak, level } }; + } + + const yesterday = new Date(Date.now() - 86400000); + const y = `${yesterday.getFullYear()}-${String(yesterday.getMonth() + 1).padStart(2, '0')}-${String(yesterday.getDate()).padStart(2, '0')}`; + const newStreak = record.lastDate === y ? (record.streak || 0) + 1 : 1; + + if (record._id) { + await coll.doc(record._id).update({ + data: { streak: newStreak, lastDate: today, updateTime: Date.now() } + }); + } else { + await coll.add({ + data: { streak: newStreak, lastDate: today, updateTime: Date.now() } + }); + } + + const level = computeLevel(newStreak); + return { errno: 0, data: { streak: newStreak, level } }; + } + + return { errno: -1, errmsg: 'Unknown action' }; + } catch (err) { + console.error('checkin error:', err); + return { errno: -1, errmsg: err.message }; + } +}; diff --git a/cloudfunctions/checkin/package.json b/cloudfunctions/checkin/package.json new file mode 100644 index 0000000..a42f9f7 --- /dev/null +++ b/cloudfunctions/checkin/package.json @@ -0,0 +1,9 @@ +{ + "name": "checkin", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.5.3" + } +} \ No newline at end of file diff --git a/cloudfunctions/cookbookAction/index.js b/cloudfunctions/cookbookAction/index.js new file mode 100644 index 0000000..920db95 --- /dev/null +++ b/cloudfunctions/cookbookAction/index.js @@ -0,0 +1,59 @@ +const cloud = require('wx-server-sdk') + +cloud.init() +const db = cloud.database() + +exports.main = async (event) => { + const { action, id, type } = event; + const { OPENID } = cloud.getWXContext(); + const coll = db.collection('cookbook'); + + if (!id || !['star', 'like'].includes(type)) { + return { errno: -1, errmsg: 'Invalid params' }; + } + // 'star' -> 'starreds', 'like' -> 'likeds' — match getCookbook's field names + const field = type === 'star' ? 'starreds' : 'likeds'; + + let { data: docs } = await coll.where({ id }).get(); + if (!docs.length) { + await coll.add({ data: { id, [field]: [] } }); + const res = await coll.where({ id }).get(); + docs = res.data; + } + const doc = docs[0]; + const items = doc[field] || []; + + if (action === 'status') { + const active = items.some(item => item.creator === OPENID && !item.isDel); + const count = items.filter(item => !item.isDel).length; + return { errno: 0, data: { active, count } }; + } + + if (action === 'toggle') { + const idx = items.findIndex(item => item.creator === OPENID); + + if (idx !== -1) { + // Toggle existing + items[idx] = { + ...items[idx], + isDel: !items[idx].isDel, + updateTime: Date.now(), + }; + } else { + // Create new + items.push({ + creator: OPENID, + isDel: false, + updateTime: Date.now(), + }); + } + + await coll.doc(doc._id).update({ data: { [field]: items } }); + + const active = items.some(item => item.creator === OPENID && !item.isDel); + const count = items.filter(item => !item.isDel).length; + return { errno: 0, data: { active, count } }; + } + + return { errno: -1, errmsg: 'Unknown action' }; +}; diff --git a/cloudfunctions/cookbookAction/package.json b/cloudfunctions/cookbookAction/package.json new file mode 100644 index 0000000..840f6e6 --- /dev/null +++ b/cloudfunctions/cookbookAction/package.json @@ -0,0 +1,9 @@ +{ + "name": "cookbookAction", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "wx-server-sdk": "~2.5.3" + } +} \ No newline at end of file diff --git a/cloudfunctions/quota/index.js b/cloudfunctions/quota/index.js index 552b981..afde240 100644 --- a/cloudfunctions/quota/index.js +++ b/cloudfunctions/quota/index.js @@ -1,6 +1,6 @@ const cloud = require('wx-server-sdk'); -cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +cloud.init() const db = cloud.database() const extraTimes = 5 diff --git a/cloudfunctions/reminders/index.js b/cloudfunctions/reminders/index.js index 9a7d008..3ef9505 100644 --- a/cloudfunctions/reminders/index.js +++ b/cloudfunctions/reminders/index.js @@ -5,7 +5,7 @@ const dayjs = require('dayjs') // cloud.init({ // env: cloud.default, // }) -cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +cloud.init() const db = cloud.database() exports.main = async (event, context) => { @@ -17,7 +17,7 @@ exports.main = async (event, context) => { const { data: [user] } = await db.collection('userinfo').skip(i++).limit(1).get(); const { data: skus } = await db.collection('sku').where({ creator: user.openid, - expiredDate: _.lte(dayjs().add(2, 'day').toDate()) + expiredDate: _.lte(dayjs().add(7, 'day').toDate()) }).get(); if (skus.length === 0) { continue; diff --git a/cloudfunctions/saveUserinfo/index.js b/cloudfunctions/saveUserinfo/index.js index 2731b4e..587e99a 100644 --- a/cloudfunctions/saveUserinfo/index.js +++ b/cloudfunctions/saveUserinfo/index.js @@ -3,22 +3,22 @@ const cloud = require('wx-server-sdk') cloud.init() const db = cloud.database() -// 云函数入口函数 exports.main = async (event) => { const { OPENID: openid } = cloud.getWXContext() const table = db.collection('user_info') - const { data } = await table.where({ openid }).get() - if (data.length > 0) return { errno: 0, errmsg: 'duplicated user' } - - await table.add({ - data: { - ...event, - openid, - createTime: new Date() - } - }) + if (data.length > 0) { + // Update existing + await table.doc(data[0]._id).update({ + data: { ...event, updateTime: Date.now() } + }) + } else { + // Create new + await table.add({ + data: { ...event, openid, createTime: Date.now() } + }) + } return { errno: 0, errmsg: 'ok' } } \ No newline at end of file diff --git a/cloudfunctions/sku/index.js b/cloudfunctions/sku/index.js index e037114..c54edd3 100644 --- a/cloudfunctions/sku/index.js +++ b/cloudfunctions/sku/index.js @@ -1,6 +1,6 @@ const cloud = require('wx-server-sdk'); -cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +cloud.init() const db = cloud.database() exports.main = async (event) => { @@ -9,9 +9,30 @@ exports.main = async (event) => { const table = db.collection('sku'); const _ = db.command; + if (action === 'lookup') { + const { barcode } = event; + const { data } = await table + .where({ creator: OPENID, barcode }) + .orderBy('create_time', 'desc') + .limit(1) + .get(); + if (data.length > 0) { + const item = data[0]; + return { + code: 0, + data: { + preserveDate: item.preserveDate || '', + unit: item.unit || 'month', + manufactureDate: item.manufactureDate || '', + } + }; + } + return { code: 0, data: null }; + } + if (action === 'get') { const { list } = await table.aggregate() - .match({ creator: OPENID, expiredDate: _.gte(new Date()) }) + .match({ creator: OPENID }) .sort({ expiredDate: 1 }) .skip(page * size) .limit(size) @@ -29,20 +50,21 @@ exports.main = async (event) => { if (action === 'save') { const { barcode, name, manufactureDate, preserveDate, unit, expiredDate, pic } = body; - const docid = await table.add({ - data: { - barcode, - name, - manufactureDate: new Date(manufactureDate), - preserveDate, - unit, - expiredDate: new Date(expiredDate), - pic, - creator: OPENID, - create_time: new Date(), - update_time: new Date() - } - }) + const data = { + barcode, + name, + preserveDate: preserveDate || '', + unit: unit || '', + expiredDate: expiredDate ? new Date(expiredDate) : null, + pic, + creator: OPENID, + create_time: new Date(), + update_time: new Date() + }; + if (manufactureDate) { + data.manufactureDate = new Date(manufactureDate); + } + const docid = await table.add({ data }) return { code: 0, errmsg: 0, data: docid } } }; diff --git a/cloudfunctions/userinfo/index.js b/cloudfunctions/userinfo/index.js index e76f8e2..74c4d25 100644 --- a/cloudfunctions/userinfo/index.js +++ b/cloudfunctions/userinfo/index.js @@ -1,6 +1,6 @@ const cloud = require('wx-server-sdk'); -cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) +cloud.init() const db = cloud.database() exports.main = async (event) => { @@ -10,7 +10,8 @@ exports.main = async (event) => { try { if (action === 'get') { - const { data: [userinfo] } = await table.where({ openid: OPENID }).get(); + const { data } = await table.where({ openid: OPENID }).get(); + const userinfo = data[0] || null; return { code: 0, message: '获取成功', @@ -18,32 +19,31 @@ exports.main = async (event) => { } } if (action === 'update') { - const { avatarUrl, nickName } = body; + const { avatarUrl, nickName, nickname } = body || {}; + const name = nickName || nickname || ''; + const updateData = { update_time: new Date() }; + if (avatarUrl !== undefined) updateData.avatarUrl = avatarUrl; + if (name) updateData.nickName = name; + + const { data } = await table.where({ openid: OPENID }).get(); + const userinfo = data[0]; - const { data: [userinfo] } = await table.where({ openid: OPENID }).get(); if (!userinfo) { await table.add({ data: { - avatarUrl, - nickName, + avatarUrl: avatarUrl || '', + nickName: name, openid: OPENID, create_time: new Date(), update_time: new Date(), } }) } else { - await table.where({ openid: OPENID }).update({ - data: { - avatarUrl, - nickName, - update_time: new Date(), - } - }) + await table.doc(userinfo._id).update({ data: updateData }) } return { code: 0, message: '更新成功', - data: userinfo, } } } diff --git a/miniprogram/app.js b/miniprogram/app.js index ed9dcfd..8436dfd 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -4,7 +4,7 @@ App({ onLaunch() { const updateManager = wx.getUpdateManager() - wx.cloud.init({ env: 'restart-9gd2a4k63f58d0c2' }) + wx.cloud.init() // wx.login({ // success: ({ code }) => { // post('miniprogram/login', { code }).then(data => { diff --git a/miniprogram/app.json b/miniprogram/app.json index e861dad..70943fd 100644 --- a/miniprogram/app.json +++ b/miniprogram/app.json @@ -1,16 +1,14 @@ { "pages": [ "pages/index/index", - "pages/kitchen/index", + "pages/stock/index", "pages/detail/index", "pages/my/index", "pages/myStarred/index", "pages/changelog/index", "pages/changelog/detail", - "pages/learn/index", - "pages/learn/detail", "pages/search/index", - "pages/stock/index", + "pages/ai-cook/index", "pages/stock-edit/index", "pages/stock-list/index", "pages/user-info/index" @@ -25,10 +23,6 @@ "pagePath": "pages/index/index", "text": "首页" }, - { - "pagePath": "pages/learn/index", - "text": "技巧" - }, { "pagePath": "pages/stock/index", "text": "仓储" diff --git a/miniprogram/components/standard-info/index.js b/miniprogram/components/standard-info/index.js index 3c99f77..cbd41f7 100644 --- a/miniprogram/components/standard-info/index.js +++ b/miniprogram/components/standard-info/index.js @@ -12,7 +12,9 @@ Component({ info: {}, hasResult: false, init: true, - noResult: false + noResult: false, + picFailed: false, + fixingPic: false, }, lifetimes: { @@ -28,37 +30,58 @@ Component({ videoAd.onClose((res) => {}) } }, - ready() { - wx.cloud.callFunction({ - name: 'barcode', - data: { - action: 'get', - barcode: this.data.barcode + async ready() { + // 先尝试自动获取(保存过该条码的用户免费) + try { + const { result } = await wx.cloud.callFunction({ + name: 'barcode', + data: { action: 'fetch', barcode: this.data.barcode } + }); + if (result && result.code === 0) { + this.setData({ hasResult: true, info: result.data, init: false }); + this.triggerEvent('dataready', { data: result.data }); + return; } - }).then(({ result }) => { - if (result.code == 0) { - const { data } = result; - - this.setData({ - hasResult: true, - info: data - }) - this.triggerEvent('dataready', { data }) - } else { - this.setData({ - hasResult: false - }) - this.getQuota(); - } - }).finally(() => { - this.setData({ - init: false - }) - }) + } catch (err) { + console.error('自动获取条码失败:', err); + } + // 兜底:展示配额UI让用户手动获取 + this.setData({ hasResult: false, init: false }); + this.getQuota(); } }, methods: { + onImageError() { + this.setData({ picFailed: true }); + }, + + async retryImage() { + this.setData({ fixingPic: true }); + try { + const { result } = await wx.cloud.callFunction({ + name: 'barcode', + data: { action: 'fixImage', barcode: this.data.info.barcode }, + }); + if (result && result.code === 0 && result.data && result.data.pic) { + this.setData({ + 'info.pic': result.data.pic, + picFailed: false, + fixingPic: false, + }); + this.triggerEvent('dataready', { data: this.data.info }); + wx.showToast({ title: '图片已修复', icon: 'success' }); + } else { + wx.showToast({ title: '修复失败', icon: 'none' }); + this.setData({ fixingPic: false }); + } + } catch (err) { + console.error('修复图片失败:', err); + wx.showToast({ title: '修复失败', icon: 'none' }); + this.setData({ fixingPic: false }); + } + }, + getQuota() { this.setData({ loading: true diff --git a/miniprogram/components/standard-info/index.less b/miniprogram/components/standard-info/index.less index d561316..21c5694 100644 --- a/miniprogram/components/standard-info/index.less +++ b/miniprogram/components/standard-info/index.less @@ -1,23 +1,63 @@ -.empty-action { +.loading-wrap { display: flex; justify-content: center; + padding: 60rpx 0; + margin-bottom: 16rpx; + background: #fff; + border-radius: 18rpx; } -.section { - margin: 16px; - padding: 16px; +.info-card { background: #fff; - border-radius: var(--td-radius-large, 18rpx); + border-radius: 18rpx; + padding: 28rpx; + margin-bottom: 16rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.03); } -.cell-title { - color: var(--td-font-gray-4, rgba(0, 0, 0, 0.26)); +.info-title { font-size: 28rpx; - margin-left: 32rpx; - padding-left: var(--td-cell-vertical-padding, 32rpx); + font-weight: 600; + color: #1a1a1a; + margin-bottom: 20rpx; } -.loading-wrap { +.info-field { + display: flex; + align-items: flex-start; + padding: 14rpx 0; + + & + & { + border-top: 1rpx solid #f5f5f5; + } +} + +.info-label { + font-size: 26rpx; + color: #999; + width: 120rpx; + flex-shrink: 0; +} + +.info-value { + font-size: 26rpx; + color: #333; + flex: 1; + + &--bold { + font-weight: 600; + color: #1a1a1a; + } +} + +.empty-card { + margin-bottom: 16rpx; + background: #fff; + border-radius: 18rpx; + padding: 40rpx 0 20rpx; +} + +.empty-action { display: flex; justify-content: center; } @@ -26,10 +66,44 @@ display: flex; flex-direction: column; align-items: center; +} + +.empty-loading { + display: flex; + justify-content: center; + padding: 20rpx 0; +} + +.desc { + margin-top: 12rpx; + font-size: 22rpx; + color: #bbb; +} + +.pic-error { + display: flex; + flex-direction: column; + align-items: center; + gap: 8rpx; + padding: 20rpx; + background: #fafafa; + border-radius: 12rpx; + border: 1rpx dashed #e0e0e0; +} + +.pic-error-text { + font-size: 22rpx; + color: #bbb; +} + +.pic-retry { + font-size: 24rpx; + color: #0052d9; + padding: 6rpx 20rpx; + background: #f0f5ff; + border-radius: 24rpx; - .desc { - margin-top: 12px; - font-size: 14px; - color: var(--td-font-gray-4, rgba(0, 0, 0, 0.26)); + &:active { + opacity: 0.7; } } \ No newline at end of file diff --git a/miniprogram/components/standard-info/index.wxml b/miniprogram/components/standard-info/index.wxml index 0f1952e..8d540d8 100644 --- a/miniprogram/components/standard-info/index.wxml +++ b/miniprogram/components/standard-info/index.wxml @@ -1,32 +1,60 @@ -
- -
- - 商品条码信息 - - - - - - - - - - - - - - - - - + + + + + + 商品条码信息 + + 条形码 + {{info.barcode}} + + + 名称 + {{info.name}} + + + 品牌 + {{info.brand}} + + + 生产商 + {{info.company}} + + + 规格 + {{info.type}} + + + 原产国 + {{info.origincountry}} + + + 分类 + {{info.description}} + + + 图片 + + 加载失败 + 重新获取 + + + + + + -
- {{ done ? '一键获取(' + quota + '次机会)' : '加载中'}} - - {{ done ? (quota > 0 ? '一键获取(' + quota + '次机会)' : '额度用完,去获取') : '加载中'}} - 去看广告(6-15秒),即可获取 5 次机会 + + + 一键获取({{quota}} 次机会) + + + 看广告获取次数 + 看 6-15 秒广告,获取 5 次机会 -
+ + + +
\ No newline at end of file diff --git a/miniprogram/config/index.js b/miniprogram/config/index.js index 8ec24b2..f62c326 100644 --- a/miniprogram/config/index.js +++ b/miniprogram/config/index.js @@ -23,7 +23,22 @@ export const titleMap = { 'meat_dish': '荤菜' } +export const categoryIcons = { + breakfast: '🥣', + condiment: '🧂', + dessert: '🍰', + drink: '🥤', + 'home-cooking': '🍳', + 'semi-finished': '🍜', + soup: '🍲', + staple: '🍚', + aquatic: '🦐', + 'vegetable_dish': '🥬', + 'meat_dish': '🥩' +} + export default { titleMap, - chineseMap + chineseMap, + categoryIcons } \ No newline at end of file diff --git a/miniprogram/custom-tab-bar/index.js b/miniprogram/custom-tab-bar/index.js index c5739c3..79e7413 100644 --- a/miniprogram/custom-tab-bar/index.js +++ b/miniprogram/custom-tab-bar/index.js @@ -6,11 +6,7 @@ Component({ icon: 'home', value: 'index', label: '首页', - },{ - icon: 'tips', - value: 'learn', - label: '发现', - },{ + }, { icon: 'face-retouching', value: 'stock', label: '仓储', diff --git a/miniprogram/pages/ai-cook/index.js b/miniprogram/pages/ai-cook/index.js new file mode 100644 index 0000000..d524454 --- /dev/null +++ b/miniprogram/pages/ai-cook/index.js @@ -0,0 +1,60 @@ +import { parseIngredients, matchRecipes } from '../../utils/ai-matcher'; +import { CATEGORIES } from '../../utils/common-ingredients'; + +Page({ + data: { + inputText: '', + ingredients: [], + results: [], + hasSearched: false, + categories: CATEGORIES, + }, + + handleInput(e) { + this.setData({ inputText: e.detail.value }); + }, + + handleTapIngredient(e) { + const { name } = e.currentTarget.dataset; + const current = this.data.inputText.trim(); + const newText = current ? current + '、' + name : name; + this.setData({ inputText: newText }); + }, + + handleSearch() { + const text = this.data.inputText.trim(); + if (!text) { + wx.showToast({ title: '请说说你有哪些食材', icon: 'none' }); + return; + } + + const ingredients = parseIngredients(text); + const results = matchRecipes(ingredients).slice(0, 20); + + this.setData({ + ingredients, + results, + hasSearched: true, + }); + + if (ingredients.length === 0) { + wx.showToast({ title: '未识别到食材,请换个说法试试', icon: 'none' }); + } + }, + + handleTapRecipe(e) { + const { id } = e.currentTarget.dataset; + wx.navigateTo({ url: '/pages/detail/index?id=' + id }); + }, + + handleBack() { + wx.navigateBack({ delta: 1 }); + }, + + onShareAppMessage() { + return { + title: '程序员做饭指南 - 智能推荐', + path: '/pages/ai-cook/index' + } + }, +}) \ No newline at end of file diff --git a/miniprogram/pages/ai-cook/index.json b/miniprogram/pages/ai-cook/index.json new file mode 100644 index 0000000..50fafca --- /dev/null +++ b/miniprogram/pages/ai-cook/index.json @@ -0,0 +1,6 @@ +{ + "navigationBarTitleText": "", + "navigationStyle": "custom", + "backgroundColor": "#f5f5f5", + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/pages/ai-cook/index.less b/miniprogram/pages/ai-cook/index.less new file mode 100644 index 0000000..cbf230c --- /dev/null +++ b/miniprogram/pages/ai-cook/index.less @@ -0,0 +1,248 @@ +@bg: #f5f5f5; +@primary: #0052d9; + +page { + background: @bg; + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); +} + +.container { + padding: 24rpx; +} + +/* ── Category ingredients ── */ +.category-section { + margin-bottom: 16rpx; +} + +.category-card { + background: #fff; + border-radius: 14rpx; + padding: 16rpx 20rpx; + margin-bottom: 10rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03); +} + +.category-label { + font-size: 22rpx; + font-weight: 600; + color: #999; + margin-bottom: 10rpx; + display: block; +} + +.ingredient-chips { + display: flex; + flex-wrap: wrap; + gap: 10rpx; +} + +.ingredient-chip { + display: inline-block; + padding: 6rpx 18rpx; + font-size: 24rpx; + color: #333; + background: #f5f5f5; + border-radius: 20rpx; + transition: all 0.15s; + + &:active { + background: #e0edff; + color: @primary; + } +} + +/* ── Intro ── */ +.intro-card { + display: flex; + flex-direction: column; + align-items: center; + background: #fff; + border-radius: 18rpx; + padding: 40rpx 24rpx 32rpx; + margin-bottom: 16rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.03); +} + +.intro-emoji { + font-size: 72rpx; + margin-bottom: 16rpx; +} + +.intro-title { + font-size: 32rpx; + font-weight: 700; + color: #1a1a1a; +} + +.intro-desc { + font-size: 24rpx; + color: #999; + margin-top: 8rpx; +} + +/* ── Input ── */ +.input-card { + background: #fff; + border-radius: 18rpx; + padding: 24rpx; + margin-bottom: 16rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.03); +} + +.input-area { + width: 100%; + min-height: 140rpx; + font-size: 28rpx; + line-height: 44rpx; + color: #333; +} + +.input-actions { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 16rpx; + padding-top: 16rpx; + border-top: 1rpx solid #f0f0f0; +} + +.input-hint { + font-size: 22rpx; + color: #bbb; +} + +/* ── Section label ── */ +.section-label { + font-size: 26rpx; + font-weight: 600; + color: #666; + margin-bottom: 16rpx; + display: block; +} + +/* ── Tags ── */ +.tags-card { + background: #fff; + border-radius: 18rpx; + padding: 24rpx; + margin-bottom: 16rpx; + box-shadow: 0 2rpx 16rpx rgba(0, 0, 0, 0.03); +} + +.tag-list { + display: flex; + flex-wrap: wrap; + gap: 12rpx; +} + +.tag { + display: inline-block; + padding: 8rpx 22rpx; + font-size: 24rpx; + color: @primary; + background: #f0f5ff; + border-radius: 20rpx; +} + +/* ── Recipe list ── */ +.recipe-list { + margin-bottom: 16rpx; +} + +.recipe-card { + display: flex; + align-items: center; + background: #fff; + border-radius: 14rpx; + padding: 24rpx 20rpx; + margin-bottom: 10rpx; + box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03); + transition: all 0.15s; + + &:active { + transform: scale(0.985); + } +} + +.recipe-rank { + width: 44rpx; + height: 44rpx; + border-radius: 10rpx; + background: #f0f5ff; + color: @primary; + font-size: 22rpx; + font-weight: 600; + display: flex; + align-items: center; + justify-content: center; + margin-right: 18rpx; + flex-shrink: 0; +} + +.recipe-info { + flex: 1; + min-width: 0; +} + +.recipe-name { + font-size: 28rpx; + font-weight: 600; + color: #1a1a1a; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.recipe-meta { + margin-top: 6rpx; +} + +.meta-match { + font-size: 22rpx; + color: #999; +} + +.match-bar { + height: 4rpx; + background: #f0f0f0; + border-radius: 2rpx; + margin-top: 8rpx; + overflow: hidden; +} + +.match-fill { + height: 100%; + background: @primary; + border-radius: 2rpx; + transition: width 0.3s ease; +} + +.match-rate { + font-size: 28rpx; + font-weight: 700; + color: @primary; + margin-left: 12rpx; + flex-shrink: 0; +} + +/* ── Empty ── */ +.empty-card { + display: flex; + flex-direction: column; + align-items: center; + padding: 80rpx 0; +} + +.empty-text { + font-size: 28rpx; + color: #999; +} + +.empty-hint { + font-size: 24rpx; + color: #ccc; + margin-top: 12rpx; +} \ No newline at end of file diff --git a/miniprogram/pages/ai-cook/index.wxml b/miniprogram/pages/ai-cook/index.wxml new file mode 100644 index 0000000..b94beb3 --- /dev/null +++ b/miniprogram/pages/ai-cook/index.wxml @@ -0,0 +1,79 @@ + + + + + 🧑‍🍳 + 告诉我你有什么食材 + 我会帮你找到能做的菜谱 + + + + + {{item.name}} + + {{ing}} + + + + + +