diff --git a/miniprogram/tcb-shop/.eslintrc.js b/miniprogram/tcb-shop/.eslintrc.js new file mode 100644 index 0000000..21d54f4 --- /dev/null +++ b/miniprogram/tcb-shop/.eslintrc.js @@ -0,0 +1,157 @@ +module.exports = { + env: { + browser: true, + commonjs: true, + es6: true, + }, + parserOptions: { + ecmaVersion: 2020, + // ECMAScript modules 模式 + sourceType: 'module', + }, + extends: ['plugin:prettier/recommended', 'prettier'], + globals: { + wx: true, + App: true, + Page: true, + Component: true, + getApp: true, + getCurrentPages: true, + Behavior: true, + global: true, + __wxConfig: true, + }, + ignorePatterns: ['*.wxs'], + rules: { + 'prettier/prettier': 'warn', + 'no-undef': 'off', + camelcase: 'off', + 'class-name-casing': 'off', + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'no-debugger': 'error', + 'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }], + 'no-empty-interface': 'off', + 'no-use-before-define': ['error', { functions: false }], + 'no-useless-constructor': 'error', + 'prefer-const': 'error', + 'prefer-destructuring': [ + 'error', + { + AssignmentExpression: { + array: false, + object: false, + }, + VariableDeclarator: { + array: false, + object: true, + }, + }, + { + enforceForRenamedProperties: false, + }, + ], + 'no-const-assign': 'error', + 'no-new-object': 'error', + 'no-prototype-builtins': 'error', + 'no-array-constructor': 'error', + 'array-callback-return': 'warn', + 'prefer-template': 'error', + 'no-useless-escape': 'error', + 'wrap-iife': ['error', 'outside'], + 'space-before-function-paren': [ + 'warn', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }, + ], + 'no-param-reassign': [ + 'warn', + { + props: true, + ignorePropertyModificationsFor: [ + 'acc', // for reduce accumulators + 'accumulator', // for reduce accumulators + 'e', // for e.returnvalue + 'ctx', // for Koa routing + 'req', // for Express requests + 'request', // for Express requests + 'res', // for Express responses + 'response', // for Express responses + '$scope', // for Angular 1 scopes + 'staticContext', // for ReactRouter context + 'state', // for Vuex + ], + }, + ], + 'no-confusing-arrow': 'warn', + 'no-dupe-class-members': 'error', + 'no-iterator': 'warn', + 'dot-notation': 'warn', + 'one-var': ['warn', 'never'], + 'no-multi-assign': 'error', + 'no-unused-vars': [ + 'error', + { + args: 'after-used', + ignoreRestSiblings: true, + argsIgnorePattern: '^_.+', + varsIgnorePattern: '^_.+', + }, + ], + eqeqeq: ['warn', 'always'], + 'no-case-declarations': 'error', + 'no-nested-ternary': 'warn', + 'no-unneeded-ternary': 'warn', + 'no-mixed-operators': [ + 'error', + { + groups: [ + ['%', '**'], + ['%', '+'], + ['%', '-'], + ['%', '*'], + ['%', '/'], + ['&', '|', '<<', '>>', '>>>'], + ['==', '!=', '===', '!=='], + ['&&', '||'], + ], + allowSamePrecedence: false, + }, + ], + 'no-else-return': [ + 'warn', + { + allowElseIf: false, + }, + ], + 'no-new-wrappers': 'warn', + indent: [ + 'warn', + 2, + { + SwitchCase: 1, + VariableDeclarator: 1, + outerIIFEBody: 1, + FunctionDeclaration: { + parameters: 1, + body: 1, + }, + FunctionExpression: { + parameters: 1, + body: 1, + }, + CallExpression: { + arguments: 1, + }, + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + ignoreComments: false, + }, + ], + 'linebreak-style': ['warn', 'unix'], + }, +}; diff --git a/miniprogram/tcb-shop/.gitignore b/miniprogram/tcb-shop/.gitignore new file mode 100644 index 0000000..68a200f --- /dev/null +++ b/miniprogram/tcb-shop/.gitignore @@ -0,0 +1,16 @@ +node_modules/ +yarn-error.log +miniprogram/ +miniprogram_npm/ +miniprogram_dist/ +.DS_Store +$node_modules/ +.history/ +**/dist +components/**/*.lock +components/**/package-lock.json +package-lock.json +yarn.lock +project.private.config.json +.eslintcache +cloudfunctions/ \ No newline at end of file diff --git a/miniprogram/tcb-shop/.npmrc b/miniprogram/tcb-shop/.npmrc new file mode 100644 index 0000000..21b91a6 --- /dev/null +++ b/miniprogram/tcb-shop/.npmrc @@ -0,0 +1,9 @@ +# 去除注释可以使用代理进行安装 +# proxy=http://127.0.0.1:1080 +# https_proxy=http://127.0.0.1:1080 + +# 去除注释可以使用淘宝源 +# registry=https://registry.npm.taobao.org + +# 去除注释可以使用腾讯源 +#registry=http://mirrors.tencent.com/npm/ diff --git a/miniprogram/tcb-shop/.prettierignore b/miniprogram/tcb-shop/.prettierignore new file mode 100644 index 0000000..9ff2448 --- /dev/null +++ b/miniprogram/tcb-shop/.prettierignore @@ -0,0 +1,3 @@ +miniprogram_npm +package.json +project.config.json \ No newline at end of file diff --git a/miniprogram/tcb-shop/.prettierrc.yml b/miniprogram/tcb-shop/.prettierrc.yml new file mode 100644 index 0000000..a13251c --- /dev/null +++ b/miniprogram/tcb-shop/.prettierrc.yml @@ -0,0 +1,49 @@ +# 一行最多 100 字符 +printWidth: 120 +# 使用 2 个空格缩进 +tabWidth: 2 +# 不使用缩进符,而使用空格 +useTabs: false +# 行尾需要分号 +semi: true +# 使用单引号 +singleQuote: true +# 对象的 key 仅在必要时用引号 +quoteProps: as-needed +# jsx 不使用单引号,而使用双引号 +jsxSingleQuote: false +# 末尾需要逗号 +trailingComma: all +# 大括号内的首尾需要空格 +bracketSpacing: true +# jsx 标签的反尖括号需要换行 +jsxBracketSameLine: false +# 箭头函数,只有一个参数的时候,不需要括号 +arrowParens: always +# 每个文件格式化的范围是文件的全部内容 +rangeStart: 0 +# 不需要写文件开头的 @prettier +requirePragma: false +# 不需要自动在文件开头插入 @prettier +insertPragma: false +# 使用默认的折行标准 +proseWrap: preserve +# 根据显示样式决定 html 要不要折行 +htmlWhitespaceSensitivity: css +# 换行符使用 lf +endOfLine: lf +# 后缀文件名特有规则 +overrides: + - files: '*.{wxss,less}' + options: + parser: less + - files: '*.json,.*rc' + options: + parser: json + - files: '*.{wxml,html}' + options: + parser: html + htmlWhitespaceSensitivity: strict + - files: '*.wxs' + options: + parser: babel diff --git a/miniprogram/tcb-shop/LICENSE b/miniprogram/tcb-shop/LICENSE new file mode 100644 index 0000000..789cbde --- /dev/null +++ b/miniprogram/tcb-shop/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2021-present TDesign + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/miniprogram/tcb-shop/README.md b/miniprogram/tcb-shop/README.md new file mode 100644 index 0000000..bcfae25 --- /dev/null +++ b/miniprogram/tcb-shop/README.md @@ -0,0 +1,302 @@ + +
+
+ Logo + +

云开发电商模板

+ +

+ 一键创建零售商城 +
+

+
+ +## 说明 + +云开发电商模板提供了一键创建零售商城小程序的能力。 + +## 快速上手 + +1. 安装本模版 +2. 导入云开发电商模板小程序 + +![](https://qcloudimg.tencent-cloud.cn/raw/5f1962e709c28af3252c0acb583e989b.png) + +3. 填入开通了云开发环境的小程序的 appId + +![](https://qcloudimg.tencent-cloud.cn/raw/800f05945779cb940ef6851d96419f6e.png) + +4. 在 `app.js` 中,填入云开发环境 id + +![](https://qcloudimg.tencent-cloud.cn/raw/1da510c60d9d552119ed7aa79c7a6826.png) + +5. 前往[小程序微信支付模版](https://tcb.cloud.tencent.com/cloud-admin#/cloud-template/detail?appName=wx-pay-v2&solutionId=solution-1sbaF7cyIqcgRj&appType=basic),填入该模版所需参数 + +![]( https://qcloudimg.tencent-cloud.cn/raw/9a11a564b8985883194f19cdd11c3f2b.png) + +6. 安装依赖 + 6.1 在命令行执行 `npm install` + 6.2 点击菜单栏中的「工具 -> 构建 npm」 + 6.3. 详情可参考[npm 支持 | 微信开放文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html#%E4%BD%BF%E7%94%A8-npm-%E5%8C%85) +7. 编译启动小程序,开始体验 +8. 正式发布前,移除云开发引导弹窗 + 8.1 删除 `components/cloud-template-dialog/` 文件夹 + + ![]( https://qcloudimg.tencent-cloud.cn/raw/fcc15824e38fbfacc1afee040c648099.png) + + 8.2 删除 `pages/home/home.wxml` 中对云开发引导弹窗的引用 + + ![]( https://qcloudimg.tencent-cloud.cn/raw/cb2a20e0f840eaee236a964fac734f28.png) + + 8.3 删除 `pages/home/home.json` 中对云开发引导弹窗的引用 + + ![](https://qcloudimg.tencent-cloud.cn/raw/7c0c32bbb494bc3b0877891d302c096c.png) + +## 页面介绍 + +### 首页 + +![](https://qcloudimg.tencent-cloud.cn/raw/89cf8dedbd3edfdbda31920447bddd51.png) + +首页由三部分组成: + +● 搜索栏 + +● 轮播图 + +● 商品列表 + +#### 搜索栏 + +点击搜索栏,可以输入搜索商品,并展示搜索到的商品列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/94e2ee9666aed0f8147affa180c879c7.png) + +#### 轮播图 + +轮播图可循环展示配置好的图片。 + +![](https://qcloudimg.tencent-cloud.cn/raw/b42bc12d0fa4fe350b83154ace0a613c.png) + +#### 商品列表 + +首页商品列表会列出所有的商品,点击单个商品后,会跳转至对应的商品详情页。 + +首页商品列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/98fca5271899639e1393753e52523fce.png) + +点击商品后跳转商品详情页: + +![](https://qcloudimg.tencent-cloud.cn/raw/54c50176b1af344b23fb61e22e611265.png) + +### 分类页 + +分类页会以侧边栏展示商品的一级分类,主内容展示商品的二级分类。 + +![](https://qcloudimg.tencent-cloud.cn/raw/1ff658bb467a4e8febf34be13ecf80da.png) + +点击二级分类后,会展示当前分类下的商品列表。同样,在此商品列表点击商品后,会跳转至对应商品详情页。 + +水果分类下的商品列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/927b12f16467f1def96e877c50b1a7e8.png) + + +### 商品详情页 + +![](https://qcloudimg.tencent-cloud.cn/raw/ef651d54d5e57d2aacaa50bc283ccfb5.png) + +商品详情页由多个部分组成: + +● 商品轮播图 + +● 商品信息 + +● 规格选择栏 + +● 评价预览 + +● 商品详情 + +● 底部动作栏 + +#### 商品轮播图 + +商品轮播图可循环展示该商品的多个图片: + +![](https://qcloudimg.tencent-cloud.cn/raw/5b59639efcc112ce592b72f6cd77fcb8.png) + +#### 商品信息 + +商品信息会展示商品的名称与最低价格: + +![](https://qcloudimg.tencent-cloud.cn/raw/5d23000c19ccb652ce9eab0afd1fd2e7.png) + +#### 规格选择栏 + +点击规格选择栏后,可在跳出的弹窗中选择商品对应的规格,并选择购买数量。确定商品规格和购买数量后,可加入购物车或选择立即购买: + +![](https://qcloudimg.tencent-cloud.cn/raw/f7aecbc84c8d691a8e6c97070b4af71b.png) + +#### 评价预览 + +当商品存在评价时,会展示评价预览,包括评论数、好评率,以及一条评价: + +![](https://qcloudimg.tencent-cloud.cn/raw/dd5e089f0be8b10dd900d65566bb2778.png) + +#### 商品详情 + +商品详情用于展示商品的详细信息,这部分的内容以富文本的形式呈现: + +![](https://qcloudimg.tencent-cloud.cn/raw/81271f5c219fb5c475493af8e8751d99.png) + +#### 底部动作栏 + +底部动作栏提供了四个按钮,包括首页和购物车页的跳转按钮,点击后会跳转至相应页面;还有加入购物车与立即购买按钮,点击后能唤出规格选择弹窗,选择完商品后就能够执行对应的操作: + +底部动作栏: + +![](https://qcloudimg.tencent-cloud.cn/raw/862684258b666755e5ea1dae45bdecfe.png) + +点击后唤出规格选择弹窗: + +![](https://qcloudimg.tencent-cloud.cn/raw/de44ca5df5e297415d1863eb6cac7c1e.png) + +### 购物车页 + +![](https://qcloudimg.tencent-cloud.cn/raw/eec6aa7cbdf1aefae293a68e867f8501.png) + +购物车页可以查看购物车项。对于每个购物车项,可以进行删除、修改数量的操作。选中购物车项后,可以点击「去结算」按钮前往订单确认页进行订单创建并购买。 + +左滑购物车项进行删除: + +![](https://qcloudimg.tencent-cloud.cn/raw/21311abac70b47bc352734e4e6da0485.png) + +选中购物车项后可进行结算: + +![](https://qcloudimg.tencent-cloud.cn/raw/a2af456542cd7afae411cffaaac9d501.png) + +点击「去结算」按钮后跳转至订单确认页: + +![](https://qcloudimg.tencent-cloud.cn/raw/c5e01f1c2cb4ef6eae6bd0fce0574a95.png) + +### 订单确认页 + +![](https://qcloudimg.tencent-cloud.cn/raw/8789c377b15e0a222475b5253cd45c0a.png) + +订单确认页提供以下功能: + +● 地址选择栏 + +● 订单信息展示 + +● 提交订单并支付 + +#### 地址选择栏 + +地址选择栏展示当前订单的配送地址。如果未选择地址,将无法提交订单。点击地址选择栏后可跳转至地址列表,对地址进行增加、删除、修改和选择的操作。选择完地址后,会返回至此页,届时可继续进行订单提交。 + +地址选择栏: + +![](https://qcloudimg.tencent-cloud.cn/raw/febf20b5ae98eacd2b69a93ecbba684e.png) + +地址列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/c4d74f2749a9029c7a731071165fdd9f.png) + +选择完地址后,跳回订单确认页: + +![](https://qcloudimg.tencent-cloud.cn/raw/4faedcef881787e25926e69f50ec18cd.png) + +#### 订单信息展示 + +在订单确认页会展示当前创建的订单的详细内容,包括订单项、总价等信息。 + +![](https://qcloudimg.tencent-cloud.cn/raw/268fe7c66225fe45d8ace3fd933e6b31.png) + +#### 提交订单并支付 + +在选择完地址后,点击提交订单,即可创建订单,并弹出支付弹窗。若支付流程失败,可在订单列表中查询到待支付的订单,重新支付。 + +![](https://qcloudimg.tencent-cloud.cn/raw/e81466d3fe35a15a1add1467e3b9114a.png) + +### 用户中心页 + +用户中心页展示用户信息、订单列表和地址列表。 + +![](https://qcloudimg.tencent-cloud.cn/raw/f7e8527c81b88c69bb800f3afbaa9e62.png) + +## 数据模型 + +云开发电商模板附带了一系列的数据模型。对于数据模型的能力说明,请查看[云开发数据模型](https://docs.cloudbase.net/model/introduce)。 + +本模版附带的数据模型有: + +|名称|标识|描述| +|---|---|---| +|电商SPU|shop_spu|一件商品,对应电商概念中的 SPU,即 Standard Product Unit。| +|电商SPU分类|shop_spu_cate|商品分类,每个商品分类都可以有自己的子分类、父分类和对应的商品。| +|电商SPU属性名|shop_attr_name|商品的属性名,如数量。| +|电商SPU属性值|shop_attr_value|商品的属性值,如数量对应的值可能为「1 件」、「10 件」。| +|电商SPU评价|shop_comment|用户对商品做出的评价。| +|电商SKU|shop_sku|一件有具体属性的商品销售单位。每个 SPU 都可以对应多个 SKU。举例来说,A 品牌的平板电脑为 SPU,其对应的 SKU 为 A 品牌的 16GB 内存、白色的平板电脑。| +|电商购物车项|shop_cart_item|| +|电商订单|shop_order|| +|电商订单项|shop_order_item|| +|电商收货信息|shop_delivery_info|| +|电商首页轮播图|shop_home_swiper_image|| + +## 应用配置说明 + +### 配置商品 + +#### 配置SPU + +在「电商SPU」数据模型中,填入 SPU 数据。这里我们添加一行「荔枝」的 SPU 数据: + +![](https://qcloudimg.tencent-cloud.cn/raw/adda72dbcf9f96d41236c128cb11522e.png) + +#### 配置SKU + +添加完 SPU 后,我们还需要为其添加 SKU 数据。SKU 可以理解为具有具体规格/属性的 SPU,比如荔枝的属性可以有数量、大小等等。不同数量、不同大小的荔枝会有不同的价格、不同的库存。 + +首先,我们为荔枝配置属性选项。 + +在「电商SPU属性名」数据模型中,我们添加一行「大小」属性: + +![](https://qcloudimg.tencent-cloud.cn/raw/dd45aedca3c2235dce20c749faf6a890.png) + +添加完属性名后,我们还需要为此属性名配置属性值。 + +我们在「电商SPU属性值」配置两个属性值,分别是大、小: + +![](https://qcloudimg.tencent-cloud.cn/raw/e42f566f0622d4825da0d56acc085e25.png) + +![](https://qcloudimg.tencent-cloud.cn/raw/5a7b0b51e9062366f5412ef851b2d606.png) + +至此,我们配置好了「大小」这个属性名,这个属性名下有两个可选的属性值,分别为「大」和「小」。 + +现在,我们可以为「荔枝」SPU 添加 SKU 了,分别是「大荔枝」和「小荔枝」,他们的价格户不相同: + +![](https://qcloudimg.tencent-cloud.cn/raw/a829aae86242a37c5130a0d1e0568d8d.png) + +![](https://qcloudimg.tencent-cloud.cn/raw/a5a6975a715e92f3abf9296cc53a279b.png) + +#### 配置商品分类 + +每个 SPU 可以有多个分类,分类下也可以有子分类。现在我们来为荔枝添加二级分类,分别是「美食」->「水果」。 + +我们在「电商SPU分类」数据模型中添加「美食」分类: + +![](https://qcloudimg.tencent-cloud.cn/raw/b511ddf9b3069097f5c95f19779392f4.png) + +然后再添加「水果」分类,添加时把父分类选为「美食」,并在 SPU 中添加「荔枝」: + +![](https://qcloudimg.tencent-cloud.cn/raw/2a3c6522f9af616740e57a7db80ef94f.png) + +### 配置首页轮播图 + +首页轮播图会展示「电商首页轮播图」数据模型的第一行数据中的图片: + +![](https://qcloudimg.tencent-cloud.cn/raw/73b6b4c119ea526506f683a4cf1f8c13.png) diff --git a/miniprogram/tcb-shop/app.js b/miniprogram/tcb-shop/app.js new file mode 100644 index 0000000..2f1800e --- /dev/null +++ b/miniprogram/tcb-shop/app.js @@ -0,0 +1,18 @@ +import updateManager from './common/updateManager'; +import { init } from '@cloudbase/wx-cloud-client-sdk'; + +wx.cloud.init({ + env: 'your-env-id', // 指定云开发环境 ID +}); + +const client = init(wx.cloud); +const models = client.models; +globalThis.dataModel = models; +// 接下来就可以调用 models 上的数据模型增删改查等方法了 + +App({ + onLaunch: function () {}, + onShow: function () { + updateManager(); + }, +}); diff --git a/miniprogram/tcb-shop/app.json b/miniprogram/tcb-shop/app.json new file mode 100644 index 0000000..b4cea02 --- /dev/null +++ b/miniprogram/tcb-shop/app.json @@ -0,0 +1,76 @@ +{ + "pages": [ + "pages/home/home", + "pages/usercenter/index", + "pages/usercenter/person-info/index", + "pages/usercenter/address/list/index", + "pages/usercenter/address/edit/index", + "pages/goods/list/index", + "pages/goods/details/index", + "pages/goods/category/index", + "pages/goods/search/index", + "pages/goods/result/index", + "pages/cart/index", + "pages/order/order-confirm/index", + "pages/order/receipt/index", + "pages/order/pay-result/index", + "pages/order/order-list/index", + "pages/order/order-detail/index", + "pages/goods/comments/index", + "pages/order/apply-service/index", + "pages/order/after-service-list/index", + "pages/order/after-service-detail/index", + "pages/goods/comments/create/index", + "pages/goods/comments/create-list/index", + "pages/coupon/coupon-list/index", + "pages/coupon/coupon-detail/index", + "pages/coupon/coupon-activity-goods/index", + "pages/promotion-detail/index", + "pages/order/fill-tracking-no/index", + "pages/order/delivery-detail/index", + "pages/order/invoice/index", + "pages/usercenter/name-edit/index" + ], + "tabBar": { + "custom": true, + "color": "#666666", + "selectedColor": "#FF5F15", + "backgroundColor": "#ffffff", + "borderStyle": "black", + "list": [ + { + "pagePath": "pages/home/home", + "text": "首页" + }, + { + "pagePath": "pages/goods/category/index", + "text": "分类" + }, + { + "pagePath": "pages/cart/index", + "text": "购物车" + }, + { + "pagePath": "pages/usercenter/index", + "text": "我的" + } + ] + }, + "requiredPrivateInfos": [ + "chooseAddress" + ], + "lazyCodeLoading": "requiredComponents", + "usingComponents": {}, + "window": { + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#fff", + "navigationBarTitleText": "Weixin", + "navigationBarTextStyle": "black" + }, + "sitemapLocation": "sitemap.json", + "permission": { + "scope.userLocation": { + "desc": "你的位置信息将用于小程序位置接口的效果展示" + } + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/app.wxss b/miniprogram/tcb-shop/app.wxss new file mode 100644 index 0000000..9ff2554 --- /dev/null +++ b/miniprogram/tcb-shop/app.wxss @@ -0,0 +1,3 @@ +@import 'style/iconfont.wxss'; + +@import 'style/theme.wxss'; \ No newline at end of file diff --git a/miniprogram/tcb-shop/commitlint.config.js b/miniprogram/tcb-shop/commitlint.config.js new file mode 100644 index 0000000..422b194 --- /dev/null +++ b/miniprogram/tcb-shop/commitlint.config.js @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/miniprogram/tcb-shop/common/updateManager.js b/miniprogram/tcb-shop/common/updateManager.js new file mode 100644 index 0000000..c45de6d --- /dev/null +++ b/miniprogram/tcb-shop/common/updateManager.js @@ -0,0 +1,29 @@ +export default () => { + if (!wx.canIUse('getUpdateManager')) { + return; + } + + const updateManager = wx.getUpdateManager(); + + updateManager.onCheckForUpdate(function (res) { + // 请求完新版本信息的回调 + console.log('版本信息', res); + }); + + updateManager.onUpdateReady(function () { + wx.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success(res) { + if (res.confirm) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + updateManager.applyUpdate(); + } + }, + }); + }); + + updateManager.onUpdateFailed(function () { + // 新版本下载失败 + }); +}; diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.js b/miniprogram/tcb-shop/components/cloud-template-dialog/index.js new file mode 100644 index 0000000..cb0422b --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.js @@ -0,0 +1,41 @@ +const NEVER_SHOW_KEY = 'never_show_cloud_template_init_dialog'; + +Component({ + properties: { + show: { + type: Boolean, + value: true, + }, + url: { + type: String, + value: '', + }, + }, + + data: { checked: false }, + + methods: { + close() { + if (this.data.checked) { + wx.setStorageSync(NEVER_SHOW_KEY, 'true'); + } + }, + copy() { + wx.setClipboardData({ + data: this.data.url, + }); + }, + neverShowChanged({ detail: { checked } }) { + this.setData({ checked }); + }, + }, + + lifetimes: { + attached: function () { + const ifNever = wx.getStorageSync(NEVER_SHOW_KEY); + this.setData({ + show: ifNever !== 'true', + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.json b/miniprogram/tcb-shop/components/cloud-template-dialog/index.json new file mode 100644 index 0000000..677ffc2 --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "styleIsolation": "shared", + "usingComponents": { + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-button": "tdesign-miniprogram/button/button", + "t-radio": "tdesign-miniprogram/radio/radio" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml new file mode 100644 index 0000000..9e4a11e --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml @@ -0,0 +1,13 @@ + + + + + {{url}} + + + + 复制 + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss new file mode 100644 index 0000000..9089da6 --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss @@ -0,0 +1,27 @@ +.url-container text { + word-break: break-all; +} + +.btns { + display: flex; + justify-content: center; + align-items: center; + gap: 10px; +} + +.template-btn { + flex-grow: 1; +} + +.template-dialog .t-dialog { + margin-bottom: 20px; +} + + +.template-dialog .t-dialog__footer { + display: none; +} + +.template-dialog .t-radio--block { + padding-left: 0; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/filter-popup/index.js b/miniprogram/tcb-shop/components/filter-popup/index.js new file mode 100644 index 0000000..14c69fd --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.js @@ -0,0 +1,36 @@ +Component({ + externalClasses: ['wr-class'], + + options: { + multipleSlots: true, + }, + + properties: { + show: { + type: Boolean, + observer(show) { + this.setData({ visible: show }); + }, + }, + closeBtn: { + type: Boolean, + value: false, + }, + }, + + data: { visible: false }, + + methods: { + reset() { + this.triggerEvent('reset'); + }, + confirm() { + this.triggerEvent('confirm'); + }, + close() { + this.triggerEvent('showFilterPopupClose'); + + this.setData({ visible: false }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/filter-popup/index.json b/miniprogram/tcb-shop/components/filter-popup/index.json new file mode 100644 index 0000000..f5d3702 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/filter-popup/index.wxml b/miniprogram/tcb-shop/components/filter-popup/index.wxml new file mode 100644 index 0000000..2932624 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.wxml @@ -0,0 +1,18 @@ + + + + + 重置 + + 确定 + + + + + diff --git a/miniprogram/tcb-shop/components/filter-popup/index.wxss b/miniprogram/tcb-shop/components/filter-popup/index.wxss new file mode 100644 index 0000000..a206a82 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.wxss @@ -0,0 +1,39 @@ +.content .filter-btns-wrap { + width: 100%; + position: absolute; + bottom: calc(20rpx + env(safe-area-inset-bottom)); + display: flex; + flex-direction: row; + border-radius: 10rpx 0 0 10rpx; + padding: 16rpx 32rpx; + border-top: 1rpx solid #e5e5e5; + box-sizing: border-box; +} + +.filter-btn { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + font-size: 28rpx; + font-weight: 500; + height: 80rpx; +} + +.btn-reset { + color: #fa4126; + background: rgba(255, 255, 255, 1); + position: relative; + border: 1rpx solid #fa4126; + border-radius: 84rpx 0 0 84rpx; +} + +.btn-confirm { + border-radius: 0 84rpx 84rpx 0; + border: 1rpx solid #fa4126; +} + +.btn-confirm { + color: #fff; + background: #fa4126; +} diff --git a/miniprogram/tcb-shop/components/filter/index.js b/miniprogram/tcb-shop/components/filter/index.js new file mode 100644 index 0000000..dc99b95 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.js @@ -0,0 +1,84 @@ +Component({ + externalClasses: ['wr-class'], + + options: { + multipleSlots: true, + }, + + properties: { + overall: { + type: Number, + value: 1, + observer(overall) { + this.setData({ + overall, + }); + }, + }, + layout: { + type: Number, + value: 1, + observer(layout) { + this.setData({ + layout, + }); + }, + }, + sorts: { + type: String, + value: '', + observer(sorts) { + this.setData({ + sorts, + }); + }, + }, + color: { + type: String, + value: '#FA550F', + }, + }, + + data: { + layout: 1, + overall: 1, + sorts: '', + }, + + methods: { + onChangeShowAction() { + const { layout } = this.data; + const nextLayout = layout === 1 ? 0 : 1; + this.triggerEvent('change', { ...this.properties, layout: nextLayout }); + }, + + handlePriseSort() { + const { sorts } = this.data; + this.triggerEvent('change', { + ...this.properties, + overall: 0, + sorts: sorts === 'desc' ? 'asc' : 'desc', + }); + }, + + open() { + this.triggerEvent('showFilterPopup', { + show: true, + }); + }, + + onOverallAction() { + const { overall } = this.data; + const nextOverall = overall === 1 ? 0 : 1; + const nextData = { + sorts: '', + prices: [], + }; + this.triggerEvent('change', { + ...this.properties, + ...nextData, + overall: nextOverall, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/filter/index.json b/miniprogram/tcb-shop/components/filter/index.json new file mode 100644 index 0000000..7464ae6 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/filter/index.wxml b/miniprogram/tcb-shop/components/filter/index.wxml new file mode 100644 index 0000000..9048bfd --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.wxml @@ -0,0 +1,37 @@ + + + + + 综合 + + + 价格 + + + + + + + 筛选 + + + + + + + diff --git a/miniprogram/tcb-shop/components/filter/index.wxss b/miniprogram/tcb-shop/components/filter/index.wxss new file mode 100644 index 0000000..97728f9 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.wxss @@ -0,0 +1,50 @@ +.filter-wrap { + width: 100%; + height: 88rpx; + display: flex; + justify-content: space-between; + position: relative; + background: #fff; +} + +.filter-right-content { + height: 100%; + flex-basis: 100rpx; + text-align: center; + line-height: 88rpx; +} + +.filter-left-content { + height: 100%; + display: flex; + flex-grow: 2; + flex-flow: row nowrap; + justify-content: space-between; +} + +.filter-left-content .filter-item { + flex: 1; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 26rpx; + line-height: 36rpx; + font-weight: 400; + color: rgba(51, 51, 51, 1); +} + +.filter-left-content .filter-item .filter-price { + display: flex; + flex-direction: column; + margin-left: 6rpx; + justify-content: space-between; +} + +.filter-left-content .filter-item .wr-filter { + margin-left: 8rpx; +} + +.filter-left-content .filter-active-item { + color: #fa550f; +} diff --git a/miniprogram/tcb-shop/components/goods-card/index.js b/miniprogram/tcb-shop/components/goods-card/index.js new file mode 100644 index 0000000..d1d63cf --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.js @@ -0,0 +1,141 @@ +Component({ + options: { + addGlobalClass: true, + }, + + properties: { + id: { + type: String, + value: '', + observer(id) { + this.genIndependentID(id); + if (this.properties.thresholds?.length) { + this.createIntersectionObserverHandle(); + } + }, + }, + data: { + type: Object, + observer(data) { + if (!data) { + return; + } + let isValidityLinePrice = true; + if (data.originPrice && data.price && data.originPrice < data.price) { + isValidityLinePrice = false; + } + this.setData({ goods: data, isValidityLinePrice }); + }, + }, + currency: { + type: String, + value: '¥', + }, + + thresholds: { + type: Array, + value: [], + observer(thresholds) { + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } else { + this.clearIntersectionObserverHandle(); + } + }, + }, + }, + + data: { + independentID: '', + goods: { id: '' }, + isValidityLinePrice: false, + }, + + lifetimes: { + ready() { + this.init(); + }, + detached() { + this.clear(); + }, + }, + + pageLifeTimes: {}, + + methods: { + clickHandle() { + this.triggerEvent('click', { goods: this.data.goods }); + }, + + clickThumbHandle() { + this.triggerEvent('thumb', { goods: this.data.goods }); + }, + + addCartHandle(e) { + const { id } = e.currentTarget; + const { id: cardID } = e.currentTarget.dataset; + this.triggerEvent('add-cart', { + ...e.detail, + id, + cardID, + goods: this.data.goods, + }); + }, + + genIndependentID(id) { + let independentID; + if (id) { + independentID = id; + } else { + independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`; + } + this.setData({ independentID }); + }, + + init() { + const { thresholds, id } = this.properties; + this.genIndependentID(id); + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } + }, + + clear() { + this.clearIntersectionObserverHandle(); + }, + + intersectionObserverContext: null, + + createIntersectionObserverHandle() { + if (this.intersectionObserverContext || !this.data.independentID) { + return; + } + this.intersectionObserverContext = this.createIntersectionObserver({ + thresholds: this.properties.thresholds, + }).relativeToViewport(); + + this.intersectionObserverContext.observe( + `#${this.data.independentID}`, + (res) => { + this.intersectionObserverCB(res); + }, + ); + }, + + intersectionObserverCB() { + this.triggerEvent('ob', { + goods: this.data.goods, + context: this.intersectionObserverContext, + }); + }, + + clearIntersectionObserverHandle() { + if (this.intersectionObserverContext) { + try { + this.intersectionObserverContext.disconnect(); + } catch (e) { } + this.intersectionObserverContext = null; + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/goods-card/index.json b/miniprogram/tcb-shop/components/goods-card/index.json new file mode 100644 index 0000000..f199647 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-card/index.wxml b/miniprogram/tcb-shop/components/goods-card/index.wxml new file mode 100644 index 0000000..b28d772 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.wxml @@ -0,0 +1,16 @@ + + + + + + + + {{ goods.name }} + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-card/index.wxss b/miniprogram/tcb-shop/components/goods-card/index.wxss new file mode 100644 index 0000000..6bf6879 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.wxss @@ -0,0 +1,136 @@ +.goods-card { + box-sizing: border-box; + font-size: 24rpx; + border-radius: 0 0 16rpx 16rpx; + border-bottom: none; +} + +.goods-card__main { + position: relative; + display: flex; + line-height: 1; + padding: 0; + background: transparent; + width: 342rpx; + border-radius: 0 0 16rpx 16rpx; + align-items: center; + justify-content: center; + margin-bottom: 16rpx; + flex-direction: column; +} + +.goods-card__thumb { + flex-shrink: 0; + position: relative; + width: 340rpx; + height: 340rpx; +} + +.goods-card__thumb:empty { + display: none; + margin: 0; +} + +.goods-card__img { + display: block; + width: 100%; + height: 100%; + border-radius: 16rpx 16rpx 0 0; + overflow: hidden; +} + +.goods-card__body { + display: flex; + /* flex-grow: 1; */ + box-sizing: border-box; + width: 100%; + flex: 1 1 auto; + background: #fff; + border-radius: 0 0 16rpx 16rpx; + padding: 16rpx 24rpx 18rpx; + flex-direction: column; +} + +.goods-card__upper { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1 1 auto; +} + +.goods-card__title { + flex-shrink: 0; + font-size: 28rpx; + color: #333; + font-weight: 400; + display: -webkit-box; + height: 72rpx; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + word-break: break-word; + line-height: 36rpx; +} + +.goods-card__tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 8rpx 0 0 0; +} + +.goods-card__tag { + color: #fa4126; + background: transparent; + font-size: 20rpx; + border: 1rpx solid #fa4126; + padding: 0 8rpx; + border-radius: 16rpx; + line-height: 30rpx; + margin: 0 8rpx 8rpx 0; + display: block; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; +} + +.goods-card__down { + display: flex; + position: relative; + flex-direction: row; + justify-content: flex-start; + align-items: baseline; + line-height: 32rpx; + margin: 8rpx 0 0 0; +} + +.goods-card__origin-price { + white-space: nowrap; + font-weight: 700; + order: 2; + color: #bbbbbb; + font-size: 24rpx; + margin: 0 0 0 8rpx; +} + +.goods-card__add-cart { + order: 3; + margin: auto 0 0 auto; + position: absolute; + bottom: 0; + right: 0; +} + +.spec-for-price { + font-size: 36rpx; + white-space: nowrap; + font-weight: 700; + order: 1; + color: #fa4126; + margin: 0; +} + +.spec-for-symbol { + font-size: 24rpx; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-list/index.js b/miniprogram/tcb-shop/components/goods-list/index.js new file mode 100644 index 0000000..5184903 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.js @@ -0,0 +1,62 @@ +Component({ + externalClasses: ['wr-class'], + + properties: { + goodsList: { + type: Array, + value: [], + }, + id: { + type: String, + value: '', + observer: (id) => { + this.genIndependentID(id); + }, + }, + thresholds: { + type: Array, + value: [], + }, + }, + + data: { + independentID: '', + }, + + lifetimes: { + ready() { + this.init(); + }, + }, + + methods: { + onClickGoods(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('click', { ...e.detail, index }); + }, + + onAddCart(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('addcart', { ...e.detail, index }); + }, + + onClickGoodsThumb(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('thumb', { ...e.detail, index }); + }, + + init() { + this.genIndependentID(this.id || ''); + }, + + genIndependentID(id) { + if (id) { + this.setData({ independentID: id }); + } else { + this.setData({ + independentID: `goods-list-${~~(Math.random() * 10 ** 8)}`, + }); + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/goods-list/index.json b/miniprogram/tcb-shop/components/goods-list/index.json new file mode 100644 index 0000000..bdaa23d --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "goods-card": "/components/goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-list/index.wxml b/miniprogram/tcb-shop/components/goods-list/index.wxml new file mode 100644 index 0000000..5a9a804 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.wxml @@ -0,0 +1,16 @@ + + + + + + diff --git a/miniprogram/tcb-shop/components/goods-list/index.wxss b/miniprogram/tcb-shop/components/goods-list/index.wxss new file mode 100644 index 0000000..7262a4d --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.wxss @@ -0,0 +1,7 @@ +.goods-list-wrap { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + padding: 0; + background: #fff; +} diff --git a/miniprogram/tcb-shop/components/load-more/index.js b/miniprogram/tcb-shop/components/load-more/index.js new file mode 100644 index 0000000..b71a839 --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.js @@ -0,0 +1,54 @@ +Component({ + externalClasses: ['wr-class', 'wr-class--no-more'], + + options: { multipleSlots: true }, + + properties: { + status: { + type: Number, + value: 0, + }, + loadingText: { + type: String, + value: '加载中...', + }, + noMoreText: { + type: String, + value: '没有更多了', + }, + failedText: { + type: String, + value: '加载失败,点击重试', + }, + color: { + type: String, + value: '#BBBBBB', + }, + failedColor: { + type: String, + value: '#FA550F', + }, + size: { + type: null, + value: '40rpx', + }, + loadingBackgroundColor: { + type: String, + value: '#F5F5F5', + }, + listIsEmpty: { + type: Boolean, + value: false, + }, + }, + + methods: { + /** 点击处理 */ + tapHandle() { + // 失败重试 + if (this.data.status === 3) { + this.triggerEvent('retry'); + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/load-more/index.json b/miniprogram/tcb-shop/components/load-more/index.json new file mode 100644 index 0000000..94e107c --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-loading": "tdesign-miniprogram/loading/loading", + "t-divider": "tdesign-miniprogram/divider/divider" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/load-more/index.wxml b/miniprogram/tcb-shop/components/load-more/index.wxml new file mode 100644 index 0000000..4b69c86 --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.wxml @@ -0,0 +1,31 @@ + + + + + + + + {{noMoreText}} + + + + + 加载失败 + 刷新 + + + + + diff --git a/miniprogram/tcb-shop/components/load-more/index.wxss b/miniprogram/tcb-shop/components/load-more/index.wxss new file mode 100644 index 0000000..bbaf636 --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.wxss @@ -0,0 +1,35 @@ +.load-more { + font-size: 24rpx; + height: 100rpx; + display: flex; + flex-direction: column; + justify-content: center; +} + +.load-more .t-class-loading { + display: flex; + justify-content: center; + + --td-loading-color: #fa4126; +} + +.load-more .t-class-loading-text { + color: #bbbbbb; +} + +.t-class-divider-content { + margin: 0 10rpx; + color: #bbbbbb; +} +.load-more .t-class-indicator { + color: #b9b9b9 !important; +} + +.load-more__error { + margin: auto; +} + +.load-more__refresh-btn { + margin-left: 16rpx; + color: #fa4126; +} diff --git a/miniprogram/tcb-shop/components/loading-content/index.js b/miniprogram/tcb-shop/components/loading-content/index.js new file mode 100644 index 0000000..4c6b925 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.js @@ -0,0 +1,23 @@ +Component({ + externalClasses: ['wr-class'], + properties: { + position: { + type: String, + value: 'static', + }, + noMask: Boolean, + type: { + type: String, + value: 'circular', + }, + vertical: Boolean, + size: { + type: String, + value: '50rpx', + }, + backgroundColor: { + type: String, + value: 'rgba(0, 0, 0, .6)', + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/loading-content/index.json b/miniprogram/tcb-shop/components/loading-content/index.json new file mode 100644 index 0000000..c08b2ef --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-loading": "tdesign-miniprogram/loading/loading" + } +} diff --git a/miniprogram/tcb-shop/components/loading-content/index.wxml b/miniprogram/tcb-shop/components/loading-content/index.wxml new file mode 100644 index 0000000..add589c --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.wxml @@ -0,0 +1,11 @@ + + + + + + diff --git a/miniprogram/tcb-shop/components/loading-content/index.wxss b/miniprogram/tcb-shop/components/loading-content/index.wxss new file mode 100644 index 0000000..d3c112b --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.wxss @@ -0,0 +1,23 @@ +.loading-content { + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + position: relative; +} +.loading-content.absolute { + position: absolute; + z-index: 1; + left: 0; + top: 0; +} +.loading-content.fixed { + position: fixed; + z-index: 1; + left: 0; + top: 0; +} +.loading-content .loading { + width: 100%; + height: 100%; + visibility: visible; +} diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.js b/miniprogram/tcb-shop/components/loading-dialog/index.js new file mode 100644 index 0000000..7c4a2b9 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.js @@ -0,0 +1,8 @@ +Component({ + properties: { + show: { + type: Boolean, + value: false, + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.json b/miniprogram/tcb-shop/components/loading-dialog/index.json new file mode 100644 index 0000000..95170eb --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-loading": "tdesign-miniprogram/loading/loading", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + }, + "styleIsolation": "shared" +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.wxml b/miniprogram/tcb-shop/components/loading-dialog/index.wxml new file mode 100644 index 0000000..44fd109 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.wxml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.wxss b/miniprogram/tcb-shop/components/loading-dialog/index.wxss new file mode 100644 index 0000000..9748562 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.wxss @@ -0,0 +1,11 @@ +.loading-dialog .t-dialog{ + width: unset; +} + +.loading-dialog .t-dialog__content { + padding: 80rpx; +} + +.loading-dialog .t-dialog__footer { + display: none; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/price/index.js b/miniprogram/tcb-shop/components/price/index.js new file mode 100644 index 0000000..c0218f7 --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.js @@ -0,0 +1,71 @@ +Component({ + externalClasses: ['wr-class', 'symbol-class', 'decimal-class'], + useStore: [], + properties: { + priceUnit: { + type: String, + value: 'fen', + }, // 价格单位,分 | 元, fen,yuan + price: { + type: null, + value: '', + observer(price) { + this.format(price); + }, + }, // 价格, 以分为单位 + type: { + type: String, + value: '', // + }, // main 粗体, lighter 细体, mini 黑色, del 中划线, delthrough 中划线,包括货币符号 + symbol: { + type: String, + value: '¥', // '¥', + }, // 货币符号,默认是人民币符号¥ + fill: Boolean, // 是否自动补齐两位小数 + decimalSmaller: Boolean, // 小数字号小一点 + lineThroughWidth: { + type: null, + value: '0.12em', + }, // 划线价线条高度 + }, + + data: { + pArr: [], + }, + + methods: { + format(price) { + price = parseFloat(`${price}`); + const pArr = []; + if (!isNaN(price)) { + const isMinus = price < 0; + if (isMinus) { + price = -price; + } + if (this.properties.priceUnit === 'yuan') { + const priceSplit = price.toString().split('.'); + pArr[0] = priceSplit[0]; + pArr[1] = !priceSplit[1] + ? '00' + : priceSplit[1].length === 1 + ? `${priceSplit[1]}0` + : priceSplit[1]; + } else { + price = Math.round(price * 10 ** 8) / 10 ** 8; // 恢复精度丢失 + price = Math.ceil(price); // 向上取整 + pArr[0] = price >= 100 ? `${price}`.slice(0, -2) : '0'; + pArr[1] = `${price + 100}`.slice(-2); + } + if (!this.properties.fill) { + // 如果 fill 为 false, 不显示小数末尾的0 + if (pArr[1] === '00') pArr[1] = ''; + else if (pArr[1][1] === '0') pArr[1] = pArr[1][0]; + } + if (isMinus) { + pArr[0] = `-${pArr[0]}`; + } + } + this.setData({ pArr }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/price/index.json b/miniprogram/tcb-shop/components/price/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/tcb-shop/components/price/index.wxml b/miniprogram/tcb-shop/components/price/index.wxml new file mode 100644 index 0000000..7f4f9d9 --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.wxml @@ -0,0 +1,21 @@ + + var REGEXP = getRegExp('^\d+(\.\d+)?$'); + function addUnit(value) { + if (value == null) { + return ''; + } + return REGEXP.test('' + value) ? value + 'rpx' : value; + } + module.exports = { + addUnit: addUnit + }; + + + + {{symbol}} + + {{pArr[0]}} + .{{pArr[1]}} + + + diff --git a/miniprogram/tcb-shop/components/price/index.wxss b/miniprogram/tcb-shop/components/price/index.wxss new file mode 100644 index 0000000..45da266 --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.wxss @@ -0,0 +1,66 @@ +:host { + display: inline-block; + display: inline-block; + font-weight: inherit; +} +.inline { + display: inline; + white-space: nowrap; +} +.price { + display: inline; + color: inherit; + font-size: inherit; + text-decoration: inherit; +} + +.lighter { + font-weight: 400; + font-size: 32rpx; +} +.mini { + font-size: 24rpx; + color: #5d5d5d; + font-weight: 400; +} +.del .pprice { + font-size: 32rpx; + color: #9b9b9b; + text-decoration: line-through; + font-weight: 400; +} +.delthrough { + position: relative; +} +.delthrough .line { + position: absolute; + top: 50%; + left: 0; + right: 0; + transform: translateY(-50%); + margin: 0; + background-color: currentColor; +} + +.symbol { + display: inline; + color: inherit; + font-size: inherit; + font-size: 0.8em; +} +.pprice { + display: inline; + margin: 0 0 0 4rpx; +} +.integer { + color: inherit; + font-size: inherit; +} +.decimal { + color: inherit; + font-size: inherit; +} +.decimal.smaller { + font-size: 0.8em; + vertical-align: baseline; +} diff --git a/miniprogram/tcb-shop/components/swipeout/index.js b/miniprogram/tcb-shop/components/swipeout/index.js new file mode 100644 index 0000000..3db6b79 --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.js @@ -0,0 +1,79 @@ +let ARRAY = []; +Component({ + externalClasses: ['wr-class'], + + options: { + multipleSlots: true, + }, + properties: { + disabled: Boolean, + leftWidth: { + type: Number, + value: 0, + }, + rightWidth: { + type: Number, + value: 0, + }, + asyncClose: Boolean, + }, + attached() { + ARRAY.push(this); + }, + + detached() { + ARRAY = ARRAY.filter((item) => item !== this); + }, + + /** + * Component initial data + */ + data: { + wrapperStyle: '', + asyncClose: false, + closed: true, + }, + + /** + * Component methods + */ + methods: { + open(position) { + this.setData({ closed: false }); + this.triggerEvent('close', { + position, + instance: this, + }); + }, + + close() { + this.setData({ closed: true }); + }, + + closeOther() { + ARRAY.filter((item) => item !== this).forEach((item) => item.close()); + }, + + noop() { + return; + }, + + onClick(event) { + const { key: position = 'outside' } = event.currentTarget.dataset; + this.triggerEvent('click', position); + + if (this.data.closed) { + return; + } + + if (this.data.asyncClose) { + this.triggerEvent('close', { + position, + instance: this, + }); + } else { + this.close(); + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/swipeout/index.json b/miniprogram/tcb-shop/components/swipeout/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/tcb-shop/components/swipeout/index.wxml b/miniprogram/tcb-shop/components/swipeout/index.wxml new file mode 100644 index 0000000..353bf72 --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.wxml @@ -0,0 +1,174 @@ + + var THRESHOLD = 0.3; + var MIN_DISTANCE = 10; + var owner; + var state; + + var getState = function(ownerInstance) { + owner = ownerInstance; + state = owner.getState(); + state.leftWidth = state.leftWidth || 0; + state.rightWidth = state.rightWidth || 0; + state.offset = state.offset || 0; + state.startOffset = state.startOffset || 0; + }; + + var initRightWidth = function(newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.rightWidth = newVal; + if (state.offset < 0) { + swipeMove(-state.rightWidth); + } + }; + + var initLeftWidth = function(newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.leftWidth = newVal; + if (state.offset > 0) { + swipeMove(state.leftWidth); + } + } + + var resetTouchStatus = function() { + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; + }; + + var touchMove = function(event) { + var touchPoint = event.touches[0]; + state.deltaX = touchPoint.clientX - state.startX; + state.deltaY = touchPoint.clientY - state.startY; + state.offsetX = Math.abs(state.deltaX); + state.offsetY = Math.abs(state.deltaY); + state.direction = state.direction || getDirection(state.offsetX, state.offsetY); + }; + + var getDirection = function(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; + }; + + var range = function(num, min, max) { + return Math.min(Math.max(num, min), max); + }; + + var swipeMove = function(_offset = 0) { + state.offset = range( + _offset, + -state.rightWidth, + +state.leftWidth, + ); + + var transform = 'translate3d(' + state.offset + 'px, 0, 0)'; + var transition = state.dragging + ? 'none' + : 'transform .6s cubic-bezier(0.18, 0.89, 0.32, 1)'; + owner.selectComponent('#wrapper').setStyle({ + '-webkit-transform': transform, + '-webkit-transition': transition, + 'transform': transform, + 'transition': transition + }); + }; + + var close = function() { + swipeMove(0); + }; + + var onCloseChange = function(newVal, oldVal, ownerInstance) { + getState(ownerInstance); + if (newVal === oldVal) return; + if (newVal) { + close(); + } + }; + + var touchStart = function(event) { + resetTouchStatus(); + state.startOffset = state.offset; + var touchPoint = event.touches[0]; + state.startX = touchPoint.clientX; + state.startY = touchPoint.clientY; + owner.callMethod('closeOther'); + }; + + var startDrag = function(event, ownerInstance) { + getState(ownerInstance); + touchStart(event); + }; + + var onDrag = function(event, ownerInstance) { + getState(ownerInstance); + touchMove(event); + if (state.direction !== 'horizontal') { + return; + } + state.dragging = true; + swipeMove(state.startOffset + state.deltaX); + }; + + var open = function(position) { + var _offset = position === 'left' ? +state.leftWidth : -state.rightWidth; + owner.callMethod('open', { position: position }); + swipeMove(_offset); + }; + + var endDrag = function(event, ownerInstance) { + getState(ownerInstance); + state.dragging = false; + // 左/右侧有可滑动区域,且当前不是已open状态,且滑动幅度超过阈值时open左/右侧(滚动到该侧的最边上) + if (+state.rightWidth > 0 && -state.startOffset < +state.rightWidth && -state.offset > +state.rightWidth * THRESHOLD) { + open('right'); + } else if (+state.leftWidth > 0 && state.startOffset < +state.leftWidth && state.offset > +state.leftWidth * THRESHOLD) { + open('left'); + } else { + // 仅在有发生侧滑的情况下自动关闭(由js控制是否异步关闭) + if (state.startOffset !== state.offset) { + close(); + } + } + }; + + module.exports = { + initLeftWidth: initLeftWidth, + initRightWidth: initRightWidth, + startDrag: startDrag, + onDrag: onDrag, + endDrag: endDrag, + onCloseChange: onCloseChange + }; + + + + + + + + + + + + + diff --git a/miniprogram/tcb-shop/components/swipeout/index.wxss b/miniprogram/tcb-shop/components/swipeout/index.wxss new file mode 100644 index 0000000..1aa2943 --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.wxss @@ -0,0 +1,18 @@ +.wr-swipeout { + position: relative; + overflow: hidden; +} +.wr-swipeout__left, +.wr-swipeout__right { + position: absolute; + top: 0; + height: 100%; +} +.wr-swipeout__left { + left: 0; + transform: translate3d(-100%, 0, 0); +} +.wr-swipeout__right { + right: 0; + transform: translate3d(100%, 0, 0); +} diff --git a/miniprogram/tcb-shop/components/webp-image/index.js b/miniprogram/tcb-shop/components/webp-image/index.js new file mode 100644 index 0000000..1706049 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/index.js @@ -0,0 +1,86 @@ +/* + * @Author: rileycai + * @Date: 2022-03-14 14:21:26 + * @LastEditTime: 2022-03-14 15:23:04 + * @LastEditors: rileycai + * @Description: webp-image组件对t-image包裹了一层,主要实现图片裁剪、webp压缩功能 + * @FilePath: /tdesign-miniprogram-starter/components/webp-image/index.js + */ +const systemInfo = wx.getSystemInfoSync(); +Component({ + externalClasses: ['t-class', 't-class-load'], + properties: { + loadFailed: { + type: String, + value: 'default', + }, + loading: { + type: String, + value: 'default', + }, + src: { + type: String, + value: '', + }, + mode: { + type: String, + value: 'aspectFill', + }, + webp: { + type: Boolean, + value: true, + }, + lazyLoad: { + type: Boolean, + value: false, + }, + showMenuByLongpress: { + type: Boolean, + value: false, + }, + }, + data: { + thumbHeight: 375, + thumbWidth: 375, + systemInfo, + }, + lifetimes: { + ready() { + const { mode } = this.properties; + // 获取容器的真实宽高,设置图片的裁剪宽度 + this.getRect('.J-image').then((res) => { + if (res) { + const { width, height } = res; + this.setData( + mode === 'heightFix' + ? { + thumbHeight: this.px2rpx(height) || 375, + } + : { + thumbWidth: this.px2rpx(width) || 375, + }, + ); + } + }); + }, + }, + methods: { + px2rpx(px) { + return (750 / (systemInfo.screenWidth || 375)) * px; + }, + getRect(selector) { + return new Promise((resolve) => { + if (!this.selectorQuery) { + this.selectorQuery = this.createSelectorQuery(); + } + this.selectorQuery.select(selector).boundingClientRect(resolve).exec(); + }); + }, + onLoad(e) { + this.triggerEvent('load', e.detail); + }, + onError(e) { + this.triggerEvent('error', e.detail); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/webp-image/index.json b/miniprogram/tcb-shop/components/webp-image/index.json new file mode 100644 index 0000000..6ffda42 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-image": "tdesign-miniprogram/image/image" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/webp-image/index.wxml b/miniprogram/tcb-shop/components/webp-image/index.wxml new file mode 100644 index 0000000..1a917b2 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/index.wxml @@ -0,0 +1,14 @@ + + diff --git a/miniprogram/tcb-shop/components/webp-image/index.wxss b/miniprogram/tcb-shop/components/webp-image/index.wxss new file mode 100644 index 0000000..e69de29 diff --git a/miniprogram/tcb-shop/components/webp-image/utils.wxs b/miniprogram/tcb-shop/components/webp-image/utils.wxs new file mode 100644 index 0000000..4f6e5d1 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/utils.wxs @@ -0,0 +1,140 @@ +var isString = function (value) { + return typeof value === 'string'; +}; + +var isNumber = function (value) { + return typeof value === 'number'; +}; + +var getFileExt = function (src) { + var fileUrl = src.split('?')[0]; + var splitUlr = fileUrl.split('/'); + var filepath = splitUlr[splitUlr.length - 1]; + return filepath.split('.')[1] || 'jpg'; +}; + +function isUrl(url) { + // NOCC:ToolNameCheck(非敏感词) + var urlReg = getRegExp( + '/[(http(s)?)://(www.)?a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/', + 'ig', + ); + + return urlReg.test(url); +} + +function rpx2px(rpx, screenWidth) { + // px / systemWidth = rpx / 750 + var result = (rpx * (screenWidth || 375)) / 750; + + return Math.round(result); +} + +function imageMogr(url, options) { + if (!isString(url) || !url) return ''; + + if ( + url.indexOf('qlogo.cn') !== -1 || + url.indexOf('wxfile://') === 0 || + url.indexOf('http://tmp/wx') === 0 || + url.indexOf('imageMogr2') !== -1 + ) { + //qlogo.cn域名或者本地图片不做转换 + return url; + } //强制转https + + if (url.indexOf('http://') === 0) { + url = url.replace('http://', 'https://'); + } else if (url.indexOf('//') === 0) { + url = 'https:' + url; + } + + if (!options) return url; + + var width = Math.ceil(options.width), + height = Math.ceil(options.height), + format = options.format, + _optionsQuality = options.quality, + quality = _optionsQuality === undefined ? 70 : _optionsQuality, + _optionsStrip = options.strip, + strip = _optionsStrip === undefined ? true : _optionsStrip, + crop = options.crop; + var isValidWidth = isNumber(width) && width > 0; + var isValidHeight = isNumber(height) && height > 0; + var imageMogrStr = ''; + var size = ''; + + if (isValidWidth && isValidHeight) { + size = ''.concat(width, 'x').concat(height); + } else if (isValidWidth) { + size = ''.concat(width, 'x'); + } else if (isValidHeight) { + size = 'x'.concat(height); + } + + if (size) { + //缩放或者裁剪 + imageMogrStr += '/'.concat(crop ? 'crop' : 'thumbnail', '/').concat(size); + + if (crop) { + //裁剪目前需求只有以图片中心为基准 + imageMogrStr += '/gravity/center'; + } + } + + if (isNumber(quality)) { + //质量变换 + imageMogrStr += '/quality/'.concat(quality); + } + + if (strip) { + //去除元信息 + imageMogrStr += '/strip'; + } + + var ext = getFileExt(url); + + // gif 图片不做格式转换,否则会损坏动图 + if (ext === 'gif') { + imageMogrStr += '/cgif/1'; + } else if (format) { + //格式转换 + imageMogrStr += '/format/'.concat(format); + } + + if (format === 'jpg' || (!format && (ext === 'jpg' || ext === 'jpeg'))) { + //渐进式 jpg 加载 + imageMogrStr += '/interlace/1'; + } + if (!imageMogrStr) return url; + return '' + .concat(url) + .concat(url.indexOf('?') !== -1 ? '&' : '?', 'imageMogr2') + .concat(imageMogrStr); +} +function getSrc(options) { + if (!options.src) return ''; + + if (options.thumbWidth || options.thumbHeight) { + return imageMogr(options.src, { + width: + options.mode !== 'heightFix' + ? rpx2px(options.thumbWidth, options.systemInfo.screenWidth) * + options.systemInfo.pixelRatio + : null, + height: + options.mode !== 'widthFix' + ? rpx2px(options.thumbHeight, options.systemInfo.screenWidth) * + options.systemInfo.pixelRatio + : null, + format: options.webp ? 'webp' : null, + }); + } + + return ''; +} + +module.exports = { + imageMogr: imageMogr, + getSrc: getSrc, +}; diff --git a/miniprogram/tcb-shop/config/eslintCheck.js b/miniprogram/tcb-shop/config/eslintCheck.js new file mode 100644 index 0000000..9294baf --- /dev/null +++ b/miniprogram/tcb-shop/config/eslintCheck.js @@ -0,0 +1,91 @@ +/* eslint-disable prefer-template */ +/** + * 工程代码pre-commit 检查工具 + * @date 2019.9.4 + * @author 310227663@qq.com + */ +const { exec } = require('child_process'); +const chalk = require('chalk'); +const { CLIEngine } = require('eslint'); +const cli = new CLIEngine({}); +const { log } = console; + +function getErrorLevel(number) { + switch (number) { + case 2: + return 'error'; + case 1: + return 'warn'; + default: + } + return 'undefined'; +} +let pass = 0; +exec( + 'git diff --cached --name-only --diff-filter=ACM | grep -Ei "\\.ts$|\\.js$"', + (error, stdout) => { + if (stdout.length) { + const array = stdout.split('\n'); + array.pop(); + const { results } = cli.executeOnFiles(array); + let errorCount = 0; + let warningCount = 0; + results.forEach((result) => { + errorCount += result.errorCount; + warningCount += result.warningCount; + if (result.messages.length > 0) { + log('\n'); + log(result.filePath); + result.messages.forEach((obj) => { + const level = getErrorLevel(obj.severity); + if (level === 'warn') + log( + ' ' + + obj.line + + ':' + + obj.column + + '\t ' + + chalk.yellow(level) + + ' \0 ' + + obj.message + + '\t\t' + + chalk.grey(obj.ruleId) + + '', + ); + if (level === 'error') + log( + ' ' + + obj.line + + ':' + + obj.column + + '\t ' + + chalk.red.bold(level) + + ' \0 ' + + obj.message + + '\t\t ' + + chalk.grey(obj.ruleId) + + '', + ); + if (level === 'error') pass = 1; + }); + } + }); + if (warningCount > 0 || errorCount > 0) { + log( + '\n' + + chalk.bgRed.bold(errorCount + warningCount + ' problems') + + ' (' + + chalk.red.bold(errorCount) + + ' errors, ' + + chalk.yellow(warningCount) + + ' warnings) \0', + ); + } + !pass && log(chalk.green.bold('~~ Done: 代码检验通过,提交成功 ~~')); + process.exit(pass); + } + if (error !== null) { + log(`exec error: ${error}`); + } + }, +); diff --git a/miniprogram/tcb-shop/config/index.js b/miniprogram/tcb-shop/config/index.js new file mode 100644 index 0000000..26ca6ee --- /dev/null +++ b/miniprogram/tcb-shop/config/index.js @@ -0,0 +1,6 @@ +export const config = { + /** 是否使用mock代替api返回 */ + useMock: true, +}; + +export const cdnBase = 'https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-mp'; diff --git a/miniprogram/tcb-shop/config/model.js b/miniprogram/tcb-shop/config/model.js new file mode 100644 index 0000000..0e7f6b4 --- /dev/null +++ b/miniprogram/tcb-shop/config/model.js @@ -0,0 +1,12 @@ +export const DATA_MODEL_KEY = { + ATTR_VALUE: 'shop_attr_value', + CATE: 'shop_spu_cate', + CART_ITEM: 'shop_cart_item', + COMMENT: 'shop_comment', + DELIVERY_INFO: 'shop_delivery_info', + HOME_SWIPER: 'shop_home_swiper_image', + ORDER: 'shop_order', + ORDER_ITEM: 'shop_order_item', + SKU: 'shop_sku', + SPU: 'shop_spu', +}; diff --git a/miniprogram/tcb-shop/custom-tab-bar/data.js b/miniprogram/tcb-shop/custom-tab-bar/data.js new file mode 100644 index 0000000..ff11d64 --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/data.js @@ -0,0 +1,22 @@ +export default [ + { + icon: 'home', + text: '首页', + url: 'pages/home/home', + }, + { + icon: 'sort', + text: '分类', + url: 'pages/goods/category/index', + }, + { + icon: 'cart', + text: '购物车', + url: 'pages/cart/index', + }, + { + icon: 'person', + text: '个人中心', + url: 'pages/usercenter/index', + }, +]; diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.js b/miniprogram/tcb-shop/custom-tab-bar/index.js new file mode 100644 index 0000000..90c58df --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.js @@ -0,0 +1,29 @@ +import TabMenu from './data'; +Component({ + data: { + active: 0, + list: TabMenu, + }, + + methods: { + onChange(event) { + this.setData({ active: event.detail.value }); + wx.switchTab({ + url: this.data.list[event.detail.value].url.startsWith('/') + ? this.data.list[event.detail.value].url + : `/${this.data.list[event.detail.value].url}`, + }); + }, + + init() { + const page = getCurrentPages().pop(); + const route = page ? page.route.split('?')[0] : ''; + const active = this.data.list.findIndex( + (item) => + (item.url.startsWith('/') ? item.url.substr(1) : item.url) === + `${route}`, + ); + this.setData({ active }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.json b/miniprogram/tcb-shop/custom-tab-bar/index.json new file mode 100644 index 0000000..c8ba0bd --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar", + "t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.wxml b/miniprogram/tcb-shop/custom-tab-bar/index.wxml new file mode 100644 index 0000000..384fd2f --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.wxml @@ -0,0 +1,18 @@ + + + + + {{ item.text }} + + + + diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.wxss b/miniprogram/tcb-shop/custom-tab-bar/index.wxss new file mode 100644 index 0000000..3b855f5 --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.wxss @@ -0,0 +1,9 @@ +.custom-tab-bar-wrapper { + display: flex; + flex-direction: column; + align-items: center; +} + +.custom-tab-bar-wrapper .text { + font-size: 20rpx; +} diff --git a/miniprogram/tcb-shop/jsconfig.json b/miniprogram/tcb-shop/jsconfig.json new file mode 100644 index 0000000..36aa1a4 --- /dev/null +++ b/miniprogram/tcb-shop/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/miniprogram/tcb-shop/model/activities.js b/miniprogram/tcb-shop/model/activities.js new file mode 100644 index 0000000..31bc3b8 --- /dev/null +++ b/miniprogram/tcb-shop/model/activities.js @@ -0,0 +1,7 @@ +import { getActivity } from './activity'; + +export function getActivityList(baseID = 0, length = 10) { + return new Array(length).fill(0).map((_, idx) => getActivity(idx + baseID)); +} + +export const activityList = getActivityList(); diff --git a/miniprogram/tcb-shop/model/activity.js b/miniprogram/tcb-shop/model/activity.js new file mode 100644 index 0000000..34abb07 --- /dev/null +++ b/miniprogram/tcb-shop/model/activity.js @@ -0,0 +1,18 @@ +/** + * @param {string|number} key 唯一值 + */ +export function getActivity(key) { + return { + promotionId: `${key}`, + title: `满减满折回归${key}`, + description: null, + promotionCode: 'MERCHANT', + promotionSubCode: key % 2 === 0 ? 'MYJ' : 'MYG', + tag: '满减', + timeType: 1, + startTime: '1588737710000', + endTime: '1601467070000', + teasingStartTime: null, + activityLadder: [{ label: '满100元减99.9元' }], + }; +} diff --git a/miniprogram/tcb-shop/model/address.js b/miniprogram/tcb-shop/model/address.js new file mode 100644 index 0000000..c7ae8dc --- /dev/null +++ b/miniprogram/tcb-shop/model/address.js @@ -0,0 +1,31 @@ +/** 地址 */ +export function genAddress(id) { + return { + saasId: '88888888', + uid: `8888888820550${id}`, + authToken: null, + id: `${id}`, + addressId: `${id}`, + phone: '17612345678', + name: `测试用户${id}`, + countryName: '中国', + countryCode: 'chn', + provinceName: '甘肃省', + provinceCode: '620000', + cityName: '甘南藏族自治州', + cityCode: '623000', + districtName: '碌曲县', + districtCode: '623026', + detailAddress: `松日鼎盛大厦${id}层${id}号`, + isDefault: `${id}` === '0' ? 1 : 0, + addressTag: id === 0 ? '' : '公司', + latitude: '34.59103', + longitude: '102.48699', + storeId: null, + }; +} + +/** 地址列表 */ +export function genAddressList(len = 10) { + return new Array(len).fill(0).map((_, idx) => genAddress(idx)); +} diff --git a/miniprogram/tcb-shop/model/cart.js b/miniprogram/tcb-shop/model/cart.js new file mode 100644 index 0000000..d0ad6c5 --- /dev/null +++ b/miniprogram/tcb-shop/model/cart.js @@ -0,0 +1,324 @@ +import { mockIp, mockReqId } from '../utils/mock'; + +export function genCartGroupData() { + const resp = { + data: { + isNotEmpty: true, + storeGoods: [ + { + storeId: '1000', + storeName: '云Mall深圳旗舰店', + storeStatus: 1, + totalDiscountSalePrice: '9990', + promotionGoodsList: [ + { + title: '满减满折回归', + promotionCode: 'MERCHANT', + promotionSubCode: 'MYJ', + promotionId: '159174555838121985', + tagText: ['满100元减99.9元'], + promotionStatus: 3, + tag: '满减', + description: '满100元减99.9元,已减99.9元', + doorSillRemain: null, + isNeedAddOnShop: 0, + goodsPromotionList: [ + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '12', + skuId: '135691622', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png', + title: + '腾讯极光盒子4智能网络电视机顶盒6K千兆网络机顶盒4K高分辨率', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png', + quantity: 1, + stockStatus: true, + stockQuantity: 3, + price: '9900', + originPrice: '16900', + tagPrice: null, + titlePrefixTags: [{ text: '新品' }, { text: '火爆' }], + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '经典白', + }, + { + specTitle: '类型', + specValue: '经典套装', + }, + ], + joinCartTime: '2020-06-29T07:55:40.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '18', + skuId: '135681631', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png', + title: + '白色短袖连衣裙荷叶边裙摆宽松韩版休闲纯白清爽优雅连衣裙', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png', + quantity: 1, + stockStatus: true, + stockQuantity: 177, + price: '29800', + originPrice: '40000', + tagPrice: null, + titlePrefixTags: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '米色荷叶边', + }, + { + specTitle: '尺码', + specValue: 'M', + }, + ], + joinCartTime: '2020-06-29T07:55:27.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '13', + skuId: '135698362', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png', + title: + '带帽午休毯虎年款多功能加厚加大加绒简约多功能午休毯连帽披肩', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png', + quantity: 13, + stockStatus: true, + stockQuantity: 9, + price: '29900', + originPrice: '0', + tagPrice: null, + titlePrefixTags: [{ text: '火爆' }], + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '浅灰色', + }, + { + specTitle: '尺码', + specValue: 'M', + }, + ], + joinCartTime: '2020-06-29T07:54:43.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '7', + skuId: '135681625', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/gh-2b.png', + title: + '不锈钢刀叉勺套装家用西餐餐具ins简约耐用不锈钢金色银色可选', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/gh-2b.png', + quantity: 1, + stockStatus: true, + stockQuantity: 0, + price: '29900', + originPrice: '29900', + tagPrice: null, + titlePrefixTags: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '奶黄色', + }, + { + specTitle: '数量', + specValue: '六件套', + }, + ], + joinCartTime: '2020-06-29T07:55:00.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + ], + lastJoinTime: '2020-06-29T07:55:40.000+0000', + }, + { + title: null, + promotionCode: 'EMPTY_PROMOTION', + promotionSubCode: null, + promotionId: null, + tagText: null, + promotionStatus: null, + tag: null, + description: null, + doorSillRemain: null, + isNeedAddOnShop: 0, + goodsPromotionList: [ + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '11', + skuId: '135691629', + isSelected: 0, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png', + title: '运动连帽拉链卫衣休闲开衫长袖多色运动细绒面料运动上衣', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png', + quantity: 1, + stockStatus: false, + stockQuantity: 0, + price: '25900', + originPrice: '39900', + tagPrice: null, + tagText: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '军绿色', + }, + { + specTitle: '尺码', + specValue: 'S', + }, + ], + joinCartTime: '2020-04-24T06:26:48.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '5', + skuId: '135691635', + isSelected: 0, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png', + title: + '迷你便携高颜值蓝牙无线耳机立体声只能触控式操作简约立体声耳机', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png', + quantity: 1, + stockStatus: true, + stockQuantity: 96, + price: '29000', + originPrice: '29900', + tagPrice: null, + tagText: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '黑色', + }, + { + specTitle: '类型', + specValue: '简约款', + }, + ], + joinCartTime: '2020-06-29T07:55:17.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + ], + lastJoinTime: null, + }, + ], + lastJoinTime: '2020-06-29T07:55:40.000+0000', + postageFreePromotionVo: { + title: null, + promotionCode: null, + promotionSubCode: null, + promotionId: null, + tagText: null, + promotionStatus: null, + tag: null, + description: null, + doorSillRemain: null, + isNeedAddOnShop: 0, + }, + }, + ], + invalidGoodItems: [ + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '1', + skuId: '135691631', + isSelected: 1, + thumb: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + title: '纯色纯棉休闲圆领短袖T恤纯白亲肤厚柔软细腻面料纯白短袖套头T恤', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + quantity: 8, + stockStatus: true, + stockQuantity: 177, + price: '26900', + originPrice: '31900', + tagPrice: null, + tagText: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '白色', + }, + { + specTitle: '尺码', + specValue: 'S', + }, + ], + joinCartTime: '2020-04-28T04:03:59.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + ], + isAllSelected: false, + selectedGoodsCount: 16, + totalAmount: '179997', + totalDiscountAmount: '110000', + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 269, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/category.js b/miniprogram/tcb-shop/model/category.js new file mode 100644 index 0000000..81b7502 --- /dev/null +++ b/miniprogram/tcb-shop/model/category.js @@ -0,0 +1,206 @@ +export function getCategoryList() { + return [ + { + groupId: '24948', + name: '女装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '女装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '卫衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-1.png', + }, + { + groupId: '249480', + name: '毛呢外套', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-2.png', + }, + { + groupId: '249480', + name: '雪纺衫', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-3.png', + }, + { + groupId: '249480', + name: '羽绒服', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-4.png', + }, + { + groupId: '249480', + name: '毛衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-5.png', + }, + { + groupId: '249480', + name: '棉衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-6.png', + }, + { + groupId: '249480', + name: '西装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-7.png', + }, + { + groupId: '249480', + name: '马甲', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png', + }, + { + groupId: '249480', + name: '连衣裙', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-9.png', + }, + { + groupId: '249480', + name: '半身裙', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-10.png', + }, + { + groupId: '249480', + name: '裤子', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png', + }, + ], + }, + ], + }, + { + groupId: '24948', + name: '男装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '男装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '卫衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-1.png', + }, + { + groupId: '249480', + name: '裤子', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png', + }, + { + groupId: '249480', + name: '西装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-7.png', + }, + { + groupId: '249480', + name: '羽绒服', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-4.png', + }, + { + groupId: '249480', + name: '马甲', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png', + }, + ], + }, + ], + }, + { + groupId: '24948', + name: '儿童装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '儿童装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '马甲', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png', + }, + { + groupId: '249480', + name: '裤子', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png', + }, + { + groupId: '249480', + name: '连衣裙', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-9.png', + }, + { + groupId: '249480', + name: '其他', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3b.png', + }, + ], + }, + ], + }, + { + groupId: '24948', + name: '美妆', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '美妆', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '唇釉', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-20a1.png', + }, + { + groupId: '249480', + name: '美妆蛋', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-11a1.png', + }, + { + groupId: '249480', + name: '眼影', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-12b.png', + }, + ], + }, + ], + }, + ]; +} diff --git a/miniprogram/tcb-shop/model/comments.js b/miniprogram/tcb-shop/model/comments.js new file mode 100644 index 0000000..78dd61d --- /dev/null +++ b/miniprogram/tcb-shop/model/comments.js @@ -0,0 +1,338 @@ +/** + * * @param {number} spuId + * @param {number} pageNum + * @param {number} pageSize + * @param {number} commentsLevel + * @param {boolean} hasImage + */ +export function getGoodsAllComments(params) { + const { hasImage } = params.queryParameter; + if (hasImage) { + return { + pageNum: 1, + pageSize: 10, + totalCount: '1', + pageList: [ + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + ], + }; + } + return { + pageNum: 1, + pageSize: 10, + totalCount: '47', + pageList: [ + { + spuId: '1722045', + skuId: '1697694', + specInfo: '很不错', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 1, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592224320000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '1697693', + specInfo: '很适合', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 1, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592224320000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: 'NICE', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592218074000', + isAutoComment: true, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592218074000', + isAutoComment: false, + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592217607000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697693', + specInfo: '测试dr超长:超长测试超长测试1;bwtgg01:bbb', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592217607000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592205599000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592188822000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881055835', + userName: 'Max', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1593792002000', + isAutoComment: true, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: '', + commentImageUrls: null, + commentScore: 5, + uid: '88881055835', + userName: 'Max', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1593792001000', + isAutoComment: true, + }, + ], + }; +} + +export function getGoodsCommentsCount() { + return { + commentCount: '47', + badCount: '0', + middleCount: '2', + goodCount: '45', + hasImageCount: '1', + goodRate: 95.7, + uidCount: '0', + }; +} diff --git a/miniprogram/tcb-shop/model/comments/queryDetail.js b/miniprogram/tcb-shop/model/comments/queryDetail.js new file mode 100644 index 0000000..3d78780 --- /dev/null +++ b/miniprogram/tcb-shop/model/comments/queryDetail.js @@ -0,0 +1,14 @@ +const queryDetail = { + commentInfos: [], + logisticsScore: null, + serviceScore: null, +}; + +/** + * @param {string} skuId + * @param {string} spuId + * @param {string} orderNo + */ +export function queryCommentDetail() { + return queryDetail; +} diff --git a/miniprogram/tcb-shop/model/coupon.js b/miniprogram/tcb-shop/model/coupon.js new file mode 100644 index 0000000..a41ff8b --- /dev/null +++ b/miniprogram/tcb-shop/model/coupon.js @@ -0,0 +1,39 @@ +/** + * 优惠券 + * + * @typedef {'default'|'useless'|'disabled'} CouponCardStatus + * @typedef {'discount'|'price'} CouponCardType + * + * @param {number} [id] + * @param {CouponCardStatus} [status] + * @param {CouponCardType} [type] + */ +export function getCoupon(id = 0, status = 'default', type = (id % 2) + 1) { + return { + /** key */ + key: `${id}`, + /** 优惠券状态 */ + status, + /** 优惠券类型 */ + type, + /** 折扣或者满减值 */ + value: type === 2 ? 5.5 : 1800, + /** 标签 */ + tag: '', + /** 描述 */ + desc: parseInt(id) > 0 ? `满${parseInt(id) * 100}元可用` : '无门槛使用', + /** 订单底价,满n元 */ + base: 10000 * (parseInt(id) || 0), + /** 标题 */ + title: type === 2 ? `生鲜折扣券 - ${id}` : `生鲜满减券 - ${id}`, + /** 有效时间限制 */ + timeLimit: '2019.11.18-2023.12.18', + /** 货币符号 */ + currency: '¥', + }; +} + +/** 优惠券列表 */ +export function getCouponList(status = 'default', length = 10) { + return new Array(length).fill(0).map((_, idx) => getCoupon(idx, status)); +} diff --git a/miniprogram/tcb-shop/model/detailsComments.js b/miniprogram/tcb-shop/model/detailsComments.js new file mode 100644 index 0000000..82fc887 --- /dev/null +++ b/miniprogram/tcb-shop/model/detailsComments.js @@ -0,0 +1,30 @@ +export function getGoodsDetailsComments() { + return { + homePageComments: [ + { + spuId: '1722045', + skuId: null, + specInfo: null, + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://wx.qlogo.cn/mmopen/vi_32/5mKrvn3ibyDNaDZSZics3aoKlz1cv0icqn4EruVm6gKjsK0xvZZhC2hkUkRWGxlIzOEc4600JkzKn9icOLE6zjgsxw/132', + }, + ], + }; +} + +export function getGoodsDetailsCommentsCount() { + return { + commentCount: '47', + badCount: '0', + middleCount: '2', + goodCount: '45', + hasImageCount: '1', + goodRate: 95.7, + uidCount: '0', + }; +} diff --git a/miniprogram/tcb-shop/model/good.js b/miniprogram/tcb-shop/model/good.js new file mode 100644 index 0000000..84b5ecd --- /dev/null +++ b/miniprogram/tcb-shop/model/good.js @@ -0,0 +1,25 @@ +import { cdnBase } from '../config/index'; +const imgPrefix = cdnBase; + +const defaultDesc = [`${imgPrefix}/goods/details-1.png`]; + +const allGoods = []; + +/** + * @param {string} id + * @param {number} [available] 库存, 默认1 + */ +export function genGood(id, available = 1) { + const specID = ['135681624', '135681628']; + if (specID.indexOf(id) > -1) { + return allGoods.filter((good) => good.spuId === id)[0]; + } + const item = allGoods[id % allGoods.length]; + return { + ...item, + spuId: `${id}`, + available: available, + desc: item?.desc || defaultDesc, + images: item?.images || [item?.primaryImage], + }; +} diff --git a/miniprogram/tcb-shop/model/goods.js b/miniprogram/tcb-shop/model/goods.js new file mode 100644 index 0000000..850e0ba --- /dev/null +++ b/miniprogram/tcb-shop/model/goods.js @@ -0,0 +1,7 @@ +import { genGood } from './good'; + +export function getGoodsList(baseID = 0, length = 10) { + return new Array(length).fill(0).map((_, idx) => genGood(idx + baseID)); +} + +export const goodsList = getGoodsList(); diff --git a/miniprogram/tcb-shop/model/order/applyService.js b/miniprogram/tcb-shop/model/order/applyService.js new file mode 100644 index 0000000..e3a2fdf --- /dev/null +++ b/miniprogram/tcb-shop/model/order/applyService.js @@ -0,0 +1,295 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +const orderResps = [ + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135691625', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: + '迷你便携高颜值蓝牙无线耳机立体声只能触控式操作简约立体声耳机', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '黑色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: '简约款', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135676631', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '白色短袖连衣裙荷叶边裙摆宽松韩版休闲纯白清爽优雅连衣裙', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '米色荷叶边', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'S', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135691622', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '腾讯极光盒子4智能网络电视机顶盒6K千兆网络机顶盒4K高分辨率', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '经典白', + }, + { + specId: '50459', + specTitle: '类型', + specValue: '经典套装', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135676629', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '带帽午休毯虎年款多功能加厚加大加绒简约多功能午休毯连帽披肩', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '浅灰色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'S', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135686631', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '运动连帽拉链卫衣休闲开衫长袖多色运动细绒面料运动上衣', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '军绿色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'XS', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '19384938948343', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '130169571554503755', + goodsInfo: { + goodsName: + '纯色纯棉休闲圆领短袖T恤纯白亲肤厚柔软细腻面料纯白短袖套头T恤', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '军绿色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'XS', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, +]; + +export function genRightsPreview(params) { + const { orderNo, skuId } = params; + const resp = orderResps.find( + (r) => r.data.orderNo === orderNo && r.data.skuId === skuId, + ); + return resp; +} + +export function genApplyReasonList(params) { + const resp = { + data: { + saasId: '70000001', + rightsReasonList: [ + { id: '1', desc: '实际商品与描述不符' }, + { id: '2', desc: '质量问题' }, + { id: '3', desc: '少件/漏发' }, + { id: '4', desc: '包装/商品/污迹/裂痕/变形' }, + { id: '5', desc: '发货太慢' }, + { id: '6', desc: '物流配送太慢' }, + { id: '7', desc: '商家发错货' }, + { id: '8', desc: '不喜欢' }, + ], + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 6, + success: true, + }; + // 未收货对应的原因列表 + if (params.rightsReasonType === 'REFUND_MONEY') { + resp.data.rightsReasonList = [ + { id: '9', desc: '空包裹' }, + { id: '10', desc: '快递/物流一直未送到' }, + { id: '11', desc: '货物破损已拒签' }, + { id: '12', desc: '不喜欢' }, + ]; + } + return resp; +} + +export function applyService() { + const resp = { + data: { + rightsNo: '123123423', + saasId: '70000001', + uid: '700000011070005', + storeId: '542', + result: null, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 269, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/order/orderConfirm.js b/miniprogram/tcb-shop/model/order/orderConfirm.js new file mode 100644 index 0000000..7170492 --- /dev/null +++ b/miniprogram/tcb-shop/model/order/orderConfirm.js @@ -0,0 +1,147 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +export const transformGoodsDataToConfirmData = (goodsDataList) => { + const list = []; + + goodsDataList.forEach((goodsData) => { + list.push({ + storeId: goodsData.storeId, + spuId: goodsData.spuId, + skuId: goodsData.skuId, + goodsName: goodsData.title, + image: goodsData.primaryImage, + reminderStock: 119, + quantity: goodsData.quantity, + payPrice: goodsData.price, + totalSkuPrice: goodsData.price, + discountSettlePrice: goodsData.price, + realSettlePrice: goodsData.price, + settlePrice: goodsData.price, + oriPrice: goodsData.originPrice, + tagPrice: null, + tagText: null, + skuSpecLst: goodsData.specInfo, + promotionIds: null, + weight: 0.0, + unit: 'KG', + volume: null, + masterGoodsType: 0, + viceGoodsType: 0, + roomId: goodsData.roomId, + egoodsName: null, + }); + }); + + return list; +}; + +/** 生成结算数据 */ +export function genSettleDetail(params) { + const { userAddressReq, couponList, goodsRequestList } = params; + + const resp = { + data: { + settleType: 0, + userAddress: null, + totalGoodsCount: 3, + packageCount: 1, + totalAmount: '289997', + totalPayAmount: '', + totalDiscountAmount: '110000', + totalPromotionAmount: '1100', + totalCouponAmount: '0', + totalSalePrice: '289997', + totalGoodsAmount: '289997', + totalDeliveryFee: '0', + invoiceRequest: null, + skuImages: null, + deliveryFeeList: null, + storeGoodsList: [ + { + storeId: '1000', + storeName: '云Mall深圳旗舰店', + remark: null, + goodsCount: 1, + deliveryFee: '0', + deliveryWords: null, + storeTotalAmount: '0', + storeTotalPayAmount: '179997', + storeTotalDiscountAmount: '110000', + storeTotalCouponAmount: '0', + skuDetailVos: [], + couponList: [ + { + couponId: 11, + storeId: '1000', + }, + ], + }, + ], + inValidGoodsList: null, + outOfStockGoodsList: null, + limitGoodsList: null, + abnormalDeliveryGoodsList: null, + invoiceSupport: 1, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 244, + success: true, + }; + + const list = transformGoodsDataToConfirmData(goodsRequestList); + + // 获取购物车传递的商品数据 + resp.data.storeGoodsList[0].skuDetailVos = list; + + // 判断是否携带优惠券数据 + const discountPrice = []; + + if (couponList && couponList.length > 0) { + couponList.forEach((coupon) => { + if (coupon.status === 'default') { + discountPrice.push({ + type: coupon.type, + value: coupon.value, + }); + } + }); + } + + // 模拟计算场景 + + // 计算总价 + const totalPrice = list.reduce((pre, cur) => { + return pre + cur.quantity * Number(cur.settlePrice); + }, 0); + + // 计算折扣 + const totalDiscountPrice = + discountPrice.length > 0 + ? discountPrice.reduce((pre, cur) => { + if (cur.type === 1) { + return pre + cur.value; + } + if (cur.type === 2) { + return pre + (Number(totalPrice) * cur.value) / 10; + } + + return pre + cur; + }, 0) + : 0; + + resp.data.totalSalePrice = totalPrice; + + resp.data.totalCouponAmount = totalDiscountPrice; + + resp.data.totalPayAmount = + totalPrice - totalDiscountPrice - Number(resp.data.totalPromotionAmount); + + if (userAddressReq) { + resp.data.settleType = 1; + resp.data.userAddress = userAddressReq; + } + return resp; +} diff --git a/miniprogram/tcb-shop/model/order/orderDetail.js b/miniprogram/tcb-shop/model/order/orderDetail.js new file mode 100644 index 0000000..0dcd233 --- /dev/null +++ b/miniprogram/tcb-shop/model/order/orderDetail.js @@ -0,0 +1,26 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +const orderResps = []; + +export function genOrderDetail(params) { + const { parameter } = params; + const resp = orderResps.find((r) => r.data.orderNo === parameter); + return resp; +} + +export function genBusinessTime() { + const resp = { + data: { + businessTime: ['周一,周二,周三,周四,周五:00:20:00-08:00:00'], + telphone: '18565372257', + saasId: '88888888', + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 3, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/order/orderList.js b/miniprogram/tcb-shop/model/order/orderList.js new file mode 100644 index 0000000..c042dec --- /dev/null +++ b/miniprogram/tcb-shop/model/order/orderList.js @@ -0,0 +1,46 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +export function genOrders(params) { + const resp = { + data: { + pageNum: 1, + pageSize: 10, + totalCount: 7, + orders: [], + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 113, + success: true, + }; + const { pageNum, pageSize, orderStatus } = params.parameter; + // 实现筛选 + if (orderStatus > -1) { + resp.data.orders = resp.data.orders.filter((order) => order.orderStatus === orderStatus); + } + // 实现分页 + resp.data.pageNum = pageNum; + resp.data.pageSize = pageSize; + resp.data.orders = resp.data.orders.slice((pageNum - 1) * pageSize, pageNum * pageSize); + return resp; +} + +export function genOrdersCount() { + const resp = { + data: [ + { tabType: 5, orderNum: 1 }, + { tabType: 10, orderNum: 1 }, + { tabType: 40, orderNum: 1 }, + { tabType: 50, orderNum: 2 }, + ], + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 41, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/promotion.js b/miniprogram/tcb-shop/model/promotion.js new file mode 100644 index 0000000..0aaee61 --- /dev/null +++ b/miniprogram/tcb-shop/model/promotion.js @@ -0,0 +1,21 @@ +import { getGoodsList } from './goods'; + +export function getPromotion(baseID = 0, length = 10) { + return { + list: getGoodsList(baseID, length).map((item) => { + return { + spuId: item.spuId, + thumb: item.primaryImage, + title: item.title, + price: item.minSalePrice, + originPrice: item.maxLinePrice, + tags: item.spuTagList.map((tag) => ({ title: tag.title })), + }; + }), + banner: + 'https://cdn-we-retail.ym.tencent.com/tsr/promotion/banner-promotion.png', + time: 1000 * 60 * 60 * 20, + showBannerDesc: true, + statusTag: 'running', + }; +} diff --git a/miniprogram/tcb-shop/model/search.js b/miniprogram/tcb-shop/model/search.js new file mode 100644 index 0000000..2d2e68d --- /dev/null +++ b/miniprogram/tcb-shop/model/search.js @@ -0,0 +1,60 @@ +import { getGoodsList } from './goods'; + +/** + * @param {number} sort + * @param {number} pageNum + * @param {number} pageSize + * @param {number} minPrice + * @param {number} maxPrice + * @param {string} keyword + */ + +export function getSearchHistory() { + return { + historyWords: [ + '鸡', + '电脑', + 'iPhone12', + '车载手机支架', + '自然堂', + '小米10', + '原浆古井贡酒', + '欧米伽', + '华为', + '针织半身裙', + '氢跑鞋', + '三盒处理器', + ], + }; +} + +export function getSearchPopular() { + return { + popularWords: [ + '鸡', + '电脑', + 'iPhone12', + '车载手机支架', + '自然堂', + '小米10', + '原浆古井贡酒', + '欧米伽', + '华为', + '针织半身裙', + '氢跑鞋', + '三盒处理器', + ], + }; +} + +export function getSearchResult() { + return { + saasId: null, + storeId: null, + pageNum: 1, + pageSize: 30, + totalCount: 1, + spuList: getGoodsList(7), + algId: 0, + }; +} diff --git a/miniprogram/tcb-shop/model/submitComment.js b/miniprogram/tcb-shop/model/submitComment.js new file mode 100644 index 0000000..e620b4f --- /dev/null +++ b/miniprogram/tcb-shop/model/submitComment.js @@ -0,0 +1,58 @@ +export function getGoods() { + return { + goods: [ + { + squid: '1', + checkItems: [ + { + name: '匿名评价', + value: 'anonymous', + checked: false, + }, + ], + detail: { + image: + 'https://wx.qlogo.cn/mmopen/vi_32/51VSMNuy1CyHiaAhAjLJ00kMZVqqnCqXeZduCLXHUBr52zFHRGxwL7kGia3fHj8GSNzFcqFDInQmRGM1eWjtQgqA/132', + title: '', + }, + goodComment: { + /** 商品评价 */ + rate: 0, + /** 评价内容 */ + label: '123', + /** 上传图片 */ + images: [], + }, + }, + { + squid: '2', + checkItems: [ + { + name: '匿名评价', + value: 'anonymous', + checked: false, + }, + ], + detail: { + image: + 'https://wx.qlogo.cn/mmopen/vi_32/51VSMNuy1CyHiaAhAjLJ00kMZVqqnCqXeZduCLXHUBr52zFHRGxwL7kGia3fHj8GSNzFcqFDInQmRGM1eWjtQgqA/132', + title: '评价内容 山姆智利进口', + }, + goodComment: { + /** 商品评价 */ + rate: 0, + /** 评价内容 */ + label: '山姆智利进口', + /** 上传图片 */ + images: [], + }, + }, + ], + storeComment: { + /** 物流评价 */ + logisticsRate: 0, + /** 服务评价 */ + servicesRate: 0, + }, + }; +} diff --git a/miniprogram/tcb-shop/model/swiper.js b/miniprogram/tcb-shop/model/swiper.js new file mode 100644 index 0000000..dd5702c --- /dev/null +++ b/miniprogram/tcb-shop/model/swiper.js @@ -0,0 +1,39 @@ +// const images = [ +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner1.png', +// text: '1', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner2.png', +// text: '2', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner3.png', +// text: '3', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner4.png', +// text: '4', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner5.png', +// text: '5', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner6.png', +// text: '6', +// }, +// ]; + +const images = [ + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner1.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner2.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner3.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner4.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner5.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner6.png', +]; + +export function genSwiperImageList() { + return images; +} diff --git a/miniprogram/tcb-shop/model/usercenter.js b/miniprogram/tcb-shop/model/usercenter.js new file mode 100644 index 0000000..d143a04 --- /dev/null +++ b/miniprogram/tcb-shop/model/usercenter.js @@ -0,0 +1,52 @@ +const userInfo = { + avatarUrl: + 'https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-ui/components-exp/avatar/avatar-1.jpg', + nickName: '云开发', + phoneNumber: '13438358888', + gender: 2, +}; +const countsData = [ + { + num: 2, + name: '积分', + type: 'point', + }, + { + num: 10, + name: '优惠券', + type: 'coupon', + }, +]; + +const orderTagInfos = [ + { + orderNum: 1, + tabType: 5, + }, + { + orderNum: 1, + tabType: 10, + }, + { + orderNum: 1, + tabType: 40, + }, + { + orderNum: 0, + tabType: 0, + }, +]; + +const customerServiceInfo = { + servicePhone: '4006336868', + serviceTimeDuration: '每周三至周五 9:00-12:00 13:00-15:00', +}; + +export const genSimpleUserInfo = () => ({ ...userInfo }); + +export const genUsercenter = () => ({ + userInfo, + countsData, + orderTagInfos, + customerServiceInfo, +}); diff --git a/miniprogram/tcb-shop/package.json b/miniprogram/tcb-shop/package.json new file mode 100644 index 0000000..b290b6b --- /dev/null +++ b/miniprogram/tcb-shop/package.json @@ -0,0 +1,46 @@ +{ + "name": "tcb-cloudbase-shop", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "lint": "eslint --cache --fix --ext .js", + "check": "node config/eslintCheck.js", + "prepare": "husky install", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" + }, + "author": "", + "license": "ISC", + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "lint-staged": { + "*.{js, ts}": "eslint --cache --fix", + "*.{js,ts,wxml,html,json,css,less}": [ + "prettier --write" + ] + }, + "dependencies": { + "@cloudbase/wx-cloud-client-sdk": "^1.2.1", + "dayjs": "^1.9.3", + "tdesign-miniprogram": "^1.6.0", + "tslib": "^1.11.1" + }, + "devDependencies": { + "@commitlint/cli": "^17.4.2", + "@commitlint/config-conventional": "^17.4.2", + "commitizen": "^4.3.0", + "conventional-changelog-cli": "^2.2.2", + "cz-conventional-changelog": "^3.3.0", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.0", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-prettier": "^3.1.2", + "husky": "^8.0.3", + "lint-staged": "^10.0.8", + "prettier": "^2.1.2" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js new file mode 100644 index 0000000..fb4f99f --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js @@ -0,0 +1,59 @@ +Component({ + options: { + addGlobalClass: true, + }, + /** + * 组件的属性列表 + */ + properties: { + isAllSelected: { + type: Boolean, + value: false, + }, + totalAmount: { + type: Number, + value: 1, + }, + totalGoodsNum: { + type: Number, + value: 0, + observer(num) { + const isDisabled = num == 0; + setTimeout(() => { + this.setData({ + isDisabled, + }); + }); + }, + }, + totalDiscountAmount: { + type: Number, + value: 0, + }, + bottomHeight: { + type: Number, + value: 100, + }, + fixed: Boolean, + }, + data: { + isDisabled: true, + }, + + methods: { + handleSelectAll() { + const { isAllSelected } = this.data; + this.setData({ + isAllSelected: !isAllSelected, + }); + this.triggerEvent('handleSelectAll', { + isAllSelected: isAllSelected, + }); + }, + + handleToSettle() { + if (this.data.isDisabled) return; + this.triggerEvent('handleToSettle'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json new file mode 100644 index 0000000..c6c4351 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml new file mode 100644 index 0000000..5205e9c --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml @@ -0,0 +1,14 @@ + + + + 全选 + + + 总计 + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss new file mode 100644 index 0000000..0e63f60 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss @@ -0,0 +1,80 @@ +.cart-bar__placeholder { + height: 100rpx; +} +.flex { + display: flex; +} +.flex-v-center { + align-items: center; +} +.flex1 { + flex: 1; +} +.algin-bottom { + text-align: end; +} +.cart-bar--fixed { + position: fixed; + left: 0; + right: 0; + z-index: 99; + bottom: calc(100rpx + env(safe-area-inset-bottom)); +} + +.cart-bar { + height: 112rpx; + background-color: #fff; + border-top: 1rpx solid #e5e5e5; + padding: 16rpx 32rpx; + box-sizing: border-box; + font-size: 24rpx; + line-height: 36rpx; + color: #333; +} + +.cart-bar .cart-bar__check { + margin-right: 12rpx; +} + +.cart-bar .cart-bar__total { + margin-left: 24rpx; +} + +.cart-bar .account-btn { + width: 192rpx; + height: 80rpx; + border-radius: 40rpx; + background-color: #fa4126; + font-size: 28rpx; + font-weight: bold; + line-height: 80rpx; + color: #ffffff; + text-align: center; +} +.cart-bar .disabled-btn { + background-color: #cccccc !important; +} +.cart-bar .hover-btn { + opacity: 0.5; +} + +.cart-bar__total .cart-bar__total--bold { + font-size: 28rpx; + line-height: 40rpx; + color: #333; + font-weight: bold; +} +.cart-bar__total .cart-bar__total--normal { + font-size: 24rpx; + line-height: 32rpx; + color: #999; +} + +.cart-bar__total .cart-bar__total--price { + color: #fa4126; + font-weight: bold; +} + +.text-padding-right { + padding-right: 4rpx; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js new file mode 100644 index 0000000..01cf9c3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js @@ -0,0 +1,23 @@ +Component({ + properties: { + imgUrl: { + type: String, + value: + 'https://cdn-we-retail.ym.tencent.com/miniapp/template/empty-cart.png', + }, + tip: { + type: String, + value: '购物车是空的', + }, + btnText: { + type: String, + value: '去首页', + }, + }, + data: {}, + methods: { + handleClick() { + this.triggerEvent('handleClick'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json new file mode 100644 index 0000000..b659310 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml new file mode 100644 index 0000000..d0cdd43 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml @@ -0,0 +1,6 @@ + + + {{tip}} + {{btnText}} + + diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss new file mode 100644 index 0000000..d074bc3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss @@ -0,0 +1,33 @@ +.cart-empty { + padding: 64rpx 0rpx; + display: flex; + flex-direction: column; + align-items: center; + box-sizing: border-box; + height: calc(100vh - 100rpx); + background-color: #f5f5f5; +} +.cart-empty .cart-img { + width: 160rpx; + height: 160rpx; + margin-bottom: 24rpx; +} + +.cart-empty .tip { + font-size: 28rpx; + line-height: 40rpx; + color: #999; + margin-bottom: 24rpx; +} +.cart-empty .btn { + width: 240rpx; + height: 72rpx; + border-radius: 36rpx; + text-align: center; + line-height: 72rpx; + border: 2rpx solid #fa4126; + color: #fa4126; + background-color: transparent; + font-size: 28rpx; + font-weight: bold; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.js b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.js new file mode 100644 index 0000000..5389941 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.js @@ -0,0 +1,168 @@ +import Toast from 'tdesign-miniprogram/toast/index'; + +const shortageImg = 'https://cdn-we-retail.ym.tencent.com/miniapp/cart/shortage.png'; + +Component({ + isSpecsTap: false, // 标记本次点击事件是否因为点击specs触发(由于底层goods-card组件没有catch specs点击事件,只能在此处加状态来避免点击specs时触发跳转商品详情) + externalClasses: ['wr-class'], + properties: { + storeGoods: { + type: Array, + observer(storeGoods) { + for (const store of storeGoods) { + for (const activity of store.promotionGoodsList) { + for (const goods of activity.goodsPromotionList) { + goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值 + } + } + for (const goods of store.shortageGoodsList) { + goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值 + } + } + + this.setData({ _storeGoods: storeGoods }); + }, + }, + invalidGoodItems: { + type: Array, + observer(invalidGoodItems) { + invalidGoodItems.forEach((goods) => { + goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值 + }); + this.setData({ _invalidGoodItems: invalidGoodItems }); + }, + }, + thumbWidth: { type: null }, + thumbHeight: { type: null }, + cartItems: { type: Array }, + }, + + data: { + shortageImg, + isShowSpecs: false, + currentGoods: null, + isShowToggle: false, + _storeGoods: [], + _invalidGoodItems: [], + }, + + methods: { + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + }, + // 删除商品 + deleteGoods(e) { + const { goods } = e.currentTarget.dataset; + this.triggerEvent('delete', { goods }); + }, + + // 清空失效商品 + clearInvalidGoods() { + this.triggerEvent('clearinvalidgoods'); + }, + + // 选中商品 + selectGoods(e) { + const { goods } = e.currentTarget.dataset; + this.triggerEvent('selectgoods', { + goods, + }); + }, + + changeQuantity(count, cartItemId) { + this.triggerEvent('changequantity', { + count, + cartItemId, + }); + }, + changeStepper({ + detail: { value: changeToValue }, + target: { + dataset: { goods }, + }, + }) { + // validation is handled by this.overlimit() + this.changeQuantity(changeToValue, goods._id); + }, + + input(e) { + const { value } = e.detail; + const { goods } = e.currentTarget.dataset; + const num = value; + this.changeQuantity(num, goods); + }, + + overlimit(e) { + const text = e.detail.type === 'minus' ? '不能再少了' : '不能再多了'; + this.toast(text); + }, + + // 去凑单/再逛逛 + gotoBuyMore(e) { + const { promotion, storeId = '' } = e.currentTarget.dataset; + this.triggerEvent('gocollect', { promotion, storeId }); + }, + + // 选中门店 + selectStore(e) { + const { storeIndex } = e.currentTarget.dataset; + const store = this.data.storeGoods[storeIndex]; + const isSelected = !store.isSelected; + if (store.storeStockShortage && isSelected) { + Toast({ + context: this, + selector: '#t-toast', + message: '部分商品库存不足', + }); + return; + } + this.triggerEvent('selectstore', { + store, + isSelected, + }); + }, + + // 展开/收起切换 + showToggle() { + this.setData({ + isShowToggle: !this.data.isShowToggle, + }); + }, + + // 展示规格popup + specsTap({ + target: { + dataset: { goods }, + }, + }) { + this.isSpecsTap = true; + this.setData({ + isShowSpecs: true, + currentGoods: goods, + }); + }, + + hideSpecsPopup() { + this.setData({ + isShowSpecs: false, + }); + }, + + goGoodsDetail(e) { + if (this.isSpecsTap) { + this.isSpecsTap = false; + return; + } + const { goods } = e.currentTarget.dataset; + this.triggerEvent('goodsclick', { goods }); + }, + + gotoCoupons() { + wx.navigateTo({ url: '/pages/coupon/coupon-list/index' }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.json b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.json new file mode 100644 index 0000000..9b8003d --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.json @@ -0,0 +1,11 @@ +{ + "component": true, + "usingComponents": { + "t-toast": "tdesign-miniprogram/toast/toast", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "swipeout": "/components/swipeout/index", + "goods-card": "../../components/goods-card/index", + "specs-popup": "../../components/specs-popup/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml new file mode 100644 index 0000000..9e2102a --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + 仅剩{{goods.sku.count}}件 + + + + + + + + + 删除 + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs new file mode 100644 index 0000000..39f1e0b --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs @@ -0,0 +1,5 @@ +var hasPromotion = function (code) { + return code && code !== 'EMPTY_PROMOTION'; +}; + +module.exports.hasPromotion = hasPromotion; diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss new file mode 100644 index 0000000..641101f --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss @@ -0,0 +1,335 @@ +.cart-group { + border-radius: 8rpx; +} +.cart-group .goods-wrap { + margin-top: 40rpx; + background-color: #fff; + border-radius: 8rpx; + overflow: hidden; +} +.cart-group .goods-wrap:first-of-type { + margin-top: 0; +} +.cart-group .cart-store { + height: 96rpx; + background-color: #fff; + box-sizing: border-box; + display: flex; + align-items: center; + padding: 0rpx 24rpx 0rpx 36rpx; +} +.cart-group .cart-store .cart-store__check { + padding: 28rpx 32rpx 28rpx 0rpx; +} +.cart-group .cart-store__content { + box-sizing: border-box; + flex: auto; + display: flex; + align-items: center; + justify-content: space-between; +} +.cart-group .cart-store__content .store-title { + flex: auto; + font-size: 28rpx; + line-height: 40rpx; + color: #333333; + display: flex; + align-items: center; + font-weight: bold; + overflow: hidden; +} + +.cart-group .cart-store__content .store-title .wr-store { + font-size: 32rpx; +} +.cart-group .cart-store__content .store-title .store-name { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin-left: 12rpx; +} +.cart-group .cart-store__content .get-coupon { + width: 112rpx; + height: 40rpx; + border-radius: 20rpx; + background-color: #ffecf9; + line-height: 40rpx; + text-align: center; + font-size: 26rpx; + color: #fa4126; +} + +.cart-group .promotion-wrap { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0rpx 24rpx 32rpx 36rpx; + background-color: #ffffff; + font-size: 24rpx; + line-height: 36rpx; + color: #222427; +} +.cart-group .promotion-wrap .promotion-title { + font-weight: bold; + flex: auto; + overflow: hidden; + margin-right: 20rpx; + display: flex; + align-items: center; +} +.cart-group .promotion-wrap .promotion-title .promotion-icon { + flex: none; + font-weight: normal; + display: inline-block; + padding: 0 8rpx; + color: #ffffff; + background: #fa4126; + font-size: 20rpx; + height: 32rpx; + line-height: 32rpx; + margin-right: 16rpx; + border-radius: 16rpx; +} +.cart-group .promotion-wrap .promotion-title .promotion-text { + flex: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.cart-group .promotion-wrap .promotion-action { + flex: none; + color: #333333; +} +.cart-group .promotion-line-wrap { + background-color: #fff; + height: 2rpx; + display: flex; + justify-content: flex-end; +} +.cart-group .promotion-line-wrap .promotion-line { + width: 684rpx; + height: 2rpx; + background-color: #e6e6e6; +} +.cart-group .goods-item-info { + display: flex; + background-color: #fff; + align-items: flex-start; +} +.cart-group .goods-item-info .check-wrap { + margin-top: 56rpx; + padding: 20rpx 28rpx 20rpx 36rpx; +} + +.cart-group .goods-item-info .check-wrap .unCheck-icon { + box-sizing: border-box; + width: 36rpx; + height: 36rpx; + border-radius: 20rpx; + background: #f5f5f5; + border: 2rpx solid #bbbbbb; +} + +.cart-group .goods-item-info .goods-sku-info { + padding: 0rpx 32rpx 40rpx 0; + flex-grow: 1; +} +.cart-group .goods-item-info .goods-sku-info .stock-mask { + position: absolute; + color: #fff; + font-size: 24rpx; + bottom: 0rpx; + background-color: rgba(0, 0, 0, 0.5); + width: 100%; + height: 40rpx; + line-height: 40rpx; + text-align: center; +} +.cart-group .goods-item-info .goods-sku-info .goods-stepper { + position: absolute; + right: 0; + bottom: 8rpx; +} +.cart-group .goods-item-info .goods-sku-info .goods-stepper .stepper-tip { + position: absolute; + top: -36rpx; + right: 0; + height: 28rpx; + color: #ff2525; + font-size: 20rpx; + line-height: 28rpx; +} + +.cart-group .shortage-line { + width: 662rpx; + height: 2rpx; + background-color: #e6e6e6; + margin: 0 auto; +} +.cart-group .shortage-goods-wrap { + background-color: #fff; +} +.cart-group .shortage-goods-wrap .shortage-tip-title { + height: 72rpx; + line-height: 72rpx; + padding-left: 28rpx; + font-size: 24rpx; + color: #999; +} +.stepper-info { + margin-left: auto; +} +.invalid-goods-wrap { + background-color: #fff; + border-radius: 8rpx; + margin-top: 40rpx; +} +.invalid-goods-wrap .invalid-head { + display: flex; + justify-content: space-between; + padding: 30rpx 20rpx; + font-size: 24rpx; + border-bottom: 2rpx solid #f6f6f6; +} +.invalid-goods-wrap .invalid-head .invalid-title { + color: #333; + font-size: 28rpx; + font-weight: 600; +} +.invalid-goods-wrap .invalid-head .invalid-clear { + color: #fa4126; +} +.invalid-goods-wrap .toggle { + display: flex; + height: 80rpx; + justify-content: center; + align-items: center; + font-size: 24rpx; + color: #fa4126; +} +.invalid-goods-wrap .toggle .m-r-6 { + margin-right: 6rpx; +} +.invalid-goods-wrap .toggle .top-icon { + display: inline-block; + width: 0; + height: 0; + border-left: 10rpx solid transparent; + border-right: 10rpx solid transparent; + border-bottom: 10rpx solid #fa4126; +} +.invalid-goods-wrap .toggle .down-icon { + display: inline-block; + width: 0; + height: 0; + border-left: 10rpx solid transparent; + border-right: 10rpx solid transparent; + border-top: 10rpx solid #fa4126; +} +.action-btn { + display: flex; + align-items: center; +} +.action-btn .action-btn-arrow { + font-size: 20rpx; + margin-left: 8rpx; +} +.action-btn--active { + opacity: 0.5; +} + +.swiper-right-del { + height: calc(100% - 40rpx); + width: 144rpx; + background-color: #fa4126; + font-size: 28rpx; + color: white; + display: flex; + justify-content: center; + align-items: center; +} +.goods-stepper .stepper { + border: none; + border-radius: 0; + height: auto; + width: 168rpx; + overflow: visible; +} +.goods-stepper .stepper .stepper__minus, +.goods-stepper .stepper .stepper__plus { + width: 44rpx; + height: 44rpx; + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus--hover, +.goods-stepper .stepper .stepper__plus--hover { + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus .wr-icon, +.goods-stepper .stepper .stepper__plus .wr-icon { + font-size: 24rpx; +} +.goods-stepper .stepper .stepper__minus { + position: relative; +} +.goods-stepper .stepper .stepper__minus::after { + position: absolute; + display: block; + content: ' '; + left: -20rpx; + right: -5rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__plus { + position: relative; +} +.goods-stepper .stepper .stepper__plus::after { + position: absolute; + display: block; + content: ' '; + left: -5rpx; + right: -20rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__input { + width: 72rpx; + height: 44rpx; + background-color: #f5f5f5; + font-size: 24rpx; + color: #222427; + font-weight: 600; + border-left: none; + border-right: none; + min-height: 40rpx; + margin: 0 4rpx; + display: flex; + align-items: center; +} + +.goods-sku-info .no-storage-mask { + position: absolute; + color: #fff; + bottom: 0rpx; + left: 0rpx; + background-color: rgba(0, 0, 0, 0.1); + height: 192rpx; + width: 192rpx; + border-radius: 8rpx; + display: flex; + justify-content: center; + align-items: center; +} + +.no-storage-mask .no-storage-content { + width: 128rpx; + height: 128rpx; + border-radius: 64rpx; + background-color: rgba(0, 0, 0, 0.4); + text-align: center; + line-height: 128rpx; + font-size: 28rpx; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs b/miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs new file mode 100644 index 0000000..f887eba --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs @@ -0,0 +1,20 @@ +module.exports.slice = function(arr) { + return arr.slice(0, 2); +}; +module.exports.imgCut = function(url, width, height) { + if (url && (url.slice(0, 5) === 'http:' || url.slice(0, 6) === 'https:' || url.slice(0, 2) === '//')) { + var argsStr = 'imageMogr2/thumbnail/!' + width + 'x' + height + 'r'; + if (url.indexOf('?') > -1) { + url = url + '&' + argsStr; + } else { + url = url + '?' + argsStr; + } + if (url.slice(0, 5) === 'http:') { + url = 'https://' + url.slice(5) + } + if (url.slice(0, 2) === '//') { + url = 'https:' + url + } + } + return url; +}; diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.js b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.js new file mode 100644 index 0000000..ccb37e4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.js @@ -0,0 +1,232 @@ +Component({ + options: { + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + addGlobalClass: true, + }, + intersectionObserverContext: null, + + externalClasses: [ + 'card-class', + 'title-class', + 'desc-class', + 'num-class', + 'thumb-class', + 'specs-class', + 'price-class', + 'origin-price-class', + 'price-prefix-class', + ], + + properties: { + hidden: { + // 设置为null代表不做类型转换 + type: null, + value: false, + observer(hidden) { + // null就是代表没有设置,没有设置的话不setData,防止祖先组件触发的setHidden操作被覆盖 + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + id: { + type: String, + // `goods-card-88888888` + // 不能在这里写生成逻辑,如果在这里写,那么假设有多个goods-list时,他们将共享这个值 + value: '', + observer: (id) => { + this.genIndependentID(id); + if (this.properties.thresholds?.length) { + this.createIntersectionObserverHandle(); + } + }, + }, + data: { + type: Object, + observer(goods) { + // 有ID的商品才渲染 + if (!goods?.sku) { + return; + } + + /** 划线价是否有效 */ + let isValidityLinePrice = true; + // 判断一次划线价格是否合理 + if (goods.originPrice && goods.price && goods.originPrice < goods.price) { + isValidityLinePrice = false; + } + + // 敲定换行数量默认值 + if (goods.lineClamp === undefined || goods.lineClamp <= 0) { + // tag数组长度 大于0 且 可见 + // 指定换行为1行 + if ((goods.sku.attr_value.length || 0) > 0) { + goods.lineClamp = 1; + } else { + goods.lineClamp = 2; + } + } + + this.setData({ goods, isValidityLinePrice, attr_str: goods.sku.attr_value.map((x) => x.value).join(',') }); + }, + }, + layout: { + type: String, + value: 'horizontal', + }, + thumbMode: { + type: String, + value: 'aspectFill', + }, + priceFill: { + type: Boolean, + value: true, + }, + currency: { + type: String, + value: '¥', + }, + lazyLoad: { + type: Boolean, + value: false, + }, + centered: { + type: Boolean, + value: false, + }, + pricePrefix: { + type: String, + value: '', + }, + /** 元素可见监控阈值, 数组长度大于0就创建 */ + thresholds: { + type: Array, + value: [], + observer(current) { + if (current && current.length) { + this.createIntersectionObserverHandle(); + } else { + this.clearIntersectionObserverHandle(); + } + }, + }, + specsIconClassPrefix: { + type: String, + value: 'wr', + }, + specsIcon: { + type: String, + value: 'expand_more', + }, + addCartIconClassPrefix: { + type: String, + value: 'wr', + }, + addCartIcon: { + type: String, + value: 'cart', + }, + }, + + data: { + hiddenInData: false, + independentID: '', + goods: { id: '' }, + /** 保证划线价格不小于原价,否则不渲染划线价 */ + isValidityLinePrice: false, + }, + + lifetimes: { + ready() { + this.init(); + }, + detached() { + this.clear(); + }, + }, + + methods: { + clickHandle() { + this.triggerEvent('click', { goods: this.data.goods }); + }, + clickThumbHandle() { + this.triggerEvent('thumb', { goods: this.data.goods }); + }, + clickSpecsHandle() { + this.triggerEvent('specs', { goods: this.data.goods }); + }, + // 加入购物车 + addCartHandle(e) { + const { id } = e.currentTarget; + const { id: cardID } = e.currentTarget.dataset; + this.triggerEvent('add-cart', { + ...e.detail, + id, + cardID, + goods: this.data.goods, + }); + }, + genIndependentID(id, cb) { + let independentID; + if (id) { + independentID = id; + } else { + independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`; + } + this.setData({ independentID }, cb); + }, + + init() { + const { thresholds, id, hidden } = this.properties; + if (hidden !== null) { + this.setHidden(!!hidden); + } + + this.genIndependentID(id || '', () => { + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } + }); + }, + + clear() { + this.clearIntersectionObserverHandle(); + }, + + setHidden(hidden) { + this.setData({ hiddenInData: !!hidden }); + }, + + createIntersectionObserverHandle() { + if (this.intersectionObserverContext || !this.data.independentID) { + return; + } + + this.intersectionObserverContext = wx + .createIntersectionObserver(this, { + thresholds: this.properties.thresholds, + }) + .relativeToViewport(); + + this.intersectionObserverContext.observe(`#${this.data.independentID}`, (res) => { + this.intersectionObserverCB(res); + }); + }, + intersectionObserverCB(ob) { + this.triggerEvent('ob', { + goods: this.data.goods, + context: this.intersectionObserverContext, + ob, + }); + }, + clearIntersectionObserverHandle() { + if (this.intersectionObserverContext) { + try { + this.intersectionObserverContext.disconnect(); + } catch (e) {} + + this.intersectionObserverContext = null; + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.json b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.json new file mode 100644 index 0000000..d76303b --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml new file mode 100644 index 0000000..ff5d792 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml @@ -0,0 +1,46 @@ + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss new file mode 100644 index 0000000..6a7a4da --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss @@ -0,0 +1,260 @@ +.wr-goods-card { + box-sizing: border-box; + font-size: 24rpx; +} +/* */ +.wr-goods-card__main { + position: relative; + display: flex; + padding: 0; + background: transparent; +} + +.wr-goods-card.center .wr-goods-card__main { + align-items: flex-start; + justify-content: center; +} + +.wr-goods-card__thumb { + flex-shrink: 0; + position: relative; + width: 140rpx; + height: 140rpx; +} + +.wr-goods-card__thumb-com { + width: 192rpx; + height: 192rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card__thumb:empty { + display: none; + margin: 0; +} + +.wr-goods-card__body { + display: flex; + margin: 0 0 0 20rpx; + flex-direction: row; + flex: 1 1 auto; + min-height: 192rpx; +} + +.wr-goods-card__long_content { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1 1 auto; +} +.wr-goods-card__long_content .goods_tips { + width: 100%; + margin-top: 16rpx; + text-align: right; + color: #fa4126; + font-size: 24rpx; + line-height: 32rpx; + font-weight: bold; +} +.wr-goods-card__title { + flex-shrink: 0; + font-size: 28rpx; + color: #333; + line-height: 40rpx; + font-weight: 400; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + word-break: break-word; +} +.wr-goods-card__title__prefix-tags { + display: inline-flex; +} +.wr-goods-card__title__prefix-tags .prefix-tag { + margin: 0 8rpx 0 0; +} +.wr-goods-card__desc { + font-size: 24rpx; + color: #f5f5f5; + line-height: 40rpx; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} +.wr-goods-card__specs__desc, +.wr-goods-card__specs__text { + font-size: 24rpx; + height: 32rpx; + line-height: 32rpx; + color: #999999; + margin: 8rpx 0; +} +.wr-goods-card__specs__desc { + display: flex; + align-self: flex-start; + flex-direction: row; + background: #f5f5f5; + border-radius: 8rpx; + padding: 4rpx 8rpx; +} +.wr-goods-card__specs__desc-text { + height: 100%; + max-width: 380rpx; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} +.wr-goods-card__specs__desc-icon { + line-height: inherit; + margin-left: 8rpx; + font-size: 24rpx; + color: #bbb; +} +.wr-goods-card__specs__text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; +} +.wr-goods-card__tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 16rpx 0 0 0; +} +.wr-goods-card__tag { + color: #fa550f; + background: transparent; + font-size: 20rpx; + border: 1rpx solid #fa550f; + padding: 0 8rpx; + height: 30rpx; + line-height: 30rpx; + margin: 0 8rpx 8rpx 0; + display: block; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; +} +.wr-goods-card__short_content { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-end; + margin: 0 0 0 46rpx; +} +.wr-goods-card__price__prefix { + order: 0; + color: #666; + margin: 0; +} +.wr-goods-card__price { + white-space: nowrap; + font-weight: bold; + order: 1; + color: #fa4126; + font-size: 36rpx; + margin: 0; + line-height: 48rpx; +} +.wr-goods-card__origin-price { + white-space: nowrap; + font-weight: normal; + order: 2; + color: #aaaaaa; + font-size: 24rpx; + margin: 0; +} +.wr-goods-card__num { + white-space: nowrap; + order: 4; + font-size: 24rpx; + color: #999; + margin: 20rpx 0 0 auto; +} +.wr-goods-card__num__prefix { + color: inherit; +} +.wr-goods-card__add-cart { + order: 3; + margin: auto 0 0 auto; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__thumb { + width: 192rpx; + height: 192rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__body { + flex-direction: column; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__short_content { + flex-direction: row; + align-items: center; + margin: 16rpx 0 0 0; +} + +.wr-goods-card.horizontal-wrap .wr-goods-card__num { + margin: 0 0 0 auto; +} +.wr-goods-card.vertical .wr-goods-card__main { + padding: 0 0 22rpx 0; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__thumb { + width: 340rpx; + height: 340rpx; +} +.wr-goods-card.vertical .wr-goods-card__body { + margin: 20rpx 20rpx 0 20rpx; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__long_content { + overflow: hidden; +} +.wr-goods-card.vertical .wr-goods-card__title { + line-height: 36rpx; +} +.wr-goods-card.vertical .wr-goods-card__short_content { + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__price { + order: 2; + color: #fa4126; + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__origin-price { + order: 1; +} +.wr-goods-card.vertical .wr-goods-card__add-cart { + position: absolute; + bottom: 20rpx; + right: 20rpx; +} + +.wr-goods-card__short_content .no_storage { + display: flex; + align-items: center; + justify-content: space-between; + height: 40rpx; + color: #333; + font-size: 24rpx; + line-height: 32rpx; + width: 100%; +} + +.no_storage .no_storage__right { + width: 80rpx; + height: 40rpx; + border-radius: 20rpx; + border: 2rpx solid #fa4126; + line-height: 40rpx; + text-align: center; + color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js new file mode 100644 index 0000000..5b71038 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js @@ -0,0 +1,48 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + properties: { + goods: { + type: Object, + value: null, + observer(goods) { + if (!goods) { + return; + } + this.setData({ + goodsWithoutAttrValue: { + ...goods, + sku: { + ...goods.sku, + attr_value: [], + }, + }, + }); + }, + }, + show: { + type: Boolean, + value: false, + }, + thumbMode: { + type: String, + value: 'aspectFit', + }, + }, + + data: { + goodsWithoutAttrValue: null, + }, + methods: { + onClose() { + this.triggerEvent('close'); + }, + + onCloseOver() { + this.triggerEvent('closeover'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json new file mode 100644 index 0000000..d59fc6d --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "goods-card": "../../components/goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml new file mode 100644 index 0000000..8833670 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml @@ -0,0 +1,16 @@ + + + + + + 已选规格 + + + {{spec.value}} + + + + + 我知道了 + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss new file mode 100644 index 0000000..359c185 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss @@ -0,0 +1,68 @@ +.specs-popup { + width: 100vw; + box-sizing: border-box; + padding: 32rpx 32rpx calc(20rpx + env(safe-area-inset-bottom)) 32rpx; + max-height: 80vh; + display: flex; + flex-direction: column; + background-color: white; + border-radius: 20rpx 20rpx 0 0; +} +.specs-popup .section { + margin-top: 44rpx; + flex: auto; + overflow-y: scroll; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; +} +.specs-popup .section .title { + font-size: 26rpx; + color: #4f5356; +} +.specs-popup .section .options { + color: #333333; + font-size: 24rpx; + margin-right: -26rpx; +} +.specs-popup .section .options .option { + display: inline-block; + margin-top: 24rpx; + height: 56rpx; + line-height: 56rpx; + padding: 0 16rpx; + border-radius: 8rpx; + background-color: #f5f5f5; + max-width: 100%; + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.specs-popup .section .options .option:not(:last-child) { + margin-right: 26rpx; +} +.specs-popup .bottom-btn { + margin-top: 42rpx; + position: relative; + height: 80rpx; + line-height: 80rpx; + text-align: center; + background-color: white; + color: #fa4126; +} +.specs-popup .bottom-btn--active { + opacity: 0.5; +} +.specs-popup .bottom-btn::after { + display: block; + content: ' '; + position: absolute; + left: 0; + top: 0; + width: 200%; + height: 200%; + border: 1px solid #fa4126; + border-radius: 80rpx; + transform: scale(0.5); + transform-origin: left top; +} diff --git a/miniprogram/tcb-shop/pages/cart/index.js b/miniprogram/tcb-shop/pages/cart/index.js new file mode 100644 index 0000000..8e50e3d --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.js @@ -0,0 +1,310 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import { fetchCartGroupData, fetchCartItems, deleteCartItem, updateCartItemCount } from '../../services/cart/cart'; +import { getSkuDetail } from '../../services/sku/sku'; +import { objectToParamString } from '../../utils/util'; +import { getSingleCloudImageTempUrl } from '../../utils/cloudImageHandler'; + +let updateCartItemCountTimeout = null; + +Component({ + data: { + cartGroupData: null, + cartItems: [], + selectedCartItemNum: 0, + allSelected: false, + totalAmount: 0, + loading: false, + inited: false, + }, + + observers: { + cartItems: function (cartItems) { + const selectedCartItems = cartItems.filter((x) => x.selected === true); + const selectedCartItemNum = selectedCartItems.length; + const totalAmount = selectedCartItems.reduce((acc, cur) => acc + cur.count * cur.sku.price, 0); + const allSelected = selectedCartItemNum === cartItems.length; + + this.setData({ + selectedCartItemNum, + allSelected, + totalAmount, + }); + }, + }, + + lifetimes: { + attached: async function () { + // console.log('called attached'); + // // 调用自定义tabbar的init函数,使页面与tabbar激活状态保持一致 + // this.getTabBar().init(); + // await this.setLoading(); + // await this.setDataPromise({ inited: true }); + // try { + // await this.init(); + // } finally { + // await this.unsetLoading(); + // } + }, + }, + + pageLifetimes: { + show: async function () { + // 调用自定义tabbar的init函数,使页面与tabbar激活状态保持一致 + this.getTabBar().init(); + + await this.setLoading(); + try { + await this.init(); + } finally { + await this.unsetLoading(); + } + }, + }, + + methods: { + async init() { + const cartItems = (await fetchCartItems()).map((x) => + Object.assign(x, { + selected: false, + }), + ); + await Promise.all( + cartItems.map(async (cartItem) => { + const skuId = cartItem.sku._id; + const sku = await getSkuDetail(skuId); + if (sku.image) { + sku.image = await getSingleCloudImageTempUrl(sku.image); + } + cartItem.sku = sku; + }), + ); + await this.setDataPromise({ + cartItems, + }); + }, + + findGoods(spuId, skuId) { + let currentStore; + let currentActivity; + let currentGoods; + const { storeGoods } = this.data.cartGroupData; + for (const store of storeGoods) { + for (const activity of store.promotionGoodsList) { + for (const goods of activity.goodsPromotionList) { + if (goods.spuId === spuId && goods.skuId === skuId) { + currentStore = store; + currentActivity = currentActivity; + currentGoods = goods; + return { + currentStore, + currentActivity, + currentGoods, + }; + } + } + } + } + return { + currentStore, + currentActivity, + currentGoods, + }; + }, + + // 注:实际场景时应该调用接口获取购物车数据 + getCartGroupData() { + const { cartGroupData } = this.data; + if (!cartGroupData) { + return fetchCartGroupData(); + } + return Promise.resolve({ data: cartGroupData }); + }, + + // 选择单个商品 + // 注:实际场景时应该调用接口更改选中状态 + selectGoodsService({ spuId, skuId, isSelected }) { + this.findGoods(spuId, skuId).currentGoods.isSelected = isSelected; + return Promise.resolve(); + }, + + // 全选门店 + // 注:实际场景时应该调用接口更改选中状态 + selectStoreService({ storeId, isSelected }) { + const currentStore = this.data.cartGroupData.storeGoods.find((s) => s.storeId === storeId); + currentStore.isSelected = isSelected; + currentStore.promotionGoodsList.forEach((activity) => { + activity.goodsPromotionList.forEach((goods) => { + goods.isSelected = isSelected; + }); + }); + return Promise.resolve(); + }, + + // 加购数量变更 + // 注:实际场景时应该调用接口 + changeQuantityService({ spuId, skuId, quantity }) { + this.findGoods(spuId, skuId).currentGoods.quantity = quantity; + return Promise.resolve(); + }, + + // 删除加购商品 + // 注:实际场景时应该调用接口 + deleteGoodsService({ spuId, skuId }) { + function deleteGoods(group) { + for (const gindex in group) { + const goods = group[gindex]; + if (goods.spuId === spuId && goods.skuId === skuId) { + group.splice(gindex, 1); + return gindex; + } + } + return -1; + } + const { storeGoods, invalidGoodItems } = this.data.cartGroupData; + for (const store of storeGoods) { + for (const activity of store.promotionGoodsList) { + if (deleteGoods(activity.goodsPromotionList) > -1) { + return Promise.resolve(); + } + } + if (deleteGoods(store.shortageGoodsList) > -1) { + return Promise.resolve(); + } + } + if (deleteGoods(invalidGoodItems) > -1) { + return Promise.resolve(); + } + return Promise.reject(); + }, + + onGoodsSelect({ detail: { goods } }) { + const { cartItems } = this.data; + const item = cartItems.find((x) => x._id === goods._id); + + if (item == null) { + console.warn('Cart item not found!'); + return; + } + + item.selected = !item.selected; + this.setData({ cartItems }); + }, + + onQuantityChange({ detail: { cartItemId, count } }) { + const { cartItems } = this.data; + const item = cartItems.find((x) => x._id === cartItemId); + if (item == null) { + console.warn('Cart item not found'); + return; + } + item.count = count; + this.setData({ cartItems }); + this.debouncedUpdateCartItemCount({ cartItemId, count }); + }, + + debouncedUpdateCartItemCount({ cartItemId, count }) { + clearTimeout(updateCartItemCountTimeout); + updateCartItemCountTimeout = setTimeout(async () => { + this.setLoading(); + try { + await updateCartItemCount({ cartItemId, count }); + } finally { + this.unsetLoading(); + } + }, 500); + }, + + goCollect() { + /** 活动肯定有一个活动ID,用来获取活动banner,活动商品列表等 */ + const promotionID = '123'; + wx.navigateTo({ + url: `/pages/promotion-detail/index?promotion_id=${promotionID}`, + }); + }, + + goGoodsDetail({ + detail: { + goods: { + sku: { + spu: { _id }, + }, + }, + }, + }) { + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${_id}`, + }); + }, + + async onGoodsDelete({ + detail: { + goods: { _id }, + }, + }) { + try { + await Dialog.confirm({ + context: this, + closeOnOverlayClick: true, + title: '确认删除该商品吗?', + confirmBtn: '确定', + cancelBtn: '取消', + }); + this.setLoading(); + try { + await deleteCartItem({ cartItemId: _id }); + const { cartItems } = this.data; + this.setData({ + cartItems: cartItems.filter((x) => x._id !== _id), + }); + } finally { + this.unsetLoading(); + } + } catch { + console.warn('deletion is cancelled'); + } + }, + + onSelectAll() { + const { cartItems, allSelected } = this.data; + cartItems.forEach((x) => (x.selected = !allSelected)); + this.setData({ cartItems }); + }, + + onToSettle() { + wx.navigateTo({ + url: `/pages/order/order-confirm/index?${objectToParamString({ + type: 'cart', + cartIds: this.data.cartItems + .filter((x) => x.selected === true) + .map((x) => x._id) + .join(','), + })}`, + }); + }, + onGotoHome() { + wx.switchTab({ url: '/pages/home/home' }); + }, + + setLoading() { + return this.setDataPromise({ + loading: true, + }); + }, + unsetLoading() { + return this.setDataPromise({ + loading: false, + }); + }, + toggleLoading() { + this.setData({ + loading: !this.data.loading, + }); + }, + + setDataPromise(data) { + return new Promise((res) => { + this.setData(data, () => res()); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/index.json b/miniprogram/tcb-shop/pages/cart/index.json new file mode 100644 index 0000000..b78f95a --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "购物车", + "usingComponents": { + "cart-group": "./components/cart-group/index", + "cart-empty": "./components/cart-empty/index", + "cart-bar": "./components/cart-bar/index", + "loading-dialog": "../../components/loading-dialog/index", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/index.wxml b/miniprogram/tcb-shop/pages/cart/index.wxml new file mode 100644 index 0000000..affa744 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.wxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/index.wxss b/miniprogram/tcb-shop/pages/cart/index.wxss new file mode 100644 index 0000000..ea19962 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.wxss @@ -0,0 +1,13 @@ +:host { + padding-bottom: 100rpx; +} + +.gap { + height: 100rpx; + width: 100%; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js new file mode 100644 index 0000000..07785fe --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js @@ -0,0 +1,57 @@ +const statusMap = { + default: { text: '去使用', theme: 'primary' }, + useless: { text: '已使用', theme: 'default' }, + disabled: { text: '已过期', theme: 'default' }, +}; +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + externalClasses: ['coupon-class'], + + properties: { + couponDTO: { + type: Object, + value: {}, // 优惠券数据 + }, + }, + + data: { + btnText: '', + btnTheme: '', + }, + + observers: { + couponDTO: function (couponDTO) { + if (!couponDTO) { + return; + } + const statusInfo = statusMap[couponDTO.status]; + + this.setData({ + btnText: statusInfo.text, + btnTheme: statusInfo.theme, + }); + }, + }, + + attached() {}, + + methods: { + // 跳转到详情页 + gotoDetail() { + wx.navigateTo({ + url: `/pages/coupon/coupon-detail/index?id=${this.data.couponDTO.key}`, + }); + }, + + // 跳转到商品列表 + gotoGoodsList() { + wx.navigateTo({ + url: `/pages/coupon/coupon-activity-goods/index?id=${this.data.couponDTO.key}`, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json new file mode 100644 index 0000000..2d851d1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "ui-coupon-card": "../ui-coupon-card/index", + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml new file mode 100644 index 0000000..4dd56e1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml @@ -0,0 +1,23 @@ + + + {{btnText}} + + + diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss new file mode 100644 index 0000000..905dc40 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss @@ -0,0 +1,9 @@ +.coupon-btn-default { + display: none; +} + +.coupon-btn-primary { + --td-button-extra-small-padding-horizontal: 26rpx; + --td-button-primary-outline-color: #fa4126; + --td-button-primary-outline-border-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js new file mode 100644 index 0000000..f29bcad --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js @@ -0,0 +1,17 @@ +Component({ + data: { icon: 'cart' }, + + properties: { + count: { + type: Number, + }, + }, + + methods: { + goToCart() { + wx.switchTab({ + url: '/pages/cart/index', + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json new file mode 100644 index 0000000..fa2d209 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml new file mode 100644 index 0000000..7f1a9e7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml @@ -0,0 +1,14 @@ + + + + + + {{count}} + + + diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss new file mode 100644 index 0000000..faad943 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss @@ -0,0 +1,30 @@ +.floating-button { + position: fixed; + right: 20rpx; + bottom: 108rpx; +} + +.floating-button .floating-inner-container { + display: flex; + align-items: center; + justify-content: center; + height: 96rpx; + width: 96rpx; + background-color: rgba(0, 0, 0, 0.8); + opacity: 0.7; + border-radius: 48rpx; +} + +.floating-button .floating-right-top { + position: absolute; + right: 0rpx; + top: 0rpx; + height: 28rpx; + background: #fa4126; + border-radius: 64rpx; + font-weight: bold; + font-size: 22rpx; + line-height: 28rpx; + color: #fff; + padding: 0 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js new file mode 100644 index 0000000..9101be8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js @@ -0,0 +1,87 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, + }, + + externalClasses: ['coupon-class'], + + properties: { + mask: { + type: Boolean, + value: false, // 是否添加遮罩 + }, + superposable: { + type: Boolean, + value: false, // 是否可叠加 + }, + type: { + type: String, + value: '', // 优惠券类型:CouponType + }, + value: { + type: String, + value: '', // 优惠金额 + }, + tag: { + type: String, + value: '', // 优惠标签,优惠券名字标签,img + }, + desc: { + type: String, + value: '', // 优惠金额描述,金额下方 + }, + title: { + type: String, // 优惠券名称 + value: '', + }, + timeLimit: { + type: String, // 优惠券时限 + value: '', + }, + ruleDesc: { + type: String, // 优惠券适用规则描述 + value: '', + }, + currency: { + type: String, + value: '¥', // 优惠货币 + }, + status: { + type: String, + value: 'default', + }, + image: { + type: String, + value: '', + }, + }, + + data: { + CouponType: { + MJ_COUPON: 1, + ZK_COUPON: 2, + MJF_COUPON: 3, + GIFT_COUPON: 4, + }, + theme: 'primary', + }, + + observers: { + status: function (value) { + let theme = 'primary'; + // 已过期或已使用的券 颜色置灰 + if (value === 'useless' || value === 'disabled') { + theme = 'weak'; + } + + this.setData({ theme }); + }, + }, + + attached() { + this.setData({ + color: `color${this.properties.colorStyle}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json new file mode 100644 index 0000000..dd874c8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml new file mode 100644 index 0000000..ec76acb --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml @@ -0,0 +1,55 @@ + + function isBigValue(value) { + var values = (value + '').split('.'); + if (values[1] && values[0].length >= 3) return true; + else return false + } + + function getBigValues(value) { + return value.split('.'); + } + + module.exports = { isBigValue: isBigValue, getBigValues: getBigValues }; + + + + + {{value}} + + {{desc}} + + + + {{tools.getBigValues(value)[0]}} + .{{tools.getBigValues(value)[1]}} + + {{value / 100}} + + {{desc}} + + + + 免邮 + + {{desc}} + + + + + + + + {{title}} + {{timeLimit}} + + {{ruleDesc}} + + + + + + + + + 可叠加 + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss new file mode 100644 index 0000000..0ef9648 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss @@ -0,0 +1,147 @@ +.wr-coupon { + display: flex; + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/coupon-bg-nocorners.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + position: relative; + margin-bottom: 24rpx; + overflow: hidden; +} +.theme-weak.wr-coupon { + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/coupon-bg-grey2.png'); +} + +.wr-coupon__left { + width: 200rpx; + height: 180rpx; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + color: #fa4126; + overflow: hidden; + position: relative; +} +.theme-weak .wr-coupon__left { + color: #333; +} + +.wr-coupon__left--value { + font-size: 64rpx; + line-height: 88rpx; + font-weight: bold; + font-family: 'DIN Alternate', cursive; +} +.wr-coupon__left--value-int { + font-size: 48rpx; + line-height: 88rpx; +} +.wr-coupon__left--value-decimal { + font-size: 36rpx; + line-height: 48rpx; +} +.wr-coupon__left--image { + width: 128rpx; + height: 128rpx; + border-radius: 8px; + margin-top: 30rpx; +} +.wr-coupon__left--unit { + font-size: 24rpx; + line-height: 32rpx; +} +.wr-coupon__left--desc { + font-size: 24rpx; + line-height: 32rpx; + color: #fa4126; +} + +.theme-weak .wr-coupon__left--desc { + color: #333; +} + +.wr-coupon__right { + flex-grow: 1; + padding: 0 20rpx; + height: 180rpx; + box-sizing: border-box; + overflow: hidden; + display: flex; + align-items: center; +} +.wr-coupon__right--title { + display: flex; + -webkit-display: flex; + flex-direction: column; + align-items: flex-start; + color: #999999; + font-size: 24rpx; + flex: 1; +} +.wr-coupon__right--title .coupon-title { + max-width: 320rpx; + color: #333333; + font-size: 28rpx; + line-height: 40rpx; + font-weight: bold; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + white-space: normal; +} +.wr-coupon__right--title .coupon-time { + margin-top: 16rpx; + /* // letter-spacing: -0.05em; */ +} +.wr-coupon__right--title .coupon-desc { + margin-top: 8rpx; +} +.wr-coupon__right--title .coupon-arrow { + font-size: 22rpx; +} +.wr-coupon__right--oper { + display: flex; + justify-content: center; + align-items: center; +} +.wr-coupon__mask { + width: 702rpx; + height: 182rpx; + position: absolute; + top: 0; + left: 0; + background-color: #ffffff; + opacity: 0.5; +} +.wr-coupon__tag { + position: absolute; + top: 8px; + right: -24rpx; + text-align: center; + width: 106rpx; + height: 28rpx; + opacity: 0.9; + font-size: 20rpx; + line-height: 28rpx; + color: #fa4126; + border: 0.5px solid #fa4126; + box-sizing: border-box; + transform: rotate(45deg); +} +.wr-coupon__seal { + width: 128rpx; + height: 128rpx; + position: absolute; + top: 0; + right: 0; + background-size: 100% 100%; +} + +.wr-coupon__seal.seal-useless { + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/seal-used.png'); +} + +.wr-coupon__seal.seal-disabled { + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/coupon-expired.png'); +} diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js new file mode 100644 index 0000000..8010400 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js @@ -0,0 +1,75 @@ +import { fetchCouponDetail } from '../../../services/coupon/index'; +import Toast from 'tdesign-miniprogram/toast/index'; + +Page({ + data: { + goods: [], + detail: {}, + couponTypeDesc: '', + showStoreInfoList: false, + cartNum: 2, + }, + + id: '', + + onLoad(query) { + const id = parseInt(query.id); + this.id = id; + + this.getCouponDetail(id); + this.getGoodsList(id); + }, + + getCouponDetail(id) { + fetchCouponDetail(id).then(({ detail }) => { + this.setData({ detail }); + if (detail.type === 2) { + if (detail.base > 0) { + this.setData({ + couponTypeDesc: `满${detail.base / 100}元${detail.value}折`, + }); + } else { + this.setData({ couponTypeDesc: `${detail.value}折` }); + } + } else if (detail.type === 1) { + if (detail.base > 0) { + this.setData({ + couponTypeDesc: `满${detail.base / 100}元减${detail.value / 100}元`, + }); + } else { + this.setData({ couponTypeDesc: `减${detail.value / 100}元` }); + } + } + }); + }, + + getGoodsList() { + throw new Error('unimplemented'); + }, + + openStoreList() { + this.setData({ + showStoreInfoList: true, + }); + }, + + closeStoreList() { + this.setData({ + showStoreInfoList: false, + }); + }, + + goodClickHandle(e) { + const { index } = e.detail; + const { spuId } = this.data.goods[index]; + wx.navigateTo({ url: `/pages/goods/details/index?spuId=${spuId}` }); + }, + + cartClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加入购物车', + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json new file mode 100644 index 0000000..31cf9c3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "活动商品", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-toast": "tdesign-miniprogram/toast/toast", + "goods-list": "/components/goods-list/index", + "floating-button": "../components/floating-button/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml new file mode 100644 index 0000000..1c55b7f --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml @@ -0,0 +1,40 @@ + + + + 以下商品可使用 + {{couponTypeDesc}} + 优惠券 + + + + + + + + + + + 规则详情 + + + 优惠券有效时间 + {{detail.timeLimit}} + + + 优惠券说明 + {{detail.desc}} + + + 使用须知 + {{detail.useNotes}} + + + + + + diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss new file mode 100644 index 0000000..3217203 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss @@ -0,0 +1,69 @@ +page { + background-color: #f5f5f5; +} + +.coupon-page-container .notice-bar-content { + display: flex; + flex-direction: row; + align-items: center; + padding: 8rpx 0; +} + +.coupon-page-container .notice-bar-text { + font-size: 26rpx; + line-height: 36rpx; + font-weight: 400; + color: #666666; + margin-left: 24rpx; + margin-right: 12rpx; +} + +.coupon-page-container .notice-bar-text .height-light { + color: #fa550f; +} + +.coupon-page-container .popup-content-wrap { + background-color: #fff; + border-top-left-radius: 20rpx; + border-top-right-radius: 20rpx; +} + +.coupon-page-container .popup-content-title { + font-size: 32rpx; + color: #333; + text-align: center; + height: 104rpx; + line-height: 104rpx; + position: relative; +} + +.coupon-page-container .desc-group-wrap { + padding-bottom: env(safe-area-inset-bottom); +} + +.coupon-page-container .desc-group-wrap .item-wrap { + margin: 0 30rpx 30rpx; +} + +.coupon-page-container .desc-group-wrap .item-title { + font-size: 26rpx; + color: #333; + font-weight: 500; +} + +.coupon-page-container .desc-group-wrap .item-label { + font-size: 24rpx; + color: #666; + margin-top: 12rpx; + white-space: pre-line; + word-break: break-all; + line-height: 34rpx; +} + +.coupon-page-container .goods-list-container { + margin: 0 24rpx 24rpx; +} + +.coupon-page-container .goods-list-wrap { + background: #f5f5f5 !important; +} diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js new file mode 100644 index 0000000..71ff5d6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js @@ -0,0 +1,32 @@ +import { fetchCouponDetail } from '../../../services/coupon/index'; + +Page({ + data: { + detail: null, + storeInfoList: [], + storeInfoStr: '', + showStoreInfoList: false, + }, + + id: '', + + onLoad(query) { + const id = parseInt(query.id); + this.id = id; + this.getGoodsList(id); + }, + + getGoodsList(id) { + fetchCouponDetail(id).then(({ detail }) => { + this.setData({ + detail, + }); + }); + }, + + navGoodListHandle() { + wx.navigateTo({ + url: `/pages/coupon/coupon-activity-goods/index?id=${this.id}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json new file mode 100644 index 0000000..d419a0b --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "优惠券详情", + "usingComponents": { + "coupon-card": "../components/coupon-card/index", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-button": "tdesign-miniprogram/button/button", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml new file mode 100644 index 0000000..8c447c2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + 查看可用商品 + + diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss new file mode 100644 index 0000000..8fb1fe7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss @@ -0,0 +1,91 @@ +page { + background-color: #f5f5f5; +} + +.coupon-card-wrap { + background-color: #fff; + padding: 32rpx 32rpx 1rpx; +} +.desc-wrap { + margin-top: 24rpx; +} +.desc-wrap .button-wrap { + margin: 50rpx 32rpx 0; +} + +.desc-group-wrap .t-class-cell { + align-items: flex-start; +} + +.desc-group-wrap .t-class-title { + font-size: 26rpx; + width: 140rpx; + flex: none; + color: #888; +} + +.desc-group-wrap .t-class-note { + font-size: 26rpx; + word-break: break-all; + white-space: pre-line; + justify-content: flex-start; + color: #333; +} + +.desc-group-wrap { + border-radius: 8rpx; + overflow: hidden; + + --cell-label-font-size: 26rpx; + --cell-label-line-height: 36rpx; + --cell-label-color: #999; +} + +.desc-group-wrap.in-popup { + border-radius: 0; + overflow: auto; + max-height: 828rpx; +} + +.desc-group-wrap .wr-cell__title { + color: #333; + font-size: 28rpx; +} + +/* .desc-group-wrap .max-width-cell { + overflow: hidden; +} */ + +/* .desc-group-wrap .signal-line-label { + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.desc-group-wrap .multi-line-label { + word-break: break-all; + white-space: pre-line; +} */ + +.popup-content-wrap { + background-color: #fff; + border-top-left-radius: 20rpx; + border-top-right-radius: 20rpx; +} + +.popup-content-title { + font-size: 32rpx; + color: #333; + + text-align: center; + height: 104rpx; + line-height: 104rpx; + + position: relative; +} + +.popup-content-title .close-icon { + position: absolute; + top: 24rpx; + right: 24rpx; +} diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.js b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.js new file mode 100644 index 0000000..f3b8675 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.js @@ -0,0 +1,77 @@ +import { fetchCouponList } from '../../../services/coupon/index'; + +Page({ + data: { + status: 0, + list: [ + { + text: '可使用', + key: 0, + }, + { + text: '已使用', + key: 1, + }, + { + text: '已失效', + key: 2, + }, + ], + + couponList: [], + }, + + onLoad() { + this.init(); + }, + + init() { + this.fetchList(); + }, + + fetchList(status = this.data.status) { + let statusInFetch = ''; + switch (Number(status)) { + case 0: { + statusInFetch = 'default'; + break; + } + case 1: { + statusInFetch = 'useless'; + break; + } + case 2: { + statusInFetch = 'disabled'; + break; + } + default: { + throw new Error(`unknown fetchStatus: ${statusInFetch}`); + } + } + fetchCouponList(statusInFetch).then((couponList) => { + this.setData({ couponList }); + }); + }, + + tabChange(e) { + const { value } = e.detail; + + this.setData({ status: value }); + this.fetchList(value); + }, + + goCouponCenterHandle() { + wx.showToast({ title: '去领券中心', icon: 'none' }); + }, + + onPullDownRefresh_() { + this.setData( + { + couponList: [], + }, + () => { + this.fetchList(); + }, + ); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.json b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.json new file mode 100644 index 0000000..64b7e4b --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "优惠券", + "usingComponents": { + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", + "t-icon": "tdesign-miniprogram/icon/icon", + "coupon-card": "../components/coupon-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml new file mode 100644 index 0000000..391fe7b --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml @@ -0,0 +1,42 @@ + + + + + + + + + + + + 领券中心 + + + + + diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss new file mode 100644 index 0000000..4e28302 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss @@ -0,0 +1,78 @@ +page { + height: 100%; +} + +.tabs-external__inner { + height: 88rpx; + width: 100%; + line-height: 88rpx; + z-index: 100; +} +.tabs-external__inner { + font-size: 26rpx; + color: #333333; + position: fixed; + width: 100vw; + top: 0; + left: 0; +} + +.tabs-external__inner .tabs-external__track { + background: #fa4126 !important; +} + +.tabs-external__inner .tabs-external__item { + color: #666; +} + +.tabs-external__inner .tabs-external__active { + font-size: 28rpx; + color: #fa4126 !important; +} + +.tabs-external__inner.order-nav .order-nav-item .bottom-line { + bottom: 12rpx; +} + +.coupon-list-wrap { + margin-top: 32rpx; + margin-left: 32rpx; + margin-right: 32rpx; + overflow-y: auto; + padding-bottom: 100rpx; + padding-bottom: calc(constant(safe-area-inset-top) + 100rpx); + padding-bottom: calc(env(safe-area-inset-bottom) + 100rpx); + -webkit-overflow-scrolling: touch; +} + +.center-entry { + box-sizing: content-box; + border-top: 1rpx solid #dce0e4; + background-color: #fff; + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 100rpx; + padding-bottom: 0; + padding-bottom: constant(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); +} + +.center-entry-btn { + color: #fa4126; + font-size: 28rpx; + text-align: center; + line-height: 100rpx; + display: flex; + align-items: center; + justify-content: center; + height: 100rpx; +} + +.coupon-list-wrap .t-pull-down-refresh__bar { + background: #fff !important; +} +.t-class-indicator { + color: #b9b9b9 !important; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md new file mode 100644 index 0000000..88d7b0b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md @@ -0,0 +1,95 @@ +# Sidebar 侧边导航 + +### 引入 + +全局引入,在miniprogram根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +// app.json 或 index.json +"usingComponents": { + "wr-sidebar": "path/to/components/goods-category/wr-sidebar/index", + "wr-sidebar-item": "path/to/component/goods-category/wr-sidebar/wr-sidebar-item/index" +} +``` + +## 代码演示 + +### 基础用法 + +通过在`wr-sidebar`上设置`activeKey`属性来控制选中项 + +```html + + + + + +``` + +``` javascript +Page({ + data: { + activeKey: 0 + }, + + onChange(event) { + wx.showToast({ + icon: 'none', + title: `切换至第${event.detail}项` + }); + } +}); +``` + +### 提示气泡(暂未实现) + +设置`dot`属性后,会在右上角展示一个小红点。设置`info`属性后,会在右上角展示相应的徽标 + +```html + + + + + +``` + +## API + +### Sidebar Props + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +|-----------|-----------|-----------|-------------|-------------| +| activeKey | 选中项的索引 | *string \| number* | `0` | - | + +### Sidebar Event + +| 事件名 | 说明 | 参数 | +|------|------|------| +| change | 切换选项时触发 | 当前选中选项的索引 | + +### Sidebar 外部样式类 + +| 类名 | 说明 | +|-----------|-----------| +| custom-class | 根节点样式类 | + +### SidebarItem Props + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +|-----------|-----------|-----------|-------------|-------------| +| title | 内容 | *string* | `''` | - | +| disabled | 是否禁用 | | *boolean* | `false` | - | +| dot | 是否显示右上角小红点 | *boolean* | `false` | - | +| info | 提示消息 | *string \| number* | `''` | - | + +### SidebarItem Event + +| 事件名 | 说明 | 参数 | +|------|------|------| +| click | 点击徽章时触发 | 当前徽章的索引 | + +### SidebarItem 外部样式类 + +| 类名 | 说明 | +|-----------|-----------| +| custom-class | 根节点样式类 | diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js new file mode 100644 index 0000000..810d5e5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js @@ -0,0 +1,51 @@ +Component({ + relations: { + '../../c-sidebar/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + externalClasses: ['custom-class'], + properties: { + title: String, + disabled: Boolean, + }, + + data: { + topRightRadius: false, + bottomRightRadius: false, + }, + + methods: { + setActive(selected) { + return this.setData({ selected }); + }, + onClick() { + const { parent } = this; + + if (!parent || this.properties.disabled) { + return; + } + + const index = parent.children.indexOf(this); + + parent.setActive(index).then(() => { + this.triggerEvent('click', index); + parent.triggerEvent('change', { index }); + }); + }, + setTopRightRadius(val) { + return this.setData({ + topRightRadius: val, + }); + }, + setBottomRightRadius(val) { + return this.setData({ + bottomRightRadius: val, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml new file mode 100644 index 0000000..435f1e9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml @@ -0,0 +1,10 @@ + + + {{ title }} + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss new file mode 100644 index 0000000..28d50dc --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss @@ -0,0 +1,60 @@ +.c-sidebar-item { + display: flex; + justify-content: center; + text-align: center; + background-color: #f5f5f5; + color: #222427; + padding: 20rpx 0; + font-size: 26rpx; +} + +.c-sidebar-item.active { + position: relative; + background: white; +} + +.c-sidebar-item.active::before { + content: ''; + position: absolute; + width: 6rpx; + height: 48rpx; + background-color: #fa4126; + left: 0; + top: 50%; + transform: translate(0, -50%); + border-radius: 64rpx; +} + +.c-sidebar-item__text { + width: 136rpx; + height: 36rpx; + padding: 8rpx 0; + line-height: 36rpx; + text-align: center; + font-size: 28rpx; + color: #666666; +} + +.c-sidebar-item.active .c-sidebar-item__text { + background-color: white; + border-radius: 36rpx; + color: #fa4126; +} + +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.top-right-radius { + border-top-right-radius: 16rpx; +} + +.bottom-right-radius { + border-bottom-right-radius: 16rpx; +} + +.c-sidebar-item-container { + background-color: white; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js new file mode 100644 index 0000000..b057ad1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js @@ -0,0 +1,106 @@ +Component({ + relations: { + './c-sidebar-item/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setActive(this.properties.activeKey, true); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + this.setActive(this.properties.activeKey, true); + }, + }, + }, + + externalClasses: ['custom-class'], + + properties: { + activeKey: { + type: Number, + value: 0, + }, + }, + observers: { + activeKey(newVal) { + this.setActive(newVal); + }, + }, + + created() { + this.children = []; + this.currentActive = -1; + this.topRightRadiusItemIndexs = []; + this.bottomRightRadiusItemIndexs = []; + }, + + methods: { + setActive(activeKey, isChildrenChange) { + const { + children, + currentActive, + topRightRadiusItemIndexs: preTopRightRadiusItemIndexs, + bottomRightRadiusItemIndexs: preBottomRightRadiusItemIndexs, + } = this; + + if (!children.length) { + return Promise.resolve(); + } + + if (activeKey === currentActive && !isChildrenChange) { + return Promise.resolve(); + } + + this.currentActive = activeKey; + this.topRightRadiusItemIndexs = this.getTopRightRadiusItemIndexs( + activeKey, + children, + ); + this.bottomRightRadiusItemIndexs = this.getBottomRightRadiusItemIndexs( + activeKey, + children, + ); + + const stack = []; // 任务列表,存放调用子组件的setActive后返回的一堆promise + + // 将旧的选中项改为false + if (currentActive !== activeKey && children[currentActive]) { + stack.push(children[currentActive].setActive(false)); + } + + // 将新的选中项改为true + if (children[activeKey]) { + stack.push(children[activeKey].setActive(true)); + } + + preTopRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setTopRightRadius(false)); + }); + + preBottomRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setBottomRightRadius(false)); + }); + + this.topRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setTopRightRadius(true)); + }); + + this.bottomRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setBottomRightRadius(true)); + }); + + return Promise.all(stack); + }, + getTopRightRadiusItemIndexs(activeKey, children) { + const { length } = children; + if (activeKey !== 0 && activeKey < length - 1) return [0, activeKey + 1]; + if (activeKey !== 0) return [0]; + if (activeKey < length - 1) return [activeKey + 1]; + return []; + }, + getBottomRightRadiusItemIndexs(activeKey) { + if (activeKey !== 0) return [activeKey - 1]; + return []; + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json new file mode 100644 index 0000000..84ff738 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json @@ -0,0 +1,4 @@ +{ + "component": true +} + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml new file mode 100644 index 0000000..a1fe026 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml @@ -0,0 +1,3 @@ + + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss new file mode 100644 index 0000000..95e59ff --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss @@ -0,0 +1,9 @@ +.c-sidebar { + width: 176rpx; + height: 100vh; +} +.c-sidebar::-webkit-scrollbar { + width: 0; + height: 0; + color: transparent; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js new file mode 100644 index 0000000..2b6f95d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js @@ -0,0 +1,37 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + tabList: Array, + }, + + data: { + unfolded: false, + boardMaxHeight: null, + }, + attached() { + wx.createSelectorQuery() + .in(this) + .select('.c-tabbar-more') + .boundingClientRect((rect) => { + this.setData({ boardMaxHeight: rect.height }); + }) + .exec(); + }, + + methods: { + changeFold() { + this.setData({ + unfolded: !this.data.unfolded, + }); + const { unfolded } = this.data; + this.triggerEvent('change', { unfolded }); + }, + + onSelect(event) { + const activeKey = event.currentTarget.dataset.index; + this.triggerEvent('select', activeKey); + this.changeFold(); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml new file mode 100644 index 0000000..0ea9ad4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml @@ -0,0 +1,25 @@ + + + + + + + + + + {{ item.name }} + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss new file mode 100644 index 0000000..d0d08b3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss @@ -0,0 +1,63 @@ +.c-tabbar-more { + width: 100%; + height: calc(100% - var(--tabbar-height, 100rpx)); + position: absolute; + top: var(--tabbar-height, 100rpx); +} +.c-tabbar-more__btn { + position: absolute; + top: calc(0rpx - var(--tabbar-height, 100rpx)); + right: 0; + width: 80rpx; + height: var(--tabbar-height, 100rpx); + line-height: var(--tabbar-height, 100rpx); + background-color: var(--tabbar-background-color, white); + box-shadow: -20rpx 0 20rpx -10rpx var(--tabbar-background-color, white); + text-align: center; +} +.c-tabbar-more__btn .market { + font-size: 20rpx; +} +.t-tabbar-more__boardwrapper { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.t-tabbar-more__mask { + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); +} +.c-tabbar-more__board { + position: absolute; + top: 0; + left: 0; + width: 100%; + max-height: 100%; +} +.c-tabbar-more__boardinner { + padding: 20rpx 0 20rpx 20rpx; + background-color: var(--tabbar-background-color, white); + display: flex; + flex-flow: row wrap; +} +.c-tabbar-more__item { + margin: 0 20rpx 20rpx 0; + flex: 0 0 calc((100% - 60rpx) / 3); + box-sizing: border-box; + padding: 0 10rpx; + border-radius: 30rpx; + height: 60rpx; + line-height: 60rpx; + text-align: center; + font-size: 22rpx; + color: #5d5d5d; + background-color: #eee; +} +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js new file mode 100644 index 0000000..ddf6367 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js @@ -0,0 +1,68 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + activeKey: { + type: Number, + value: 0, + }, + tabList: { + type: Array, + value: [], + }, + showMore: Boolean, // 是否需要下拉功能 + }, + observers: { + activeKey(newVal) { + if (this.properties.tabList && newVal) { + this.setActive(newVal).catch((e) => { + console.error(e); + }); + } + }, + }, + + data: { + currentActive: -1, + }, + attached() { + this.setActive(this.properties.activeKey).catch((e) => { + console.error(e); + }); + }, + + methods: { + setActive(activeKey) { + if ( + !this.properties.tabList[activeKey] || + this.properties.tabList[activeKey].disabled + ) { + return Promise.reject('数据异常或不可操作'); + } + return new Promise((resolve) => { + this.setData( + { + currentActive: activeKey, + }, + () => resolve(), + ); + }); + }, + onClick(event) { + let activeKey; + if (event.type === 'select') { + activeKey = event.detail; + } else { + activeKey = event.currentTarget.dataset.index; + } + this.setActive(activeKey) + .then(() => { + const { currentActive } = this.data; + this.triggerEvent('change', { index: currentActive }); + }) + .catch((e) => { + console.error(e); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json new file mode 100644 index 0000000..644e632 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "c-tabbar-more": "./c-tabbar-more/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml new file mode 100644 index 0000000..1227f88 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml @@ -0,0 +1,29 @@ + + + + + {{ item.name }} + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss new file mode 100644 index 0000000..d89475f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss @@ -0,0 +1,53 @@ +.c-tabbar { + width: 100%; + height: 100%; + position: relative; + --tabbar-height: 100rpx; + --tabbar-fontsize: 28rpx; + --tabbar-background-color: white; +} +.c-tabbar__inner { + display: flex; + flex-flow: row nowrap; +} +.c-tabbar__scroll { + position: relative; +} +.c-tabbar__scroll::after { + content: ''; + display: block; + position: absolute; + width: 100%; + left: 0; + bottom: -1px; + height: 1px; + background-color: #eee; + z-index: 1; +} +.c-tabbar__inner.c-tabbar__inner_more::after { + content: ''; + display: block; + width: 100rpx; + height: 100rpx; + flex: none; +} +.c-tabbar-item { + flex: none; + height: 100rpx; + color: #282828; + font-size: 28rpx; + padding: 0 20rpx; +} +.c-tabbar-item.active:not(.disabled) { + color: #0071ce; + position: relative; +} +.c-tabbar-item.disabled { + color: #ccc; +} +.c-tabbar-item__text { + width: 100%; + text-align: center; + height: 100rpx; + line-height: 100rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js new file mode 100644 index 0000000..8ea282f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js @@ -0,0 +1,69 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + category: { + type: Array, + }, + initActive: { + type: Array, + value: [], + observer(newVal, oldVal) { + if (newVal[0] !== oldVal[0]) { + this.setActiveKey(newVal[0], 0); + } + }, + }, + isSlotRight: { + type: Boolean, + value: false, + }, + level: { + type: Number, + value: 2, + }, + }, + data: { + activeKey: 0, + subActiveKey: 0, + }, + attached() { + if (this.properties.initActive && this.properties.initActive.length > 0) { + this.setData({ + activeKey: this.properties.initActive[0], + subActiveKey: this.properties.initActive[1] || 0, + }); + } + }, + methods: { + onParentChange(event) { + this.setActiveKey(event.detail.index, 0).then(() => { + this.triggerEvent('change', [this.data.activeKey, this.data.subActiveKey]); + }); + }, + onChildChange(event) { + this.setActiveKey(this.data.activeKey, event.detail.index).then(() => { + this.triggerEvent('change', [this.data.activeKey, this.data.subActiveKey]); + }); + }, + changCategory(event) { + const { item } = event.currentTarget.dataset; + this.triggerEvent('changeCategory', { + item, + }); + }, + setActiveKey(key, subKey) { + return new Promise((resolve) => { + this.setData( + { + activeKey: key, + subActiveKey: subKey, + }, + () => { + resolve(); + }, + ); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json new file mode 100644 index 0000000..8ca3d3c --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "usingComponents": { + "c-tabbar": "./components/c-tabbar/index", + "c-sidebar": "./components/c-sidebar/index", + "c-sidebar-item": "./components/c-sidebar/c-sidebar-item/index", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml new file mode 100644 index 0000000..c183ff9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + {{item.name}} + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss new file mode 100644 index 0000000..fbf4569 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss @@ -0,0 +1,102 @@ +.goods-category { + display: flex; +} + +.custom-sidebar { + height: 100%; +} + +.goods-category__right { + height: 100%; + flex: auto; + width: 0; + position: relative; + overflow: scroll; + -webkit-overflow-scrolling: touch; + background-color: white; +} + +.flex { + display: flex; +} + +.goods-category-normal { + margin: 28rpx 34rpx 0rpx 32rpx; +} + +.goods-category-normal-item-title { + font-size: 28rpx; + font-weight: 500; +} + +.goods-category-normal-item-container { + background-color: #fff; + border-radius: 8rpx; + padding-top: 28rpx; + margin-top: -24rpx; + margin-bottom: 30rpx; + display: flex; + flex-wrap: wrap; +} + +.goods-category-normal-item-container-item { + height: 196rpx; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 24rpx; + width: 33.3%; +} + +.goods-category-normal-item-container-item .image { + width: 144rpx; + height: 144rpx; +} + +.goods-category-normal-item-container-item-title { + justify-content: center; + font-size: 24rpx; + color: #666666; + margin-top: 20rpx; +} + +.goods-category .custom-sidebar { + background-color: #f5f5f5; +} + +.custom-sidebar { + width: 180rpx; + height: 100vh; +} + +.custom-sidebar::-webkit-scrollbar { + width: 0; + height: 0; + color: transparent; +} + +.goods-category-normal-item-second-container { + background-color: #fff; + border-radius: 8rpx; + margin-top: 8rpx; + margin-bottom: 30rpx; + display: grid; + grid-template-columns: 33.33% 33.33% 33.33%; +} + +.goods-category-normal-item-second-container-item { + height: 200rpx; + text-align: center; + margin-top: 20rpx; +} + +.goods-category-normal-item-second-container-item .image { + width: 144rpx; + height: 144rpx; +} + +.goods-category-normal-item-second-container-item-title { + justify-content: center; + font-size: 24rpx; + color: #222427; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/index.js b/miniprogram/tcb-shop/pages/goods/category/index.js new file mode 100644 index 0000000..b7867d7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.js @@ -0,0 +1,36 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { getCates } from '../../../services/cate/cate'; + +Page({ + data: { + cates: [], + }, + async init() { + try { + const cates = await getCates(); + this.setData({ cates }); + } catch (e) { + console.error('获取商品分类列表失败', e); + Toast({ + context: this, + selector: '#t-toast', + message: '获取商品分类列表失败', + duration: 1000, + icon: '', + }); + } + }, + + onShow() { + this.getTabBar().init(); + }, + onChange(e) { + const cateId = e?.detail?.item?._id; + wx.navigateTo({ + url: `/pages/goods/list/index?cateId=${cateId}`, + }); + }, + onLoad() { + this.init(true); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/index.json b/miniprogram/tcb-shop/pages/goods/category/index.json new file mode 100644 index 0000000..0bb1263 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.json @@ -0,0 +1,7 @@ +{ + "navigationBarTitleText": "分类", + "usingComponents": { + "goods-category": "./components/goods-category/index", + "t-toast": "tdesign-miniprogram/toast/toast" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/index.wxml b/miniprogram/tcb-shop/pages/goods/category/index.wxml new file mode 100644 index 0000000..cdfd458 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.wxml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/index.wxss b/miniprogram/tcb-shop/pages/goods/category/index.wxss new file mode 100644 index 0000000..ac2e0a7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.wxss @@ -0,0 +1,23 @@ +.tabbar-position { + position: fixed !important; + bottom: 0; + left: 0; + width: 100%; +} + +.wrap { + height: 100vh; + overflow: hidden; +} +.goods-category-class { + background-color: #f6f6f6 !important; + height: 100%; +} +.goods-category-class .goods-category-normal-item-container-item { + margin-top: 20rpx; +} + +page { + min-height: none; + padding-bottom: 0; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/assets/play.png b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/assets/play.png new file mode 100644 index 0000000..d816ef7 Binary files /dev/null and b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/assets/play.png differ diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js new file mode 100644 index 0000000..a0a3c48 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js @@ -0,0 +1,36 @@ +// pages/goods/comments/components/comments-card/images-videos/index.js +Component({ + /** + * 组件的属性列表 + */ + properties: { + resources: { + type: Array, + value: [], + }, + }, + + /** + * 组件的初始数据 + */ + data: { + classType: 'single', + }, + + observers: { + resources: function (newVal) { + if (newVal.length <= 1) { + this.setData({ classType: 'single' }); + } else if (newVal.length === 2) { + this.setData({ classType: 'double' }); + } else { + this.setData({ classType: 'multiple' }); + } + }, + }, + + /** + * 组件的方法列表 + */ + methods: {}, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json new file mode 100644 index 0000000..83597fd --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "my-video": "../my-video/index", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml new file mode 100644 index 0000000..2515909 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss new file mode 100644 index 0000000..e7944be --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss @@ -0,0 +1,68 @@ +.resource-item-single { + width: 360rpx; + height: 360rpx; + border-radius: 8rpx; +} + +.resource-item-double { + width: 334rpx; + height: 334rpx; + border-radius: 8rpx; +} + +.resource-item-multiple { + width: 218rpx; + height: 218rpx; + border-radius: 8rpx; +} + +.resource-container-single { + padding-left: 0; + padding-top: 0; +} + +.resource-container-double { + padding-left: 18rpx; + padding-top: 18rpx; +} + +.resource-container-multiple { + padding-left: 16rpx; + padding-top: 16rpx; +} + +.container-single { + margin-left: 0; +} + +.container-double { + margin-left: -18rpx; + margin-top: -18rpx; +} + +.container-multiple { + margin-left: -16rpx; + margin-top: -16rpx; +} + +.resource-container { + display: flex; +} + +.play-icon { + width: 96rpx; + height: 96rpx; +} + +.images-videos-container { + display: flex; + flex-wrap: wrap; +} + +.image { + border-radius: 8rpx; +} + +.cover-img-container { + background-color: white; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js new file mode 100644 index 0000000..987cf18 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js @@ -0,0 +1,55 @@ +Component({ + externalClasses: ['my-video', 'my-cover-img', 'my-play-icon'], + properties: { + videoSrc: { type: String }, + }, + data: { + isShow: true, + }, + + options: { + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + attached() { + this.videoContext = wx.createVideoContext('myVideo', this); + }, + + fullScreen: false, + + methods: { + // 点击封面自定义播放按钮时触发 + bindplay(e) { + this.setData({ + isShow: false, + }); + this.videoContext.play(); + this.triggerEvent('play', e); + }, + + bindplayByVideo(e) { + this.setData({ + isShow: false, + }); + this.triggerEvent('play', e); + }, + + // 监听播放到末尾时触发 + bindended(e) { + if (!this.fullScreen) { + this.setData({ + isShow: true, + }); + } + this.triggerEvent('ended', e); + }, + // 监听暂停播放时触发 + bindpause(e) { + this.triggerEvent('pause', e); + }, + bindfullscreenchange(e) { + const fullScreen = e?.detail?.fullScreen; + this.fullScreen = fullScreen; + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml new file mode 100644 index 0000000..af38919 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml @@ -0,0 +1,26 @@ + diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss new file mode 100644 index 0000000..63b962b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss @@ -0,0 +1,21 @@ +.video .video_cover { + width: 100%; + height: 100%; + position: relative; +} + +.video .video_play_icon { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + z-index: 5; +} + +.video .video_txt { + margin: 10rpx auto; +} + +.video { + display: flex; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js new file mode 100644 index 0000000..53f6103 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js @@ -0,0 +1,51 @@ +Component({ + externalClasses: ['wr-class'], + options: { + multipleSlots: true, + }, + properties: { + goodsDetailInfo: { + type: String, + value: '', + }, + sellerReply: { + type: String, + value: '', + }, + userHeadUrl: { + type: String, + value: '', + }, + userName: { + type: String, + default: '', + }, + commentContent: { + type: String, + value: '', + }, + commentScore: { + type: Number, + value: 0, + }, + commentTime: { + type: String, + value: '', + }, + commentResources: { + type: Array, + value: [], + }, + }, + + data: { + showMoreStatus: false, + showContent: false, + hideText: false, + eleHeight: null, + overText: false, + isDisabled: true, + startColors: ['#FFC51C', '#DDDDDD'], + }, + methods: {}, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json new file mode 100644 index 0000000..1130655 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-rate": "tdesign-miniprogram/rate/rate", + "images-videos": "./components/images-videos", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml new file mode 100644 index 0000000..af86507 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml @@ -0,0 +1,27 @@ + + + + + + + {{userName}} + {{commentTime}} + + + + + + {{goodsDetailInfo}} + + + + + + + + + 店家回复: + {{sellerReply}} + + + diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss new file mode 100644 index 0000000..c5e0564 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss @@ -0,0 +1,172 @@ +@import '../../../../../style/theme.wxss'; + +.comments-card-item { + padding: 32rpx; + display: flex; + background-color: #fff; + position: relative; +} + +.comments-card-item::after { + content: ''; + position: absolute; + bottom: 0rpx; + width: 686rpx; + height: 2rpx; + background-color: #f5f5f5; +} + +.comments-card-item-userImg { + display: flex; +} + +.comments-card-item-userImg .userImg { + width: 64rpx; + height: 64rpx; + border-radius: 50%; +} + +.comments-card-item-container { + width: 100%; +} + +.comments-card-item-container-name { + display: flex; + font-size: 28rpx; + color: #333; + font-weight: 600; + align-items: center; +} + +.comments-card-item-container-name .userName { + margin-right: 12rpx; +} + +.comments-card-item-container-date { + font-size: 22rpx; + color: #999; + margin-top: 4rpx; + display: flex; +} + +.comments-card-item-container-content { + margin-top: 16rpx; + position: relative; +} + +.comments-card-item-container-content .content-text { + font-size: 28rpx; + white-space: normal; + word-break: break-all; + font-weight: normal; +} + +.comments-card-item-container-content .hide-text { + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 5; + text-align: justify; + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.comments-card-item-container-content .showMore { + position: absolute; + width: 112rpx; + height: 36rpx; + bottom: 0; + right: 0; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0.2) 0, + rgba(255, 255, 255, 0.45) 20%, + rgba(255, 255, 255, 0.7) 25%, + rgba(255, 255, 255, 0.9) 30%, + rgba(255, 255, 255, 0.95) 35%, + #ffffff 50%, + #fff 100% + ); + font-size: 26rpx; + color: #fa550f; + line-height: 36rpx; + text-align: right; +} + +.comments-card-item-container-image { + margin-top: 24rpx; + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.comments-card-item-container-image .commentImg { + border-radius: 8rpx; + margin-top: 12rpx; +} + +.comments-card-item-container-image .commentImg3 { + width: 196rpx; + height: 196rpx; +} + +.comments-card-item-container-image .commentImg2 { + width: 300rpx; + height: 300rpx; +} + +.comments-card-item-container-image .commentImg1 { + width: 404rpx; + height: 404rpx; +} + +.comments-card-item-container .comments-title { + display: flex; + align-items: center; + position: relative; +} + +.comments-title .userName { + font-size: 26rpx; + color: #333333; + margin-left: 24rpx; +} + +.comments-title .commentTime { + font-size: 24rpx; + color: #999999; + position: absolute; + right: 0; +} + +.comments-info { + display: flex; + align-items: center; + margin-top: 18rpx; +} + +.comments-info .rate { + margin-right: 24rpx; +} + +.comments-info .goods-info-text { + font-size: 24rpx; + + color: #999999; +} + +.comments-card-item-container .comments-card-reply { + background-color: #f5f5f5; + padding: 24rpx 16rpx; + margin-top: 24rpx; +} + +.comments-card-item-container .comments-card-reply .prefix { + font-size: 26rpx; + font-weight: bold; + color: #666666; +} + +.comments-card-item-container .comments-card-reply .content { + font-size: 26rpx; + color: #666666; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.js b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.js new file mode 100644 index 0000000..922ec2c --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.js @@ -0,0 +1,56 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { getOrder } from '../../../../services/order/order'; +import { getAllOrderItemsOfAnOrder } from '../../../../services/order/orderItem'; +import { getCloudImageTempUrl } from '../../../../utils/cloudImageHandler'; + +Page({ + data: { + order: null, + }, + + async onLoad(options) { + const orderId = options?.orderId; + if (typeof orderId !== 'string') { + this.failedAndBack('获取订单详情失败'); + return; + } + + try { + const [order, items] = await Promise.all([getOrder(orderId), getAllOrderItemsOfAnOrder({ orderId })]); + + const images = items.map((x) => x.sku.image ?? ''); + try { + const handleImages = await getCloudImageTempUrl(images); + handleImages.forEach((image, index) => (items[index].sku.image = image)); + } catch (e) { + console.error('处理商品图片失败', e); + } + order.orderItems = items; + this.setData({ order }); + } catch (e) { + this.failedAndBack('获取订单详情失败', e); + } + }, + + failedAndBack(message, e) { + e && console.error(e); + Toast({ + context: this, + selector: '#t-toast', + message, + }); + setTimeout(() => wx.navigateBack(), 1000); + }, + + toComment(e) { + const orderItemId = e?.target?.dataset?.orderItem?._id; + if (typeof orderItemId !== 'string') { + this.failedAndBack('获取订单详情失败'); + return; + } + + wx.navigateTo({ + url: `/pages/goods/comments/create/index?orderItemId=${orderItemId}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.json b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.json new file mode 100644 index 0000000..b594baf --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "选择评价商品", + "usingComponents": { + "order-card": "../../../order/components/order-card", + "specs-goods-card": "../../../order/components/specs-goods-card/index", + "t-button": "tdesign-miniprogram/button/button", + "t-toast": "tdesign-miniprogram/toast/toast" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml new file mode 100644 index 0000000..c2e2a2a --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml @@ -0,0 +1,22 @@ + + + + + 订单号  + {{order._id}} + + + + + + + 评价 + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss new file mode 100644 index 0000000..dc90d1f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss @@ -0,0 +1,54 @@ +:host { + width: 100%; +} +.btn-bar { + display: flex; + justify-content: flex-end; + align-items: center; + line-height: 1; +} +.btn-bar .order-btn { + line-height: 1; + /* border-radius: unset; */ + /* min-width: 160rpx; */ +} + +.btn-bar .right { + display: flex; + align-items: center; +} +.btn-bar .t-button { + width: 160rpx; + font-weight: 400; + margin-left: 24rpx; +} +.btn-bar .t-button--max { + width: 176rpx; + margin-left: 24rpx; + + --td-button-extra-small-height: 72rpx; +} + +.btn-bar .left .delete-btn { + font-size: 22rpx; +} +.btn-bar .left .delete-btn::after { + display: none; +} + +.btn-bar .right .normal { + --td-button-default-color: #333333; + --td-button-default-border-color: #dddddd; +} + +.btn-bar .right .primary { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.js b/miniprogram/tcb-shop/pages/goods/comments/create/index.js new file mode 100644 index 0000000..7c3e081 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.js @@ -0,0 +1,114 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { getOrderItem } from '../../../../services/order/orderItem'; +import { getSkuDetail } from '../../../../services/sku/sku'; +import { getCloudImageTempUrl } from '../../../../utils/cloudImageHandler'; +import { createComment } from '../../../../services/comments/comments'; + +Page({ + data: { + sku: null, + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + }, + + failedAndBack(message, e) { + e && console.error(e); + this.toast(message); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + }, + + async onLoad(options) { + const orderItemId = options.orderItemId; + if (typeof orderItemId !== 'string') return this.failedAndBack('获取订单失败'); + + const orderItem = await getOrderItem(orderItemId); + + const comment = orderItem?.comment; + if (comment != null) return this.failedAndBack('已经评价过了'); + + let sku = orderItem?.sku; + if (sku == null) return this.failedAndBack('获取商品信息失败'); + + try { + sku = await getSkuDetail(sku._id); + sku.image = (await getCloudImageTempUrl([sku.image ?? '']))[0]; + sku.spec = sku.attr_value.map((x) => x.value).join(','); + this.setData({ sku, orderItemId }); + } catch (e) { + return this.failedAndBack('获取商品信息失败', e); + } + }, + + onRateChange(e) { + const { value } = e?.detail; + const item = e?.currentTarget?.dataset?.item; + this.setData({ [item]: value }, () => { + this.updateButtonStatus(); + }); + }, + + onAnonymousChange(e) { + const status = !!e?.detail?.checked; + this.setData({ isAnonymous: status }); + }, + + handleSuccess(e) { + const { files } = e.detail; + + this.setData({ + uploadFiles: files, + }); + }, + + handleRemove(e) { + const { index } = e.detail; + const { uploadFiles } = this.data; + uploadFiles.splice(index, 1); + this.setData({ + uploadFiles, + }); + }, + + onTextAreaChange(e) { + const value = e?.detail?.value; + this.textAreaValue = value; + this.updateButtonStatus(); + }, + + updateButtonStatus() { + const { goodRateValue, isAllowedSubmit } = this.data; + const { textAreaValue } = this; + const temp = Boolean(goodRateValue) && Boolean(textAreaValue); + if (temp !== isAllowedSubmit) this.setData({ isAllowedSubmit: temp }); + }, + + async onSubmitBtnClick() { + const { goodRateValue, isAllowedSubmit, orderItemId, sku } = this.data; + const { textAreaValue } = this; + + if (!isAllowedSubmit) return; + + try { + await createComment({ orderItemId, content: textAreaValue, rating: goodRateValue, spuId: sku.spu._id }); + Toast({ + context: this, + selector: '#t-toast', + message: '评价提交成功', + icon: 'check-circle', + }); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + } catch (e) { + return this.failedAndBack('创建评论失败', e); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.json b/miniprogram/tcb-shop/pages/goods/comments/create/index.json new file mode 100644 index 0000000..7c6d3eb --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.json @@ -0,0 +1,13 @@ +{ + "navigationBarTitleText": "评价商品", + "usingComponents": { + "t-image": "/components/webp-image/index", + "t-rate": "tdesign-miniprogram/rate/rate", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-checkbox": "tdesign-miniprogram/checkbox/checkbox", + "t-button": "tdesign-miniprogram/button/button", + "t-upload": "tdesign-miniprogram/upload/upload", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxml new file mode 100644 index 0000000..2bd1cc8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxml @@ -0,0 +1,26 @@ + + + + + + + + {{sku.spu.name}} + {{sku.spec}} + + + + 商品评价 + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxss new file mode 100644 index 0000000..abf20e4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxss @@ -0,0 +1,168 @@ +page { + background-color: #f5f5f5; +} + +.page-container .comment-card { + padding: 24rpx 32rpx 28rpx; + background-color: #ffffff; +} + +.comment-card .goods-info-container .goods-image { + width: 112rpx; + height: 112rpx; + border-radius: 8rpx; +} + +.comment-card .goods-info-container { + display: flex; + align-items: center; +} + +.comment-card .goods-info-container .goods-title-container { + padding-left: 24rpx; +} + +.comment-card .goods-info-container .goods-title { + font-size: 28rpx; + font-weight: normal; +} + +.comment-card .goods-info-container .goods-detail { + font-size: 24rpx; + font-weight: normal; + color: #999999; + margin-top: 16rpx; +} + +.comment-card .rate-container { + display: flex; + align-items: center; + margin-top: 22rpx; +} + +.comment-card .rate-container .rate-title { + font-size: 28rpx; + font-weight: bold; + margin-right: 12rpx; +} + +.comment-card .textarea-container { + margin-top: 22rpx; +} + +.comment-card .textarea-container .textarea { + height: 294rpx; + background-color: #f5f5f5; + border-radius: 16rpx; + font-size: 28rpx; + font-weight: normal; +} + +.page-container .t-checkbox__bordered { + display: none; +} + +.page-container .anonymous-box { + display: flex; + align-items: center; + padding-top: 52rpx; +} + +.page-container .anonymous-box .name { + font-size: 28rpx; + font-weight: normal; + color: #999999; + padding-left: 28rpx; +} + +.page-container .t-checkbox { + padding: 0rpx !important; +} + +.page-container .t-checkbox__content { + display: none; +} + +.comment-card .convey-comment-title { + font-size: 28rpx; + font-weight: bold; +} + +.convey-card { + background-color: #ffffff; + margin-top: 24rpx; + padding: 32rpx; + padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx); +} + +.convey-card .rate-container .rate-title { + font-weight: normal; +} + +.page-container .t-checkbox__icon-left { + margin-right: 0rpx !important; +} + +.submit-button-container { + padding: 12rpx 32rpx; + display: flex; + width: 100vw; + box-sizing: border-box; + justify-content: center; + position: fixed; + bottom: 0; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); + background-color: #ffffff; + z-index: 99; +} + +.submit-button-container .submit-button { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} + +.submit-button-container .submit-button-disabled { + --td-button-default-color: #fff; + --td-button-default-bg-color: #ccc; + --td-button-default-border-color: #ccc; + --td-button-default-active-bg-color: rgba(204, 204, 204, 0.789); +} + +.page-container .upload-container { + margin-top: 24rpx; +} + +.page-container .t-upload__wrapper { + border-radius: 8rpx; + overflow: hidden; +} + +.page-container .submmit-bar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 12; + padding: 12rpx 32rpx; + padding-bottom: env(safe-area-inset-bottom); + background-color: #fff; + height: 112rpx; +} + +.page-container .submmit-bar-button { + border-radius: 48rpx !important; + padding: 0 !important; +} + +.page-container .t-upload__close-btn { + background-color: rgba(0, 0, 0, 0.4); + border-bottom-left-radius: 8rpx; + width: 36rpx; + height: 36rpx; +} + +.upload-container .upload-addcontent-slot { + font-size: 26rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.js b/miniprogram/tcb-shop/pages/goods/comments/index.js new file mode 100644 index 0000000..4171e0a --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.js @@ -0,0 +1,71 @@ +import { getSkuDetail } from '../../../services/sku/sku'; +import { getOrderItem } from '../../../services/order/orderItem'; +import { getCommentsOfSpu } from '../../../services/comments/comments'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; +import dayjs from 'dayjs'; + +const layoutMap = { + 0: 'vertical', +}; +Page({ + data: { + commentList: [], + layoutText: layoutMap[0], + loadMoreStatus: 0, + spuId: null, + }, + + pageNumber: 1, + pageSize: 10, + + async loadComments(refresh = false) { + refresh && (this.pageNumber = 1); + + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.LOADING }); + + const { spuId } = this.data; + const { pageNumber, pageSize } = this; + try { + const { records: newComments, total } = await getCommentsOfSpu({ spuId, pageNumber, pageSize }); + await Promise.all( + newComments.map(async (x) => { + const orderItemId = x.order_item._id; + const { + sku: { _id: skuId }, + } = await getOrderItem(orderItemId); + const sku = await getSkuDetail(skuId); + x.desc = sku.attr_value.map((x) => x.value).join(','); + x.createdTimeString = dayjs(new Date(x.createdAt)).format('YYYY-MM-DD HH:mm:ss'); + x.user = x.createBy.substring(0, 10); + }), + ); + + this.pageNumber++; + const commentList = refresh ? newComments : this.data.commentList.concat(newComments); + if (commentList.length >= total) { + this.setData; + } + this.setData({ + commentList, + loadMoreStatus: commentList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY, + }); + } catch (e) { + console.error(e); + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + + async onLoad(options) { + const spuId = options?.spuId; + if (typeof spuId !== 'string') { + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.FAILED }); + } else { + this.setData({ spuId }); + } + + this.loadComments(true); + }, + onReachBottom() { + if (this.data.loadMoreStatus == LIST_LOADING_STATUS.READY) this.loadComments(false); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.json b/miniprogram/tcb-shop/pages/goods/comments/index.json new file mode 100644 index 0000000..a6418c9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "全部评价", + "usingComponents": { + "t-tag": "tdesign-miniprogram/tag/tag", + "comments-card": "./components/comments-card/index", + "t-load-more": "/components/load-more/index" + } +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/index.wxml new file mode 100644 index 0000000..66a4514 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.wxml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/index.wxss new file mode 100644 index 0000000..b98f36b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.wxss @@ -0,0 +1,49 @@ +/* 层级定义 +@z-index-0: 1; +@z-index-1: 100; +@z-index-2: 200; +@z-index-5: 500; +@z-index-component: 1000; // 通用组件级别 +@z-index-dropdown: @z-index-component; +@z-index-sticky: @z-index-component + 20; +@z-index-fixed: @z-index-component + 30; +@z-index-modal-backdrop:@z-index-component + 40; +@z-index-modal:@z-index-component + 50; +@z-index-popover:@z-index-component + 60; +@z-index-tooltip:@z-index-component + 70; +*/ +/* var() css变量适配*/ +page { + background-color: #FFFFFF; +} + +.comments-header { + display: flex; + flex-wrap: wrap; + padding: 32rpx 32rpx 0rpx; + background-color: #fff; + margin-top: -24rpx; + margin-left: -24rpx; +} + +.comments-header-tag { + margin-top: 24rpx; + margin-left: 24rpx; + height: 56rpx !important; + font-size: 24rpx !important; + justify-content: center; + background-color: #F5F5F5 !important; + border-radius: 8rpx !important; + border: 1px solid #F5F5F5 !important; +} + +.comments-header-active { + background-color: #FFECE9 !important; + color: #FA4126 !important; + border: 1px solid #FA4126 !important; +} + +.no-more { + padding-left: 20rpx; + padding-right: 20rpx; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js new file mode 100644 index 0000000..ae274e2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js @@ -0,0 +1,66 @@ +Component({ + externalClasses: ['wr-sold-out', 'wr-class'], + + options: { multipleSlots: true }, + + properties: { + soldout: { + // 商品是否下架 + type: Boolean, + value: false, + }, + jumpArray: { + type: Array, + value: [], + }, + isStock: { + type: Boolean, + value: true, + }, // 是否有库存 + isSlotButton: { + type: Boolean, + value: false, + }, // 是否开启按钮插槽 + shopCartNum: { + type: Number, // 购物车气泡数量 + }, + buttonType: { + type: Number, + value: 0, + }, + minDiscountPrice: { + type: String, + value: '', + }, + minSalePrice: { + type: String, + value: '', + }, + }, + + data: { + fillPrice: false, + }, + + methods: { + toAddCart() { + const { isStock } = this.properties; + if (!isStock) return; + this.triggerEvent('toAddCart'); + }, + + toBuyNow(e) { + const { isStock } = this.properties; + if (!isStock) return; + this.triggerEvent('toBuyNow', e); + }, + + toNav(e) { + const { url } = e.currentTarget.dataset; + return this.triggerEvent('toNav', { + e, + url, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json new file mode 100644 index 0000000..7464ae6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml new file mode 100644 index 0000000..604afb4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml @@ -0,0 +1,38 @@ + + {{soldout ? '商品已下架' : '商品已售馨'}} + + + + + + + {{shopCartNum > 99 ? '99+' : shopCartNum}} + + + {{item.title}} + + + + + + + 加入购物车 + + + 立即购买 + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss new file mode 100644 index 0000000..44e8ad0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss @@ -0,0 +1,107 @@ +.footer-cont { + background-color: #fff; + padding: 16rpx; +} + +.icon-warp { + width: 110rpx; + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} + +.operate-wrap { + position: relative; +} + +.bottom-operate-left { + width: 100%; +} + +.bottom-operate-left .icon-warp { + width: 50%; +} + +.tag-cart-num { + display: inline-block; + position: absolute; + left: 50rpx; + right: auto; + top: 6rpx; + color: #fff; + line-height: 24rpx; + text-align: center; + z-index: 99; + white-space: nowrap; + min-width: 28rpx; + border-radius: 14rpx; + background-color: #fa550f !important; + font-size: 20rpx; + font-weight: 400; + padding: 2rpx 6rpx; +} + +.operate-text { + color: #666; + font-size: 20rpx; +} + +.soldout { + height: 80rpx; + background: rgba(170, 170, 170, 1); + width: 100%; + color: #fff; +} + +.addCart-disabled, +.bar-addCart-disabled { + background: rgba(221, 221, 221, 1) !important; + color: #fff !important; + font-size: 28rpx; +} + +.buyNow-disabled, +.bar-buyNow-disabled { + background: rgba(198, 198, 198, 1) !important; + color: #fff !important; + font-size: 28rpx; +} + +.bar-separately, +.bar-buy { + width: 254rpx; + height: 80rpx; + color: #fff; + display: flex; + align-items: center; + justify-content: center; +} + +.bar-separately { + background: #ffece9; + color: #fa4126; + border-radius: 40rpx 0 0 40rpx; +} + +.bar-buy { + background-color: #fa4126; + border-radius: 0rpx 40rpx 40rpx 0rpx; +} + +.flex { + display: flex; + display: -webkit-flex; +} + +.flex-center { + justify-content: center; + -webkit-justify-content: center; + align-items: center; + -webkit-align-items: center; +} + +.flex-between { + justify-content: space-between; + -webkit-justify-content: space-between; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js new file mode 100644 index 0000000..e43fac2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js @@ -0,0 +1,303 @@ +const ATTR_VALUE_STATUS = { + PICKED: 'picked', + UNPICKED: 'unpicked', + DISABLED: 'disabled', +}; + +Component({ + options: { + multipleSlots: true, + addGlobalClass: true, + }, + + properties: { + title: String, + show: { + type: Boolean, + value: false, + }, + initProps: { + type: Object, + observer(initProps) { + if (!initProps) { + return; + } + const { skus, spu } = initProps; + + const attrValues = collectAttrValueSet(skus); + const attrList = initAttrList(attrValues); + + this.calculateValue(attrList, skus); + + this.setData({ + skus, + attrList, + spu, + }); + }, + }, + outOperateStatus: { + type: String, + value: 'no', + }, + }, + + data: { + price: 0, + imgSrc: '', + max: 1, + + skus: [], + attrList: [], + spu: null, + pickedSku: null, + value: 1, + }, + + observers: { + 'spu, pickedSku': function (spu, pickedSku) { + const imgSrc = pickedSku?.image ? pickedSku.image : spu.cover_image; + this.setData({ + imgSrc, + }); + }, + 'skus, pickedSku': function (skus, pickedSku) { + let max; + let price; + if (pickedSku != null) { + max = pickedSku.count; + price = pickedSku.price; + } else { + price = skus.reduce((acc, cur) => Math.min(acc, cur.price), Infinity); + max = 1; + } + this.setData({ price, max }); + }, + max: function (max) { + const { value } = this.data; + if (value > max) { + this.setData({ + value: max, + }); + } + }, + 'attrList, skus': function (attrList, skus) { + this.calculateValue(attrList, skus); + const sku = this.pickOnlySku(attrList); + sku && this.triggerEvent('picked'); + this.setData({ pickedSku: sku }); + }, + }, + + methods: { + clickAttrValue({ + target: { + dataset: { attrValueIndex, attrNameIndex }, + }, + }) { + const setAttrListWithCalculation = (attrList) => { + const { skus } = this.data; + this.calculateValue(attrList, skus); + const sku = this.pickOnlySku(attrList); + this.setData({ attrList, pickedSku: sku }); + }; + const { attrList } = this.data; + + const attrName = attrList[attrNameIndex]; + const attrValue = attrName.values[attrValueIndex]; + + switch (attrValue.status) { + case ATTR_VALUE_STATUS.UNPICKED: + // pick it, and set others unpicked + attrName.values.forEach((value) => { + value.status = value === attrValue ? ATTR_VALUE_STATUS.PICKED : ATTR_VALUE_STATUS.UNPICKED; + }); + setAttrListWithCalculation(attrList); + break; + case ATTR_VALUE_STATUS.DISABLED: + // do nothing + break; + case ATTR_VALUE_STATUS.PICKED: + // unpick it + attrValue.status = ATTR_VALUE_STATUS.UNPICKED; + setAttrListWithCalculation(attrList); + break; + default: + // invalid status, skip + return; + } + }, + + /** + * + * @param {[]} attrList + * @param {[]} skus + */ + calculateValue(attrList, skus) { + // Any row of attr values, should be filter out by picked attr values from rest of the rows. + attrList.forEach((attrName, index) => { + const restPickedValues = attrList + .filter((_, i) => i !== index) // exclude current line + .map((x) => x.values.find((y) => y.status === ATTR_VALUE_STATUS.PICKED)) + .filter((x) => x != null); + + const filteredSkus = skus.filter( + (sku) => + sku.count > 0 && + contains({ + container: sku.attrValues, + arr: restPickedValues, + eq: (a, b) => a._id === b._id, + }), + ); + const filteredAttrValues = collectAttrValueSet(filteredSkus); + + attrName.values.forEach((value) => { + if (filteredAttrValues.find((x) => x._id === value._id) == null) { + value.status = ATTR_VALUE_STATUS.DISABLED; + } else { + if (value.status === ATTR_VALUE_STATUS.DISABLED) { + value.status = ATTR_VALUE_STATUS.UNPICKED; + } + } + }); + }); + }, + + pickOnlySku(attrList) { + const pickedAttrValues = attrList + .map((x) => x.values.find((x) => x.status === ATTR_VALUE_STATUS.PICKED)) + .filter((x) => x != null); + if (pickedAttrValues.length !== attrList.length) { + // should pick more + return null; + } + + const { skus } = this.data; + const pickedSku = skus.find( + (sku) => + sku.attrValues.length === pickedAttrValues.length && // a and b have the same size + sku.attrValues.every((x) => pickedAttrValues.find((y) => y._id === x._id) != null), // every item in a can be found in b + ); + if (pickedSku == null) { + console.error( + 'Something went wrong! With enough picked attr values, an sku should be picked! Picked attr values:', + pickedAttrValues, + ); + return null; + } + return pickedSku; + }, + + specsConfirm() { + if (this.data.outOperateStatus === 'cart') { + this.addCart(); + } else if (this.data.outOperateStatus === 'buy') { + this.buyNow(); + } + }, + + handlePopupHide() { + this.triggerEvent('closeSpecsPopup', { + show: false, + }); + }, + + addCart() { + const { pickedSku, max, value } = this.properties; + if (pickedSku == null) { + return; + } + if (value > max) { + return; + } + if (value < 1) { + return; + } + this.triggerEvent('addCart', { pickedSku, count: value }); + }, + + buyNow() { + const { pickedSku, max, value } = this.properties; + if (pickedSku == null) { + return; + } + if (value > max) { + return; + } + if (value < 1) { + return; + } + this.triggerEvent('buyNow', { pickedSku, count: value }); + }, + + handleBuyNumChange({ detail: { value } }) { + this.setData({ value }); + }, + }, + lifetimes: {}, +}); + +/** + * + * @param {Array} attrValues + * @returns {Array} + */ +function initAttrList(attrValues) { + const list = attrValues.reduce((acc, cur) => { + const item = acc.find((x) => x._id === cur.attr_name._id); + if (item != null) { + // already has this attr name, push value to this item + if (item.values.find((x) => x._id === cur._id) != null) { + // already has this attr value, do nothing + } else { + item.values.push({ + value: cur.value, + _id: cur._id, + status: ATTR_VALUE_STATUS.UNPICKED, + }); + } + } else { + // not added attr kind, make a new one + acc.push({ + ...cur.attr_name, + values: [ + { + value: cur.value, + _id: cur._id, + status: ATTR_VALUE_STATUS.UNPICKED, + }, + ], + }); + } + return acc; + }, []); + return list; +} + +/** + * + * @param {{container: Array, arr: Array, eq: (a, b) => boolean}} param0 + * @returns + */ +function contains({ container, arr, eq }) { + return arr.every((itemInArr) => container.findIndex((x) => eq(x, itemInArr)) !== -1); +} + +/** + * + * @param {Array} skus + */ +function collectAttrValueSet(skus) { + const attrValues = skus.reduce((acc, sku) => { + sku.attrValues.forEach((value) => { + if (acc.find((x) => x._id === value._id) != null) { + // exists, skip + } else { + acc.push(Object.assign({}, value)); + } + }); + return acc; + }, []); + return attrValues; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json new file mode 100644 index 0000000..3d351b0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json @@ -0,0 +1,11 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "t-toast": "tdesign-miniprogram/toast/toast", + "price": "/components/price/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml new file mode 100644 index 0000000..7600498 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml @@ -0,0 +1,63 @@ + + + + + + + + + {{title}} + + + + + + 选择: + + + {{selectedItem.value}} + + + + + + + + + {{item.name}} + + + {{valuesItem.value}} + + + + + + + + 购买数量 + ({{limitBuyInfo}}) + + + + + + + 确定 + + + + + 加入购物车 + + + + + 立即购买 + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss new file mode 100644 index 0000000..41d852d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss @@ -0,0 +1,302 @@ +.popup-container { + background-color: #ffffff; + position: relative; + z-index: 100; + border-radius: 16rpx 16rpx 0 0; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); +} + +.popup-container .popup-close { + position: absolute; + right: 30rpx; + top: 30rpx; + z-index: 9; + color: #999999; +} + +.popup-sku-header { + display: flex; + padding: 30rpx 28rpx 0 30rpx; +} + +.popup-sku-header .popup-sku-header__img { + width: 176rpx; + height: 176rpx; + border-radius: 8rpx; + background: #d8d8d8; + margin-right: 24rpx; +} + +.popup-sku-header .popup-sku-header__goods-info { + position: relative; + width: 500rpx; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__goods-name { + font-size: 28rpx; + line-height: 40rpx; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; + overflow: hidden; + width: 430rpx; + text-overflow: ellipsis; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__selected-spec { + display: flex; + color: #333333; + font-size: 26rpx; + line-height: 36rpx; +} + +.popup-sku-header + .popup-sku-header__goods-info + .popup-sku__selected-spec + .popup-sku__selected-item { + margin-right: 10rpx; + margin-left: 10rpx; + display: inline-block; +} + +.popup-sku-body { + margin: 0 30rpx 40rpx; + max-height: 600rpx; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.popup-sku-body .popup-sku-group-container .popup-sku-row { + padding: 32rpx 0; + border-bottom: 1rpx solid #f5f5f5; +} + +.popup-sku-body + .popup-sku-group-container + .popup-sku-row + .popup-sku-row__title { + font-size: 26rpx; + color: #333; +} + +.popup-sku-body .popup-sku-group-container .popup-sku-row .popup-sku-row__item { + font-size: 24rpx; + color: #333; + min-width: 128rpx; + height: 56rpx; + background-color: #f5f5f5; + border-radius: 8rpx; + border: 2rpx solid #f5f5f5; + margin: 19rpx 26rpx 0 0; + padding: 0 16rpx; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.popup-sku-body + .popup-sku-group-container + .popup-sku-row + .popup-sku-row__item.popup-sku-row__item--active { + border: 2rpx solid #fa4126; + color: #fa4126; + background: rgba(255, 95, 21, 0.04); +} + +.popup-sku-body + .popup-sku-group-container + .popup-sku-row + .disabled-sku-selected { + background: #f5f5f5 !important; + color: #cccccc; +} + +.popup-sku-body .popup-sku-stepper-stock .popup-sku-stepper-container { + display: flex; + align-items: center; + justify-content: space-between; + margin: 40rpx 0; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-sku__stepper-title { + display: flex; + font-size: 26rpx; + color: #333; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-sku__stepper-title + .limit-text { + margin-left: 10rpx; + color: #999999; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper { + display: flex; + flex-flow: row nowrap; + align-items: center; + font-size: 28px; + height: 48rpx; + line-height: 62rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-btn, +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-num-wrap { + position: relative; + height: 100%; + text-align: center; + background-color: #f5f5f5; + border-radius: 4rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-num-wrap { + color: #282828; + display: flex; + max-width: 76rpx; + align-items: center; + justify-content: space-between; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-num-wrap + .input-num { + height: 100%; + width: auto; + font-weight: 600; + font-size: 30rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-btn { + width: 48rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__minus { + margin-right: 4rpx; + border-radius: 4rpx; + color: #9a979b; + display: flex; + align-items: center; + justify-content: center; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__plus { + margin-left: 4rpx; + border-radius: 4rpx; + color: #9a979b; + display: flex; + align-items: center; + justify-content: center; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__plus::after { + width: 24rpx; + height: 3rpx; + background-color: #999999; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__plus::before { + width: 3rpx; + height: 24rpx; + background-color: #999999; +} + +.popup-sku-actions { + font-size: 32rpx; + height: 80rpx; + text-align: center; + line-height: 80rpx; + padding: 0 20rpx; +} + +.popup-sku-actions .sku-operate { + height: 80rpx; + width: 50%; + color: #fff; + border-radius: 48rpx; +} + +.popup-sku-actions .sku-operate .sku-operate-addCart { + background-color: #ffece9; + color: #fa4126; + border-radius: 48rpx 0 0 48rpx; +} + +.popup-sku-actions .sku-operate .sku-operate-addCart.disabled { + background: rgb(221, 221, 221); + color: #fff; +} + +.popup-sku-actions .sku-operate .sku-operate-buyNow { + background-color: #fa4126; + border-radius: 0 48rpx 48rpx 0; +} + +.popup-sku-actions .sku-operate .sku-operate-buyNow.disabled { + color: #fff; + background: rgb(198, 198, 198); +} + +.popup-sku-actions .sku-operate .selected-sku-btn { + width: 100%; +} + +.popup-container .single-confirm-btn { + border-radius: 48rpx; + color: #ffffff; + margin: 0 32rpx; + font-size: 32rpx; + height: 80rpx; + text-align: center; + line-height: 88rpx; + background-color: #fa4126; +} + +.popup-container .single-confirm-btn.disabled { + font-size: 32rpx; + color: #fff; + background-color: #dddddd; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js new file mode 100644 index 0000000..9743cc1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js @@ -0,0 +1,35 @@ +Component({ + options: { + multipleSlots: true, + }, + + properties: { + list: Array, + title: { + type: String, + value: '促销说明', + }, + show: { + type: Boolean, + }, + }, + + // data: { + // list: [], + // }, + + methods: { + change(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('promotionChange', { + index, + }); + }, + + closePromotionPopup() { + this.triggerEvent('closePromotionPopup', { + show: false, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json new file mode 100644 index 0000000..a9de77d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml new file mode 100644 index 0000000..c1cea9d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml @@ -0,0 +1,34 @@ + + + + + + + {{title}} + + + + + {{item.tag}} + + {{item.label ? item.label : ''}} + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss new file mode 100644 index 0000000..6e0e167 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss @@ -0,0 +1,131 @@ +.promotion-popup-container { + background-color: #ffffff; + position: relative; + z-index: 100; + border-radius: 16rpx 16rpx 0 0; +} + +.promotion-popup-container .promotion-popup-close { + position: absolute; + right: 30rpx; + top: 30rpx; + z-index: 9; + color: rgba(153, 153, 153, 1); +} + +.promotion-popup-container .promotion-popup-close .market { + font-size: 25rpx; + color: #999; +} + +.promotion-popup-container .promotion-popup-title { + height: 100rpx; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.promotion-popup-container .promotion-popup-title { + font-size: 32rpx; + color: #222427; + font-weight: 600; +} + +.promotion-popup-container .promotion-popup-content { + min-height: 400rpx; + max-height: 600rpx; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.promotion-popup-container .promotion-popup-content .promotion-detail-list { + margin: 0 30rpx; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item:last-child { + margin-bottom: env(safe-area-inset-bottom); + border-bottom: 0; + padding-bottom: calc(28rpx + env(safe-area-inset-bottom)); +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item { + display: flex; + justify-content: space-between; + padding: 10rpx 0 28rpx; + position: relative; + font-size: 24rpx; + color: #222427; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .tag { + box-sizing: border-box; + font-size: 20rpx; + line-height: 32rpx; + padding: 2rpx 12rpx; + background-color: #ffece9; + margin-right: 16rpx; + display: inline-flex; + color: #fa4126; + border-radius: 54rpx; + flex-shrink: 0; + position: relative; + top: 2rpx; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .content { + font-size: 28rpx; + color: #222427; + flex: 1; + line-height: 40rpx; + display: flex; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .content + .list-content { + width: 440rpx; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .collect-btn { + font-size: 24rpx; + flex-shrink: 0; + margin-left: 20rpx; + display: flex; + align-items: center; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .collect-btn + .linkText { + margin-right: 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/index.js b/miniprogram/tcb-shop/pages/goods/details/index.js new file mode 100644 index 0000000..1172eb5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.js @@ -0,0 +1,421 @@ +// 第三方库 +import Toast from 'tdesign-miniprogram/toast/index'; + +// 服务层 +import { getAllAttrValues } from '../../../services/attrValue/attrValue'; +import { handleSpuCloudImage, getSpu } from '../../../services/good/spu'; +import { fetchCartItems, createCartItem, updateCartItemCount } from '../../../services/cart/cart'; +import { getGoodsDetailCommentInfo } from '../../../services/comments/comments'; +import { getAllSku } from '../../../services/sku/sku'; + +// 工具函数 +import { cdnBase } from '../../../config/index'; +import { cartShouldFresh } from '../../../utils/cartFresh'; +import { getCloudImageTempUrl, getSingleCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { objectToParamString } from '../../../utils/util'; + +const imgPrefix = `${cdnBase}/`; + +const recLeftImg = `${imgPrefix}common/rec-left.png`; +const recRightImg = `${imgPrefix}common/rec-right.png`; + +async function replaceCloudImageWithTempUrl(text) { + let ret = text; + + // 使用正则表达式匹配所有被双引号包裹的链接 + const regex = /"(cloud:\/\/[^"]*)"/g; + let match; + // 使用一个循环来处理所有匹配的链接 + while ((match = regex.exec(ret)) !== null) { + const originalLink = match[0]; + const pureLink = match[1]; + // 处理链接 + const processedLink = await getSingleCloudImageTempUrl(pureLink); + // 替换文本中的原始链接 + ret = ret.replace(originalLink, `"${processedLink}"`); + } + + return ret; +} + +const OUT_OPERATE_STATUS = { + CART: 'cart', + BUY: 'buy', + NO: 'no', +}; + +Page({ + data: { + commentsList: [], + commentsStatistics: { + badCount: 0, + commentCount: 0, + goodCount: 0, + goodRate: 0, + hasImageCount: 0, + middleCount: 0, + }, + isShowPromotionPop: false, + activityList: [], + recLeftImg, + recRightImg, + details: {}, + jumpArray: [ + { + title: '首页', + url: '/pages/home/home', + iconName: 'home', + }, + { + title: '购物车', + url: '/pages/cart/index', + iconName: 'cart', + showCartNum: true, + }, + ], + isStock: true, + cartNum: 0, + soldout: false, + buttonType: 1, + buyNum: 1, + selectedAttrStr: '', + skuArray: [], + primaryImage: '', + specImg: '', + isSpuSelectPopupShow: false, + isAllSelectedSku: false, + buyType: 0, + outOperateStatus: OUT_OPERATE_STATUS.NO, // 是否外层加入购物车 + operateType: 0, + selectSkuSellsPrice: 0, + maxLinePrice: 0, + minSalePrice: 0, + maxSalePrice: 0, + list: [], + spuId: '', + navigation: { type: 'fraction' }, + current: 0, + autoplay: true, + duration: 500, + interval: 5000, + soldNum: 0, // 已售数量, + loading: false, + }, + + setLoading() { + this.setData({ loading: true }); + }, + unsetLoading() { + this.setData({ loading: false }); + }, + + handlePopupHide() { + this.setData({ + isSpuSelectPopupShow: false, + }); + }, + + onSpecSelectTap() { + this.showSkuSelectPopup(OUT_OPERATE_STATUS.NO); + }, + + showSkuSelectPopup(status) { + this.setData({ + outOperateStatus: status, + isSpuSelectPopupShow: true, + }); + }, + + buyItNow() { + this.showSkuSelectPopup(OUT_OPERATE_STATUS.BUY); + }, + + toAddCart() { + this.showSkuSelectPopup(OUT_OPERATE_STATUS.CART); + }, + + toNav(e) { + const { url } = e.detail; + wx.switchTab({ + url: url, + }); + }, + + showCurImg(e) { + const { index } = e.detail; + const { images } = this.data.details; + wx.previewImage({ + current: images[index], + urls: images, // 需要预览的图片http链接列表 + }); + }, + + onPageScroll({ scrollTop }) { + const goodsTab = this.selectComponent('#goodsTab'); + goodsTab && goodsTab.onScroll(scrollTop); + }, + + chooseSpecItem(e) { + const { specList } = this.data.details; + const { selectedSku, isAllSelectedSku } = e.detail; + if (!isAllSelectedSku) { + this.setData({ + selectSkuSellsPrice: 0, + }); + } + this.setData({ + isAllSelectedSku, + }); + this.getSkuItem(specList, selectedSku); + }, + + getSkuItem(specList, selectedSku) { + const { skuArray, primaryImage } = this.data; + const selectedSkuValues = this.getSelectedSkuValues(specList, selectedSku); + let selectedAttrStr = ` 件 `; + selectedSkuValues.forEach((item) => { + selectedAttrStr += `,${item.specValue} `; + }); + // eslint-disable-next-line array-callback-return + const skuItem = skuArray.filter((item) => { + let status = true; + (item.specInfo || []).forEach((subItem) => { + if (!selectedSku[subItem.specId] || selectedSku[subItem.specId] !== subItem.specValueId) { + status = false; + } + }); + if (status) return item; + }); + this.selectSpecsName(selectedSkuValues.length > 0 ? selectedAttrStr : ''); + if (skuItem) { + this.setData({ + selectItem: skuItem, + selectSkuSellsPrice: skuItem.price || 0, + }); + } else { + this.setData({ + selectItem: null, + selectSkuSellsPrice: 0, + }); + } + this.setData({ + specImg: skuItem && skuItem.skuImage ? skuItem.skuImage : primaryImage, + }); + }, + + // 获取已选择的sku名称 + getSelectedSkuValues(skuTree, selectedSku) { + const normalizedTree = this.normalizeSkuTree(skuTree); + return Object.keys(selectedSku).reduce((selectedValues, skuKeyStr) => { + const skuValues = normalizedTree[skuKeyStr]; + const skuValueId = selectedSku[skuKeyStr]; + if (skuValueId !== '') { + const skuValue = skuValues.filter((value) => { + return value.specValueId === skuValueId; + })[0]; + skuValue && selectedValues.push(skuValue); + } + return selectedValues; + }, []); + }, + + normalizeSkuTree(skuTree) { + const normalizedTree = {}; + skuTree.forEach((treeItem) => { + normalizedTree[treeItem.specId] = treeItem.specValueList; + }); + return normalizedTree; + }, + + selectSpecsName(selectSpecsName) { + if (selectSpecsName) { + this.setData({ + selectedAttrStr: selectSpecsName, + }); + } else { + this.setData({ + selectedAttrStr: '', + }); + } + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + icon: '', + duration: 1000, + }); + }, + + async addCart({ detail: { count, pickedSku } }) { + const overCount = () => this.toast('超过购买上限了'); + const addCartSucceed = () => { + cartShouldFresh(); + this.handlePopupHide(); + this.toast('加入购物车成功'); + }; + const addCartFail = () => this.toast('加入购物车失败'); + + const records = await fetchCartItems(); + + const cartItem = records.find((x) => x.sku._id === pickedSku._id); + // eslint-disable-next-line eqeqeq + if (cartItem == null) { + // cart item empty, create + if (count <= pickedSku.count) { + await createCartItem({ skuId: pickedSku._id, count }).then(addCartSucceed, addCartFail); + } else { + overCount(); + } + } else { + // cart item exists, update count + const finalCount = cartItem.count + count; + if (finalCount <= pickedSku.count) { + await updateCartItemCount({ cartItemId: cartItem._id, count: finalCount }).then(addCartSucceed, addCartFail); + } else { + overCount(); + } + } + }, + + onPicked() { + this.setData({ isAllSelectedSku: true }); + }, + + gotoBuy(e) { + const overCount = () => this.toast('超过购买上限了'); + const buyCount = e.detail.count; + const skuId = e.detail.pickedSku._id; + const skuCount = e.detail.pickedSku.count; + if (buyCount > skuCount) return overCount(); + + wx.navigateTo({ + url: `/pages/order/order-confirm/index?${objectToParamString({ type: 'direct', count: buyCount, skuId })}`, + }); + }, + + specsConfirm() { + const { buyType } = this.data; + if (buyType === 1) { + this.gotoBuy(); + } else { + this.addCart(); + } + }, + + changeNum(e) { + this.setData({ + buyNum: e.detail.buyNum, + }); + }, + + closePromotionPopup() { + this.setData({ + isShowPromotionPop: false, + }); + }, + + promotionChange(e) { + const { index } = e.detail; + wx.navigateTo({ + url: `/pages/promotion-detail/index?promotion_id=${index}`, + }); + }, + + showPromotionPopup() { + this.setData({ + isShowPromotionPop: true, + }); + }, + + async getInfo(spuId) { + // pics + // min price + // spu title + // attrs => sku => count + price + // spu detail + // comment + const spu = await getSpu(spuId); + + const loadSkus = async () => { + const skus = await getAllSku(spuId); + const minPrice = skus.reduce((acc, current) => Math.min(acc, current.price), Infinity) * 100; + const loadSkuAttrValues = async () => { + return Promise.all( + skus.map(async (sku) => { + const attrValues = await getAllAttrValues(sku._id); + sku.attrValues = attrValues; + }), + ); + }; + const handleSkuImages = async () => { + const images = skus.map((s) => s.image ?? ''); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (skus[index].image = image)); + }; + await Promise.all([handleSkuImages(), loadSkuAttrValues()]); + return { skus, minPrice }; + }; + + const [_x, { skus, minPrice }, commentInfo] = await Promise.all([ + handleSpuCloudImage(spu), + loadSkus(), + getGoodsDetailCommentInfo(spu._id), + ]); + + const [ + { + data: { records, total: commentCount }, + }, + { + data: { total: goodCommentCount }, + }, + ] = commentInfo; + + records.forEach((x) => (x.userNameString = x.createBy.substring(0, 10))); + + const detail = await replaceCloudImageWithTempUrl(spu.detail); + + this.setData({ + details: { + images: spu.swiper_images, + title: spu.name, + }, + minSalePrice: minPrice, + detail, + descPopUpInitProps: { + skus, + minPrice, + spu, + }, + commentsStatistics: { + commentCount, + goodRate: (goodCommentCount / commentCount) * 100, + }, + spu, + commentsList: records, + }); + }, + + /** 跳转到评价列表 */ + navToCommentsListPage() { + wx.navigateTo({ + url: `/pages/goods/comments/index?spuId=${this.data.spu._id}`, + }); + }, + + async onLoad(query) { + const { spuId } = query; + this.setLoading(); + try { + await this.getInfo(spuId); + } catch (e) { + console.error(e); + this.toast('获取商品详情失败'); + } finally { + this.unsetLoading(); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/details/index.json b/miniprogram/tcb-shop/pages/goods/details/index.json new file mode 100644 index 0000000..f8cab8b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.json @@ -0,0 +1,19 @@ +{ + "navigationBarTitleText": "商品详情", + "usingComponents": { + "t-image": "/components/webp-image/index", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-rate": "tdesign-miniprogram/rate/rate", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav", + "t-button": "tdesign-miniprogram/button/button", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "price": "/components/price/index", + "buy-bar": "./components/buy-bar/index", + "promotion-popup": "./components/promotion-popup/index", + "goods-specs-popup": "./components/goods-specs-popup/index", + "loading-dialog": "../../../components/loading-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/index.wxml b/miniprogram/tcb-shop/pages/goods/details/index.wxml new file mode 100644 index 0000000..3e438db --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.wxml @@ -0,0 +1,71 @@ + + + + + + + + + + + + {{details.title}} + + + + 已选 + + + {{selectedAttrStr ? buyNum : ''}}{{selectedAttrStr || '请选择'}} + + + + + + + + 商品评价 + ({{ commentsStatistics.commentCount }}) + + + {{commentsStatistics.goodRate}}% 好评 + + + + + + + + {{commentItem.userNameString}} + + + + {{commentItem.content}} + + + + + + + 详情介绍 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/index.wxss b/miniprogram/tcb-shop/pages/goods/details/index.wxss new file mode 100644 index 0000000..7459f9e --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.wxss @@ -0,0 +1,344 @@ +@import '../../../style/global.wxss'; + +page { + width: 100vw; +} + +.goods-detail-page .goods-info { + margin: 0 auto; + padding: 26rpx 0 28rpx 30rpx; + background-color: #fff; +} + +.goods-detail-page .swipe-img { + width: 100%; + height: 750rpx; +} + +.goods-detail-page .goods-info .goods-price { + display: flex; + align-items: baseline; +} + +.goods-detail-page .goods-info .goods-price-up { + color: #fa4126; + font-size: 28rpx; + position: relative; + bottom: 4rpx; + left: 8rpx; +} + +.goods-detail-page .goods-info .goods-price .class-goods-price { + font-size: 64rpx; + color: #fa4126; + font-weight: bold; + font-family: DIN Alternate; +} + +.goods-detail-page .goods-info .goods-price .class-goods-symbol { + font-size: 36rpx; + color: #fa4126; +} + +.goods-detail-page .goods-info .goods-price .class-goods-del { + position: relative; + font-weight: normal; + left: 16rpx; + bottom: 2rpx; + color: #999999; + font-size: 32rpx; +} + +.goods-detail-page .goods-info .goods-number { + display: flex; + align-items: center; + justify-content: space-between; +} + +.goods-detail-page .goods-info .goods-number .sold-num { + font-size: 24rpx; + color: #999999; + display: flex; + align-items: flex-end; + margin-right: 32rpx; +} + +.goods-detail-page .goods-info .goods-activity { + display: flex; + margin-top: 16rpx; + justify-content: space-between; +} + +.goods-detail-page .goods-info .goods-activity .tags-container { + display: flex; +} + +.goods-detail-page .goods-info .goods-activity .tags-container .goods-activity-tag { + background: #ffece9; + color: #fa4126; + font-size: 24rpx; + margin-right: 16rpx; + padding: 4rpx 8rpx; + border-radius: 4rpx; +} + +.goods-detail-page .goods-info .goods-activity .activity-show { + display: flex; + justify-content: center; + align-items: center; + color: #fa4126; + font-size: 24rpx; + padding-right: 32rpx; +} + +.goods-detail-page .goods-info .goods-activity .activity-show-text { + line-height: 42rpx; +} + +.goods-detail-page .goods-info .goods-title { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 20rpx; +} + +.goods-detail-page .goods-info .goods-title .goods-name { + width: 600rpx; + font-weight: 500; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + font-size: 32rpx; + word-break: break-all; + color: #333333; +} + +.goods-detail-page .goods-info .goods-title .goods-tag { + width: 104rpx; + margin-left: 26rpx; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .shareBtn { + border-radius: 200rpx 0px 0px 200rpx; + width: 100rpx; + height: 96rpx; + border: none; + padding-right: 36rpx !important; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .shareBtn::after { + border: none; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .btn-icon { + font-size: 20rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 96rpx; + color: #999; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .btn-icon .share-text { + padding-top: 8rpx; + font-size: 20rpx; + line-height: 24rpx; +} + +.goods-detail-page .goods-info .goods-intro { + font-size: 26rpx; + color: #888; + line-height: 36rpx; + word-break: break-all; + padding-right: 30rpx; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; + overflow: hidden; +} + +.spu-select { + height: 80rpx; + background-color: #fff; + margin-top: 20rpx; + display: flex; + align-items: center; + padding: 30rpx; + font-size: 28rpx; +} + +.spu-select .label { + margin-right: 30rpx; + text-align: center; + flex-shrink: 0; + color: #999999; + font-weight: normal; +} + +.spu-select .content { + display: flex; + flex: 1; + justify-content: space-between; + align-items: center; +} + +.spu-select .content .tintColor { + color: #aaa; +} + +.goods-detail-page .desc-content { + margin-top: 20rpx; + background-color: #fff; + /* padding-bottom: 120rpx; */ + padding-bottom: 220rpx; +} + +.goods-detail-page .desc-content__title { + font-size: 28rpx; + line-height: 36rpx; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + padding: 30rpx 20rpx; +} + +.goods-detail-page .desc-content__title .img { + width: 206rpx; + height: 10rpx; +} + +.goods-detail-page .desc-content__title--text { + font-size: 26rpx; + margin: 0 32rpx; + font-weight: 600; +} + +.goods-detail-page .desc-content__img { + width: 100%; + height: auto; +} + +.goods-bottom-operation { + position: fixed; + left: 0; + bottom: 0; + width: 100%; + background-color: #fff; + padding-bottom: env(safe-area-inset-bottom); +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price { + display: flex; + align-items: baseline; + color: #fa4126; + margin-top: 48rpx; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-num { + font-size: 64rpx; + color: #fa4126; + font-weight: bold; + font-family: DIN Alternate; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-del { + position: relative; + font-weight: normal; + left: 12rpx; + bottom: 2rpx; + color: #999999; + font-size: 32rpx; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-symbol { + font-size: 36rpx; + color: #fa4126; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-max-num { + font-size: 48rpx; +} + +.goods-detail-page .goods-head { + --td-swiper-radius: 0; +} + +.t-toast__content { + z-index: 12000 !important; +} + +.comments-wrap { + margin-top: 20rpx; + padding: 32rpx; + background-color: #fff; +} + +.comments-wrap .comments-head { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.comments-wrap .comments-head .comments-title-wrap { + display: flex; +} + +.comments-title-label, +.comments-title-count { + color: #333333; + font-size: 32rpx; + font-weight: 500; + line-height: 48rpx; +} + +.comments-rate-wrap { + display: flex; + justify-content: center; + align-items: center; + font-size: 24rpx; +} + +.comments-rate-wrap .comments-good-rate { + color: #999999; + font-size: 26rpx; + font-weight: 400; + font-style: normal; + line-height: 36rpx; +} + +.comment-item-wrap .comment-item-head { + display: flex; + flex-direction: row; + align-items: center; + margin-top: 32rpx; +} + +.comment-item-wrap .comment-item-head .comment-item-avatar { + width: 64rpx; + height: 64rpx; + border-radius: 64rpx; +} + +.comment-item-wrap .comment-item-head .comment-head-right { + margin-left: 24rpx; +} + +.comment-head-right .comment-username { + font-size: 26rpx; + color: #333333; + line-height: 36rpx; + font-weight: 400; +} + +.comment-item-wrap .comment-item-content { + margin-top: 20rpx; + color: #333333; + line-height: 40rpx; + font-size: 28rpx; + font-weight: 400; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/list/index.js b/miniprogram/tcb-shop/pages/goods/list/index.js new file mode 100644 index 0000000..d9700a9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.js @@ -0,0 +1,208 @@ +/* eslint-disable no-param-reassign */ +import { getPrice } from '../../../services/good/spu'; +import { getAllSpuOfCate } from '../../../services/cate/cate'; +import { getCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; +import Toast from 'tdesign-miniprogram/toast/index'; + +const initFilters = { + overall: 1, + sorts: '', + layout: 0, +}; + +Page({ + data: { + goodsList: [], + layout: 0, + sorts: '', + overall: 1, + show: false, + minVal: '', + maxVal: '', + filter: initFilters, + hasLoaded: false, + loadMoreStatus: 0, + loading: true, + goodsListLoadStatus: LIST_LOADING_STATUS.READY, + }, + + pageNum: 1, + pageSize: 30, + total: 0, + + handleFilterChange(e) { + const { layout, overall, sorts } = e.detail; + this.pageNum = 1; + this.setData({ + layout, + sorts, + overall, + loadMoreStatus: 0, + }); + this.init(true); + }, + + generalQueryData(reset = false) { + const { filter, keywords, minVal, maxVal } = this.data; + const { pageNum, pageSize } = this; + const { sorts, overall } = filter; + const params = { + sort: 0, // 0 综合,1 价格 + pageNum: 1, + pageSize: 30, + keyword: keywords, + }; + + if (sorts) { + params.sort = 1; + params.sortType = sorts === 'desc' ? 1 : 0; + } + + if (overall) { + params.sort = 0; + } else { + params.sort = 1; + } + params.minPrice = minVal ? minVal * 100 : 0; + params.maxPrice = maxVal ? maxVal * 100 : undefined; + if (reset) return params; + return { + ...params, + pageNum: pageNum + 1, + pageSize, + }; + }, + + async init(reset = true) { + this.loadGoodsList(reset); + }, + + async loadGoodsList() { + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.LOADING }); + + try { + const { spu: goodsList } = await getAllSpuOfCate(this.cateId); + goodsList.sort((a, b) => (b?.priority ?? 0) - (a?.priority ?? 0)); + const images = goodsList.map((x) => x.cover_image); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (goodsList[index].cover_image = image)); + await Promise.all(goodsList.map(async (spu) => (spu.price = await getPrice(spu._id)))); + + this.setData({ + goodsList, + // TODO: add pagination + goodsListLoadStatus: LIST_LOADING_STATUS.NO_MORE, + }); + } catch (err) { + console.error('error', err); + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + cateId: null, + + onLoad(query) { + const { cateId } = query; + if (typeof cateId !== 'string') return; + + this.cateId = cateId; + this.init(true); + }, + + onReachBottom() { + const { goodsList } = this.data; + const { total = 0 } = this; + if (goodsList.length === total) { + this.setData({ + loadMoreStatus: 2, + }); + return; + } + this.init(false); + }, + + handleAddCart() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加购', + }); + }, + + tagClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击标签', + }); + }, + + gotoGoodsDetail(e) { + const spuId = e?.detail?.goods?._id; + if (typeof spuId !== 'string') return; + + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${spuId}`, + }); + }, + + showFilterPopup() { + this.setData({ + show: true, + }); + }, + + showFilterPopupClose() { + this.setData({ + show: false, + }); + }, + + onMinValAction(e) { + const { value } = e.detail; + this.setData({ minVal: value }); + }, + + onMaxValAction(e) { + const { value } = e.detail; + this.setData({ maxVal: value }); + }, + + reset() { + this.setData({ minVal: '', maxVal: '' }); + }, + + confirm() { + const { minVal, maxVal } = this.data; + let message = ''; + if (minVal && !maxVal) { + message = `价格最小是${minVal}`; + } else if (!minVal && maxVal) { + message = `价格范围是0-${minVal}`; + } else if (minVal && maxVal && minVal <= maxVal) { + message = `价格范围${minVal}-${this.data.maxVal}`; + } else { + message = '请输入正确范围'; + } + if (message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + } + this.pageNum = 1; + this.setData( + { + show: false, + minVal: '', + goodsList: [], + loadMoreStatus: 0, + maxVal: '', + }, + () => { + this.init(); + }, + ); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/list/index.json b/miniprogram/tcb-shop/pages/goods/list/index.json new file mode 100644 index 0000000..0811511 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "商品列表", + "usingComponents": { + "t-input": "tdesign-miniprogram/input/input", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-toast": "tdesign-miniprogram/toast/toast", + "goods-list": "/components/goods-list/index", + "filter": "/components/filter/index", + "filter-popup": "/components/filter-popup/index", + "load-more": "/components/load-more/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/list/index.wxml b/miniprogram/tcb-shop/pages/goods/list/index.wxml new file mode 100644 index 0000000..32c4b1f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.wxml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/list/index.wxss b/miniprogram/tcb-shop/pages/goods/list/index.wxss new file mode 100644 index 0000000..5073012 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.wxss @@ -0,0 +1,99 @@ +page { + background-color: #fff; +} + +.goods-list-container { + display: block; +} + +.goods-list-container .t-search { + padding: 0 30rpx; + background-color: #fff; +} + +.goods-list-container .t-class__input-container { + height: 64rpx !important; + border-radius: 32rpx !important; +} + +.goods-list-container .t-search__left-icon { + display: flex; + align-items: center; +} + +.goods-list-container .t-search__input { + font-size: 28rpx !important; + color: rgb(116, 116, 116) !important; +} + +.goods-list-container .category-goods-list { + background-color: #f2f2f2; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + padding: 20rpx 24rpx; + -webkit-overflow-scrolling: touch; +} + +.goods-list-container .wr-goods-list { + background: #f2f2f2 !important; +} + +.goods-list-container .t-image__mask { + display: flex !important; +} + +.goods-list-container .empty-wrap { + margin-top: 184rpx; + margin-bottom: 120rpx; + height: 300rpx; +} + +.goods-list-container .empty-wrap .empty-tips .empty-content .content-text { + margin-top: 40rpx; +} + +.goods-list-container .price-container { + padding: 32rpx; + height: 100vh; + max-width: 632rpx; + background-color: #fff; + border-radius: 30rpx 0 0 30rpx; + box-sizing: border-box; +} + +.goods-list-container .price-between { + font-size: 26rpx; + font-weight: 500; + color: rgba(51, 51, 51, 1); +} + +.goods-list-container .price-ipts-wrap { + width: 100%; + display: flex; + align-items: center; + justify-content: space-around; + margin-top: 24rpx; + + --td-input-bg-color: rgba(245, 245, 245, 1); + --td-input-vertical-padding: 4rpx; + --td-input-border-color: transparent; +} + +.goods-list-container .price-ipts-wrap .price-divided { + width: 16rpx; + margin: 0 24rpx; + color: #333333; +} + +.goods-list-container .price-ipts-wrap .t-input__wrapper { + margin: 0 !important; +} + +.goods-list-container .price-ipts-wrap .t-input__content, +.goods-list-container .price-ipts-wrap .t-input__placeholder { + font-size: 24rpx !important; +} + +.goods-list-container .price-ipts-wrap .price-ipt { + border-radius: 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/result/index.js b/miniprogram/tcb-shop/pages/goods/result/index.js new file mode 100644 index 0000000..bca4c21 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.js @@ -0,0 +1,242 @@ +/* eslint-disable no-param-reassign */ +import Toast from 'tdesign-miniprogram/toast/index'; +import { listGood, getPrice } from '../../../services/good/spu'; +import { getCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; + +const initFilters = { + overall: 1, + sorts: '', +}; + +Page({ + data: { + goodsList: [], + sorts: '', + overall: 1, + show: false, + minVal: '', + maxVal: '', + minSalePriceFocus: false, + maxSalePriceFocus: false, + filter: initFilters, + hasLoaded: false, + keywords: '', + loadMoreStatus: 0, + loading: true, + }, + + total: 0, + pageNum: 1, + pageSize: 30, + + onLoad(options) { + const { searchValue = '' } = options || {}; + this.setData( + { + keywords: searchValue, + }, + () => { + this.init(true); + }, + ); + }, + + generalQueryData(reset = false) { + const { filter, keywords, minVal, maxVal } = this.data; + const { pageNum, pageSize } = this; + const { sorts, overall } = filter; + const params = { + sort: 0, // 0 综合,1 价格 + pageNum: 1, + pageSize: 30, + keyword: keywords, + }; + + if (sorts) { + params.sort = 1; + params.sortType = sorts === 'desc' ? 1 : 0; + } + if (overall) { + params.sort = 0; + } else { + params.sort = 1; + } + params.minPrice = minVal ? minVal * 100 : 0; + params.maxPrice = maxVal ? maxVal * 100 : undefined; + if (reset) return params; + return { + ...params, + pageNum: pageNum + 1, + pageSize, + }; + }, + + async init(reset = true) { + return this.loadGoodsList(reset); + }, + + async loadGoodsList(fresh = false) { + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.LOADING }); + + const pageSize = this.pageSize; + const pageIndex = fresh ? 1 : this.pageNum; + + try { + const { records: nextList, total } = await listGood({ + pageNumber: pageIndex, + pageSize, + search: this.data.keywords, + }); + const images = nextList.map((x) => x.cover_image); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (nextList[index].cover_image = image)); + await Promise.all(nextList.map(async (spu) => (spu.price = await getPrice(spu._id)))); + + const goodsList = fresh ? nextList : this.data.goodsList.concat(nextList); + + this.setData({ + goodsList, + loadMoreStatus: goodsList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY, + hasLoaded: true, + }); + + this.pageNum = pageIndex + 1; + } catch (err) { + console.log('error', err); + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + + handleCartTap() { + wx.switchTab({ + url: '/pages/cart/index', + }); + }, + + handleSubmit(e) { + const keywords = e?.detail?.value ?? ''; + this.setData( + { + keywords, + goodsList: [], + loadMoreStatus: 0, + hasLoaded: false, + }, + () => { + this.init(true); + }, + ); + }, + + onReachBottom() { + const { goodsList } = this.data; + const { total = 0 } = this; + if (goodsList.length === total) { + this.setData({ + loadMoreStatus: 2, + }); + return; + } + this.init(false); + }, + + handleAddCart() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加购', + }); + }, + + gotoGoodsDetail(e) { + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${e.detail.goods._id}`, + }); + }, + + handleFilterChange(e) { + const { overall, sorts } = e.detail; + const { total } = this; + const _filter = { + sorts, + overall, + }; + this.setData({ + filter: _filter, + sorts, + overall, + }); + + this.pageNum = 1; + this.setData( + { + goodsList: [], + loadMoreStatus: 0, + }, + () => { + total && this.init(true); + }, + ); + }, + + showFilterPopup() { + this.setData({ + show: true, + }); + }, + + showFilterPopupClose() { + this.setData({ + show: false, + }); + }, + + onMinValAction(e) { + const { value } = e.detail; + this.setData({ minVal: value }); + }, + + onMaxValAction(e) { + const { value } = e.detail; + this.setData({ maxVal: value }); + }, + + reset() { + this.setData({ minVal: '', maxVal: '' }); + }, + + confirm() { + const { minVal, maxVal } = this.data; + let message = ''; + if (minVal && !maxVal) { + message = `价格最小是${minVal}`; + } else if (!minVal && maxVal) { + message = `价格范围是0-${minVal}`; + } else if (minVal && maxVal && minVal <= maxVal) { + message = `价格范围${minVal}-${this.data.maxVal}`; + } else { + message = '请输入正确范围'; + } + if (message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + } + this.pageNum = 1; + this.setData( + { + show: false, + minVal: '', + goodsList: [], + loadMoreStatus: 0, + maxVal: '', + }, + () => { + this.init(); + }, + ); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/result/index.json b/miniprogram/tcb-shop/pages/goods/result/index.json new file mode 100644 index 0000000..130b48d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.json @@ -0,0 +1,15 @@ +{ + "navigationBarTitleText": "搜索", + "usingComponents": { + "t-search": "tdesign-miniprogram/search/search", + "t-input": "tdesign-miniprogram/input/input", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-toast": "tdesign-miniprogram/toast/toast", + "goods-list": "/components/goods-list/index", + "filter": "/components/filter/index", + "filter-popup": "/components/filter-popup/index", + "load-more": "/components/load-more/index", + "t-icon": "tdesign-miniprogram/icon/icon" + }, + "onReachBottomDistance": 50 +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/result/index.wxml b/miniprogram/tcb-shop/pages/goods/result/index.wxml new file mode 100644 index 0000000..723ef03 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.wxml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/result/index.wxss b/miniprogram/tcb-shop/pages/goods/result/index.wxss new file mode 100644 index 0000000..5fee10e --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.wxss @@ -0,0 +1,114 @@ +page { + background-color: #fff; +} + +page view { + box-sizing: border-box; +} + +.result-container { + display: block; +} + +.result-container .t-search { + padding: 0 30rpx; + background-color: #fff; +} + +.result-container .t-class__input-container { + height: 64rpx !important; + border-radius: 32rpx !important; +} + +.result-container .t-search__left-icon { + display: flex; + align-items: center; +} + +.result-container .t-search__input { + font-size: 28rpx !important; + color: #333 !important; +} + +.result-container .category-goods-list { + background-color: #f2f2f2; + overflow-y: scroll; + padding: 20rpx 24rpx; + -webkit-overflow-scrolling: touch; +} + +.result-container .wr-goods-list { + background: #f2f2f2 !important; +} + +.result-container .t-image__mask { + display: flex !important; +} + +.result-container .empty-wrap { + margin-top: 184rpx; + margin-bottom: 120rpx; + height: 300rpx; +} + +.result-container .empty-wrap .empty-tips .empty-content .content-text { + margin-top: 40rpx; +} + +.result-container .price-container { + padding: 32rpx; + height: 100vh; + max-width: 632rpx; + background-color: #fff; + border-radius: 30rpx 0 0 30rpx; +} + +.result-container .price-between { + font-size: 26rpx; + font-weight: 500; + color: rgba(51, 51, 51, 1); +} + +.result-container .price-ipts-wrap { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-around; + margin-top: 24rpx; +} + +.result-container .price-ipts-wrap .price-divided { + position: relative; + width: 22rpx; + margin: 0 20rpx; + color: #222427; +} + +.result-container .price-ipts-wrap .price-ipt { + box-sizing: border-box; + width: 246rpx; + font-size: 24rpx; + height: 56rpx; + padding: 0 24rpx; + text-align: center; + border-radius: 8rpx; + color: #333; + background: rgba(245, 245, 245, 1); +} + +.t-class-input { + font-size: 24rpx !important; +} + +.t-search__clear { + font-size: 40rpx !important; +} + +.result-container .price-ipts-wrap .price-ipt::after { + border: none !important; +} + +.result-container .t-input__control { + font-size: 24rpx !important; + text-align: center; +} diff --git a/miniprogram/tcb-shop/pages/goods/search/index.js b/miniprogram/tcb-shop/pages/goods/search/index.js new file mode 100644 index 0000000..1adf407 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.js @@ -0,0 +1,116 @@ +import { getSearchHistory, getSearchPopular } from '../../../services/good/fetchSearchHistory'; + +Page({ + data: { + historyWords: [], + popularWords: [], + searchValue: '', + dialog: { + title: '确认删除当前历史记录', + showCancelButton: true, + message: '', + }, + dialogShow: false, + }, + + deleteType: 0, + deleteIndex: '', + + onShow() { + this.queryHistory(); + this.queryPopular(); + }, + + async queryHistory() { + try { + const data = await getSearchHistory(); + const code = 'Success'; + if (String(code).toUpperCase() === 'SUCCESS') { + const { historyWords = [] } = data; + this.setData({ + historyWords, + }); + } + } catch (error) { + console.error(error); + } + }, + + async queryPopular() { + try { + const data = await getSearchPopular(); + const code = 'Success'; + if (String(code).toUpperCase() === 'SUCCESS') { + const { popularWords = [] } = data; + this.setData({ + popularWords, + }); + } + } catch (error) { + console.error(error); + } + }, + + confirm() { + const { historyWords } = this.data; + const { deleteType, deleteIndex } = this; + historyWords.splice(deleteIndex, 1); + if (deleteType === 0) { + this.setData({ + historyWords, + dialogShow: false, + }); + } else { + this.setData({ historyWords: [], dialogShow: false }); + } + }, + + close() { + this.setData({ dialogShow: false }); + }, + + handleClearHistory() { + const { dialog } = this.data; + this.deleteType = 1; + this.setData({ + dialog: { + ...dialog, + message: '确认删除所有历史记录', + }, + dialogShow: true, + }); + }, + + deleteCurr(e) { + const { index } = e.currentTarget.dataset; + const { dialog } = this.data; + this.deleteIndex = index; + this.setData({ + dialog: { + ...dialog, + message: '确认删除当前历史记录', + deleteType: 0, + }, + dialogShow: true, + }); + }, + + handleHistoryTap(e) { + const { historyWords } = this.data; + const { dataset } = e.currentTarget; + const _searchValue = historyWords[dataset.index || 0] || ''; + if (_searchValue) { + wx.navigateTo({ + url: `/pages/goods/result/index?searchValue=${_searchValue}`, + }); + } + }, + + handleSubmit(e) { + const value = e?.detail?.value; + if (!value) return; + wx.navigateTo({ + url: `/pages/goods/result/index?searchValue=${value}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/search/index.json b/miniprogram/tcb-shop/pages/goods/search/index.json new file mode 100644 index 0000000..74abdf7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "搜索", + "usingComponents": { + "t-search": "tdesign-miniprogram/search/search", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + } +} diff --git a/miniprogram/tcb-shop/pages/goods/search/index.wxml b/miniprogram/tcb-shop/pages/goods/search/index.wxml new file mode 100644 index 0000000..59dd11f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.wxml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/search/index.wxss b/miniprogram/tcb-shop/pages/goods/search/index.wxss new file mode 100644 index 0000000..676603e --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.wxss @@ -0,0 +1,79 @@ +.search-page { + box-sizing: border-box; + width: 100vw; + height: 100vh; + padding: 0 30rpx; +} + +.search-page .t-class__input-container { + height: 64rpx !important; + border-radius: 32rpx !important; +} + +.search-page .t-search__input { + font-size: 28rpx !important; + color: #333 !important; +} + +.search-page .search-wrap { + margin-top: 44rpx; +} + +.search-page .history-wrap { + margin-bottom: 20px; +} + +.search-page .search-header { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; +} + +.search-page .search-title { + font-size: 30rpx; + font-family: PingFangSC-Semibold, PingFang SC; + font-weight: 600; + color: rgba(51, 51, 51, 1); + line-height: 42rpx; +} + +.search-page .search-clear { + font-size: 24rpx; + font-family: PingFang SC; + line-height: 32rpx; + color: #999999; + font-weight: normal; +} + +.search-page .search-content { + overflow: hidden; + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + align-items: flex-start; + margin-top: 24rpx; +} + +.search-page .search-item { + color: #333333; + font-size: 24rpx; + line-height: 32rpx; + font-weight: normal; + margin-right: 24rpx; + margin-bottom: 24rpx; + background: #f5f5f5; + border-radius: 38rpx; + padding: 12rpx 24rpx; +} + +.search-page .hover-history-item { + position: relative; + top: 3rpx; + left: 3rpx; + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1) inset; +} + +.add-notes__confirm { + color: #fa4126 !important; +} diff --git a/miniprogram/tcb-shop/pages/home/home.js b/miniprogram/tcb-shop/pages/home/home.js new file mode 100644 index 0000000..f5412dd --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.js @@ -0,0 +1,135 @@ +/* eslint-disable no-param-reassign */ +import { getHomeSwiper } from '../../services/home/home'; +import { listGood, getPrice } from '../../services/good/spu'; +import { getCloudImageTempUrl } from '../../utils/cloudImageHandler'; +import { LIST_LOADING_STATUS } from '../../utils/listLoading'; + +Page({ + data: { + imgSrcs: [], + tabList: [], + goodsList: [], + goodsListLoadStatus: LIST_LOADING_STATUS.READY, + pageLoading: false, + current: 1, + autoplay: true, + duration: '500', + interval: 5000, + navigation: { type: 'dots' }, + swiperImageProps: { mode: 'scaleToFill' }, + }, + + goodListPagination: { + index: 1, + num: 20, + }, + + privateData: { + tabIndex: 0, + }, + + onShow() { + this.getTabBar().init(); + }, + + onLoad() { + this.init(); + }, + + onReachBottom() { + if (this.data.goodsListLoadStatus === LIST_LOADING_STATUS.READY) { + this.loadGoodsList(); + } + }, + + onPullDownRefresh() { + this.init(); + }, + + async init() { + wx.stopPullDownRefresh(); + + this.setData({ + pageLoading: false, + }); + + this.loadGoodsList(true); + this.loadHomeSwiper(); + }, + + async loadHomeSwiper() { + const { images } = await getHomeSwiper(); + const imgSrcs = ( + await wx.cloud.getTempFileURL({ + fileList: images, + }) + ).fileList.map((x) => x.tempFileURL); + + this.setData({ imgSrcs }); + }, + + onReTry() { + this.loadGoodsList(); + }, + + async loadGoodsList(fresh = false) { + if (fresh) { + wx.pageScrollTo({ + scrollTop: 0, + }); + } + + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.LOADING }); + + const pageSize = this.goodListPagination.num; + const pageIndex = fresh ? 1 : this.goodListPagination.num; + + try { + const { records: nextList, total } = await listGood({ pageNumber: pageIndex, pageSize }); + const images = nextList.map((x) => x.cover_image); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (nextList[index].cover_image = image)); + await Promise.all(nextList.map(async (spu) => (spu.price = await getPrice(spu._id)))); + + const goodsList = fresh ? nextList : this.data.goodsList.concat(nextList); + + this.setData({ + goodsList, + goodsListLoadStatus: goodsList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY, + }); + + this.goodListPagination.index = pageIndex + 1; + this.goodListPagination.num = pageSize; + } catch (err) { + console.error('error', err); + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + + goodListClickHandle(e) { + const spuId = e?.detail?.goods?._id; + if (typeof spuId !== 'string') return; + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${spuId}`, + }); + }, + + goodListAddCartHandle(e) { + const spuId = e?.detail?.goods?._id; + if (typeof spuId !== 'string') return; + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${spuId}`, + }); + }, + + navToSearchPage() { + wx.navigateTo({ url: '/pages/goods/search/index' }); + }, + + navToActivityDetail({ detail }) { + const { index: promotionID = 0 } = detail || {}; + wx.navigateTo({ + url: `/pages/promotion-detail/index?promotion_id=${promotionID}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/home/home.json b/miniprogram/tcb-shop/pages/home/home.json new file mode 100644 index 0000000..8df2da4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.json @@ -0,0 +1,20 @@ +{ + "navigationBarTitleText": "首页", + "onReachBottomDistance": 10, + "backgroundTextStyle": "light", + "enablePullDownRefresh": true, + "usingComponents": { + "t-search": "tdesign-miniprogram/search/search", + "t-loading": "tdesign-miniprogram/loading/loading", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", + "goods-list": "/components/goods-list/index", + "load-more": "/components/load-more/index", + "cloud-template-dialog": "/components/cloud-template-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/home/home.wxml b/miniprogram/tcb-shop/pages/home/home.wxml new file mode 100644 index 0000000..b2bff61 --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.wxml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/home/home.wxss b/miniprogram/tcb-shop/pages/home/home.wxss new file mode 100644 index 0000000..e0e7d6f --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.wxss @@ -0,0 +1,107 @@ +page { + box-sizing: border-box; + padding-bottom: calc(env(safe-area-inset-bottom) + 96rpx); +} + +.t-tabs.t-tabs--top .t-tabs__scroll { + border-bottom: none !important; +} + +.home-page-header { + background: linear-gradient(#fff, #f5f5f5); +} + +.home-page-container { + background: #f5f5f5; +} + +.home-page-container, +.home-page-header { + display: block; + padding: 0 24rpx; +} + +.home-page-header .t-search__input-container { + border-radius: 32rpx !important; + height: 64rpx !important; +} + +.home-page-header .t-search__input { + font-size: 28rpx !important; + color: rgb(116, 116, 116) !important; +} + +.home-page-header .swiper-wrap { + margin-top: 20rpx; +} + +.home-page-header .t-image__swiper { + width: 100%; + height: 300rpx; + border-radius: 10rpx; +} + +.home-page-container .t-tabs { + background: #f5f5f5 !important; +} + +.home-page-container .t-tabs .t-tabs-nav { + background-color: transparent; + line-height: 80rpx; + font-size: 28rpx; + color: #333; +} + +.home-page-container .t-tabs .t-tabs-scroll { + border: none !important; +} + +/* 半个字 */ +.home-page-container .tab.order-nav .order-nav-item.scroll-width { + min-width: 165rpx; +} +.home-page-container .tab .order-nav-item.active { + color: #fa550f !important; +} + +.home-page-container .tab .bottom-line { + border-radius: 4rpx; +} + +.home-page-container .tab .order-nav-item.active .bottom-line { + background-color: #fa550f !important; +} + +.home-page-container .tabs-external__item { + /* color: #666 !important; */ + font-size: 28rpx; +} + +.home-page-container .tabs-external__active { + color: #333333 !important; + font-size: 32rpx; +} + +.home-page-container .tabs-external__track { + /* background-color: #fa4126 !important; */ + height: 6rpx !important; + border-radius: 4rpx !important; + width: 48rpx !important; +} + +.t-tabs.t-tabs--top .t-tabs__item, +.t-tabs.t-tabs--bottom .t-tabs__item { + height: 86rpx !important; +} + +.home-page-container .goods-list-container { + background: #f5f5f5 !important; + margin-top: 16rpx; +} + +.home-page-tabs { + --td-tab-nav-bg-color: transparent; + --td-tab-border-color: transparent; + --td-tab-item-color: #666; + --td-tab-track-color: red; +} diff --git a/miniprogram/tcb-shop/pages/home/readme b/miniprogram/tcb-shop/pages/home/readme new file mode 100644 index 0000000..fcf75d5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/readme @@ -0,0 +1,8 @@ +首页功能设定 +1. loading入场 +2. 下拉刷新 +3. 搜索栏 +4. 分类切换 +5. 商品列表 +6. 规格弹层 +7. 加载更多 \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/api.js b/miniprogram/tcb-shop/pages/order/after-service-detail/api.js new file mode 100644 index 0000000..22d62fe --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/api.js @@ -0,0 +1,34 @@ +import { resp } from '../after-service-list/api'; +import dayjs from 'dayjs'; +import { mockIp, mockReqId } from '../../../utils/mock'; + +export const formatTime = (date, template) => dayjs(date).format(template); + +export function getRightsDetail({ rightsNo }) { + const _resq = { + data: {}, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + _resq.data = + resp.data.dataList.filter((item) => item.rights.rightsNo === rightsNo) || + {}; + return Promise.resolve(_resq); +} + +export function cancelRights() { + const _resq = { + data: {}, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + return Promise.resolve(_resq); +} diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.js b/miniprogram/tcb-shop/pages/order/after-service-detail/index.js new file mode 100644 index 0000000..d90dba4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.js @@ -0,0 +1,205 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { ServiceType, ServiceTypeDesc, ServiceStatus } from '../config'; +import { formatTime, getRightsDetail } from './api'; + +const TitleConfig = { + [ServiceType.ORDER_CANCEL]: '退款详情', + [ServiceType.ONLY_REFUND]: '退款详情', + [ServiceType.RETURN_GOODS]: '退货退款详情', +}; + +Page({ + data: { + pageLoading: true, + serviceRaw: {}, + service: {}, + deliveryButton: {}, + gallery: { + current: 0, + show: false, + proofs: [], + }, + showProofs: false, + backRefresh: false, + }, + + onLoad(query) { + this.rightsNo = query.rightsNo; + this.inputDialog = this.selectComponent('#input-dialog'); + this.init(); + }, + + onShow() { + // 当从其他页面返回,并且 backRefresh 被置为 true 时,刷新数据 + if (!this.data.backRefresh) return; + this.init(); + this.setData({ backRefresh: false }); + }, + + // 页面刷新,展示下拉刷新 + onPullDownRefresh_(e) { + const { callback } = e.detail; + return this.getService().then(() => callback && callback()); + }, + + init() { + this.setData({ pageLoading: true }); + this.getService().then(() => { + this.setData({ pageLoading: false }); + }); + }, + + getService() { + const params = { rightsNo: this.rightsNo }; + return getRightsDetail(params).then((res) => { + const serviceRaw = res.data[0]; + // 滤掉填写运单号、修改运单号按钮,这两个按钮特殊处理,不在底部按钮栏展示 + if (!serviceRaw.buttonVOs) serviceRaw.buttonVOs = []; + const deliveryButton = {}; + const service = { + id: serviceRaw.rights.rightsNo, + serviceNo: serviceRaw.rights.rightsNo, + storeName: serviceRaw.rights.storeName, + type: serviceRaw.rights.rightsType, + typeDesc: ServiceTypeDesc[serviceRaw.rights.rightsType], + status: serviceRaw.rights.rightsStatus, + statusIcon: this.genStatusIcon(serviceRaw.rights), + statusName: serviceRaw.rights.userRightsStatusName, + statusDesc: serviceRaw.rights.userRightsStatusDesc, + amount: serviceRaw.rights.refundRequestAmount, + goodsList: (serviceRaw.rightsItem || []).map((item, i) => ({ + id: i, + thumb: item.goodsPictureUrl, + title: item.goodsName, + specs: (item.specInfo || []).map((s) => s.specValues || ''), + itemRefundAmount: item.itemRefundAmount, + rightsQuantity: item.rightsQuantity, + })), + orderNo: serviceRaw.rights.orderNo, // 订单编号 + rightsNo: serviceRaw.rights.rightsNo, // 售后服务单号 + rightsReasonDesc: serviceRaw.rights.rightsReasonDesc, // 申请售后原因 + isRefunded: serviceRaw.rights.userRightsStatus === ServiceStatus.REFUNDED, // 是否已退款 + refundMethodList: (serviceRaw.refundMethodList || []).map((m) => ({ + name: m.refundMethodName, + amount: m.refundMethodAmount, + })), // 退款明细 + refundRequestAmount: serviceRaw.rights.refundRequestAmount, // 申请退款金额 + payTraceNo: serviceRaw.rightsRefund.traceNo, // 交易流水号 + createTime: formatTime(parseFloat(`${serviceRaw.rights.createTime}`), 'YYYY-MM-DD HH:mm'), // 申请时间 + logisticsNo: serviceRaw.logisticsVO.logisticsNo, // 退货物流单号 + logisticsCompanyName: serviceRaw.logisticsVO.logisticsCompanyName, // 退货物流公司 + logisticsCompanyCode: serviceRaw.logisticsVO.logisticsCompanyCode, // 退货物流公司 + remark: serviceRaw.logisticsVO.remark, // 退货备注 + receiverName: serviceRaw.logisticsVO.receiverName, // 收货人 + receiverPhone: serviceRaw.logisticsVO.receiverPhone, // 收货人电话 + receiverAddress: this.composeAddress(serviceRaw), // 收货人地址 + applyRemark: serviceRaw.rightsRefund.refundDesc, // 申请退款时的填写的说明 + buttons: serviceRaw.buttonVOs || [], + logistics: serviceRaw.logisticsVO, + }; + const proofs = serviceRaw.rights.rightsImageUrls || []; + this.setData({ + serviceRaw, + service, + deliveryButton, + 'gallery.proofs': proofs, + showProofs: + serviceRaw.rights.userRightsStatus === ServiceStatus.PENDING_VERIFY && + (service.applyRemark || proofs.length > 0), + }); + wx.setNavigationBarTitle({ + title: TitleConfig[service.type], + }); + }); + }, + + composeAddress(service) { + return [ + service.logisticsVO.receiverProvince, + service.logisticsVO.receiverCity, + service.logisticsVO.receiverCountry, + service.logisticsVO.receiverArea, + service.logisticsVO.receiverAddress, + ] + .filter((item) => !!item) + .join(' '); + }, + + onRefresh() { + this.init(); + }, + + editLogistices() { + this.setData({ + inputDialogVisible: true, + }); + this.inputDialog.setData({ + cancelBtn: '取消', + confirmBtn: '确定', + }); + this.inputDialog._onConfirm = () => { + Toast({ + message: '确定填写物流单号', + }); + }; + }, + + onProofTap(e) { + if (this.data.gallery.show) { + this.setData({ + 'gallery.show': false, + }); + return; + } + const { index } = e.currentTarget.dataset; + this.setData({ + 'gallery.show': true, + 'gallery.current': index, + }); + }, + + onGoodsCardTap(e) { + const { index } = e.currentTarget.dataset; + const goods = this.data.serviceRaw.rightsItem[index]; + wx.navigateTo({ url: `/pages/goods/details/index?skuId=${goods.skuId}` }); + }, + + onServiceNoCopy() { + wx.setClipboardData({ + data: this.data.service.serviceNo, + }); + }, + + onAddressCopy() { + wx.setClipboardData({ + data: `${this.data.service.receiverName} ${this.data.service.receiverPhone}\n${this.data.service.receiverAddress}`, + }); + }, + + /** 获取状态ICON */ + genStatusIcon(item) { + const { userRightsStatus, afterSaleRequireType } = item; + switch (userRightsStatus) { + // 退款成功 + case ServiceStatus.REFUNDED: { + return 'succeed'; + } + // 已取消、已关闭 + case ServiceStatus.CLOSED: { + return 'indent_close'; + } + default: { + switch (afterSaleRequireType) { + case 'REFUND_MONEY': { + return 'goods_refund'; + } + case 'REFUND_GOODS_MONEY': + return 'goods_return'; + default: { + return 'goods_return'; + } + } + } + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.json b/miniprogram/tcb-shop/pages/order/after-service-detail/index.json new file mode 100644 index 0000000..28e72ee --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.json @@ -0,0 +1,21 @@ +{ + "navigationBarTitleText": "", + "usingComponents": { + "wr-loading-content": "/components/loading-content/index", + "wr-price": "/components/price/index", + "wr-service-goods-card": "../components/order-goods-card/index", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "t-grid": "tdesign-miniprogram/grid/grid", + "t-grid-item": "tdesign-miniprogram/grid-item/grid-item", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-input": "tdesign-miniprogram/input/input", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav", + "wr-after-service-button-bar": "../components/after-service-button-bar/index", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml new file mode 100644 index 0000000..9ad5f79 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml @@ -0,0 +1,197 @@ + + + + + + + + + + {{service.statusName}} + + {{service.statusDesc}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 复制 + + + {{service.receiverAddress}} + 收货人:{{service.receiverName}} + 收货人手机:{{service.receiverName}} + + + + + + + + + + x {{goods.rightsQuantity}} + + + + + + + + + 复制 + + + + + + + + + + + + + + + + + + + + + + + 物流单号 + + {{amountTip}} + + + diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss new file mode 100644 index 0000000..8431507 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss @@ -0,0 +1,435 @@ +:host { + background-color: #f5f5f5; +} + +.service-detail { + position: relative; +} + +.service-detail wr-service-goods-card .wr-goods-card__body { + margin-left: 50rpx; +} + +.order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 20rpx; + left: 190rpx; +} + +.order-goods-card-footer-num { + color: #999; + line-height: 40rpx; +} + +.service-detail .order-goods-card-footer .order-goods-card-footer-price-class { + font-size: 36rpx; + color: #333; + font-family: DIN Alternate; +} + +.service-detail .order-goods-card-footer .order-goods-card-footer-price-decimal { + font-size: 28rpx; + color: #333; + font-family: DIN Alternate; +} + +.service-detail .order-goods-card-footer .order-goods-card-footer-price-symbol { + color: #333; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.service-detail .service-detail__header { + padding: 60rpx 0 48rpx 40rpx; + box-sizing: border-box; + height: 220rpx; + background-color: #fff; +} +.service-detail .service-detail__header .title, +.service-detail .service-detail__header .desc { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.service-detail .service-detail__header .title { + -webkit-line-clamp: 1; + font-size: 48rpx; + font-weight: bold; + color: #333; + display: flex; +} + +.service-detail .service-detail__header .desc { + -webkit-line-clamp: 2; + margin-top: 10rpx; + font-size: 28rpx; + color: #999; +} + +.service-detail .service-detail__header .desc .count-down { + color: #fff185; + display: inline; +} + +.service-detail .service-section { + margin: 20rpx 0 20rpx 0; + /* padding: 30rpx 32rpx; */ + width: auto; + border-radius: 8rpx; + background-color: white; + overflow: hidden; +} +.service-section__pay { + margin: 0 0 20rpx 0; + width: auto; + border-radius: 8rpx; + background-color: white; + overflow: hidden; +} +.service-detail .service-section__title { + color: #333333; + margin-bottom: 10rpx; + padding-bottom: 18rpx; + height: 224rpx; + position: relative; +} +.service-detail .service-section__title .icon { + margin-right: 16rpx; + font-size: 40rpx !important; +} +.service-detail .service-section__title .right { + flex: none; + font-weight: normal; + font-size: 26rpx; +} +.service-detail .section-content { + margin: 16rpx 0 0 52rpx; +} + +.service-detail .main { + font-size: 28rpx; + color: #222427; + font-weight: bold; +} + +.service-detail .main .phone-num { + margin-left: 16rpx; + display: inline; +} +.service-detail .label { + color: #999999; + font-size: 26rpx; +} + +.service-detail .custom-remark { + font-size: 26rpx; + line-height: 36rpx; + color: #333333; + word-wrap: break-word; +} +.service-detail .proofs { + margin-top: 20rpx; +} + +.service-detail .proofs .proof { + width: 100%; + height: 100%; + background-color: #f9f9f9; +} + +.service-detail .pay-result .t-cell-title, +.service-detail .pay-result .t-cell-value { + color: #666666; + font-size: 28rpx; +} + +.t-class-wrapper { + padding: 10rpx 24rpx !important; +} + +.t-class-wrapper-first-child { + padding: 24rpx !important; +} + +.service-detail .pay-result .wr-cell__value { + font-weight: bold; +} +.service-detail .right { + font-size: 36rpx; + color: #fa550f; + font-weight: bold; +} + +.service-detail .title { + font-weight: bold; +} + +.service-detail .pay-result .service-section__title .right.integer { + font-size: 48rpx; +} +.service-detail .pay-result .split-line { + position: relative; +} + +.service-detail .pay-result .split-line::after { + position: absolute; + display: block; + content: ' '; + height: 1px; + left: -50%; + right: -50%; + transform: scale(0.5); + background-color: #e6e6e6; +} + +.service-detail .pay-result .section-content { + margin-left: 0; +} + +.service-detail .pay-result .section-content .label { + color: #999999; + font-size: 24rpx; +} + +.service-detail .pay-result .wr-cell::after { + left: 0; +} + +.service-detail .footer-bar-wrapper { + height: 100rpx; +} + +.service-detail .footer-bar-wrapper .footer-bar { + position: fixed; + left: 0; + bottom: 0; + height: 100rpx; + width: 100vw; + box-sizing: border-box; + padding: 0 20rpx; + background-color: white; + display: flex; + justify-content: space-between; + align-items: center; +} + +.service-detail .text-btn { + display: inline; + box-sizing: border-box; + color: #333; + border: 2rpx solid #ddd; + border-radius: 32rpx; + margin-left: 10rpx; + padding: 0 16rpx; + font-weight: normal; + font-size: 24rpx; + line-height: 32rpx; +} +.service-detail .text-btn--active { + opacity: 0.5; +} + +.service-detail .specs-popup .bottom-btn { + color: #fa550f; +} +.service-detail .specs-popup .bottom-btn::after { + color: #fa550f; +} + +.dialog .dialog__button-confirm { + color: #fa550f; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__bottom .price { + top: 100rpx; + left: 10rpx; + position: absolute; + color: #333; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__num { + top: 100rpx; + right: 0; + position: absolute; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__bottom .price::before { + display: inline; + content: '退款金额:'; + margin-right: 1em; + font-size: 24rpx; + color: #333333; + font-weight: normal; +} + +.page-container .wr-goods-card__specs { + margin: 14rpx 20rpx 0 0; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__title { + margin-right: 0; + -webkit-line-clamp: 1; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + width: 80%; +} + +.page-container .order-card .header .store-name { + -webkit-line-clamp: 1; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + width: 80%; +} + +.page-container .status-desc { + box-sizing: border-box; + padding: 22rpx 20rpx; + font-size: 26rpx; + line-height: 1.3; + text-align: left; + color: #333333; + background-color: #f5f5f5; + border-radius: 8rpx; + word-wrap: break-word; + margin-top: 40rpx; + margin-bottom: 20rpx; +} + +.page-container .header__right { + font-size: 24rpx; + color: #333333; + display: flex; + align-items: center; +} + +.page-container .header__right__icon { + color: #d05b27; + font-size: 16px !important; + margin-right: 10rpx; +} + +.page-container .wr-goods-card__thumb { + width: 140rpx; +} +.page-container .page-background { + position: absolute; + z-index: -1; + top: 0; + left: 0; + width: 100vw; + color: #fff; + overflow: hidden; +} +.page-container .page-background-img { + width: 100%; + height: 320rpx !important; +} +.page-container .navbar-bg .nav-back, +.page-container .navbar-bg .page-background { + background: linear-gradient(to right, rgba(250, 85, 15, 1) 0%, rgba(250, 85, 15, 0.6) 100%) !important; +} + +.page-container .navigation-bar__btn { + font-size: 40rpx !important; + font-weight: bold !important; + color: #333; +} + +.t-class-title { + color: #000; +} + +.refresh-bar { + background: linear-gradient(90deg, #ff6b44 0%, #ed3427 100%) !important; +} + +.page-container .navigation-bar__inner .navigation-bar__left { + padding-left: 16rpx; +} + +.t-refund-info { + font-size: 26rpx; + color: #666; +} + +.t-refund-grid-image { + width: 212rpx !important; + height: 212rpx !important; +} + +.t-refund-info-img { + width: 100%; + height: 100%; +} + +.t-refund-wrapper { + padding-top: 18rpx !important; + padding-bottom: 18rpx !important; +} + +.t-refund-title { + font-size: 28rpx; + color: #333; + font-weight: bold; +} + +.t-refund-note { + font-size: 26rpx; + color: #333 !important; +} + +.service-detail .logistics { + padding-top: 0; + padding-bottom: 0; + padding-right: 0; +} + +.service-section__title__header { + display: flex; + align-items: center; + color: #333; + font-weight: normal; + font-size: 32rpx; +} + +.safe-bottom { + padding-bottom: env(safe-area-inset-bottom); +} + +.service-section-logistics { + display: flex; + justify-content: center; + color: #fa4126; + align-items: center; + margin-top: 24rpx; +} + +.t-class-indicator { + color: #b9b9b9 !important; +} + +.service-detail .goods-refund-address { + padding-top: 0; + padding-bottom: 0; +} + +.service-detail .goods-refund-address .goods-refund-address-copy-btn { + position: absolute; + top: 22rpx; + right: 32rpx; +} + +.service-detail .service-goods-card-wrap { + padding: 0 32rpx; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/api.js b/miniprogram/tcb-shop/pages/order/after-service-list/api.js new file mode 100644 index 0000000..5350229 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/api.js @@ -0,0 +1,32 @@ +/* eslint-disable */ +import { mockIp, mockReqId } from '../../../utils/mock'; + +export const resp = { + data: { + pageNum: 1, + pageSize: 10, + totalCount: 51, + states: { + audit: 1, + approved: 6, + complete: 2, + closed: 1, + }, + dataList: [], + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, +}; + +export function getRightsList({ parameter: { afterServiceStatus, pageNum } }) { + const _resq = JSON.parse(JSON.stringify(resp)); + if (pageNum > 3) _resq.data.dataList = []; + if (afterServiceStatus > -1) { + _resq.data.dataList = _resq.data.dataList.filter((item) => item.rights.rightsStatus === afterServiceStatus); + } + return Promise.resolve(_resq); +} diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.js b/miniprogram/tcb-shop/pages/order/after-service-list/index.js new file mode 100644 index 0000000..7cd0cd5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.js @@ -0,0 +1,220 @@ +import { getRightsList } from './api'; +import { AfterServiceStatus, ServiceType, ServiceTypeDesc } from '../config'; + +Page({ + page: { + size: 10, + num: 1, + }, + + data: { + tabs: [ + { + key: -1, + text: '全部', + }, + { + key: AfterServiceStatus.TO_AUDIT, + text: '待审核', + }, + { + key: AfterServiceStatus.THE_APPROVED, + text: '已审核', + }, + { + key: AfterServiceStatus.COMPLETE, + text: '已完成', + }, + { + key: AfterServiceStatus.CLOSED, + text: '已关闭', + }, + ], + curTab: -1, + dataList: [], + listLoading: 0, // 0-未加载,1-加载中,2-已全部加载 + pullDownRefreshing: false, // 下拉刷新时不显示load-more + emptyImg: + 'https://cdn-we-retail.ym.tencent.com/miniapp/order/empty-order-list.png', + backRefresh: false, + }, + + onLoad(query) { + let status = parseInt(query.status); + status = this.data.tabs.map((t) => t.key).includes(status) ? status : -1; + this.init(status); + this.pullDownRefresh = this.selectComponent('#wr-pull-down-refresh'); + }, + + onShow() { + // 当从其他页面返回,并且 backRefresh 被置为 true 时,刷新数据 + if (!this.data.backRefresh) return; + this.onRefresh(); + this.setData({ + backRefresh: false, + }); + }, + + onReachBottom() { + if (this.data.listLoading === 0) { + this.getAfterServiceList(this.data.curTab); + } + }, + + onPageScroll(e) { + this.pullDownRefresh && this.pullDownRefresh.onPageScroll(e); + }, + + onPullDownRefresh_(e) { + const { callback } = e.detail; + this.setData({ + pullDownRefreshing: true, + }); // 下拉刷新时不显示load-more + this.refreshList(this.data.curTab) + .then(() => { + this.setData({ + pullDownRefreshing: false, + }); + callback && callback(); + }) + .catch((err) => { + this.setData({ + pullDownRefreshing: false, + }); + Promise.reject(err); + }); + }, + + init(status) { + status = status !== undefined ? status : this.data.curTab; + this.refreshList(status); + }, + + getAfterServiceList(statusCode = -1, reset = false) { + const params = { + parameter: { + pageSize: this.page.size, + pageNum: this.page.num, + }, + }; + if (statusCode !== -1) params.parameter.afterServiceStatus = statusCode; + this.setData({ + listLoading: 1, + }); + return getRightsList(params) + .then((res) => { + this.page.num++; + let dataList = []; + let { tabs } = this.data; + if (res && res.data && res.data.states) { + tabs = this.data.tabs.map((item) => { + switch (item.key) { + case AfterServiceStatus.TO_AUDIT: + item.info = res.data.states.audit; + break; + case AfterServiceStatus.THE_APPROVED: + item.info = res.data.states.approved; + break; + case AfterServiceStatus.COMPLETE: + item.info = res.data.states.complete; + break; + case AfterServiceStatus.CLOSED: + item.info = res.data.states.closed; + break; + } + return item; + }); + } + if (res && res.data && res.data.dataList) { + dataList = (res.data.dataList || []).map((_data) => { + return { + id: _data.rights.rightsNo, + serviceNo: _data.rights.rightsNo, + storeName: _data.rights.storeName, + type: _data.rights.rightsType, + typeDesc: ServiceTypeDesc[_data.rights.rightsType], + typeDescIcon: + _data.rightsType === ServiceType.ONLY_REFUND + ? 'money-circle' + : 'return-goods-1', + status: _data.rights.rightsStatus, + statusName: _data.rights.userRightsStatusName, + statusDesc: _data.rights.userRightsStatusDesc, + amount: _data.rights.refundAmount, + goodsList: _data.rightsItem.map((item, i) => ({ + id: i, + thumb: item.goodsPictureUrl, + title: item.goodsName, + specs: (item.specInfo || []).map((s) => s.specValues || ''), + itemRefundAmount: item.itemRefundAmount, + rightsQuantity: item.itemRefundAmount, + })), + storeId: _data.storeId, + buttons: _data.buttonVOs || [], + logisticsNo: _data.logisticsVO.logisticsNo, // 退货物流单号 + logisticsCompanyName: _data.logisticsVO.logisticsCompanyName, // 退货物流公司 + logisticsCompanyCode: _data.logisticsVO.logisticsCompanyCode, // 退货物流公司 + remark: _data.logisticsVO.remark, // 退货备注 + logisticsVO: _data.logisticsVO, + }; + }); + } + return new Promise((resolve) => { + if (reset) { + this.setData( + { + dataList: [], + }, + () => resolve(), + ); + } else resolve(); + }).then(() => { + this.setData({ + tabs, + dataList: this.data.dataList.concat(dataList), + listLoading: dataList.length > 0 ? 0 : 2, + }); + }); + }) + .catch((err) => { + this.setData({ + listLoading: 3, + }); + return Promise.reject(err); + }); + }, + + onReTryLoad() { + this.getAfterServiceList(this.data.curTab); + }, + + onTabChange(e) { + const { value } = e.detail; + const tab = this.data.tabs.find((v) => v.key === value); + if (!tab) return; + this.refreshList(value); + }, + + refreshList(status = -1) { + this.page = { + size: 10, + num: 1, + }; + this.setData({ + curTab: status, + dataList: [], + }); + return this.getAfterServiceList(status, true); + }, + + onRefresh() { + this.refreshList(this.data.curTab); + }, + + // 点击订单卡片 + onAfterServiceCardTap(e) { + wx.navigateTo({ + url: `/pages/order/after-service-detail/index?rightsNo=${e.currentTarget.dataset.order.id}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.json b/miniprogram/tcb-shop/pages/order/after-service-list/index.json new file mode 100644 index 0000000..46b5d09 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.json @@ -0,0 +1,15 @@ +{ + "navigationBarTitleText": "退款/售后", + "usingComponents": { + "wr-load-more": "/components/load-more/index", + "wr-after-service-button-bar": "../components/after-service-button-bar/index", + "wr-price": "/components/price/index", + "wr-order-card": "../components/order-card/index", + "wr-goods-card": "../components/goods-card/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.wxml b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxml new file mode 100644 index 0000000..7b4f649 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxml @@ -0,0 +1,61 @@ + + + + + + + {{order.typeDesc}} + + + + + + x {{goods.rightsQuantity}} + + + + {{order.statusDesc}} + + + + + + + + + 暂无退款或售后申请记录 + + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.wxss b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxss new file mode 100644 index 0000000..8a784bf --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxss @@ -0,0 +1,104 @@ +:host { + background-color: #f5f5f5; +} + +.list-loading { + height: 100rpx; +} + +.empty-wrapper { + height: calc(100vh - 88rpx); +} + +.page-container .order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 20rpx; + left: 190rpx; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-num { + color: #999; + line-height: 40rpx; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-price-class { + font-size: 36rpx; + color: #333; + font-family: DIN Alternate; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-price-decimal { + font-size: 28rpx; + color: #333; + font-family: DIN Alternate; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-price-symbol { + color: #333; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.page-container .wr-goods-card__specs { + margin: 14rpx 20rpx 0 0; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__title { + margin-right: 0; + -webkit-line-clamp: 1; +} + +.page-container .order-card .header .store-name { + width: 80%; + -webkit-line-clamp: 1; +} + +.page-container .order-card .header .store-name > view { + overflow: hidden; + width: 100%; + white-space: nowrap; + text-overflow: ellipsis; +} + +.page-container .status-desc { + box-sizing: border-box; + padding: 22rpx 20rpx; + font-size: 26rpx; + line-height: 1.3; + text-align: left; + color: #333333; + background-color: #f5f5f5; + border-radius: 8rpx; + word-wrap: break-word; + margin-top: 24rpx; + margin-bottom: 20rpx; +} + +.page-container .header__right { + font-size: 24rpx; + color: #fa4126; + display: flex; + align-items: center; +} + +.page-container .header__right__icon { + color: #d05b27; + font-size: 16px !important; + margin-right: 10rpx; +} + +.t-class-indicator { + color: #b9b9b9 !important; +} + +.page-container .header-class { + margin-bottom: 5rpx !important; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.js b/miniprogram/tcb-shop/pages/order/apply-service/index.js new file mode 100644 index 0000000..0642e69 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.js @@ -0,0 +1,441 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; +import { priceFormat } from '../../../utils/util'; +import { OrderStatus, ServiceType, ServiceReceiptStatus } from '../config'; +import reasonSheet from '../components/reason-sheet/reasonSheet'; +import { + fetchRightsPreview, + dispatchConfirmReceived, + fetchApplyReasonList, + dispatchApplyService, +} from '../../../services/order/applyService'; + +Page({ + query: {}, + data: { + uploading: false, // 凭证上传状态 + canApplyReturn: true, // 是否可退货 + goodsInfo: {}, + receiptStatusList: [ + { desc: '未收到货', status: ServiceReceiptStatus.NOT_RECEIPTED }, + { desc: '已收到货', status: ServiceReceiptStatus.RECEIPTED }, + ], + applyReasons: [], + serviceType: null, // 20-仅退款,10-退货退款 + serviceFrom: { + returnNum: 1, + receiptStatus: { desc: '请选择', status: null }, + applyReason: { desc: '请选择', type: null }, + // max-填写上限(单位分),current-当前值(单位分),temp输入框中的值(单位元) + amount: { max: 0, current: 0, temp: 0, focus: false }, + remark: '', + rightsImageUrls: [], + }, + maxApplyNum: 2, // 最大可申请售后的商品数 + amountTip: '', + showReceiptStatusDialog: false, + validateRes: { + valid: false, + msg: '', + }, + submitting: false, + inputDialogVisible: false, + uploadGridConfig: { + column: 3, + width: 212, + height: 212, + }, + serviceRequireType: '', + }, + + setWatcher(key, callback) { + let lastData = this.data; + const keys = key.split('.'); + keys.slice(0, -1).forEach((k) => { + lastData = lastData[k]; + }); + const lastKey = keys[keys.length - 1]; + this.observe(lastData, lastKey, callback); + }, + + observe(data, k, callback) { + let val = data[k]; + Object.defineProperty(data, k, { + configurable: true, + enumerable: true, + set: (value) => { + val = value; + callback(); + }, + get: () => { + return val; + }, + }); + }, + + validate() { + let valid = true; + let msg = ''; + // 检查必填项 + if (!this.data.serviceFrom.applyReason.type) { + valid = false; + msg = '请填写退款原因'; + } else if (!this.data.serviceFrom.amount.current) { + valid = false; + msg = '请填写退款金额'; + } + if (this.data.serviceFrom.amount.current <= 0) { + valid = false; + msg = '退款金额必须大于0'; + } + this.setData({ validateRes: { valid, msg } }); + }, + + onLoad(query) { + this.query = query; + if (!this.checkQuery()) return; + this.setData({ + canApplyReturn: query.canApplyReturn === 'true', + }); + this.init(); + this.inputDialog = this.selectComponent('#input-dialog'); + this.setWatcher('serviceFrom.returnNum', this.validate.bind(this)); + this.setWatcher('serviceFrom.applyReason', this.validate.bind(this)); + this.setWatcher('serviceFrom.amount', this.validate.bind(this)); + this.setWatcher('serviceFrom.rightsImageUrls', this.validate.bind(this)); + }, + + async init() { + try { + await this.refresh(); + } catch (e) {} + }, + + checkQuery() { + const { orderNo, skuId } = this.query; + if (!orderNo) { + Dialog.alert({ + content: '请先选择订单', + }).then(() => { + wx.redirectTo({ url: 'pages/order/order-list/index' }); + }); + return false; + } + if (!skuId) { + Dialog.alert({ + content: '请先选择商品', + }).then(() => { + wx.redirectTo(`pages/order/order-detail/index?orderNo=${orderNo}`); + }); + return false; + } + return true; + }, + + async refresh() { + wx.showLoading({ title: 'loading' }); + try { + const res = await this.getRightsPreview(); + wx.hideLoading(); + const goodsInfo = { + id: res.data.skuId, + thumb: res.data.goodsInfo && res.data.goodsInfo.skuImage, + title: res.data.goodsInfo && res.data.goodsInfo.goodsName, + spuId: res.data.spuId, + skuId: res.data.skuId, + specs: ((res.data.goodsInfo && res.data.goodsInfo.specInfo) || []).map((s) => s.specValue), + paidAmountEach: res.data.paidAmountEach, + boughtQuantity: res.data.boughtQuantity, + }; + this.setData({ + goodsInfo, + 'serviceFrom.amount': { + max: res.data.refundableAmount, + current: res.data.refundableAmount, + }, + 'serviceFrom.returnNum': res.data.numOfSku, + amountTip: `最多可申请退款¥ ${priceFormat(res.data.refundableAmount, 2)},含发货运费¥ ${priceFormat( + res.data.shippingFeeIncluded, + 2, + )}`, + maxApplyNum: res.data.numOfSkuAvailable, + }); + } catch (err) { + wx.hideLoading(); + throw err; + } + }, + + async getRightsPreview() { + const { orderNo, skuId, spuId } = this.query; + const params = { + orderNo, + skuId, + spuId, + numOfSku: this.data.serviceFrom.returnNum, + }; + const res = await fetchRightsPreview(params); + return res; + }, + + onApplyOnlyRefund() { + wx.setNavigationBarTitle({ title: '申请退款' }); + this.setData({ serviceRequireType: 'REFUND_MONEY' }); + this.switchReceiptStatus(0); + }, + + onApplyReturnGoods() { + wx.setNavigationBarTitle({ title: '申请退货退款' }); + this.setData({ serviceRequireType: 'REFUND_GOODS' }); + const orderStatus = parseInt(this.query.orderStatus); + Promise.resolve() + .then(() => { + if (orderStatus === OrderStatus.PENDING_RECEIPT) { + return Dialog.confirm({ + title: '订单商品是否已经收到货', + content: '', + confirmBtn: '确认收货,并申请退货', + cancelBtn: '未收到货', + }).then(() => { + return dispatchConfirmReceived({ + parameter: { + logisticsNo: this.query.logisticsNo, + orderNo: this.query.orderNo, + }, + }); + }); + } + return; + }) + .then(() => { + this.setData({ serviceType: ServiceType.RETURN_GOODS }); + this.switchReceiptStatus(1); + }); + }, + + onApplyReturnGoodsStatus() { + reasonSheet({ + show: true, + title: '选择退款原因', + options: this.data.applyReasons.map((r) => ({ + title: r.desc, + })), + showConfirmButton: true, + showCancelButton: true, + emptyTip: '请选择退款原因', + }).then((indexes) => { + this.setData({ + 'serviceFrom.applyReason': this.data.applyReasons[indexes[0]], + }); + }); + }, + + onChangeReturnNum(e) { + const { value } = e.detail; + this.setData({ + 'serviceFrom.returnNum': value, + }); + }, + + onApplyGoodsStatus() { + reasonSheet({ + show: true, + title: '请选择收货状态', + options: this.data.receiptStatusList.map((r) => ({ + title: r.desc, + })), + showConfirmButton: true, + emptyTip: '请选择收货状态', + }).then((indexes) => { + this.setData({ + 'serviceFrom.receiptStatus': this.data.receiptStatusList[indexes[0]], + }); + }); + }, + + switchReceiptStatus(index) { + const statusItem = this.data.receiptStatusList[index]; + // 没有找到对应的状态,则清空/初始化 + if (!statusItem) { + this.setData({ + showReceiptStatusDialog: false, + 'serviceFrom.receiptStatus': { desc: '请选择', status: null }, + 'serviceFrom.applyReason': { desc: '请选择', type: null }, // 收货状态改变时,初始化申请原因 + applyReasons: [], + }); + return; + } + // 仅选中项与当前项不一致时,才切换申请原因列表applyReasons + if (!statusItem || statusItem.status === this.data.serviceFrom.receiptStatus.status) { + this.setData({ showReceiptStatusDialog: false }); + return; + } + this.getApplyReasons(statusItem.status).then((reasons) => { + this.setData({ + showReceiptStatusDialog: false, + 'serviceFrom.receiptStatus': statusItem, + 'serviceFrom.applyReason': { desc: '请选择', type: null }, // 收货状态改变时,重置申请原因 + applyReasons: reasons, + }); + }); + }, + + getApplyReasons(receiptStatus) { + const params = { rightsReasonType: receiptStatus }; + return fetchApplyReasonList(params) + .then((res) => { + return res.data.rightsReasonList.map((reason) => ({ + type: reason.id, + desc: reason.desc, + })); + }) + .catch(() => { + return []; + }); + }, + + onReceiptStatusDialogConfirm(e) { + const { index } = e.currentTarget.dataset; + this.switchReceiptStatus(index); + }, + + onAmountTap() { + this.setData({ + 'serviceFrom.amount.temp': priceFormat(this.data.serviceFrom.amount.current), + 'serviceFrom.amount.focus': true, + inputDialogVisible: true, + }); + this.inputDialog.setData({ + cancelBtn: '取消', + confirmBtn: '确定', + }); + this.inputDialog._onConfirm = () => { + this.setData({ + 'serviceFrom.amount.current': this.data.serviceFrom.amount.temp * 100, + }); + }; + this.inputDialog._onCancel = () => {}; + }, + + // 对输入的值进行过滤 + onAmountInput(e) { + let { value } = e.detail; + const regRes = value.match(/\d+(\.?\d*)?/); // 输入中,允许末尾为小数点 + value = regRes ? regRes[0] : ''; + this.setData({ 'serviceFrom.amount.temp': value }); + }, + + // 失去焦点时,更严格的过滤并转化为float + onAmountBlur(e) { + let { value } = e.detail; + const regRes = value.match(/\d+(\.?\d+)?/); // 失去焦点时,不允许末尾为小数点 + value = regRes ? regRes[0] : '0'; + value = parseFloat(value) * 100; + if (value > this.data.serviceFrom.amount.max) { + value = this.data.serviceFrom.amount.max; + } + this.setData({ + 'serviceFrom.amount.temp': priceFormat(value), + 'serviceFrom.amount.focus': false, + }); + }, + + onAmountFocus() { + this.setData({ 'serviceFrom.amount.focus': true }); + }, + + onRemarkChange(e) { + const { value } = e.detail; + this.setData({ + 'serviceFrom.remark': value, + }); + }, + + // 发起申请售后请求 + onSubmit() { + this.submitCheck().then(() => { + const params = { + rights: { + orderNo: this.query.orderNo, + refundRequestAmount: this.data.serviceFrom.amount.current, + rightsImageUrls: this.data.serviceFrom.rightsImageUrls, + rightsReasonDesc: this.data.serviceFrom.applyReason.desc, + rightsReasonType: this.data.serviceFrom.receiptStatus.status, + rightsType: this.data.serviceType, + }, + rightsItem: [ + { + itemTotalAmount: this.data.goodsInfo.price * this.data.serviceFrom.returnNum, + rightsQuantity: this.data.serviceFrom.returnNum, + skuId: this.query.skuId, + spuId: this.query.spuId, + }, + ], + refundMemo: this.data.serviceFrom.remark.current, + }; + this.setData({ submitting: true }); + // 发起申请售后请求 + dispatchApplyService(params) + .then((res) => { + Toast({ + context: this, + selector: '#t-toast', + message: '申请成功', + icon: '', + }); + + wx.redirectTo({ + url: `/pages/order/after-service-detail/index?rightsNo=${res.data.rightsNo}`, + }); + }) + .then(() => this.setData({ submitting: false })) + .catch(() => this.setData({ submitting: false })); + }); + }, + + submitCheck() { + return new Promise((resolve) => { + const { msg, valid } = this.data.validateRes; + if (!valid) { + Toast({ + context: this, + selector: '#t-toast', + message: msg, + icon: '', + }); + return; + } + resolve(); + }); + }, + + handleSuccess(e) { + const { files } = e.detail; + this.setData({ + 'sessionFrom.rightsImageUrls': files, + }); + }, + + handleRemove(e) { + const { index } = e.detail; + const { + sessionFrom: { rightsImageUrls }, + } = this.data; + rightsImageUrls.splice(index, 1); + this.setData({ + 'sessionFrom.rightsImageUrls': rightsImageUrls, + }); + }, + + handleComplete() { + this.setData({ + uploading: false, + }); + }, + + handleSelectChange() { + this.setData({ + uploading: true, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.json b/miniprogram/tcb-shop/pages/order/apply-service/index.json new file mode 100644 index 0000000..b5c4f62 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.json @@ -0,0 +1,19 @@ +{ + "navigationBarTitleText": "选择售后类型", + "usingComponents": { + "wr-price": "/components/price/index", + "wr-order-goods-card": "../components/order-goods-card/index", + "wr-reason-sheet": "../components/reason-sheet/index", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-button": "tdesign-miniprogram/button/button", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-input": "tdesign-miniprogram/input/input", + "t-upload": "tdesign-miniprogram/upload/upload" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.wxml b/miniprogram/tcb-shop/pages/order/apply-service/index.wxml new file mode 100644 index 0000000..b6471ea --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.wxml @@ -0,0 +1,198 @@ + + + + + + x {{goodsInfo.boughtQuantity}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 修改 + + + + + + + + 退款说明 + + + + + + + + 上传凭证 + (最多3张) + + + + + + + 提交 + + + + + + + + + + {{item.desc}} + + + 取消 + + + + + + + 退款金额 + + + {{amountTip}} + + + + diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.wxss b/miniprogram/tcb-shop/pages/order/apply-service/index.wxss new file mode 100644 index 0000000..86fb12f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.wxss @@ -0,0 +1,308 @@ +:host { + background-color: #f5f5f5; +} +.select-service .service-form .service-from-group { + margin-top: 20rpx; +} +.select-service .service-form { + padding-bottom: calc(env(safe-area-inset-bottom) + 80rpx); +} + +.order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 0; + left: 190rpx; +} + +.order-goods-card-footer-num { + color: #999; +} + +.select-service .order-goods-card-footer .order-goods-card-footer-price-class { + font-size: 36rpx; + color: #333; + font-family: DIN Alternate; +} +.select-service .order-goods-card-footer .order-goods-card-footer-price-decimal { + font-size: 28rpx; + color: #333; + font-family: DIN Alternate; +} +.select-service .order-goods-card-footer .order-goods-card-footer-price-symbol { + color: #333; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.select-service .remark { + min-height: 110rpx; + border-radius: 10rpx; + margin-top: 20rpx; + background-color: #f5f5f5; +} +.select-service .remark::after { + border: none; +} + +.select-service .special-cell .special-cell-note { + display: flex; + flex-direction: column; +} + +.select-service .special-cell .wr-cell__title { + margin-right: 100rpx; +} + +.select-service .special-cell .special-cell-note-price-class { + font-size: 36rpx; + color: #fa4126; + font-family: DIN Alternate; +} +.select-service .special-cell .special-cell-note-price-decimal { + font-size: 28rpx; + color: #fa4126; + font-family: DIN Alternate; +} +.select-service .special-cell .special-cell-note-price-symbol { + color: #fa4126; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.select-service .bottom-bar__btn { + width: 686rpx; + background-color: #fa4126; + color: white; + font-size: 32rpx; + border-radius: 48rpx; + position: absolute; + left: 50%; + top: 20rpx; + transform: translateX(-50%); +} +.select-service .bottom-bar__btn::after { + border: none; +} +.select-service .bottom-bar__btn.disabled { + background-color: #c6c6c6; + --td-button-default-active-bg-color: #c6c6c6; + --td-button-default-border-bg-color: #c6c6c6; +} +.select-service .bottom-bar__btn.disabled::after { + border: none; +} +.select-service .order-goods-card .wr-goods-card { + padding: 0 30rpx; +} + +.order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 20rpx; + left: 190rpx; +} + +.order-goods-card-footer-num { + color: #999; + line-height: 40rpx; +} + +.order-goods-card-title-class { + width: 10rpx !important; +} + +.input-dialog__content .input-dialog__input { + font-size: 72rpx !important; + height: 64rpx; +} + +.t-input__label { + margin-right: 0 !important; +} + +.input-dialog__label { + font-size: 48rpx; + color: #333; +} + +.input-dialog__content .input-dialog__input, +.input-dialog__label { + height: 64rpx; + line-height: 64rpx !important; +} + +.input-dialog__content .input { + font-size: 48rpx; + padding-left: 0; + padding-right: 0; + --td-input-border-left-space: 0; +} + +.input-dialog__content .tips { + margin-top: 24rpx; + font-size: 24rpx; + color: #999999; +} + +.t-input__name { + width: 10rpx !important; +} + +.input-dialog__title { + color: #333; + font-size: 32rpx; + font-weight: normal; +} + +.dialog--service-status { + background-color: #f3f4f5; + overflow: hidden; +} +.dialog--service-status .options .option { + color: #333333; + font-size: 30rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + background-color: white; +} +.dialog--service-status .options .option:not(:last-child) { + border-bottom: 1rpx solid #e6e6e6; +} +.dialog--service-status .options .option--active { + opacity: 0.5; +} +.dialog--service-status .options .option.main { + color: #fa4126; +} +.dialog--service-status .cancel { + color: #333333; + font-size: 30rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + background-color: white; + margin-top: 20rpx; +} +.dialog--service-status .cancel--active { + opacity: 0.5; +} +.amount-dialog--focus .popup__content--center, +.remark-dialog--focus .popup__content--center { + top: 100rpx; + transform: translate(-50%, 0); +} +.dialog .dialog__button-confirm { + color: #fa4126; + color: var(--color-primary, #fa4126); +} +.select-service .bottom-bar { + background-color: #fff; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + height: 158rpx; + z-index: 3; +} +.order-goods-card { + background: #fff; + margin-bottom: 24rpx; +} + +.service-from-group__wrapper { + display: flex; + flex-direction: column; + font-family: DIN Alternate; + font-weight: bold; + font-size: 36rpx; + text-align: right; + color: #fa4126; +} +.service-from-group__price { + display: flex; + align-items: center; + color: #bbb; + font-size: 24rpx; + position: relative; + left: 30rpx; +} +.textarea--label { +} +.service-from-group__textarea { + margin-top: 20rpx; + background-color: #fff; + padding: 32rpx 32rpx 24rpx; +} + +.textarea--content { + margin-top: 32rpx; + background: #f5f5f5 !important; + border-radius: 16rpx; +} +.service-from-group__textarea .t-textarea__wrapper .t-textarea__wrapper-textarea { + height: 136rpx; + box-sizing: border-box; +} +.service-from-group__grid { + padding: 0 32rpx 48rpx; + background: #fff; + margin-bottom: 148rpx; +} + +.upload-addcontent-slot { + background-color: #f5f5f5; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.refund-money__description { + font-size: 24rpx !important; +} + +.upload-desc { + text-align: center; + display: flex; + flex-direction: column; + font-size: 24rpx; + color: #999; +} + +.t-cell__left__icon { + position: relative; + top: -24rpx; + margin-right: 18rpx; +} + +.service-choice .t-cell__title-text { + color: #333; + font-weight: bold; +} + +.service-form .service-from-group .service-from-group__wrapper .refund-money-price-class { + font-size: 36rpx; + font-family: DIN Alternate; +} + +.service-form .service-from-group .service-from-group__wrapper .refund-money-price-decimal { + font-size: 28rpx; + font-family: DIN Alternate; +} + +.service-form .service-from-group .service-from-group__wrapper .refund-money-price-symbol { + font-size: 24rpx; + font-family: DIN Alternate; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js new file mode 100644 index 0000000..c1f813d --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js @@ -0,0 +1,95 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; + +import { cancelRights } from '../../after-service-detail/api'; +import { ServiceButtonTypes } from '../../config'; + +Component({ + properties: { + service: { + type: Object, + observer(service) { + const buttonsRight = service.buttons || service.buttonVOs || []; + this.setData({ + buttons: { + left: [], + right: buttonsRight, + }, + }); + }, + }, + }, + + data: { + service: {}, + buttons: { + left: [], + right: [], + }, + }, + + methods: { + // 点击【订单操作】按钮,根据按钮类型分发 + onServiceBtnTap(e) { + const { type } = e.currentTarget.dataset; + switch (type) { + case ServiceButtonTypes.REVOKE: + this.onConfirm(this.data.service); + break; + case ServiceButtonTypes.FILL_TRACKING_NO: + this.onFillTrackingNo(this.data.service); + break; + case ServiceButtonTypes.CHANGE_TRACKING_NO: + this.onChangeTrackingNo(this.data.service); + break; + case ServiceButtonTypes.VIEW_DELIVERY: + this.viewDelivery(this.data.service); + break; + } + }, + + onFillTrackingNo(service) { + wx.navigateTo({ + url: `/pages/order/fill-tracking-no/index?rightsNo=${service.id}`, + }); + }, + + viewDelivery(service) { + wx.navigateTo({ + url: `/pages/order/delivery-detail/index?data=${JSON.stringify( + service.logistics || service.logisticsVO, + )}&source=2`, + }); + }, + + onChangeTrackingNo(service) { + wx.navigateTo({ + url: `/pages/order/fill-tracking-no/index?rightsNo=${ + service.id + }&logisticsNo=${service.logisticsNo}&logisticsCompanyName=${ + service.logisticsCompanyName + }&logisticsCompanyCode=${service.logisticsCompanyCode}&remark=${ + service.remark || '' + }`, + }); + }, + + onConfirm() { + Dialog.confirm({ + title: '是否撤销退货申请?', + content: '', + confirmBtn: '撤销申请', + cancelBtn: '不撤销', + }).then(() => { + const params = { rightsNo: this.data.service.id }; + return cancelRights(params).then(() => { + Toast({ + context: this, + selector: '#t-toast', + message: '你确认撤销申请', + }); + }); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json new file mode 100644 index 0000000..75d1e24 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml new file mode 100644 index 0000000..c19c824 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml @@ -0,0 +1,33 @@ + + + + {{leftBtn.name}} + + + + + {{rightBtn.name}} + + + diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss new file mode 100644 index 0000000..e83597f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss @@ -0,0 +1,43 @@ +:host { + width: 100%; +} +.btn-bar { + display: flex; + justify-content: space-between; + align-items: center; + line-height: 1; +} +.btn-bar .order-btn { + background-color: inherit; + font-size: 26rpx; + padding: 16rpx 28rpx; + line-height: 1; + border-radius: unset; + min-width: 160rpx; + border-radius: 32rpx; + height: 60rpx; + margin-right: 10rpx; +} + +.btn-bar .left .order-btn:not(:first-child), +.btn-bar .right .order-btn:not(:first-child) { + margin-left: 20rpx; +} +.btn-bar .left .delete-btn { + font-size: 22rpx; +} +.btn-bar .left .delete-btn::after { + display: none; +} + +.btn-bar .right .normal { + --td-button-default-color: #333333; + --td-button-default-border-color: #dddddd; +} + +.btn-bar .right .primary { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.js b/miniprogram/tcb-shop/pages/order/components/customer-service/index.js new file mode 100644 index 0000000..419b97f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.js @@ -0,0 +1,38 @@ +Component({ + externalClasses: ['wr-class'], + + properties: { + phoneNumber: String, + desc: String, + }, + + data: { + show: false, + }, + + methods: { + onBtnTap() { + this.setData({ + show: true, + }); + }, + + onDialogClose() { + this.setData({ + show: false, + }); + }, + + onCall() { + const { phoneNumber } = this.properties; + wx.makePhoneCall({ + phoneNumber, + }); + }, + onCallOnlineService() { + wx.showToast({ + title: '你点击了在线客服', + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.json b/miniprogram/tcb-shop/pages/order/components/customer-service/index.json new file mode 100644 index 0000000..6e4c04e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup" + } +} diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml new file mode 100644 index 0000000..f5413a4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml @@ -0,0 +1,23 @@ + +联系客服 + + + + + 服务时间: + {{desc}} + + + 呼叫 {{phoneNumber}} + + 在线客服 + 取消 + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss new file mode 100644 index 0000000..75f0363 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss @@ -0,0 +1,48 @@ +.text-btn { + display: inline; + color: #333; + font-size: 24rpx; +} +.text-btn--active { + opacity: 0.5; +} +.dialog--customer-service { + background-color: #f3f4f5; + overflow: hidden; +} +.dialog--customer-service .content { + font-size: 26rpx; + margin: 32rpx 30rpx; + text-align: center; +} +.dialog--customer-service .content .title { + display: inline; + color: #999999; + font-weight: bold; +} +.dialog--customer-service .content .subtitle { + display: inline; + color: #999999; +} +.dialog--customer-service .options .option { + color: #333333; + font-size: 30rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + background-color: white; +} +.dialog--customer-service .options .option:not(:last-child) { + margin-bottom: 20rpx; +} +.dialog--customer-service .options .option--active { + opacity: 0.5; +} +.dialog--customer-service .options .option.main { + color: #333; +} +.dialog--customer-service .options .option.online { + position: relative; + top: -17rpx; + margin-bottom: 2rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.js b/miniprogram/tcb-shop/pages/order/components/goods-card/index.js new file mode 100644 index 0000000..293f3f0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.js @@ -0,0 +1,251 @@ +Component({ + options: { + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + addGlobalClass: true, + }, + intersectionObserverContext: null, + + externalClasses: [ + 'card-class', + 'title-class', + 'desc-class', + 'num-class', + 'thumb-class', + 'specs-class', + 'price-class', + 'origin-price-class', + 'price-prefix-class', + ], + + relations: { + '../order-card/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + properties: { + hidden: { + // 设置为null代表不做类型转换 + type: null, + value: false, + observer(hidden) { + // null就是代表没有设置,没有设置的话不setData,防止祖先组件触发的setHidden操作被覆盖 + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + id: { + type: String, + // `goods-card-88888888` + // 不能在这里写生成逻辑,如果在这里写,那么假设有多个goods-list时,他们将共享这个值 + value: '', + observer: (id) => { + this.genIndependentID(id); + if (this.properties.thresholds?.length) { + this.createIntersectionObserverHandle(); + } + }, + }, + data: { + type: Object, + observer(goods) { + // 有ID的商品才渲染 + if (!goods) { + return; + } + + // 敲定换行数量默认值 + if (goods.lineClamp === undefined || goods.lineClamp <= 0) { + // tag数组长度 大于0 且 可见 + // 指定换行为1行 + if ((goods.tags?.length || 0) > 0 && !goods.hideKey?.tags) { + goods.lineClamp = 1; + } else { + goods.lineClamp = 2; + } + } + goods.specs = goods.sku.attr_value.map((v) => v.value).join(','); + + this.setData({ goods }); + }, + }, + layout: { + type: String, + value: 'horizontal', + }, + thumbMode: { + type: String, + value: 'aspectFill', + }, + thumbWidth: Number, + thumbHeight: Number, + priceFill: { + type: Boolean, + value: true, + }, + currency: { + type: String, + value: '¥', + }, + lazyLoad: { + type: Boolean, + value: false, + }, + centered: { + type: Boolean, + value: false, + }, + showCart: { + type: Boolean, + value: false, + }, + pricePrefix: { + type: String, + value: '', + }, + cartSize: { + type: Number, + value: 48, + }, + cartColor: { + type: String, + value: '#FA550F', + }, + /** 元素可见监控阈值, 数组长度大于0就创建 */ + thresholds: { + type: Array, + value: [], + observer(current) { + if (current && current.length) { + this.createIntersectionObserverHandle(); + } else { + this.clearIntersectionObserverHandle(); + } + }, + }, + specsIconClassPrefix: { + type: String, + value: 'wr', + }, + specsIcon: { + type: String, + value: 'expand_more', + }, + addCartIconClassPrefix: { + type: String, + value: 'wr', + }, + addCartIcon: { + type: String, + value: 'cart', + }, + }, + + data: { + hiddenInData: false, + independentID: '', + goods: { id: '' }, + /** 保证划线价格不小于原价,否则不渲染划线价 */ + isValidityLinePrice: false, + }, + + lifetimes: { + ready() { + this.init(); + }, + detached() { + this.clear(); + }, + }, + + methods: { + clickHandle() { + this.triggerEvent('click', { goods: this.data.goods }); + }, + clickThumbHandle() { + this.triggerEvent('thumb', { goods: this.data.goods }); + }, + clickTagHandle(evt) { + const { index } = evt.currentTarget.dataset; + this.triggerEvent('tag', { goods: this.data.goods, index }); + }, + // 加入购物车 + addCartHandle(e) { + const { id } = e.currentTarget; + const { id: cardID } = e.currentTarget.dataset; + this.triggerEvent('add-cart', { + ...e.detail, + id, + cardID, + goods: this.data.goods, + }); + }, + genIndependentID(id, cb) { + let independentID; + if (id) { + independentID = id; + } else { + // `goods-card-88888888` + independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`; + } + this.setData({ independentID }, cb); + }, + + init() { + const { thresholds, id, hidden } = this.properties; + if (hidden !== null) { + this.setHidden(!!hidden); + } + + this.genIndependentID(id || '', () => { + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } + }); + }, + + clear() { + this.clearIntersectionObserverHandle(); + }, + + setHidden(hidden) { + this.setData({ hiddenInData: !!hidden }); + }, + + createIntersectionObserverHandle() { + if (this.intersectionObserverContext || !this.data.independentID) { + return; + } + + this.intersectionObserverContext = wx + .createIntersectionObserver(this, { + thresholds: this.properties.thresholds, + }) + .relativeToViewport(); + + this.intersectionObserverContext.observe(`#${this.data.independentID}`, (res) => { + this.intersectionObserverCB(res); + }); + }, + intersectionObserverCB(ob) { + this.triggerEvent('ob', { + goods: this.data.goods, + context: this.intersectionObserverContext, + ob, + }); + }, + clearIntersectionObserverHandle() { + if (this.intersectionObserverContext) { + try { + this.intersectionObserverContext.disconnect(); + } catch (e) {} + + this.intersectionObserverContext = null; + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.json b/miniprogram/tcb-shop/pages/order/components/goods-card/index.json new file mode 100644 index 0000000..0dbb3d8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml new file mode 100644 index 0000000..fab9cce --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml @@ -0,0 +1,40 @@ + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss new file mode 100644 index 0000000..7bcb096 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss @@ -0,0 +1,254 @@ +.wr-goods-card { + box-sizing: border-box; + font-size: 24rpx; +} +.wr-goods-card__main { + position: relative; + display: flex; + line-height: 1; + flex-direction: row; + background: transparent; + padding: 16rpx 0rpx; +} +.wr-goods-card.center .wr-goods-card__main { + align-items: center; + justify-content: center; +} +.wr-goods-card__thumb { + flex-shrink: 0; + position: relative; + width: 176rpx; + height: 176rpx; +} +.wr-goods-card__thumb-com { + width: 176rpx; + height: 176rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card__thumb:empty { + display: none; + margin: 0; +} + +.wr-goods-card__body { + display: flex; + margin: 0 0 0 16rpx; + flex-direction: row; + flex: 1 1 auto; + min-height: 176rpx; +} +.wr-goods-card__long_content { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1 1 auto; +} +.wr-goods-card__long_content .goods_tips { + width: 100%; + margin-top: 16rpx; + text-align: right; + color: #fa4126; + font-size: 24rpx; + line-height: 32rpx; + font-weight: bold; +} +.wr-goods-card__title { + flex-shrink: 0; + font-size: 28rpx; + color: #333; + line-height: 40rpx; + font-weight: 400; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + word-break: break-word; +} +.wr-goods-card__title__prefix-tags { + display: inline-flex; +} +.wr-goods-card__title__prefix-tags .prefix-tag { + margin: 0 8rpx 0 0; +} +.wr-goods-card__desc { + font-size: 24rpx; + color: #f5f5f5; + line-height: 40rpx; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} +.wr-goods-card__specs__desc, +.wr-goods-card__specs__text { + font-size: 24rpx; + height: 32rpx; + line-height: 32rpx; + color: #999999; + margin: 8rpx 0; +} +.wr-goods-card__specs__desc { + display: flex; + align-self: flex-start; + flex-direction: row; +} +.wr-goods-card__specs__desc-text { + height: 100%; + max-width: 380rpx; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} +.wr-goods-card__specs__desc-icon { + line-height: inherit; + margin-left: 8rpx; + font-size: 24rpx; + color: #bbb; +} +.wr-goods-card__specs__text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; +} +.wr-goods-card__tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 16rpx 0 0 0; +} +.wr-goods-card__tag { + color: #fa550f; + background: transparent; + font-size: 20rpx; + border: 1rpx solid #fa550f; + padding: 0 8rpx; + height: 30rpx; + line-height: 30rpx; + margin: 0 8rpx 8rpx 0; + display: block; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; +} +.wr-goods-card__short_content { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-end; + margin: 0 0 0 46rpx; +} +.wr-goods-card__price__prefix { + order: 0; + color: #666; + margin: 0; +} +.wr-goods-card__price { + white-space: nowrap; + font-weight: bold; + order: 1; + color: #fa4126; + font-size: 36rpx; + margin: 0; + line-height: 48rpx; +} +.wr-goods-card__origin-price { + white-space: nowrap; + font-weight: normal; + order: 2; + color: #aaaaaa; + font-size: 24rpx; + margin: 0; +} +.wr-goods-card__num { + white-space: nowrap; + order: 4; + font-size: 24rpx; + color: #999; + margin: 20rpx 0 0 auto; +} +.wr-goods-card__num__prefix { + color: inherit; +} +.wr-goods-card__add-cart { + order: 3; + margin: auto 0 0 auto; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__thumb { + width: 192rpx; + height: 192rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__body { + flex-direction: column; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__short_content { + flex-direction: row; + align-items: center; + margin: 16rpx 0 0 0; +} + +.wr-goods-card.horizontal-wrap .wr-goods-card__num { + margin: 0 0 0 auto; +} +.wr-goods-card.vertical .wr-goods-card__main { + padding: 0 0 22rpx 0; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__thumb { + width: 340rpx; + height: 340rpx; +} +.wr-goods-card.vertical .wr-goods-card__body { + margin: 20rpx 20rpx 0 20rpx; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__long_content { + overflow: hidden; +} +.wr-goods-card.vertical .wr-goods-card__title { + line-height: 36rpx; +} +.wr-goods-card.vertical .wr-goods-card__short_content { + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__price { + order: 2; + color: #fa4126; + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__origin-price { + order: 1; +} +.wr-goods-card.vertical .wr-goods-card__add-cart { + position: absolute; + bottom: 20rpx; + right: 20rpx; +} + +.wr-goods-card__short_content .no_storage { + display: flex; + align-items: center; + justify-content: space-between; + height: 40rpx; + color: #333; + font-size: 24rpx; + line-height: 32rpx; + width: 100%; +} + +.no_storage .no_storage__right { + width: 80rpx; + height: 40rpx; + border-radius: 20rpx; + border: 2rpx solid #fa4126; + line-height: 40rpx; + text-align: center; + color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs b/miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs new file mode 100644 index 0000000..f9b13d0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs @@ -0,0 +1,17 @@ +var isOnlyBack = function (data) { + return data.limitGoodsList || (data.inValidGoodsList && !data.storeGoodsList); +}; + +var isShowChangeAddress = function (data) { + return data.abnormalDeliveryGoodsList; +}; + +var isShowKeepPay = function (data) { + return data.outOfStockGoodsList || (data.storeGoodsList && data.inValidGoodsList); +}; + +module.exports = { + isOnlyBack: isOnlyBack, + isShowChangeAddress: isShowChangeAddress, + isShowKeepPay: isShowKeepPay, +}; diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js new file mode 100644 index 0000000..53b6f86 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js @@ -0,0 +1,57 @@ +Component({ + properties: { + settleDetailData: { + type: Object, + value: {}, + observer(settleDetailData) { + const { + outOfStockGoodsList, + abnormalDeliveryGoodsList, + inValidGoodsList, + limitGoodsList, + } = settleDetailData; + // 弹窗逻辑 限购 超出配送范围 失效 库存不足; + const tempList = + limitGoodsList || + abnormalDeliveryGoodsList || + inValidGoodsList || + outOfStockGoodsList || + []; + + tempList.forEach((goods, index) => { + goods.id = index; + goods.unSettlementGoods && + goods.unSettlementGoods.forEach((ele) => { + ele.name = ele.goodsName; + ele.price = ele.payPrice; + ele.imgUrl = ele.image; + }); + }); + this.setData({ + // settleDetailData, + goodsList: tempList, + }); + }, + }, + }, + + data: { + goodList: [], + }, + methods: { + onCard(e) { + const { item } = e.currentTarget.dataset; + if (item === 'cart') { + // 购物车 + Navigator.gotoPage('/cart'); + } else if (item === 'orderSure') { + // 结算页 + this.triggerEvent('change', undefined); + } + }, + onDelive() { + // 修改配送地址 + Navigator.gotoPage('/address', { type: 'orderSure' }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json new file mode 100644 index 0000000..31f62da --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "wr-order-card": "/pages/order/components/order-card/index", + "wr-goods-card": "/components/goods-card/index", + "wr-order-goods-card": "/pages/order/components/order-goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml new file mode 100644 index 0000000..04cc10a --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml @@ -0,0 +1,53 @@ + + + + + 限购商品信息 + 以下商品限购数量,建议您修改商品数量 + + + 不支持配送 + 以下店铺的商品不支持配送,请更改地址或去掉对应店铺商品再进行结算 + + + 部分商品库存不足或失效 + 请返回购物车重新选择商品,如果继续结算将自动忽略库存不足或失效的商品。 + + + 全部商品库存不足或失效 + 请返回购物车重新选择商品 + + + + + + + + + + + 返回购物车 + + + 修改配送地址 + + + 继续结算 + + + diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss new file mode 100644 index 0000000..0331d40 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss @@ -0,0 +1,68 @@ +/* 层级定义 +@z-index-0: 1; +@z-index-1: 100; +@z-index-2: 200; +@z-index-5: 500; +@z-index-component: 1000; // 通用组件级别 +@z-index-dropdown: @z-index-component; +@z-index-sticky: @z-index-component + 20; +@z-index-fixed: @z-index-component + 30; +@z-index-modal-backdrop:@z-index-component + 40; +@z-index-modal:@z-index-component + 50; +@z-index-popover:@z-index-component + 60; +@z-index-tooltip:@z-index-component + 70; +*/ +/* var() css变量适配*/ +.goods-fail { + display: block; + background: #fff; + font-size: 30rpx; + border-radius: 20rpx 20rpx 0 0; +} +.goods-fail .title { + display: inline-block; + width: 100%; + text-align: center; + margin-top: 30rpx; + line-height: 42rpx; + font-weight: bold; + font-size: 32rpx; +} +.goods-fail .info { + display: block; + font-size: 26rpx; + font-weight: 400; + line-height: 36rpx; + margin: 20rpx auto 10rpx; + text-align: center; + width: 560rpx; + color: #999; +} +.goods-fail .goods-fail-btn { + display: flex; + padding: 30rpx; + justify-content: space-between; + align-items: center; + font-size: 30rpx; +} +.goods-fail .goods-fail-btn .btn { + width: 330rpx; + height: 80rpx; + line-height: 80rpx; + border-radius: 8rpx; + text-align: center; + border: 1rpx solid #999; + background: #fff; + font-size: 32rpx; + color: #666; +} +.goods-fail .goods-fail-btn .btn.origin, +.goods-fail .goods-fail-btn .btn.limit { + color: #fa550f; + color: var(--color-primary, #fa550f); + border: 1rpx solid #fa550f; + border: 1rpx solid var(--color-primary, #fa550f); +} +.goods-fail .goods-fail-btn .btn.limit { + flex-grow: 1; +} diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js new file mode 100644 index 0000000..0ea93cd --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js @@ -0,0 +1,274 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import { ORDER_STATUS, updateOrderStatus } from '../../../../services/order/order'; +import { pay, refund } from '../../../../services/pay/pay'; +import { OrderButtonTypes } from '../../config'; +import { objectToParamString } from '../../../../utils/util'; +import { OPERATION_TYPE } from '../../../../utils/orderOperation'; + +const OPERATION_DONE_EVENT = 'operation'; + +Component({ + options: { + addGlobalClass: true, + }, + properties: { + order: { + type: Object, + observer(order) { + this.init(order); + }, + }, + goodsIndex: { + type: Number, + value: null, + }, + isBtnMax: { + type: Boolean, + value: false, + }, + }, + + data: { + order: {}, + buttons: { + left: [], + right: [], + }, + }, + + methods: { + // 点击【订单操作】按钮,根据按钮类型分发 + onOrderBtnTap(e) { + const { type } = e.currentTarget.dataset; + switch (type) { + case OrderButtonTypes.CANCEL: + this.onCancel(this.data.order); + break; + case OrderButtonTypes.CONFIRM: + this.onConfirm(this.data.order); + break; + case OrderButtonTypes.PAY: + this.onPay(this.data.order); + break; + case OrderButtonTypes.APPLY_REFUND: + this.onApplyRefund(this.data.order); + break; + case OrderButtonTypes.COMMENT: + this.onAddComment(this.data.order); + break; + } + }, + + checkOrder(order, operationType) { + if (order != null) { + return true; + } + this.triggerEvent(OPERATION_DONE_EVENT, { + type: operationType, + message: 'no order', + success: false, + }); + return false; + }, + + async onCancel(order) { + if (!this.checkOrder(order, OPERATION_TYPE.CANCEL)) return; + + // if order is paid, we should first refund + if (order.status !== ORDER_STATUS.TO_PAY) { + try { + await refund(order._id); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CANCEL, + message: 'refund failed', + success: false, + detail: e, + }); + return; + } + } + + try { + await updateOrderStatus({ orderId: order._id, status: ORDER_STATUS.CANCELED }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CANCEL, + message: 'update order status failed', + success: false, + detail: e, + }); + return; + } + + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CANCEL, + success: true, + }); + }, + + async onConfirm(order) { + if (!this.checkOrder(order, OPERATION_TYPE.CONFIRM)) return; + + try { + await Dialog.confirm({ + title: '确认是否已经收到货?', + content: '', + confirmBtn: '确认收货', + cancelBtn: '取消', + }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CONFIRM, + message: 'confirm dialog failed', + success: false, + detail: e, + }); + return; + } + + try { + await updateOrderStatus({ orderId: order._id, status: ORDER_STATUS.FINISHED }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CONFIRM, + message: 'update order status failed', + success: false, + detail: e, + }); + } + + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CONFIRM, + success: true, + }); + }, + + async onPay(order) { + if (!this.checkOrder(order, OPERATION_TYPE.PAY)) return; + + try { + await pay({ id: order._id, totalPrice: order.totalPrice }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.PAY, + message: 'pay failed', + success: false, + detail: e, + }); + return; + } + + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.PAY, + success: true, + }); + }, + + onApplyRefund(order) { + wx.navigateTo({ url: `/pages/order/apply-service/index?${objectToParamString({ orderId: order._id })}` }); + }, + + /** 添加订单评论 */ + onAddComment(order) { + wx.navigateTo({ + url: `/pages/goods/comments/create-list/index?${objectToParamString({ orderId: order._id })}`, + }); + }, + + init(order) { + if (order == null) return; + + if (order.status === ORDER_STATUS.TO_PAY) { + this.setData({ + buttons: { + left: [], + right: [ + { type: OrderButtonTypes.CANCEL, name: '取消订单' }, + { type: OrderButtonTypes.PAY, name: '付款', primary: true }, + ], + }, + }); + return; + } + if (order.status === ORDER_STATUS.TO_SEND) { + this.setData({ + buttons: { + left: [], + right: [{ type: OrderButtonTypes.CANCEL, name: '取消订单' }], + }, + }); + return; + } + if (order.status === ORDER_STATUS.TO_RECEIVE) { + this.setData({ + buttons: { + left: [], + right: [{ type: OrderButtonTypes.CONFIRM, name: '确认收货', primary: true }], + }, + }); + return; + } + if (order.status === ORDER_STATUS.FINISHED) { + this.setData({ + buttons: { + left: [], + right: [{ type: OrderButtonTypes.COMMENT, name: '评价', primary: true }], + }, + }); + return; + } + if (order.status === ORDER_STATUS.CANCELED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_APPLIED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_REFUSED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_FINISH) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_MONEY_REFUSED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + }, + }, + + lifetimes: { + attached() { + this.init(this.data.order); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json new file mode 100644 index 0000000..3084bb3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-button": "tdesign-miniprogram/button/button", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml new file mode 100644 index 0000000..492b87a --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml @@ -0,0 +1,37 @@ + + + + {{leftBtn.name}} + + + + + {{rightBtn.name}} + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss new file mode 100644 index 0000000..73385a2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss @@ -0,0 +1,54 @@ +:host { + width: 100%; +} +.btn-bar { + display: flex; + justify-content: space-between; + align-items: center; + line-height: 1; +} +.btn-bar .order-btn { + line-height: 1; + /* border-radius: unset; */ + /* min-width: 160rpx; */ +} + +.btn-bar .right { + display: flex; + align-items: center; +} +.btn-bar .t-button { + width: 160rpx; + font-weight: 400; + margin-left: 24rpx; +} +.btn-bar .t-button--max { + width: 176rpx; + margin-left: 24rpx; + + --td-button-extra-small-height: 72rpx; +} + +.btn-bar .left .delete-btn { + font-size: 22rpx; +} +.btn-bar .left .delete-btn::after { + display: none; +} + +.btn-bar .right .normal { + --td-button-default-color: #333333; + --td-button-default-border-color: #dddddd; +} + +.btn-bar .right .primary { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.js b/miniprogram/tcb-shop/pages/order/components/order-card/index.js new file mode 100644 index 0000000..9b615d2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.js @@ -0,0 +1,88 @@ +import { orderStatusToName } from '../../../../services/order/order'; + +Component({ + externalClasses: ['wr-class', 'header-class', 'title-class'], + + options: { + multipleSlots: true, + }, + + relations: { + '../order-goods-card/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setHidden(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + }, + }, + '../goods-card/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setHidden(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + }, + }, + '../specs-goods-card/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setHidden(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + }, + }, + }, + + created() { + this.children = []; + }, + + properties: { + order: { + type: Object, + observer(order) { + const goodsCount = order?.orderItems?.length; + if (typeof goodsCount !== 'number') return; + + this.setData({ + goodsCount, + }); + }, + }, + useTopLeftSlot: Boolean, + useTopRightSlot: Boolean, + // 初始显示的商品数量,超出部分会隐藏。 + defaultShowNum: { + type: null, + value: 10, + }, + }, + + data: { + showAll: true, // 是否展示所有商品,设置为false,可以使用展开更多功能 + goodsCount: 0, + }, + + methods: { + setHidden() { + const isHidden = !this.data.showAll; + this.children.forEach((c, i) => i >= this.properties.defaultShowNum && c.setHidden(isHidden)); + }, + + onOrderCardTap() { + this.triggerEvent('cardtap'); + }, + + onShowMoreTap() { + this.setData({ showAll: true }, () => this.setHidden()); + this.triggerEvent('showall'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.json b/miniprogram/tcb-shop/pages/order/components/order-card/index.json new file mode 100644 index 0000000..8c3cde6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxml new file mode 100644 index 0000000..83258ee --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxml @@ -0,0 +1,17 @@ + + + + + + {{order.statusDesc}} + + + + + + + 展开商品信息(共 {{goodsCount}} 个) + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.wxss b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxss new file mode 100644 index 0000000..d241796 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxss @@ -0,0 +1,45 @@ +.order-card { + margin: 24rpx 0; + padding: 24rpx 32rpx 24rpx; + background-color: white; + border-radius: 8rpx; +} +.order-card .header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24rpx; +} +.order-card .header .store-name { + font-size: 28rpx; + font-weight: normal; + color: #333333; + display: flex; + align-items: center; + line-height: 40rpx; +} +.order-card .header .store-name__logo { + margin-right: 16rpx; + font-size: 40rpx; + width: 48rpx; + height: 48rpx; +} +.order-card .header .store-name__label { + max-width: 500rpx; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; + white-space: nowrap; +} +.order-card .header .order-status { + font-size: 26rpx; + line-height: 40rpx; + color: #fa4126; +} +.order-card .more-mask { + padding: 20rpx 0; + text-align: center; + background-color: white; + color: #fa4126; + font-size: 24rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js new file mode 100644 index 0000000..edaf299 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js @@ -0,0 +1,43 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + relations: { + '../order-card/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + properties: { + goods: Object, + thumbWidth: Number, + thumbHeight: Number, + thumbWidthInPopup: Number, + thumbHeightInPopup: Number, + noTopLine: Boolean, + step: Boolean, + stepDisabled: Boolean, + }, + + data: { + goods: {}, + hidden: false, + }, + + methods: { + setHidden(hidden) { + if (this.data.hidden === hidden) return; + this.setData({ hidden }); + }, + + onNumChange(e) { + const { value } = e.detail; + this.triggerEvent('num-change', { value }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json new file mode 100644 index 0000000..7743bba --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "goods-card": "../specs-goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml new file mode 100644 index 0000000..59778e8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js new file mode 100644 index 0000000..e5741e6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js @@ -0,0 +1,114 @@ +Component({ + properties: { + show: Boolean, + title: String, + options: { + type: Object, + observer() { + this.init(); + }, + }, + multiple: { + type: Boolean, + observer() { + this.init(); + }, + }, + showConfirmButton: Boolean, + showCloseButton: Boolean, + confirmButtonText: { + type: String, + value: '确定', + }, + cancelButtonText: { + type: String, + value: '取消', + }, + emptyTip: { + type: String, + value: '请选择', + }, + }, + + data: { + _options: [], + checkedIndexes: [], + }, + + methods: { + attached() { + this.toast = this.selectComponent('#t-toast'); + }, + + init() { + const checkedIndexes = []; + const _options = this.properties.options.map((opt, i) => { + const checked = !!opt.checked; + if (checked) { + if (this.properties.multiple) checkedIndexes[0] = i; + else checkedIndexes.push(i); + } + return { + title: opt.title, + checked, + }; + }); + this.setData({ checkedIndexes, _options }); + }, + + onOptionTap(e) { + const { index } = e.currentTarget.dataset; + const { checkedIndexes } = this.data; + let data = {}; + if (this.properties.multiple) { + if (checkedIndexes.includes(index)) { + checkedIndexes.splice(index, 1); + data = { checkedIndexes, [`_options[${index}].checked`]: false }; + } else { + checkedIndexes.push(index); + data = { checkedIndexes, [`_options[${index}].checked`]: true }; + } + } else { + if (checkedIndexes[0] === index) { + // 单选不可取消选择 + return; + } + data = { + [`_options[${index}].checked`]: true, + checkedIndexes: [index], + }; + if (checkedIndexes[0] !== undefined) { + data[`_options[${checkedIndexes[0]}].checked`] = false; + } + } + this.setData(data); + this.triggerEvent('select', { index }); + this._onOptionTap && this._onOptionTap(index); + if (!this.properties.showConfirmButton && !this.properties.multiple) { + // 没有确认按钮且是单选的情况下,选择选项则自动确定 + this._onConfirm && this._onConfirm([index]); + this.setData({ show: false }); + } + }, + + onCancel() { + this.triggerEvent('cancel'); + this._onCancel && this._onCancel(); + this.setData({ show: false }); + }, + + onConfirm() { + if (this.data.checkedIndexes.length === 0) { + this.toast.show({ + icon: '', + text: this.properties.emptyTip, + }); + return; + } + const indexed = this.data.checkedIndexes; + this.triggerEvent('confirm', { indexed }); + this._onConfirm && this._onConfirm(indexed); + this.setData({ show: false }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json new file mode 100644 index 0000000..a22be6b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json @@ -0,0 +1,10 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-button": "tdesign-miniprogram/button/button" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml new file mode 100644 index 0000000..35a5df2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml @@ -0,0 +1,50 @@ + + + + {{title}} + + + + + + + + + + + + {{confirmButtonText}} + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss new file mode 100644 index 0000000..ed92d44 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss @@ -0,0 +1,47 @@ +page view { + box-sizing: border-box; +} +.popup-content { + background-color: white; + color: #222427; + border-radius: 20rpx 20rpx 0 0; + overflow: hidden; +} +.popup-content .header { + height: 100rpx; + line-height: 100rpx; + text-align: center; + vertical-align: middle; + font-size: 32rpx; + font-weight: bold; + position: relative; +} +.popup-content .options { + max-height: 60vh; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} +.popup-content .options .cell { + height: 100rpx; + align-items: center; + font-size: 30rpx; + color: #333333; +} +.popup-content .button-bar { + width: 100%; + padding: 20rpx 30rpx; + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); +} +.popup-content .button-bar .btn { + width: 100%; + background: #fa4126; + color: #fff; + border-radius: 48rpx; +} +.button-bar .btnWrapper { + width: 100%; +} diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js b/miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js new file mode 100644 index 0000000..384f68f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js @@ -0,0 +1,25 @@ +function getInstance(context, selector = '#wr-reason-sheet') { + if (!context) { + const pages = getCurrentPages(); + const page = pages[pages.length - 1]; + context = page; + } + const instance = context && context.selectComponent(selector); + if (!instance) { + console.warn(`未找到reason-sheet组件,请检查selector是否正确`); + return null; + } + return instance; +} + +export default function (options) { + const { context, selector, ..._options } = options; + return new Promise((resolve, reject) => { + const instance = getInstance(context, selector); + if (instance) { + instance.setData(Object.assign({}, _options)); + instance._onCancel = () => reject(); + instance._onConfirm = (indexes) => resolve(indexes); + } + }); +} diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js b/miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js new file mode 100644 index 0000000..6e67895 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js @@ -0,0 +1,22 @@ +export const couponsData = { + couponResultList: [ + { + couponVO: { + condition: '满200元可用', + couponId: 11, + endTime: 1584530282686, + name: '折扣券', + profit: '5.5折', + promotionCode: 90, + promotionSubCode: 1, + scopeText: '部分商品可用', + startTime: 1584530282686, + storeId: 90, + value: 550, + type: 2, + }, + status: 0, // 0:未勾选。1:勾选。-1:置灰 + }, + ], + reduce: 1000, +}; diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs new file mode 100644 index 0000000..8c4ce2e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs @@ -0,0 +1,16 @@ +function formatDays(value) { + if (value < 10) { + return '0' + value; + } + return value; +} +var dateFormat = function (d) { + var date = getDate(+d); + return ( + date.getFullYear() + + '-' + + formatDays(date.getMonth() + 1) + + formatDays(date.getDate()) + ); +}; +module.exports.dateFormat = dateFormat; diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js new file mode 100644 index 0000000..235ffba --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js @@ -0,0 +1,160 @@ +import dayjs from 'dayjs'; +import { couponsData } from './mock'; + +const emptyCouponImg = `https://cdn-we-retail.ym.tencent.com/miniapp/coupon/ordersure-coupon-newempty.png`; + +Component({ + properties: { + storeId: String, + promotionGoodsList: { + type: Array, + value: [], + }, + orderSureCouponList: { + type: Array, + value: [], + }, + couponsShow: { + type: Boolean, + value: false, + observer(couponsShow) { + if (couponsShow) { + const { promotionGoodsList, orderSureCouponList, storeId } = + this.data; + const products = + promotionGoodsList && + promotionGoodsList.map((goods) => { + this.storeId = goods.storeId; + return { + skuId: goods.skuId, + spuId: goods.spuId, + storeId: goods.storeId, + selected: true, + quantity: goods.num, + prices: { + sale: goods.settlePrice, + }, + }; + }); + const selectedCoupons = + orderSureCouponList && + orderSureCouponList.map((ele) => { + return { + promotionId: ele.promotionId, + storeId: ele.storeId, + couponId: ele.couponId, + }; + }); + this.setData({ + products, + }); + this.coupons({ + products, + selectedCoupons, + storeId, + }).then((res) => { + this.initData(res); + }); + } + }, + }, + }, + data: { + emptyCouponImg, + goodsList: [], + selectedList: [], + couponsList: [], + orderSureCouponList: [], + promotionGoodsList: [], + }, + methods: { + initData(data = {}) { + const { couponResultList = [], reduce = 0 } = data; + const selectedList = []; + let selectedNum = 0; + const couponsList = + couponResultList && + couponResultList.map((coupon) => { + const { status, couponVO } = coupon; + const { + couponId, + condition = '', + endTime = 0, + name = '', + startTime = 0, + value, + type, + } = couponVO; + if (status === 1) { + selectedNum++; + selectedList.push({ + couponId, + promotionId: ruleId, + storeId: this.storeId, + }); + } + const val = type === 2 ? value / 100 : value / 10; + return { + key: couponId, + title: name, + isSelected: false, + timeLimit: `${dayjs(+startTime).format('YYYY-MM-DD')}-${dayjs( + +endTime, + ).format('YYYY-MM-DD')}`, + value: val, + status: status === -1 ? 'useless' : 'default', + desc: condition, + type, + tag: '', + }; + }); + this.setData({ + selectedList, + couponsList, + reduce, + selectedNum, + }); + }, + selectCoupon(e) { + const { key } = e.currentTarget.dataset; + const { couponsList, selectedList } = this.data; + couponsList.forEach((coupon) => { + if (coupon.key === key) { + coupon.isSelected = !coupon.isSelected; + } + }); + + const couponSelected = couponsList.filter( + (coupon) => coupon.isSelected === true, + ); + + this.setData({ + selectedList: [...selectedList, ...couponSelected], + couponsList: [...couponsList], + }); + + this.triggerEvent('sure', { + selectedList: [...selectedList, ...couponSelected], + }); + }, + hide() { + this.setData({ + couponsShow: false, + }); + }, + coupons(coupon = {}) { + return new Promise((resolve, reject) => { + if (coupon?.selectedCoupons) { + resolve({ + couponResultList: couponsData.couponResultList, + reduce: couponsData.reduce, + }); + } + return reject({ + couponResultList: [], + reduce: undefined, + }); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json new file mode 100644 index 0000000..c46dc55 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json @@ -0,0 +1,10 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "wr-price": "/components/price/index", + "coupon-card": "/pages/coupon/components/ui-coupon-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml new file mode 100644 index 0000000..10b8898 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml @@ -0,0 +1,43 @@ + + + + + + 选择优惠券 + + + 你有{{couponsList.length}}张可用优惠券 + + 已选中{{selectedNum}}张推荐优惠券, 共抵扣 + + + + + + + + + + + + 此优惠券不能和已勾选的优惠券叠加使用 + + + + + + + 暂无优惠券 + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss new file mode 100644 index 0000000..a8c795f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss @@ -0,0 +1,104 @@ +.select-coupons { + background: #fff; + width: 100%; + position: relative; + border-radius: 20rpx 20rpx 0 0; + padding-top: 28rpx; + padding-bottom: env(safe-area-inset-bottom); +} +.select-coupons .title { + width: 100%; + text-align: center; + font-size: 32rpx; + color: #333; + font-weight: 600; + line-height: 44rpx; +} +.select-coupons .info { + width: 100%; + height: 34rpx; + font-size: 24rpx; + color: #999; + line-height: 34rpx; + margin: 20rpx 0; + padding: 0 20rpx; +} +.select-coupons .info .price { + color: #fa4126; +} +.select-coupons .coupons-list { + max-height: 500rpx; +} +.select-coupons .coupons-list .coupons-wrap { + padding: 0rpx 20rpx; +} +.select-coupons .coupons-list .disable { + font-size: 24rpx; + color: #ff2525; + padding-top: 20rpx; +} +.select-coupons .coupons-list .slot-radio { + position: absolute; + right: 22rpx; + top: 50%; + transform: translateY(-50%); + display: inline-block; +} +.select-coupons .coupons-list .slot-radio .wr-check-filled { + font-size: 36rpx; +} +.select-coupons .coupons-list .slot-radio .check { + width: 36rpx; +} +.select-coupons .coupons-list .slot-radio .text-primary { + color: #fa4126; +} +.select-coupons .coupons-list .slot-radio .wr-check { + font-size: 36rpx; +} +.select-coupons .coupons-list .slot-radio .wr-uncheck { + font-size: 36rpx; + color: #999; +} +.select-coupons .couponp-empty-wrap { + padding: 40rpx; +} +.select-coupons .couponp-empty-wrap .couponp-empty-img { + display: block; + width: 240rpx; + height: 240rpx; + margin: 0 auto; +} +.select-coupons .couponp-empty-wrap .couponp-empty-title { + font-size: 28rpx; + color: #999; + text-align: center; + line-height: 40rpx; + margin-top: 40rpx; +} +.select-coupons .coupons-cover { + height: 112rpx; + width: 100%; + box-sizing: border-box; + margin-top: 30rpx; + padding: 12rpx 32rpx; + display: flex; + justify-content: space-between; + align-items: center; +} +.select-coupons .coupons-cover .btn { + width: 332rpx; + height: 88rpx; + text-align: center; + line-height: 88rpx; + font-size: 32rpx; + border-radius: 44rpx; + box-sizing: border-box; + border: 2rpx solid #dddddd; + color: #333333; +} +.select-coupons .coupons-cover .red { + border-color: #fa4126; + background-color: #fa4126; + color: #ffffff; +} diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js new file mode 100644 index 0000000..d5cd284 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js @@ -0,0 +1,132 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + externalClasses: [ + 'title-class', + 'desc-class', + 'num-class', + 'thumb-class', + 'specs-class', + 'price-class', + 'origin-price-class', + 'price-prefix-class', + ], + + relations: { + '../order-card/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + properties: { + id: String, + hidden: { + // 设置为null代表不做类型转换 + type: null, + observer(hidden) { + // null就是代表没有设置,没有设置的话不setData,防止祖先组件触发的setHidden操作被覆盖 + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + data: Object, + layout: { + type: String, + value: 'horizontal', + }, + thumbMode: { + type: String, + value: 'aspectFill', + }, + thumbWidth: Number, + thumbHeight: Number, + thumbWidthInPopup: Number, + thumbHeightInPopup: Number, + priceFill: { + type: Boolean, + value: true, + }, + currency: { + type: String, + value: '¥', + }, + lazyLoad: Boolean, + centered: Boolean, + showCart: Boolean, + pricePrefix: String, + cartSize: { + type: Number, + value: 48, + }, + cartColor: { + type: String, + value: '#FA550F', + }, + disablePopup: Boolean, + }, + + data: { + hiddenInData: false, + specsPopup: { + insert: false, + show: false, + }, + }, + + currentInTapSpecs: false, + + lifetimes: { + ready() { + const { hidden } = this.properties; + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + + methods: { + closeSpecsPopup() { + this.setData({ + 'specsPopup.show': false, + }); + this.triggerEvent('specsclose', { good: this.properties.data }); + }, + + removeSpecsPopup() { + this.setData({ + 'specsPopup.insert': false, + }); + }, + + onClick(e) { + if (this.currentInTapSpecs) { + this.currentInTapSpecs = false; + return; + } + this.triggerEvent('click', e.detail); + }, + + onClickThumb(e) { + this.triggerEvent('thumb', e.detail); + }, + + onClickTag(e) { + this.triggerEvent('tag', e.detail); + }, + + onClickCart(e) { + this.triggerEvent('add-cart', e.detail); + }, + + setHidden(hidden) { + this.setData({ hiddenInData: !!hidden }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json new file mode 100644 index 0000000..ccbce72 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "goods-card": "../goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml new file mode 100644 index 0000000..f6f2a38 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml @@ -0,0 +1,40 @@ + + diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxss b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxss new file mode 100644 index 0000000..e69de29 diff --git a/miniprogram/tcb-shop/pages/order/config.js b/miniprogram/tcb-shop/pages/order/config.js new file mode 100644 index 0000000..20a3317 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/config.js @@ -0,0 +1,94 @@ +export const OrderStatus = { + PENDING_PAYMENT: 5, // 待支付 + PENDING_DELIVERY: 10, // 待发货 + PENDING_RECEIPT: 40, // 待收货 + COMPLETE: 50, // 已完成/待评价 + PAYMENT_TIMEOUT: 80, // 已取消,支付超时 + CANCELED_NOT_PAYMENT: 80, // 已取消,未支付主动取消 + CANCELED_PAYMENT: 80, // 已取消,已支付主动取消 + CANCELED_REJECTION: 80, // 已取消,拒收 +}; + +// 售后状态 10:待审核,20:已审核,30:已收货,40:收货异常,50:已完成,60:已关闭; +export const AfterServiceStatus = { + TO_AUDIT: 10, // 待审核 + THE_APPROVED: 20, // 已审核 + HAVE_THE_GOODS: 30, // 已收货 + ABNORMAL_RECEIVING: 40, // 收货异常 + COMPLETE: 50, // 已完成 + CLOSED: 60, // 已关闭 +}; + +// 售后类型 +export const ServiceType = { + RETURN_GOODS: 10, // 退货退款 + ONLY_REFUND: 20, // 仅退款 + ORDER_CANCEL: 30, // 支付后取消 +}; + +export const ServiceTypeDesc = { + [ServiceType.RETURN_GOODS]: '退货', + [ServiceType.ONLY_REFUND]: '退款', + [ServiceType.ORDER_CANCEL]: '支付后取消', +}; + +// 订单按钮类型 +export const OrderButtonTypes = { + PAY: 1, // 付款 + CANCEL: 2, // 取消订单 + CONFIRM: 3, // 确认收货 + APPLY_REFUND: 4, // 申请售后 + VIEW_REFUND: 5, // 查看退款 + COMMENT: 6, // 评价 + DELETE: 7, // 删除订单 + DELIVERY: 8, // 查看物流 + REBUY: 9, // 再次购买 + INVITE_GROUPON: 11, //邀请好友拼团 +}; + +// 售后服务按钮类型 +export const ServiceButtonTypes = { + REVOKE: 2, // 撤销 + FILL_TRACKING_NO: 3, // 填写运单号 + CHANGE_TRACKING_NO: 4, // 修改运单号 + VIEW_DELIVERY: 5, // 查看物流 +}; + +// 售后状态 +export const ServiceStatus = { + PENDING_VERIFY: 100, //待审核 + VERIFIED: 110, // 已审核待寄回商品 + PENDING_DELIVERY: 120, // 等待买家寄回商品 + PENDING_RECEIPT: 130, // 已寄回商品,待收货 + RECEIVED: 140, // 已收货 + EXCEPTION: 150, // 收货异常 + REFUNDED: 160, // 已退款 + CLOSED: 170, // 已关闭 +}; + +// 售后收货状态 +export const ServiceReceiptStatus = { + RECEIPTED: 1, // 已收到货 + NOT_RECEIPTED: 2, // 未收到货 +}; + +// 物流节点 +export const LogisticsNodeTypes = { + SUBMITTED: 200001, // 已提交订单 + PAYMENTED: 200002, // 已付款/已下单 + SHIPPED: 200003, // 已发货 + CANCELED: 200004, // 已取消 + RECEIVED: 200005, // 已签收 + ADDRESS_CHANGED: 200006, // 已修改地址 + IN_TRANSIT: 200007, // 运输中 +}; + +export const LogisticsIconMap = { + [LogisticsNodeTypes.SUBMITTED]: '', + [LogisticsNodeTypes.PAYMENTED]: 'credit_card', + [LogisticsNodeTypes.SHIPPED]: 'deliver', + [LogisticsNodeTypes.CANCELED]: '', + [LogisticsNodeTypes.RECEIVED]: 'check', + [LogisticsNodeTypes.ADDRESS_CHANGED]: '', + [LogisticsNodeTypes.IN_TRANSIT]: 'yunshuzhong', +}; diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.js b/miniprogram/tcb-shop/pages/order/delivery-detail/index.js new file mode 100644 index 0000000..f10ff02 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.js @@ -0,0 +1,43 @@ +Page({ + data: { + logisticsData: { + logisticsNo: '', + nodes: [], + company: '', + phoneNumber: '', + }, + active: 0, + }, + + onLoad(query) { + let data; + try { + data = JSON.parse(decodeURIComponent(query.data || '{}')); + } catch (e) { + console.warn('物流节点数据解析失败', e); + } + if (Number(query.source) === 2) { + const service = { + company: data.logisticsCompanyName, + logisticsNo: data.logisticsNo, + nodes: data.nodes, + }; + this.setData({ + logisticsData: service, + }); + } else if (data) { + this.setData({ logisticsData: data }); + } + }, + + onLogisticsNoCopy() { + wx.setClipboardData({ data: this.data.logisticsData.logisticsNo }); + }, + + onCall() { + const { phoneNumber } = this.data.logisticsData; + wx.makePhoneCall({ + phoneNumber, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.json b/miniprogram/tcb-shop/pages/order/delivery-detail/index.json new file mode 100644 index 0000000..2ba9163 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "物流信息", + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-steps": "tdesign-miniprogram/steps/steps", + "t-step": "tdesign-miniprogram/step-item/step-item" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml new file mode 100644 index 0000000..b5a8bf5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml @@ -0,0 +1,91 @@ + + var isUrl = function(item) { + return item.indexOf('http') > -1; + } + module.exports = { + isUrl: isUrl, + } + + + + + + {{logisticsData.logisticsNo}} + 复制 + + + + + 拨打 + + + + + + + + + + + + + + + {{item.desc}} + {{item.date}} + + + + + diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss new file mode 100644 index 0000000..6a2524c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss @@ -0,0 +1,99 @@ +page { + background-color: #f5f5f5; +} +.page-section { + margin-top: 24rpx; + background-color: white; +} +.page-section .order-group__left { + margin-right: 0 !important; +} +.cell-steps { + padding: 8rpx; +} +.wr-cell__title { + flex: none; + font-size: 28rpx; + color: #666; +} +.wr-cell__value { + flex: auto; + margin-left: 30rpx; + font-size: 28rpx; + color: #333 !important; +} +.logistics-no { + display: inline-block; + text-align: left; + word-break: break-all; + color: #333; +} +.text-btn { + margin-left: 20rpx; + display: inline; + font-size: 24rpx; + padding: 0 15rpx; + border: 1rpx solid #ddd; + border-radius: 28rpx; + color: #333; +} +.text-btn--active { + opacity: 0.5; +} +.steps .step-title { + font-weight: bold; + color: #333 !important; + font-size: 30rpx; +} +.steps .step-desc { + color: #333333; + font-size: 28rpx; +} +.steps .step-date { + color: #999999; + font-size: 24rpx; +} + +.cell-steps__img, +.cell-steps__imgWrapper { + width: 48rpx; + height: 48rpx; +} + +.steps + .t-step--vertical.t-step--default-anchor + .t-steps-item--process + .t-steps-item__icon-number { + background: #ffece9 !important; + color: white !important; + border: none; +} + +.steps + .t-step--vertical.t-step--default-anchor + .t-steps-item--default + .t-steps-item__icon-number { + color: white !important; + background: #f5f5f5 !important; + border: none; +} + +.steps + .t-step--vertical.t-step--default-anchor.t-step--not-last-child + .t-steps-item__inner::after { + top: 48rpx; + height: calc(100% - 44rpx - 4rpx); +} + +.steps + .t-step--vertical.t-step--default-anchor.t-step--not-last-child + .t-steps-item__inner::after, +.steps + .t-step--vertical.t-step--default-anchor.t-step--not-last-child + .t-steps-item--default + .t-steps-item__inner:after { + background: #f5f5f5 !important; +} +.page-section__steps { + padding: 24rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js b/miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js new file mode 100644 index 0000000..33f41de --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js @@ -0,0 +1,71 @@ +import { mockIp, mockReqId } from '../../../utils/mock'; + +export function create() { + const _resq = { + data: null, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + return Promise.resolve(_resq); +} + +export function update() { + const _resq = { + data: null, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + return Promise.resolve(_resq); +} + +export function getDeliverCompanyList() { + const _resq = { + data: [ + { + name: '中通快递', + code: '0001', + }, + { + name: '申通快递', + code: '0002', + }, + { + name: '圆通快递', + code: '0003', + }, + { + name: '顺丰快递', + code: '0004', + }, + { + name: '百世快递', + code: '0005', + }, + { + name: '韵达快递', + code: '0006', + }, + { + name: '邮政快递', + code: '0007', + }, + { + name: '丰网快递', + code: '0008', + }, + { + name: '顺丰直邮', + code: '0009', + }, + ], + }; + return Promise.resolve(_resq); +} diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js new file mode 100644 index 0000000..dd6ff3b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js @@ -0,0 +1,190 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; +import reasonSheet from '../components/reason-sheet/reasonSheet'; +import { getDeliverCompanyList, create, update } from './api'; + +Page({ + deliveryCompanyList: [], + + data: { + trackingNo: '', + remark: '', + deliveryCompany: null, + submitActived: false, + submitting: false, + }, + onLoad(query) { + const { + rightsNo = '', + logisticsNo = '', + logisticsCompanyName = '', + logisticsCompanyCode = '', + remark = '', + } = query; + + if (!rightsNo) { + Dialog.confirm({ + title: '请选择售后单?', + content: '', + confirmBtn: '确认', + }).then(() => { + wx.navigateBack({ backRefresh: true }); + }); + } + this.rightsNo = rightsNo; + if (logisticsNo) { + wx.setNavigationBarTitle({ + title: '修改运单号', + fail() {}, + }); + this.isChange = true; + this.setData({ + deliveryCompany: { + name: logisticsCompanyName, + code: logisticsCompanyCode, + }, + trackingNo: logisticsNo, + remark, + submitActived: true, + }); + } + this.setWatcher('trackingNo', this.checkParams.bind(this)); + this.setWatcher('deliveryCompany', this.checkParams.bind(this)); + }, + + setWatcher(key, callback) { + let lastData = this.data; + const keys = key.split('.'); + keys.slice(0, -1).forEach((k) => { + lastData = lastData[k]; + }); + const lastKey = keys[keys.length - 1]; + this.observe(lastData, lastKey, callback); + }, + + observe(data, k, callback) { + let val = data[k]; + Object.defineProperty(data, k, { + configurable: true, + enumerable: true, + set: (value) => { + val = value; + callback(); + }, + get: () => { + return val; + }, + }); + }, + + getDeliveryCompanyList() { + if (this.deliveryCompanyList.length > 0) { + return Promise.resolve(this.deliveryCompanyList); + } + return getDeliverCompanyList().then((res) => { + this.deliveryCompanyList = res.data || []; + return this.deliveryCompanyList; + }); + }, + + onInput(e) { + const { key } = e.currentTarget.dataset; + const { value } = e.detail; + this.setData({ [key]: value }); + }, + + onCompanyTap() { + this.getDeliveryCompanyList().then((deliveryCompanyList) => { + reasonSheet({ + show: true, + title: '选择物流公司', + options: deliveryCompanyList.map((company) => ({ + title: company.name, + checked: this.data.deliveryCompany + ? company.code === this.data.deliveryCompany.code + : false, + })), + showConfirmButton: true, + showCancelButton: true, + emptyTip: '请选择物流公司', + }).then((indexes) => { + this.setData({ + deliveryCompany: deliveryCompanyList[indexes[0]], + }); + }); + }); + }, + + checkParams() { + const res = { errMsg: '', require: false }; + + if (!this.data.trackingNo) { + res.errMsg = '请填写运单号'; + res.require = true; + } else if (!this.data.deliveryCompany) { + res.errMsg = '请选择物流公司'; + res.require = true; + } + this.setData({ submitActived: !res.require }); + return res; + }, + + onSubmit() { + const checkRes = this.checkParams(); + if (checkRes.errMsg) { + Toast({ + context: this, + selector: '#t-toast', + message: checkRes.errMsg, + icon: '', + }); + return; + } + + const { + trackingNo, + remark, + deliveryCompany: { code, name }, + } = this.data; + + const params = { + rightsNo: this.rightsNo, + logisticsCompanyCode: code, + logisticsCompanyName: name, + logisticsNo: trackingNo, + remark, + }; + const api = this.isChange ? create : update; + this.setData({ submitting: true }); + api(params) + .then(() => { + this.setData({ submitting: false }); + Toast({ + context: this, + selector: '#t-toast', + message: '保存成功', + icon: '', + }); + setTimeout(() => wx.navigateBack({ backRefresh: true }), 1000); + }) + .catch(() => { + this.setData({ submitting: false }); + }); + }, + + onScanTap() { + wx.scanCode({ + scanType: ['barCode'], + success: (res) => { + Toast({ + context: this, + selector: '#t-toast', + message: '扫码成功', + icon: '', + }); + this.setData({ trackingNo: res.result }); + }, + fail: () => {}, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json new file mode 100644 index 0000000..bd1bb20 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json @@ -0,0 +1,14 @@ +{ + "navigationBarTitleText": "填写运单号", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-input": "tdesign-miniprogram/input/input", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-button": "tdesign-miniprogram/button/button", + "ui-reason-sheet": "../components/reason-sheet/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml new file mode 100644 index 0000000..7949a0e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml @@ -0,0 +1,51 @@ + + 请填写正确的退货包裹运单信息,以免影响退款进度 + + + + + + + + + + + 备注信息 + + + + + + 保存 + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss new file mode 100644 index 0000000..726ab89 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss @@ -0,0 +1,103 @@ +@import '../../../style/theme.wxss'; + +:host { + background-color: #f5f5f5; +} + +.notice-bar { + padding: 24rpx 30rpx; + text-align: center; + font-size: 26rpx; + color: #e17349; + background: #fefcef; +} + +.fill-tracking-no__form { + margin-top: 20rpx; +} + +.fill-tracking-no__form .t-cell__note { + justify-content: flex-start; +} + +.fill-tracking-no__form .t-cell__value { + color: #333 !important; + font-size: 30rpx; + text-align: left; + padding: 0; +} + +.fill-tracking-no__form .t-cell__value::after { + border: none !important; +} + +.fill-tracking-no__form .t-cell__value .t-textarea__wrapper { + padding: 0; +} + +.fill-tracking-no__form .t-input__control, +.fill-tracking-no__form .t-textarea__placeholder, +.fill-tracking-no__form .t-cell__placeholder { + font-size: 30rpx !important; +} + +.fill-tracking-no__form .t-textarea__placeholder, +.fill-tracking-no__form .t-cell__placeholder { + color: #bbbbbb !important; +} + +.t-textarea__note { + width: 100%; +} + +.fill-tracking-no__button-bar { + margin: 38rpx 30rpx 0; +} + +.fill-tracking-no__button-bar .btn { + background-color: transparent; + font-size: 32rpx; + width: 100%; + border-radius: 48rpx; +} + +.fill-tracking-no__button-bar .btn:first-child { + margin-bottom: 20rpx; +} + +.fill-tracking-no__button-bar .btn.confirmBtn { + background: #fa4126; + color: #fff; +} + +.fill-tracking-no__button-bar .btn.disabled { + background-color: #c6c6c6; + color: #fff; +} + +.t-cell-title-width { + width: 160rpx; + flex: none !important; +} +.textarea-wrapper { + background: #fff; + display: flex; + align-items: flex-start; + padding: 24rpx 32rpx 0 32rpx; +} +.t-textarea-wrapper { + box-sizing: border-box; +} + +.fill-tracking-no__form .t-input__wrapper { + margin: 0 !important; +} + +.fill-tracking-no__form { + --td-input-vertical-padding: 0; +} + +.t-button { + --td-button-default-color: #aeb3b7; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.js b/miniprogram/tcb-shop/pages/order/invoice/index.js new file mode 100644 index 0000000..5333802 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.js @@ -0,0 +1,38 @@ +import { fetchOrderDetail } from '../../../services/order/orderDetail'; + +Page({ + data: { + invoice: {}, + }, + onLoad({ orderNo }) { + this.orderNo = orderNo; + this.init(); + }, + init() { + this.getDetail(); + }, + getDetail() { + const params = { + parameter: this.orderNo, + }; + return fetchOrderDetail(params).then((res) => { + const order = res.data; + + const invoice = { + buyerName: order?.invoiceVO?.buyerName, //个人或公司名称 + buyerTaxNo: order?.invoiceVO?.buyerTaxNo, //税号 + buyerPhone: order?.invoiceVO?.buyerPhone, //手机 + email: order?.invoiceVO?.email, //邮箱 + titleType: order?.invoiceVO?.titleType === 1 ? '个人' : '公司', //发票抬头 1-个人 2-公司 + ontentType: order?.invoiceVO?.ontentType === 1 ? '商品明细' : '2类别', //发票内容 1-明细 2类别 + invoiceType: + order?.invoiceVO?.invoiceType === 5 ? '电子普通发票' : '不开发票', //是否开票 0-不开 5-电子发票 + isInvoice: order?.invoiceVO?.buyerName ? '已开票' : '未开票', + money: order?.invoiceVO?.money, + }; + this.setData({ + invoice, + }); + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.json b/miniprogram/tcb-shop/pages/order/invoice/index.json new file mode 100644 index 0000000..ea83b65 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "发票详情", + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-button": "tdesign-miniprogram/button/button", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group" + } +} diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.wxml b/miniprogram/tcb-shop/pages/order/invoice/index.wxml new file mode 100644 index 0000000..b3bc794 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.wxml @@ -0,0 +1,40 @@ + + + 发票详情 + + 发票类型 + {{invoice.invoiceType}} + + + 发票抬头 + {{invoice.buyerName}} + + + 纳税人识别号 + {{invoice.buyerTaxNo}} + + + 发票内容 + {{invoice.ontentType}} + + + 发票金额 + {{invoice.money}} + + + + 收票人信息 + + 邮箱 + {{invoice.email}} + + + 手机号 + {{invoice.buyerPhone}} + + + 开票状态 + {{invoice.isInvoice}} + + + diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.wxss b/miniprogram/tcb-shop/pages/order/invoice/index.wxss new file mode 100644 index 0000000..0a0379b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.wxss @@ -0,0 +1,31 @@ +:host { + background-color: #f5f5f5; +} + +.invoice-detail .invoice-detail-box { + background-color: #fff; + padding: 24rpx 32rpx; + margin-top: 24rpx; +} + +.invoice-detail-title { + font-size: 14px; + font-weight: 600; +} + +.invoice-detail-box-row { + display: flex; + margin-top: 44rpx; +} + +.invoice-detail-box-title { + font-size: 13px; + color: #666666; + width: 156rpx; + margin-right: 32rpx; +} + +.invoice-detail-box-value { + font-size: 13px; + color: #333333; +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js new file mode 100644 index 0000000..96cf8ef --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js @@ -0,0 +1,25 @@ +/* + * @Author: rileycai + * @Date: 2022-03-05 16:47:16 + * @LastEditTime: 2022-03-05 16:48:32 + * @LastEditors: rileycai + * @Description: + * @FilePath: /tdesign-miniprogram-starter/pages/order/order-confirm/components/address-card/index.js + */ +Component({ + externalClasses: ['wr-class'], + properties: { + addressData: { + type: Object, + value: {}, + }, + }, + methods: { + onAddressTap() { + this.triggerEvent('addressclick'); + }, + onAddTap() { + this.triggerEvent('addclick'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json new file mode 100644 index 0000000..08ecc96 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml new file mode 100644 index 0000000..a6294aa --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml @@ -0,0 +1,25 @@ + + var hidePhoneNum = function(array) { + if (!array) return; + var mphone = array.substring(0, 3) + '****' + array.substring(7); + return mphone; + } + module.exports = { + hidePhoneNum:hidePhoneNum + } + + + + + + {{addressData.detailAddress}} + {{addressData.name}} {{utils.hidePhoneNum(addressData.phone)}} + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss new file mode 100644 index 0000000..aaff4bd --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss @@ -0,0 +1,66 @@ +.address-card { + background: #fff; + margin: 0rpx 0rpx 24rpx; +} +.address-card .wr-cell__title { + color: #999; + margin-left: 6rpx; +} +.address-card .order-address { + display: flex; + width: 100%; +} +.address-card .order-address .address-content { + flex: 1; +} +.order-address .address__right { + align-self: center; +} +.address-card .order-address .title { + display: flex; + align-items: center; + height: 40rpx; + font-size: 28rpx; + font-weight: normal; + color: #999999; + line-height: 40rpx; +} +.address-card .order-address .title .address-tag { + width: 52rpx; + height: 29rpx; + border: 1rpx solid #0091ff; + background-color: rgba(122, 167, 251, 0.1); + text-align: center; + line-height: 29rpx; + border-radius: 8rpx; + color: #0091ff; + font-size: 20rpx; + margin-right: 12rpx; +} +.address-card .order-address .detail { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + font-size: 36rpx; + font-weight: bold; + color: #333333; + line-height: 48rpx; + margin: 8rpx 0; +} +.address-card .order-address .info { + height: 40rpx; + font-size: 28rpx; + font-weight: normal; + color: #333333; + line-height: 40rpx; +} +.address-card .top-line { + width: 100%; + height: 6rpx; + background-color: #fff; + background-image: url(https://cdn-we-retail.ym.tencent.com/miniapp/order/stripe.png); + background-repeat: repeat-x; + display: block; +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs b/miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs new file mode 100644 index 0000000..935962c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs @@ -0,0 +1,11 @@ +var getNotes = function (storeInfoList, storeIndex) { + if (!storeInfoList) { + return ''; + } + var storeInfo = storeInfoList[storeIndex]; + if (!storeInfo) { + return ''; + } + return storeInfoList[storeIndex].remark; +}; +module.exports = getNotes; diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs b/miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs new file mode 100644 index 0000000..30e50f9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs @@ -0,0 +1,11 @@ +var handleInvoice = function (invoiceData) { + if (!invoiceData || invoiceData.invoiceType == 0) { + return '暂不开发票'; + } + var title = invoiceData.titleType == 2 ? '公司' : '个人'; + var content = invoiceData.contentType == 2 ? '商品类别' : '商品明细'; + return invoiceData.email + ? '电子普通发票 (' + content + ' - ' + title + ')' + : '暂不开发票'; +}; +module.exports = handleInvoice; diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.js b/miniprogram/tcb-shop/pages/order/order-confirm/index.js new file mode 100644 index 0000000..f2b3cf8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.js @@ -0,0 +1,308 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { createOrderItem } from '../../../services/order/orderItem'; +import { createOrder, ORDER_STATUS, updateOrderStatus } from '../../../services/order/order'; +import { getCartItem, deleteCartItem } from '../../../services/cart/cart'; +import { getSkuDetail, updateSku } from '../../../services/sku/sku'; +import { getAddressPromise } from '../../usercenter/address/list/util'; +import { getSingleCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { cartShouldFresh } from '../../../utils/cartFresh'; +import { pay } from '../../../services/pay/pay'; + +const stripeImg = `https://cdn-we-retail.ym.tencent.com/miniapp/order/stripe.png`; + +async function createOrderItemFromSku({ count, orderId, skuId }) { + const latestSku = await getSkuDetail(skuId); + const finalCount = latestSku.count - count; + + // check if sku is enough + if (finalCount < 0) { + return Promise.reject({ reason: 'SKU_NOT_ENOUGH' }); + } + + try { + // decrease sku's count + await updateSku({ + skuId, + data: { + count: finalCount, + }, + }); + try { + // create order item + await createOrderItem({ count, orderId, skuId }); + } catch (e) { + console.error(e); + return Promise.reject({ reason: 'CREATE_ORDER_ITEM_FAILED' }); + } + } catch (e) { + console.error(e); + return Promise.reject({ reason: 'SKU_DECREASE_FAILED' }); + } +} + +/** + * + * @param {Object} cartItem + * @param {String} orderId + */ +function createOrderItemFromCartItem(cartItem, orderId) { + return createOrderItemFromSku({ count: cartItem.count, orderId, skuId: cartItem.sku._id }); +} + +/** + * + * @param {Array} cartItems + */ +function cartItemToGoodList(cartItems) { + return cartItems.map((item) => ({ + thumb: item.sku.image, + title: item.sku.spu.name, + specs: item.sku.attr_value.map((v) => v.value).join(','), + price: item.sku.price, + num: item.count, + })); +} + +Page({ + data: { + placeholder: '备注信息', + stripeImg, + loading: true, + orderCardList: [], // 仅用于商品卡片展示 + goodsRequestList: [], + userAddressReq: null, + storeInfoList: [], + promotionGoodsList: [], //当前门店商品列表(优惠券) + currentStoreId: null, //当前优惠券storeId + userAddress: null, + goodsList: [], + cartItems: [], + totalSalePrice: 0, + directSku: null, + }, + + payLock: false, + + type: null, + + async onLoadFromCart(cartIds) { + if (typeof cartIds !== 'string') { + console.error('invalid cart item ids', cartIds); + this.failedAndBack('获取购物车信息失败'); + return; + } + const ids = cartIds.split(','); + try { + const cartItems = await Promise.all( + ids.map(async (id) => { + const cartItem = (await getCartItem({ id })).data; + cartItem.sku = { ...cartItem.sku, ...(await getSkuDetail(cartItem.sku._id)) }; + cartItem.sku.image = await getSingleCloudImageTempUrl(cartItem.sku.image); + return cartItem; + }), + ); + const goodsList = cartItemToGoodList(cartItems); + const totalSalePrice = goodsList.reduce((acc, cur) => acc + cur.price * cur.num, 0); + this.setData({ + goodsList, + totalSalePrice, + cartItems, + }); + } catch (e) { + this.failedAndBack('获取购物车信息失败', e); + } + }, + async onLoadFromDirect(countStr, skuId) { + const count = parseInt(countStr); + if (typeof count !== 'number' || isNaN(count) || typeof skuId !== 'string') { + console.error('invalid cunt or skiId', count, skuId); + this.failedAndBack('初始化信息有误'); + return; + } + + try { + const sku = await getSkuDetail(skuId); + sku.image = await getSingleCloudImageTempUrl(sku.image); + + const goodsList = [ + { + thumb: sku.image, + title: sku.spu.name, + specs: sku.attr_value.map((v) => v.value).join(','), + price: sku.price, + num: count, + }, + ]; + + const totalSalePrice = goodsList.reduce((acc, cur) => acc + cur.price * cur.num, 0); + this.setData({ + goodsList, + totalSalePrice, + directSku: sku, + }); + } catch (e) { + this.failedAndBack('获取商品信息失败', e); + } + }, + + async onLoad(options) { + this.type = options?.type; + if (this.type === 'cart') { + await this.onLoadFromCart(options?.cartIds); + } else if (this.type === 'direct') { + await this.onLoadFromDirect(options?.count, options?.skuId); + } else { + this.failedAndBack('初始化信息有误', 'invalid type'); + } + + this.setData({ + loading: false, + }); + }, + + init() { + this.setData({ + loading: false, + }); + const { goodsRequestList } = this; + this.handleOptionsParams({ goodsRequestList }); + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + duration: 1000, + icon: '', + }); + }, + + onGotoAddress() { + /** 获取一个Promise */ + getAddressPromise() + .then((address) => { + this.setData({ + userAddress: { + ...address, + detailAddress: address.address, + }, + }); + }) + .catch(() => {}); + + wx.navigateTo({ + url: `/pages/usercenter/address/list/index?selectMode=true`, + }); + }, + onTap() { + this.setData({ + placeholder: '', + }); + }, + + async payImpl(totalPrice, orderId) { + try { + await pay({ _id: orderId, totalPrice }); + try { + await updateOrderStatus({ orderId, status: ORDER_STATUS.TO_SEND }); + this.toast('支付成功'); + } catch (e) { + console.error(e); + this.toast('支付成功,但订单状态更新失败'); + } finally { + setTimeout(() => { + wx.navigateBack(); + }, 1000); + } + } catch (e) { + this.failedAndBack('支付失败', e); + } + }, + + async submitOrderFromCart() { + /** + * 1.创建订单 + * 2.创建订单项 + * 3.删除购物车项 + */ + + const { cartItems, userAddress } = this.data; + const { id: orderId } = await createOrder({ status: ORDER_STATUS.TO_PAY, addressId: userAddress._id }); + + try { + await cartItems.map(async (cartItem) => { + await createOrderItemFromCartItem(cartItem, orderId); + }); + + try { + await cartItems.map(async (cartItem) => { + await deleteCartItem({ cartItemId: cartItem._id }); + // any deletion should notify cart to fresh + cartShouldFresh(); + }); + } catch (e) { + console.error(e); + this.toast('删除购物车失败,请手动删除'); + // do not return, continue to pay + } + + const totalPrice = cartItems.reduce((acc, cur) => acc + cur.count * cur.sku.price, 0); + await this.payImpl(totalPrice, orderId); + } catch (e) { + this.failedAndBack('创建订单失败', e); + } + }, + + async submitOrderFromDirect() { + /** + * 1.创建订单 + * 2.创建订单项 + */ + + const { directSku, userAddress, goodsList } = this.data; + const goods = goodsList[0]; + const { id: orderId } = await createOrder({ status: ORDER_STATUS.TO_PAY, addressId: userAddress._id }); + + try { + await createOrderItemFromSku({ count: goods.num, orderId, skuId: directSku._id }); + const totalPrice = goods.price * goods.num; + + await this.payImpl(totalPrice, orderId); + } catch (e) { + this.failedAndBack('创建订单失败', e); + } + }, + + failedAndBack(message, e) { + e && console.error(e); + this.toast(message); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + }, + + // 提交订单 + async submitOrder() { + const { userAddress } = this.data; + if (!userAddress) { + Toast({ + context: this, + selector: '#t-toast', + message: '请添加收货地址', + duration: 2000, + icon: 'help-circle', + }); + return; + } + + if (this.type === 'cart') { + this.submitOrderFromCart(); + } else if (this.type === 'direct') { + this.submitOrderFromDirect(); + } else { + console.error('invalid type', this.type); + this.failedAndBack('初始化信息有误'); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.json b/miniprogram/tcb-shop/pages/order/order-confirm/index.json new file mode 100644 index 0000000..11f25ac --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.json @@ -0,0 +1,16 @@ +{ + "navigationBarTitleText": "订单确认", + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "price": "/components/price/index", + "select-coupons": "../components/selectCoupons/selectCoupons", + "no-goods": "../components/noGoods/noGoods", + "t-image": "/components/webp-image/index", + "address-card": "./components/address-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.wxml b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxml new file mode 100644 index 0000000..b01ce44 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxml @@ -0,0 +1,39 @@ + + + + + + + {{goods.title}} + {{goods.specs}} + + + + x{{goods.num}} + + + + + + 商品总额 + + + + + + 共{{goodsList.length}}件 + 小计 + + + + + + + + 提交订单 + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.wxss b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxss new file mode 100644 index 0000000..8b201c8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxss @@ -0,0 +1,221 @@ +.order-sure { + box-sizing: border-box; + background: #f6f6f6; + padding: 24rpx 0 calc(env(safe-area-inset-bottom) + 136rpx); + min-height: 100vh; +} + +.order-sure .wx-pay-cover { + position: fixed; + left: 0; + bottom: 0; + right: 0; + z-index: 10; + background: #fff; + height: 112rpx; + padding-bottom: env(safe-area-inset-bottom); +} +.order-sure .wx-pay-cover .wx-pay { + width: 100%; + height: 100rpx; + box-sizing: border-box; + padding: 0rpx 32rpx; + display: flex; + justify-content: space-between; + align-items: center; +} +.order-sure .wx-pay-cover .wx-pay .price { + color: #fa4126; + font-weight: bold; + font-size: 63rpx; + line-height: 88rpx; +} + +.order-sure .wx-pay-cover .wx-pay .submit-btn { + height: 80rpx; + width: 240rpx; + border-radius: 40rpx; + background-color: #fa4126; + color: #ffffff; + line-height: 80rpx; + font-weight: bold; + font-size: 28rpx; + text-align: center; +} +.order-sure .wx-pay-cover .wx-pay .btn-gray { + background: #cccccc; +} + +.order-wrapper .store-wrapper { + width: 100%; + height: 96rpx; + box-sizing: border-box; + padding: 0 32rpx; + display: flex; + align-items: center; + font-size: 28rpx; + line-height: 40rpx; + color: #333333; + background-color: #ffffff; +} +.order-wrapper .store-wrapper .store-logo { + margin-right: 16rpx; +} +.order-wrapper .goods-wrapper { + width: 100%; + box-sizing: border-box; + padding: 16rpx 32rpx; + display: flex; + align-items: flex-start; + justify-content: space-between; + font-size: 24rpx; + line-height: 32rpx; + color: #999999; + background-color: #ffffff; +} +.goods-wrapper .goods-image { + width: 176rpx; + height: 176rpx; + border-radius: 8rpx; + overflow: hidden; + margin-right: 16rpx; +} +.goods-wrapper .goods-content { + flex: 1; +} + +.goods-wrapper .goods-content .goods-title { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + font-size: 28rpx; + line-height: 40rpx; + margin-bottom: 12rpx; + color: #333333; + margin-right: 16rpx; +} + +.goods-wrapper .goods-right { + min-width: 128rpx; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.goods-right .goods-price { + color: #333333; + font-size: 32rpx; + line-height: 48rpx; + font-weight: bold; + margin-bottom: 16rpx; +} + +.goods-right .goods-num { + text-align: right; +} + +.order-sure .pay-detail { + background-color: #ffffff; + padding: 16rpx 32rpx; + width: 100%; + box-sizing: border-box; +} + +.order-sure .pay-detail .pay-item { + width: 100%; + height: 72rpx; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 26rpx; + line-height: 36rpx; + color: #666666; +} +.order-sure .pay-detail .pay-item .pay-item__right { + color: #333333; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: flex-end; + max-width: 400rpx; +} +.order-sure .pay-detail .pay-item .pay-item__right .pay-remark { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + max-width: 400rpx; + text-overflow: ellipsis; + overflow: hidden; +} +.order-sure .pay-detail .pay-item .font-bold { + font-weight: bold; +} +.order-sure .pay-detail .pay-item .primary { + color: #fa4126; +} + +.add-notes .add-notes__content { + --td-textarea-background-color: #f5f5f5; +} + +.add-notes .t-textarea__placeholder { + color: #aeb3b7; +} + +.add-notes .add-notes__textarea__font { + font-size: 26rpx; +} +.add-notes .add-notes__textarea { + margin-top: 32rpx; +} + +.order-sure .add-notes .dialog__message { + border-radius: 8rpx; +} + +.order-sure .add-notes .dialog__button-cancel::after { + border-right: 0; +} + +.order-sure .amount-wrapper { + width: 100%; + box-sizing: border-box; + background-color: #ffffff; + padding: 0rpx 32rpx; + height: 96rpx; +} + +.order-sure .pay-amount { + width: 100%; + height: 96rpx; + display: flex; + align-items: center; + justify-content: flex-end; + font-size: 28rpx; + color: #333333; + position: relative; +} +.order-sure .pay-amount::after { + position: absolute; + content: ' '; + top: 0; + left: 0; + width: 200%; + height: 200%; + transform: scale(0.5); + transform-origin: 0 0; + border-top: 2rpx solid #f5f5f5; +} +.order-sure .pay-amount .order-num { + color: #999999; + padding-right: 8rpx; +} + +.order-sure .pay-amount .total-price { + font-size: 36rpx; + color: #fa4126; + font-weight: bold; + padding-left: 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/order.wxs b/miniprogram/tcb-shop/pages/order/order-confirm/order.wxs new file mode 100644 index 0000000..42f3de1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/order.wxs @@ -0,0 +1,8 @@ +var toHide = function (array) { + if (!array) return; + var mphone = array.substring(0, 3) + '****' + array.substring(7); + return mphone; +}; +module.exports = { + toHide: toHide, +}; diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/pay.js b/miniprogram/tcb-shop/pages/order/order-confirm/pay.js new file mode 100644 index 0000000..0c27c3c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/pay.js @@ -0,0 +1,115 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; + +import { dispatchCommitPay } from '../../../services/order/orderConfirm'; + +// 真实的提交支付 +export const commitPay = (params) => { + return dispatchCommitPay({ + goodsRequestList: params.goodsRequestList, // 待结算的商品集合 + invoiceRequest: params.invoiceRequest, // 发票信息 + // isIgnore: params.isIgnore || false, // 删掉 是否忽视库存不足和商品失效,继续结算,true=继续结算 购物车请赋值false + userAddressReq: params.userAddressReq, // 地址信息(用户在购物选择更换地址) + currency: params.currency || 'CNY', // 支付货币: 人民币=CNY,美元=USD + logisticsType: params.logisticsType || 1, // 配送方式 0=无需配送 1=快递 2=商家 3=同城 4=自提 + // orderMark: params.orderMark, // 下单备注 + orderType: params.orderType || 0, // 订单类型 0=普通订单 1=虚拟订单 + payType: params.payType || 1, // 支付类型(0=线上、1=线下) + totalAmount: params.totalAmount, // 新增字段"totalAmount"总的支付金额 + userName: params.userName, // 用户名 + payWay: 1, + authorizationCode: '', //loginCode, // 登录凭证 + storeInfoList: params.storeInfoList, //备注信息列表 + couponList: params.couponList, + groupInfo: params.groupInfo, + }); +}; + +export const paySuccess = (payOrderInfo) => { + const { payAmt, tradeNo, groupId, promotionId } = payOrderInfo; + // 支付成功 + Toast({ + context: this, + selector: '#t-toast', + message: '支付成功', + duration: 2000, + icon: 'check-circle', + }); + + const params = { + totalPaid: payAmt, + orderNo: tradeNo, + }; + if (groupId) { + params.groupId = groupId; + } + if (promotionId) { + params.promotionId = promotionId; + } + const paramsStr = Object.keys(params) + .map((k) => `${k}=${params[k]}`) + .join('&'); + // 跳转支付结果页面 + wx.redirectTo({ url: `/pages/order/pay-result/index?${paramsStr}` }); +}; + +export const payFail = (payOrderInfo, resultMsg) => { + if (resultMsg === 'requestPayment:fail cancel') { + if (payOrderInfo.dialogOnCancel) { + //结算页,取消付款,dialog提示 + Dialog.confirm({ + title: '是否放弃付款', + content: '商品可能很快就会被抢空哦,是否放弃付款?', + confirmBtn: '放弃', + cancelBtn: '继续付款', + }).then(() => { + wx.redirectTo({ url: '/pages/order/order-list/index' }); + }); + } else { + //订单列表页,订单详情页,取消付款,toast提示 + Toast({ + context: this, + selector: '#t-toast', + message: '支付取消', + duration: 2000, + icon: 'close-circle', + }); + } + } else { + Toast({ + context: this, + selector: '#t-toast', + message: `支付失败:${resultMsg}`, + duration: 2000, + icon: 'close-circle', + }); + setTimeout(() => { + wx.redirectTo({ url: '/pages/order/order-list/index' }); + }, 2000); + } +}; + +// 微信支付方式 +export const wechatPayOrder = (payOrderInfo) => { + // const payInfo = JSON.parse(payOrderInfo.payInfo); + // const { timeStamp, nonceStr, signType, paySign } = payInfo; + return new Promise((resolve) => { + // demo 中直接走支付成功 + paySuccess(payOrderInfo); + resolve(); + /* wx.requestPayment({ + timeStamp, + nonceStr, + package: payInfo.package, + signType, + paySign, + success: function () { + paySuccess(payOrderInfo); + resolve(); + }, + fail: function (err) { + payFail(payOrderInfo, err.errMsg); + }, + }); */ + }); +}; diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.js b/miniprogram/tcb-shop/pages/order/order-detail/index.js new file mode 100644 index 0000000..5ca9433 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.js @@ -0,0 +1,315 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import dayjs from 'dayjs'; +import { orderListShouldFresh } from '../../../utils/orderListFresh'; +import { OrderStatus } from '../config'; +import { getAllOrderItemsOfAnOrder } from '../../../services/order/orderItem'; +import { getOrder, orderStatusToName, ORDER_STATUS, updateOrderDeliveryInfo } from '../../../services/order/order'; +import { fetchBusinessTime } from '../../../services/order/orderDetail'; +import { getAddressPromise } from '../../usercenter/address/list/util'; +import { OPERATION_TYPE } from '../../../utils/orderOperation'; + +Page({ + data: { + pageLoading: true, + order: {}, // 后台返回的原始数据 + _order: {}, // 内部使用和提供给 order-card 的数据 + storeDetail: {}, + countDownTime: null, + addressEditable: false, + backRefresh: false, // 用于接收其他页面back时的状态 + formatCreateTime: '', //格式化订单创建时间 + logisticsNodes: [], + /** 订单评论状态 */ + orderHasCommented: true, + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + }, + + onLoad({ orderId }) { + if (orderId == null) { + toast('异常订单号'); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + } + this.orderId = orderId; + this.init(); + this.navbar = this.selectComponent('#navbar'); + this.pullDownRefresh = this.selectComponent('#wr-pull-down-refresh'); + }, + + onShow() { + // 当从其他页面返回,并且 backRefresh 被置为 true 时,刷新数据 + if (!this.data.backRefresh) return; + this.onRefresh(); + this.setData({ backRefresh: false }); + }, + + onPageScroll(e) { + this.pullDownRefresh && this.pullDownRefresh.onPageScroll(e); + }, + + onImgError(e) { + if (e.detail) { + console.error('img 加载失败'); + } + }, + + // 页面初始化,会展示pageLoading + init() { + this.setData({ pageLoading: true }); + this.getStoreDetail(); + this.getDetail() + .then(() => { + this.setData({ pageLoading: false }); + }) + .catch((e) => { + console.error(e); + }); + }, + + // 页面刷新,展示下拉刷新 + onRefresh() { + this.init(); + // 如果上一页为订单列表,通知其刷新数据 + const pages = getCurrentPages(); + const lastPage = pages[pages.length - 2]; + if (lastPage) { + lastPage.data.backRefresh = true; + } + }, + + // 页面刷新,展示下拉刷新 + onPullDownRefresh_(e) { + const { callback } = e.detail; + return this.getDetail().then(() => callback && callback()); + }, + + async getDetail() { + const orderId = this.orderId; + const [order, orderItems] = await Promise.all([getOrder(orderId), getAllOrderItemsOfAnOrder({ orderId })]); + order.orderItems = orderItems; + order.totalPrice = orderItems.reduce((acc, cur) => acc + cur.count * cur.sku.price, 0); + order.statusDesc = orderStatusToName(order.status); + order.isPaid = order.status !== ORDER_STATUS.TO_PAY; + order.createdTimeString = dayjs(new Date(order.createdAt)).format('YYYY-MM-DD HH:mm:ss'); + + this.setData({ order, addressEditable: order.statusDesc === '待付款' }); + // console.log('ha', order); + // return fetchOrderDetail(params).then((res) => { + // const order = res.data; + // const _order = { + // id: order.orderId, + // orderNo: order.orderNo, + // parentOrderNo: order.parentOrderNo, + // storeId: order.storeId, + // storeName: order.storeName, + // status: order.orderStatus, + // statusDesc: order.orderStatusName, + // amount: order.paymentAmount, + // totalAmount: order.goodsAmountApp, + // logisticsNo: order.logisticsVO.logisticsNo, + // goodsList: (order.orderItemVOs || []).map((goods) => + // Object.assign({}, goods, { + // id: goods.id, + // thumb: goods.goodsPictureUrl, + // title: goods.goodsName, + // skuId: goods.skuId, + // spuId: goods.spuId, + // specs: (goods.specifications || []).map((s) => s.specValue), + // price: goods.tagPrice ? goods.tagPrice : goods.actualPrice, // 商品销售单价, 优先取限时活动价 + // num: goods.buyQuantity, + // titlePrefixTags: goods.tagText ? [{ text: goods.tagText }] : [], + // buttons: goods.buttonVOs || [], + // }), + // ), + // buttons: order.buttonVOs || [], + // createTime: order.createTime, + // receiverAddress: this.composeAddress(order), + // groupInfoVo: order.groupInfoVo, + // }; + // this.setData({ + // order, + // _order, + // formatCreateTime: formatTime(parseFloat(`${order.createTime}`), 'YYYY-MM-DD HH:mm'), // 格式化订单创建时间 + // countDownTime: this.computeCountDownTime(order), + // addressEditable: + // [OrderStatus.PENDING_PAYMENT, OrderStatus.PENDING_DELIVERY].includes(order.orderStatus) && + // order.orderSubStatus !== -1, // 订单正在取消审核时不允许修改地址(但是返回的状态码与待发货一致) + // isPaid: !!order.paymentVO.paySuccessTime, + // invoiceStatus: this.datermineInvoiceStatus(order), + // invoiceDesc: order.invoiceDesc, + // invoiceType: order.invoiceVO?.invoiceType === 5 ? '电子普通发票' : '不开发票', //是否开票 0-不开 5-电子发票 + // logisticsNodes: this.flattenNodes(order.trajectoryVos || []), + // }); + // }); + }, + + datermineInvoiceStatus(order) { + // 1-已开票 + // 2-未开票(可补开) + // 3-未开票 + // 4-门店不支持开票 + return order.invoiceStatus; + }, + + // 拼接省市区 + composeAddress(order) { + return [ + //order.logisticsVO.receiverProvince, + order.logisticsVO.receiverCity, + order.logisticsVO.receiverCountry, + order.logisticsVO.receiverArea, + order.logisticsVO.receiverAddress, + ] + .filter((s) => !!s) + .join(' '); + }, + + getStoreDetail() { + fetchBusinessTime().then((res) => { + const storeDetail = { + storeTel: res.data.telphone, + storeBusiness: res.data.businessTime.join('\n'), + }; + this.setData({ storeDetail }); + }); + }, + + // 仅对待支付状态计算付款倒计时 + // 返回时间若是大于2020.01.01,说明返回的是关闭时间,否则说明返回的直接就是剩余时间 + computeCountDownTime(order) { + if (order.orderStatus !== OrderStatus.PENDING_PAYMENT) return null; + return order.autoCancelTime > 1577808000000 ? order.autoCancelTime - Date.now() : order.autoCancelTime; + }, + + onCountDownFinish() { + //this.setData({ countDownTime: -1 }); + const { countDownTime, order } = this.data; + if (countDownTime > 0 || (order && order.groupInfoVo && order.groupInfoVo.residueTime > 0)) { + this.onRefresh(); + } + }, + + onGoodsCardTap(e) { + const { index } = e.currentTarget.dataset; + const goods = this.data.order.orderItemVOs[index]; + wx.navigateTo({ url: `/pages/goods/details/index?spuId=${goods.spuId}` }); + }, + + async onEditAddressTap() { + const deliveryInfoPromise = getAddressPromise(); + // TODO: check url param + wx.navigateTo({ + url: `/pages/usercenter/address/list/index?selectMode=true`, + }); + try { + const deliveryInfo = await deliveryInfoPromise; + try { + await updateOrderDeliveryInfo({ orderId: this.data.order._id, deliveryInfoId: deliveryInfo._id }); + this.setData({ + 'order.delivery_info._id': deliveryInfo._id, + 'order.delivery_info.phone': deliveryInfo.phone, + 'order.delivery_info.address': deliveryInfo.address, + 'order.delivery_info.name': deliveryInfo.name, + }); + } catch (e) { + console.error(e); + this.toast('更新地址失败'); + } + } catch {} + }, + + onOrderNumCopy() { + wx.setClipboardData({ + data: this.data.order.orderNo, + }); + }, + + onDeliveryNumCopy() { + wx.setClipboardData({ + data: this.data.order.logisticsVO.logisticsNo, + }); + }, + + onToInvoice() { + wx.navigateTo({ + url: `/pages/order/invoice/index?orderNo=${this.data._order.orderNo}`, + }); + }, + + onSuppleMentInvoice() { + wx.navigateTo({ + url: `/pages/order/receipt/index?orderNo=${this.data._order.orderNo}`, + }); + }, + + onDeliveryClick() { + const logisticsData = { + nodes: this.data.logisticsNodes, + company: this.data.order.logisticsVO.logisticsCompanyName, + logisticsNo: this.data.order.logisticsVO.logisticsNo, + phoneNumber: this.data.order.logisticsVO.logisticsCompanyTel, + }; + wx.navigateTo({ + url: `/pages/order/delivery-detail/index?data=${encodeURIComponent(JSON.stringify(logisticsData))}`, + }); + }, + + /** 跳转订单评价 */ + navToCommentCreate() { + wx.navigateTo({ + url: `/pages/order/createComment/index?orderNo=${this.orderNo}`, + }); + }, + + /** 跳转拼团详情/分享页*/ + toGrouponDetail() { + wx.showToast({ title: '点击了拼团' }); + }, + + clickService() { + Toast({ + context: this, + selector: '#t-toast', + message: '您点击了联系客服', + }); + }, + + onOrderInvoiceView() { + wx.navigateTo({ + url: `/pages/order/invoice/index?orderNo=${this.orderNo}`, + }); + }, + + onOperation(e) { + const type = e?.detail?.type; + const success = e?.detail?.success; + + if (type == null) return; + + const resultMessage = success ? '成功' : '失败'; + + let operationMessage; + + if (type === OPERATION_TYPE.CANCEL) { + operationMessage = '取消订单'; + } else if (type === OPERATION_TYPE.CONFIRM) { + operationMessage = '确认收货'; + } else { + operationMessage = '支付'; + } + + this.toast(`${operationMessage}${resultMessage}`); + orderListShouldFresh(); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.json b/miniprogram/tcb-shop/pages/order/order-detail/index.json new file mode 100644 index 0000000..b8bbaec --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.json @@ -0,0 +1,17 @@ +{ + "navigationBarTitleText": "订单详情", + "usingComponents": { + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "t-button": "tdesign-miniprogram/button/button", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "t-count-down": "tdesign-miniprogram/count-down/count-down", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "price": "/components/price/index", + "order-card": "../components/order-card/index", + "order-goods-card": "../components/order-goods-card/index", + "order-button-bar": "../components/order-button-bar/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.wxml b/miniprogram/tcb-shop/pages/order/order-detail/index.wxml new file mode 100644 index 0000000..67f1d57 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.wxml @@ -0,0 +1,60 @@ + + + + + + {{order.statusDesc}} + + + + + + {{order.delivery_info.name + ' '}}{{order.delivery_info.phone}} + {{order.delivery_info.address}} + + 修改 + + + + + + + + + + 商品总额 + + + + {{order.isPaid ? '实付' : '应付'}} + + + + + + + 订单编号 + + {{order._id}} + 复制 + + + + 下单时间 + + {{order.createdTimeString}} + + + + + +  联系客服 + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.wxss b/miniprogram/tcb-shop/pages/order/order-detail/index.wxss new file mode 100644 index 0000000..6bae005 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.wxss @@ -0,0 +1,245 @@ +:host { + background-color: #f8f8f8; +} + +.order-detail { + width: 100%; + box-sizing: border-box; + padding: 0rpx 0rpx calc(env(safe-area-inset-bottom) + 144rpx); +} + +.order-detail .count-down { + color: #ffffff; +} +.order-detail .header { + width: 100%; + background-color: #ffffff; +} +.order-detail .order-detail__header { + width: 700rpx; + height: 200rpx; + border-radius: 24rpx; + margin: 0 auto; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/template/order-bg.png'); + background-repeat: no-repeat; + background-size: contain; +} +.order-detail .order-detail__header .title, +.order-detail .order-detail__header .desc { + color: #ffffff; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; +} +.order-detail .order-detail__header .title { + -webkit-line-clamp: 1; + font-size: 44rpx; + line-height: 64rpx; + margin-bottom: 8rpx; + font-weight: bold; +} +.order-detail .order-detail__header .desc { + -webkit-line-clamp: 2; + font-size: 24rpx; + line-height: 32rpx; +} +.order-detail .order-detail__header .desc .count-down { + display: inline; +} +.order-detail .order-logistics { + box-sizing: border-box; + padding: 32rpx; + width: 100%; + background-color: #ffffff; + overflow: hidden; + color: #333333; + font-size: 32rpx; + line-height: 48rpx; + display: flex; + position: relative; +} + +.order-logistics .logistics-icon { + width: 40rpx; + height: 40rpx; + margin-right: 16rpx; + margin-top: 4rpx; +} + +.order-logistics .logistics-content { + flex: 1; +} + +.order-logistics .logistics-content .logistics-time { + font-size: 28rpx; + line-height: 40rpx; + color: #999999; + margin-top: 12rpx; +} + +.order-logistics .logistics-back { + color: #999999; + align-self: center; +} + +.order-logistics .edit-text { + color: #fa4126; + font-size: 26rpx; + line-height: 36rpx; +} + +.order-detail .border-bottom { + margin: 0 auto; + width: 686rpx; + scale: 1 0.5; + height: 2rpx; + background-color: #e5e5e5; +} + +.order-detail .border-bottom-margin { + margin: 16rpx auto; +} + +.order-detail .pay-detail { + background-color: #ffffff; + width: 100%; + box-sizing: border-box; +} + +.order-detail .padding-inline { + padding: 16rpx 32rpx; +} + +.order-detail .pay-detail .pay-item { + width: 100%; + height: 72rpx; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 26rpx; + line-height: 36rpx; + color: #666666; + background-color: #ffffff; +} +.order-detail .pay-detail .pay-item .pay-item__right { + color: #333333; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: flex-end; + max-width: 400rpx; +} +.order-detail .pay-detail .pay-item .pay-item__right .pay-remark { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + max-width: 400rpx; + text-overflow: ellipsis; + overflow: hidden; +} +.order-detail .pay-detail .pay-item .font-bold { + font-weight: bold; +} +.order-detail .pay-detail .pay-item .primary { + color: #fa4126; +} +.order-detail .pay-detail .pay-item .max-size { + font-size: 36rpx; + line-height: 48rpx; +} + +.pay-item .pay-item__right .pay-item__right__copy { + width: 80rpx; + height: 40rpx; + text-align: center; + font-size: 24rpx; + line-height: 40rpx; + color: #333333; + position: relative; +} + +.pay-item .pay-item__right .pay-item__right__copy::before { + position: absolute; + content: ''; + width: 200%; + height: 200%; + border-radius: 40rpx; + border: 2rpx solid #dddddd; + transform: scale(0.5); + left: 0; + top: 0; + transform-origin: left top; +} + +.pay-item .pay-item__right .order-no { + color: #333333; + font-size: 26rpx; + line-height: 40rpx; + padding-right: 16rpx; +} + +.pay-item .pay-item__right .normal-color { + color: #333333; +} + +.order-detail .pay-detail .pay-service { + width: 100%; + height: 72rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + line-height: 36rpx; + color: #333333; + background-color: #ffffff; +} + +.bottom-bar { + position: fixed; + left: 0; + bottom: 0; + right: 0; + z-index: 10; + background: #fff; + height: 112rpx; + width: 686rpx; + padding: 0rpx 32rpx env(safe-area-inset-bottom); + display: flex; + align-items: center; +} + +.bottom-bar::before { + position: absolute; + content: ''; + width: 200%; + height: 200%; + border-top: 2rpx solid #dddddd; + transform: scale(0.5); + left: 0; + top: 0; + transform-origin: left top; +} + +.goods-button-bar { + height: 112rpx; + width: 686rpx; + margin-bottom: 16rpx; +} + +.t-class-indicator { + color: #b9b9b9 !important; +} + +.add-notes__confirm { + color: #fa4126 !important; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.js b/miniprogram/tcb-shop/pages/order/order-list/index.js new file mode 100644 index 0000000..31cbb1c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.js @@ -0,0 +1,186 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { ORDER_STATUS, listOrder, orderStatusToName } from '../../../services/order/order'; +import { getAllOrderItemsOfAnOrder } from '../../../services/order/orderItem'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; +import { getCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { OPERATION_TYPE } from '../../../utils/orderOperation'; +import { shouldFresh, orderListFinishFresh } from '../../../utils/orderListFresh'; + +const ORDER_STATUS_ALL = '0'; + +Page({ + page: { + size: 5, + num: 1, + }, + + data: { + tabs: [ + { key: ORDER_STATUS_ALL, text: '全部', total: 0 }, + { key: ORDER_STATUS.TO_PAY, text: '待付款', total: 0 }, + { key: ORDER_STATUS.TO_SEND, text: '待发货', total: 0 }, + { key: ORDER_STATUS.TO_RECEIVE, text: '待收货', total: 0 }, + { key: ORDER_STATUS.FINISHED, text: '已完成', total: 0 }, + ], + curTab: ORDER_STATUS_ALL, + orderList: [], + listLoading: LIST_LOADING_STATUS.READY, + emptyImg: 'https://cdn-we-retail.ym.tencent.com/miniapp/order/empty-order-list.png', + backRefresh: false, + status: ORDER_STATUS_ALL, + pullDownRefreshing: false, + loadingProps: { + theme: 'circular', + size: '40rpx', + }, + }, + + errorToast(message, e) { + console.error(message, e); + this.toast(message); + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + duration: 1000, + icon: '', + }); + }, + + onLoad(query) { + const status = this.data.tabs.find((x) => x.key === query.status)?.key ?? ORDER_STATUS_ALL; + this.setData({ + status, + }); + this.refreshList(status); + }, + + async pullRefresh() { + this.setData({ pullDownRefreshing: true }); + try { + await this.onRefresh(); + orderListFinishFresh(); + } catch (e) { + this.errorToast('获取订单列表失败', e); + } finally { + this.setData({ pullDownRefreshing: false }); + } + }, + + async onShow() { + if (!shouldFresh) return; + try { + await this.onRefresh(); + orderListFinishFresh(); + } catch (e) { + this.errorToast('获取订单列表失败', e); + } + }, + + onReachBottom() { + if (this.data.listLoading === LIST_LOADING_STATUS.READY) { + this.getOrderList(this.data.curTab); + } + }, + + async getOrderItems(order) { + const orderId = order._id; + try { + const orderItems = await getAllOrderItemsOfAnOrder({ orderId }); + + const images = orderItems.map((x) => x.sku.image ?? ''); + (await getCloudImageTempUrl(images)).forEach((image, index) => (orderItems[index].sku.image = image)); + + order.orderItems = orderItems; + order.totalPrice = orderItems.reduce((acc, cur) => acc + cur.sku.price * cur.count, 0); + } catch (e) { + this.errorToast('获取订单详情失败', e); + } + }, + + async getOrderList(statusCode = ORDER_STATUS_ALL, reset = false) { + this.setData({ + listLoading: LIST_LOADING_STATUS.LOADING, + }); + try { + const { records, total } = await listOrder({ + pageSize: this.page.size, + pageNumber: this.page.num, + status: statusCode !== ORDER_STATUS_ALL ? statusCode : undefined, + }); + + records.forEach((order) => (order.statusDesc = orderStatusToName(order.status))); + + // async get items for each order + await Promise.all(records.map((order) => this.getOrderItems(order))); + + const orderList = reset ? records : this.data.orderList.concat(records); + const listLoading = orderList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY; // TODO: maybe we should notify user when `length > total`? + + this.setData({ listLoading, orderList }); + const currentNum = reset ? 1 : this.page.num; + this.page.num = currentNum + 1; + } catch (e) { + console.error('获取订单列表失败', e); + this.setData({ listLoading: LIST_LOADING_STATUS.FAILED }); + } + }, + + onReTryLoad() { + this.getOrderList(this.data.curTab); + }, + + onTabChange(e) { + const { value } = e.detail; + this.setData({ + status: value, + }); + this.refreshList(value); + }, + + refreshList(status = ORDER_STATUS_ALL) { + this.page = { + size: this.page.size, + num: 1, + }; + this.setData({ curTab: status, orderList: [] }); + + return this.getOrderList(status, true); + }, + + onRefresh() { + return this.refreshList(this.data.curTab); + }, + + onOrderCardTap(e) { + const { order } = e.currentTarget.dataset; + wx.navigateTo({ + url: `/pages/order/order-detail/index?orderId=${order._id}`, + }); + }, + + onOperation(e) { + const type = e?.detail?.type; + const success = e?.detail?.success; + + if (type == null) return; + + const resultMessage = success ? '成功' : '失败'; + + let operationMessage; + + if (type === OPERATION_TYPE.CANCEL) { + operationMessage = '取消订单'; + } else if (type === OPERATION_TYPE.CONFIRM) { + operationMessage = '确认收货'; + } else { + operationMessage = '支付'; + } + + this.toast(`${operationMessage}${resultMessage}`); + this.onRefresh(); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.json b/miniprogram/tcb-shop/pages/order/order-list/index.json new file mode 100644 index 0000000..eaa089e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.json @@ -0,0 +1,17 @@ +{ + "navigationBarTitleText": "我的订单", + "usingComponents": { + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-loading": "tdesign-miniprogram/loading/loading", + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "load-more": "/components/load-more/index", + "order-button-bar": "../components/order-button-bar/index", + "price": "/components/price/index", + "order-card": "../components/order-card/index", + "specs-goods-card": "../components/specs-goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.wxml b/miniprogram/tcb-shop/pages/order/order-list/index.wxml new file mode 100644 index 0000000..9cd0d09 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.wxml @@ -0,0 +1,36 @@ + + + + + + + + + + + 订单号  + {{order._id}} + + + + + + + 总价  + + + + + + + + + + + 暂无相关订单 + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.wxss b/miniprogram/tcb-shop/pages/order/order-list/index.wxss new file mode 100644 index 0000000..c675b3d --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.wxss @@ -0,0 +1,117 @@ +:host { + background-color: #f5f5f5; +} +.page-container .tab-bar__placeholder, +.page-container .tab-bar__inner { + height: 88rpx; + line-height: 88rpx; + background: #fff; +} +.page-container .tab-bar__inner { + font-size: 26rpx; + color: #333333; + position: fixed; + width: 100vw; + top: 0; + left: 0; +} +.page-container .tab-bar__inner.order-nav .order-nav-item .bottom-line { + bottom: 12rpx; +} +.tab-bar__inner .t-tabs-is-active { + color: #fa4126 !important; +} + +.tab-bar__inner .t-tabs-track { + background: #fa4126 !important; +} + +.page-container .tab-bar__active { + font-size: 28rpx; +} +.page-container .specs-popup .bottom-btn { + color: #fa4126; + color: var(--color-primary, #fa4126); +} +.page-container .specs-popup .bottom-btn::after { + border-color: #fa4126; + border-color: var(--color-primary, #fa4126); +} +.dialog .dialog__button-confirm { + color: #fa4126; + color: var(--color-primary, #fa4126); +} +.list-loading { + height: 100rpx; +} +.empty-wrapper { + height: calc(100vh - 88rpx); +} +.btn-bar { + margin-top: 20rpx; +} +.load-more { + margin: 0 24rpx; +} +wr-order-goods-card:not(:first-child) .wr-goods-card { + margin-top: 40rpx; +} + +.price-total { + font-size: 24rpx; + line-height: 32rpx; + color: #999999; + padding-top: 10rpx; + width: 100%; + display: flex; + align-items: baseline; + justify-content: flex-end; +} +.price-total .bold-price { + color: #333333; + font-size: 28rpx; + line-height: 40rpx; + color: #333333; +} +.price-total .real-pay { + font-size: 36rpx; + line-height: 48rpx; + color: #fa4126; + font-weight: bold; +} + +.t-tabs.t-tabs--top .t-tabs-scroll { + border: none !important; +} +.t-empty-text { + font-size: 28rpx; + color: #999; +} + +.page-container .order-number { + color: #666666; + font-size: 28rpx; +} +.t-class-indicator { + color: #b9b9b9 !important; +} +.tab-bar .tab-bar__active { + color: #333333 !important; +} + +.tab-bar .t-tabs-track { + background: #333333 !important; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} + +.order-card-loading-container { + text-align: center; +} + +.t-loading__spinner--circular .t-loading__circular { + color: #b9b9b9 !important; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.js b/miniprogram/tcb-shop/pages/order/pay-result/index.js new file mode 100644 index 0000000..bd6dad6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.js @@ -0,0 +1,47 @@ +/* + * @Author: rileycai + * @Date: 2022-03-14 21:18:07 + * @LastEditTime: 2022-03-22 21:17:16 + * @LastEditors: rileycai + * @Description: + * @FilePath: /tdesign-miniprogram-starter/pages/order/pay-result/index.js + */ +Page({ + data: { + totalPaid: 0, + orderNo: '', + groupId: '', + groupon: null, + spu: null, + adUrl: '', + }, + + onLoad(options) { + const { totalPaid = 0, orderNo = '', groupId = '' } = options; + this.setData({ + totalPaid, + orderNo, + groupId, + }); + }, + + onTapReturn(e) { + const target = e.currentTarget.dataset.type; + const { orderNo } = this.data; + if (target === 'home') { + wx.switchTab({ url: '/pages/home/home' }); + } else if (target === 'orderList') { + wx.navigateTo({ + url: `/pages/order/order-list/index?orderNo=${orderNo}`, + }); + } else if (target === 'order') { + wx.navigateTo({ + url: `/pages/order/order-detail/index?orderNo=${orderNo}`, + }); + } + }, + + navBackHandle() { + wx.navigateBack(); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.json b/miniprogram/tcb-shop/pages/order/pay-result/index.json new file mode 100644 index 0000000..f572818 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "支付结果", + "navigationStyle": "custom", + "usingComponents": { + "t-navbar": "tdesign-miniprogram/navbar/navbar", + "t-icon": "tdesign-miniprogram/icon/icon", + "price": "/components/price/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.wxml b/miniprogram/tcb-shop/pages/order/pay-result/index.wxml new file mode 100644 index 0000000..3153fe6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.wxml @@ -0,0 +1,22 @@ + + + + + 支付成功 + + + 微信支付: + + + + 查看订单 + 返回首页 + + + diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.wxss b/miniprogram/tcb-shop/pages/order/pay-result/index.wxss new file mode 100644 index 0000000..abe1b25 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.wxss @@ -0,0 +1,54 @@ +.pay-result { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.pay-result .pay-status { + margin-top: 100rpx; + font-size: 48rpx; + line-height: 72rpx; + font-weight: bold; + color: #333333; + display: flex; + align-items: center; +} +.pay-result .pay-status text { + padding-left: 12rpx; +} +.pay-result .pay-money { + color: #666666; + font-size: 28rpx; + line-height: 48rpx; + margin-top: 28rpx; + display: flex; + align-items: baseline; +} + +.pay-result .pay-money .pay-money__price { + font-size: 36rpx; + line-height: 48rpx; + color: #fa4126; +} +.pay-result .btn-wrapper { + margin-top: 48rpx; + padding: 12rpx 32rpx; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + box-sizing: border-box; +} + +.pay-result .btn-wrapper .status-btn { + height: 88rpx; + width: 334rpx; + border-radius: 44rpx; + border: 2rpx solid #fa4126; + color: #fa4126; + font-size: 28rpx; + font-weight: bold; + line-height: 88rpx; + text-align: center; +} diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.js b/miniprogram/tcb-shop/pages/order/receipt/index.js new file mode 100644 index 0000000..3d03225 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.js @@ -0,0 +1,182 @@ +/* eslint-disable no-nested-ternary */ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; +import { dispatchSupplementInvoice } from '../../../services/order/orderConfirm'; + +const invoiceJson = { + info: [ + '1.根据当地税务局的要求,开具有效的企业发票需填写税务局登记证号。开具个人发票不需要填写纳税人识别码。 ', + '2.电子普通发票: 电子普通发票是税局认可的有效首付款凭证,其法律效力、基本用途及使用规定同纸质发票,如需纸质发票可自行下载打印。 ', + '3.增值税专用发票: 增值税发票暂时不可开,可查看《开局增值税发票》或致电400-633-6868。', + ], + codeTitle: [ + '1.什么是纳税人识别号/统一社会信用代码? 纳税人识别号,一律由15位、17位、18或者20位码(字符型)组成,其中:企业、事业单位等组织机构纳税人,以国家质量监督检验检疫总局编制的9位码(其中区分主码位与校检位之间的“—”符省略不打印)并在其“纳税人识别号”。国家税务总局下达的纳税人代码为15位,其中:1—2位为省、市代码,3—6位为地区代码,7—8位为经济性质代码,9—10位行业代码,11—15位为各地区自设的顺序码。', + '2.入户获取/知晓纳税人识别号/统一社会信用代码? 纳税人识别号是税务登记证上的号码,通常简称为“税号”,每个企业的纳税人识别号都是唯一的。这个属于每个人自己且终身不变的数字代码很可能成为我们的第二张“身份证”。 ', + ], +}; + +Page({ + orderNo: '', + data: { + receiptIndex: 0, + addressTagsIndex: 0, + goodsClassesIndex: 0, + dialogShow: false, + codeShow: false, + receipts: [ + { title: '不开发票', id: 0, name: 'receipt' }, + { title: '电子发票', id: 1, name: 'receipt' }, + ], + addressTags: [ + { title: '个人', id: 0, name: 'addressTags', type: 1 }, + { title: '公司', id: 1, name: 'addressTags', type: 2 }, + ], + goodsClasses: [ + { title: '商品明细', id: 0, name: 'goodsClasses' }, + { title: '商品类别', id: 1, name: 'goodsClasses' }, + ], + name: '', + componentName: '', + code: '', + phone: '', + email: '', + invoiceInfo: invoiceJson, + }, + onLoad(query) { + const { orderNo, invoiceData } = query; + const tempData = JSON.parse(invoiceData || '{}'); + const invoice = { + receiptIndex: tempData.invoiceType === 5 ? 1 : 0, + name: tempData.buyerName || '', + email: tempData.email || '', + phone: tempData.buyerPhone || '', + addressTagsIndex: tempData.titleType === 2 ? 1 : 0, + goodsClassesIndex: tempData.contentType === 2 ? 1 : 0, + code: tempData.buyerTaxNo || '', + componentName: tempData.titleType === 2 ? tempData.buyerName : '', + }; + this.orderNo = orderNo; + this.setData({ ...invoice }); + }, + onLabels(e) { + const { item } = e.currentTarget.dataset; + const nameIndex = `${item.name}Index`; + this.setData({ [nameIndex]: item.id }); + }, + onInput(e) { + const { addressTagsIndex } = this.data; + const { item } = e.currentTarget.dataset; + const { value } = e.detail; + const key = + item === 'name' + ? addressTagsIndex === 0 + ? 'name' + : 'componentName' + : item === 'code' + ? addressTagsIndex === 0 + ? 'phone' + : 'code' + : 'email'; + this.setData({ [key]: value }); + }, + onSure() { + const result = this.checkSure(); + if (!result) { + Dialog.alert({ + title: '请填写发票信息', + content: '', + confirmBtn: '确认', + }); + return; + } + const { + receiptIndex, + addressTagsIndex, + receipts, + addressTags, + name, + componentName, + code, + phone, + email, + goodsClassesIndex, + } = this.data; + + const data = { + buyerName: addressTagsIndex === 0 ? name : componentName, + buyerTaxNo: code, + buyerPhone: phone, + email, + titleType: addressTags[addressTagsIndex].type, + contentType: goodsClassesIndex === 0 ? 1 : 2, + invoiceType: receiptIndex === 1 ? 5 : 0, + }; + if (this.orderNo) { + if (this.submitting) return; + const params = { + parameter: { + orderNo: this.orderNo, + invoiceVO: data, + }, + }; + this.submitting = true; + dispatchSupplementInvoice(params) + .then(() => { + Toast({ + context: this, + selector: '#t-toast', + message: '保存成功', + duration: 2000, + icon: '', + }); + setTimeout(() => { + this.submitting = false; + wx.navigateBack({ delta: 1 }); + }, 1000); + }) + .catch((err) => { + this.submitting = false; + console.error(err); + }); + } else { + Object.assign(data, { + receipts: receipts[receiptIndex], + addressTags: addressTags[addressTagsIndex], + }); + wx.setStorageSync('invoiceData', data); + wx.navigateBack({ delta: 1 }); + } + }, + checkSure() { + const { name, componentName, code, phone, email, addressTagsIndex, receiptIndex } = this.data; + if (receiptIndex === 0) { + return true; + } + if (addressTagsIndex === 0) { + if (!name.length || !phone.length) { + return false; + } + } else if (addressTagsIndex === 1) { + if (!componentName.length || !code.length) { + return false; + } + } + if (!email.length) { + return false; + } + return true; + }, + onDialogTap() { + const { dialogShow } = this.data; + this.setData({ + dialogShow: !dialogShow, + codeShow: false, + }); + }, + onKnoeCode() { + this.setData({ + dialogShow: !this.data.dialogShow, + codeShow: true, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.json b/miniprogram/tcb-shop/pages/order/receipt/index.json new file mode 100644 index 0000000..5996047 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "发票", + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-input": "tdesign-miniprogram/input/input", + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.wxml b/miniprogram/tcb-shop/pages/order/receipt/index.wxml new file mode 100644 index 0000000..a79397b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.wxml @@ -0,0 +1,135 @@ + + + + + + {{item.title}} + + + + + + + + + {{tag.title}} + + + + + + + + + + + + + + + + + + + + {{good.title}} + + + + 发票内容将显示详细商品名称与价格信息,发票金额为实际支付金额,不包含优惠等扣减金额 + + + 发票须知 + + + + + + + + {{item}} + + + {{item}} + + + + + + + + + 确定 + + + + diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.wxss b/miniprogram/tcb-shop/pages/order/receipt/index.wxss new file mode 100644 index 0000000..c12d654 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.wxss @@ -0,0 +1,220 @@ +@import '../../../style/theme.wxss'; + +.receipt { + height: 100vh; + background: #f5f5f5; + position: relative; + padding-top: 20rpx; + + --td-input-vertical-padding: 0; +} + +.receipt-cell .t-cell__title { + width: 144rpx; + padding-right: 32rpx; + flex: none !important; +} + +.receipt .t-input__wrapper { + margin: 0 !important; +} +.srcoll-view-wrap { + margin-top: 20rpx; +} +.receipt .flex { + display: flex; + align-items: center; + justify-content: space-between; +} +.receipt .head-title { + color: #333; + font-size: 30rpx; + font-weight: bold; +} +.receipt .btn-wrap { + display: flex; +} +.receipt .btn-wrap .btn { + width: 128rpx; + background: #f5f5f5; + font-size: 24rpx; + color: #333; + margin-right: 22rpx; + text-align: center; + border-radius: 8rpx; + position: relative; + border: 2rpx solid #f5f5f5; +} +.receipt .btn-wrap .active-btn { + background-color: transparent; + border-color: #fa4126; + color: #fa4126; +} +.receipt .title { + width: 100%; + background-color: #fff; + margin-bottom: 20rpx; +} + +.receipt .receipt-label { + display: flex; +} +.receipt .receipt-label .btn { + width: 128rpx; + background: #f5f5f5; + font-size: 24rpx; + color: #333; + margin-left: 22rpx; + text-align: center; + border-radius: 8rpx; + border: 2rpx solid #f5f5f5; +} +.receipt .receipt-label .active-btn { + background-color: transparent; + border-color: #fa4126; + color: #fa4126; +} +.receipt .receipt-label .wr-cell__title { + font-size: 30rpx; + color: #333; + font-weight: bold; +} +.receipt .receipt-content { + background: #fff; + margin-top: 20rpx; +} +.receipt .receipt-content .addressTags { + padding: 0 30rpx; + height: 100rpx; +} +.receipt .receipt-content .addressTags .btn-wrap { + display: flex; +} +.receipt .receipt-content .line { + width: 720rpx; + margin-left: 30rpx; + background-color: #e6e6e6; + height: 1rpx; +} +.receipt .receipt-content .receipt-input { + display: flex; + padding: 0 30rpx; + align-items: center; + height: 100rpx; + color: #666; +} +.receipt .receipt-content .receipt-input .title { + color: #333; + display: inline-block; + width: 140rpx; + margin-right: 30rpx; + font-size: 30rpx; + font-weight: bold; +} +.input-com { + display: inline-block; + flex: 1; + font-size: 30rpx; + font-weight: 400; + line-height: 30rpx; + padding: 0 !important; + color: #666; +} +.input-com::after { + border: none !important; +} + +.receipt .receipt-content .receipt-input .wr-icon { + font-size: 28rpx !important; + margin-left: 20rpx; +} +.receipt .receipt-info { + background: #fff; + margin-top: 20rpx; +} +.receipt .receipt-info .info-con { + padding: 0 30rpx; + height: 100rpx; +} +.receipt .receipt-info .title { + font-size: 24rpx; + color: #999999; + line-height: 36rpx; + padding: 0 30rpx 20rpx; + box-sizing: border-box; +} +.receipt .receipt-know { + display: flex; + align-items: center; + font-size: 26rpx; + font-weight: 400; + color: #999999; + padding: 20rpx 30rpx; + line-height: 26rpx; +} +.receipt .receipt-know .icon { + margin-left: 16rpx; + font-size: 26rpx; +} +.receipt .dialog-receipt .dialog__message { + padding: 0; +} +.receipt .dialog-receipt .dialog-info { + max-height: 622rpx; +} +.receipt .dialog-receipt .info-wrap { + padding: 0 18rpx; +} +.receipt .dialog-receipt .info .title { + display: inline-block; + font-size: 28rpx; + font-weight: 400; + color: #999; + line-height: 40rpx; + margin-bottom: 40rpx; + text-align: left; +} +.receipt .receipt-btn { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 100; + background: #fff; + width: 100%; + padding: 0 20rpx; + box-sizing: border-box; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); +} +.receipt .receipt-btn .receipt-btn-con { + margin-top: 20rpx; + display: inline-block; + width: 100%; + line-height: 80rpx; + background: #fa4126; + text-align: center; + color: #fff; + border-radius: 48rpx; +} + +.cell-left { + margin-right: 0 !important; +} + +.cell-right { + display: flex; + justify-content: flex-start; + width: 480rpx; +} + +.addressTagsIndex-cell { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.js b/miniprogram/tcb-shop/pages/promotion-detail/index.js new file mode 100644 index 0000000..dd6614e --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.js @@ -0,0 +1,57 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { fetchPromotion } from '../../services/promotion/detail'; + +Page({ + data: { + list: [], + banner: '', + time: 0, + showBannerDesc: false, + statusTag: '', + }, + + onLoad(query) { + const promotionID = parseInt(query.promotion_id); + this.getGoodsList(promotionID); + }, + + getGoodsList(promotionID) { + fetchPromotion(promotionID).then( + ({ list, banner, time, showBannerDesc, statusTag }) => { + const goods = list.map((item) => ({ + ...item, + tags: item.tags.map((v) => v.title), + })); + this.setData({ + list: goods, + banner, + time, + showBannerDesc, + statusTag, + }); + }, + ); + }, + + goodClickHandle(e) { + const { index } = e.detail; + const { spuId } = this.data.list[index]; + wx.navigateTo({ url: `/pages/goods/details/index?spuId=${spuId}` }); + }, + + cardClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加购', + }); + }, + + bannerClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击规则详情', + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.json b/miniprogram/tcb-shop/pages/promotion-detail/index.json new file mode 100644 index 0000000..2eb4145 --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "营销详情", + "usingComponents": { + "t-toast": "tdesign-miniprogram/toast/toast", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "count-down": "tdesign-miniprogram/count-down/count-down", + "goods-list": "/components/goods-list/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.wxml b/miniprogram/tcb-shop/pages/promotion-detail/index.wxml new file mode 100644 index 0000000..be5b5c8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.wxml @@ -0,0 +1,76 @@ + + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.wxss b/miniprogram/tcb-shop/pages/promotion-detail/index.wxss new file mode 100644 index 0000000..2151b7a --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.wxss @@ -0,0 +1,111 @@ +.promotion-detail-container .wrap { + display: block; + padding: 0 24rpx; + background: linear-gradient(#fff, #f5f5f5); +} + +.promotion-detail-container .t-class-promotion-head { + width: 702rpx; + height: 160rpx; + border-radius: 8rpx; +} + +.promotion-detail-container .wrap .count-down-wrap { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: baseline; + line-height: 34rpx; +} + +.promotion-detail-container .wrap .count-down-wrap.in-banner-count-down-wrap { + position: absolute; + bottom: 32rpx; + left: 32rpx; + right: 32rpx; +} + +.promotion-detail-container .wrap .count-down-wrap .status-tag { + height: 32rpx; + line-height: 32rpx; + font-size: 20rpx; + margin-right: 12rpx; + border-radius: 16rpx; + padding: 0 12rpx; +} + +.promotion-detail-container .wrap .count-down-wrap .status-tag.before { + color: #fff; + background-color: #ff9853; +} + +.promotion-detail-container .wrap .count-down-wrap .status-tag.finish { + color: #fff; + background-color: #ccc; +} + +.promotion-detail-container .wrap .count-down-wrap .count-down-label { + color: #666; + font-size: 24rpx; + margin-right: 0.5em; +} + +.promotion-detail-container .wrap .count-down-wrap .detail-entry { + margin-left: auto; + height: 40rpx; +} + +.promotion-detail-container .wrap .count-down-wrap .detail-entry-label { + color: #fff; + font-size: 24rpx; + margin-right: 12rpx; +} + +.promotion-detail-container + .wrap + .count-down-wrap.after-banner-count-down-wrap { + padding: 10rpx; +} + +.promotion-detail-container + .wrap + .count-down-wrap.after-banner-count-down-wrap + .detail-entry { + display: flex; + align-items: center; +} + +.promotion-detail-container + .wrap + .count-down-wrap.after-banner-count-down-wrap + .detail-entry-label { + color: #999; + margin-right: 0; +} + +.promotion-detail-container .wrap .gl-empty-wrap { + margin-top: 180rpx; +} + +.promotion-detail-container .wrap .gl-empty-img { + width: 240rpx; + height: 240rpx; + display: block; + margin: 0 auto; +} + +.promotion-detail-container .wrap .gl-empty-label { + font-size: 28rpx; + color: #999; + margin-top: 40rpx; + text-align: center; +} + +.promotion-detail-container .goods-list-container { + background: #f5f5f5 !important; +} + +.promotion-detail-container .promotion-goods-list { + padding: 20rpx 24rpx; + background-color: #f5f5f5; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.js b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.js new file mode 100644 index 0000000..0e0acfc --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.js @@ -0,0 +1,141 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { createAddress, updateAddress } from '../../../../services/address/address'; +import { addressListShouldFresh } from '../../../../utils/addressListFresh'; + +const innerPhoneReg = '^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[0-8]|8\\d|9\\d)\\d{8}$'; +const innerNameReg = '^[a-zA-Z\\d\\u4e00-\\u9fa5]+$'; + +Page({ + options: { + multipleSlots: true, + }, + externalClasses: ['theme-wrapper-class'], + data: { + detailAddress: '', + name: '', + phone: '', + addressId: null, + + loading: false, + }, + setLoading() { + this.setData({ loading: true }); + }, + unsetLoading() { + this.setData({ loading: false }); + }, + onLoad(options) { + const { name, address, _id, phone } = options; + if (![name, address, _id, phone].every((x) => typeof x === 'string')) return; + + this.setData({ + name, + detailAddress: address, + addressId: _id, + phone, + }); + }, + onInputValue(event) { + const { + detail: { value }, + target: { + dataset: { item }, + }, + } = event; + this.setData({ [item]: value }); + }, + onCheckDefaultAddress({ detail }) { + const { value } = detail; + this.setData({ + 'locationState.isDefault': value, + }); + }, + onVerifyInputLegal() { + const { name, phone, detailAddress } = this.data; + const prefixPhoneReg = String(this.properties.phoneReg || innerPhoneReg); + const prefixNameReg = String(this.properties.nameReg || innerNameReg); + const nameRegExp = new RegExp(prefixNameReg); + const phoneRegExp = new RegExp(prefixPhoneReg); + + if (!name || !name.trim()) { + return { + isLegal: false, + tips: '请填写收货人', + }; + } + if (!nameRegExp.test(name)) { + return { + isLegal: false, + tips: '收货人仅支持输入中文、英文(区分大小写)、数字', + }; + } + if (!phone || !phone.trim()) { + return { + isLegal: false, + tips: '请填写手机号', + }; + } + if (!phoneRegExp.test(phone)) { + return { + isLegal: false, + tips: '请填写正确的手机号', + }; + } + if (!detailAddress || !detailAddress.trim()) { + return { + isLegal: false, + tips: '请完善详细地址', + }; + } + if (detailAddress && detailAddress.trim().length > 50) { + return { + isLegal: false, + tips: '详细地址不能超过50个字符', + }; + } + return { + isLegal: true, + tips: '添加成功', + }; + }, + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + icon: '', + duration: 1000, + }); + }, + async formSubmit() { + const { isLegal, tips } = this.onVerifyInputLegal(); + + if (isLegal) { + const { detailAddress, name, phone, addressId } = this.data; + this.setLoading(); + + let action, failedMessage; + if (typeof addressId === 'string') { + console.log('to update'); + action = () => updateAddress({ name, address: detailAddress, phone, _id: addressId }); + failedMessage = '修改地址失败,请稍候重试'; + } else { + console.log('to create'); + action = () => createAddress({ name, phone, address: detailAddress }); + failedMessage = '添加地址失败,请稍候重试'; + } + + try { + await action(); + addressListShouldFresh(); + wx.navigateBack({ delta: 1 }); + } catch { + this.toast(failedMessage); + } finally { + this.unsetLoading(); + } + } else { + this.toast(tips); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.json b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.json new file mode 100644 index 0000000..e4691da --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.json @@ -0,0 +1,16 @@ +{ + "navigationBarTitleText": "添加新地址", + "usingComponents": { + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-input": "tdesign-miniprogram/input/input", + "t-button": "tdesign-miniprogram/button/button", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-switch": "tdesign-miniprogram/switch/switch", + "t-cascader": "tdesign-miniprogram/cascader/cascader", + "loading-dialog": "../../../../components/loading-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml new file mode 100644 index 0000000..f9a38b2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml @@ -0,0 +1,25 @@ + + + +
+ + + + + + + + + + + + + + + 保存 + +
+
+
+ + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss new file mode 100644 index 0000000..f89e06f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss @@ -0,0 +1,95 @@ +page { + background-color: #f5f5f5; +} +page .divider-line { + width: 100%; + height: 20rpx; + background-color: #f5f5f5; +} +.address-flex-box { + display: flex; + flex-wrap: wrap; +} +.address-detail { + font-size: 30rpx; +} +.address-detail-wx-location { + background: #fff; + padding: 24rpx 32rpx; + display: flex; + align-items: center; + justify-content: space-between; +} +.address-detail-wx-arrow { + align-items: flex-end; +} + +.form-cell .t-cell__title { + width: 144rpx; + padding-right: 32rpx; + flex: none !important; +} + +.textarea__wrapper { + width: 100%; +} + +.textarea__wrapper .t-textarea { + padding: 0 !important; +} + +.form-address .map { + font-size: 48rpx !important; + margin-left: 20rpx; + color: #9d9d9f; +} + +.address__tag { + justify-content: flex-start !important; +} + +.form-address .label-list { + background: #f5f5f5; + color: #333; + min-width: 100rpx; + margin-right: 32rpx; + font-size: 26rpx; + border: 2rpx solid transparent; + width: auto; +} +.form-address .label-list::after { + content: none; +} +.form-address .active-btn { + color: #fa4126; + border: 2rpx solid #fa4126; + background: rgba(255, 95, 21, 0.04); +} +.form-address .active-btn::after { + border: 4rpx solid #ff5f15; +} + +.submit { + box-sizing: border-box; + padding: 64rpx 30rpx 88rpx 30rpx; +} +.submit .btn-submit-address { + background: #fa4126 !important; + color: #fff !important; +} + +.dialog__button-confirm { + color: #fa4126 !important; +} + +.form-address .form-content { + --td-input-vertical-padding: 0; +} + +.dialog__input { + margin-top: 32rpx; + border-radius: 8rpx; + box-sizing: border-box; + --td-input-vertical-padding: 12px; + --td-input-bg-color: #f3f3f3; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/util.js b/miniprogram/tcb-shop/pages/usercenter/address/edit/util.js new file mode 100644 index 0000000..17d619a --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/util.js @@ -0,0 +1,33 @@ +let addressPromise = []; + +/** 地址编辑Promise */ +export const getAddressPromise = () => { + let resolver; + let rejecter; + const nextPromise = new Promise((resolve, reject) => { + resolver = resolve; + rejecter = reject; + }); + + addressPromise.push({ resolver, rejecter }); + + return nextPromise; +}; + +/** 用户保存了一个地址 */ +export const resolveAddress = (address) => { + const allAddress = [...addressPromise]; + addressPromise = []; + + console.info('用户保存了一个地址', address); + + allAddress.forEach(({ resolver }) => resolver(address)); +}; + +/** 取消编辑 */ +export const rejectAddress = () => { + const allAddress = [...addressPromise]; + addressPromise = []; + + allAddress.forEach(({ rejecter }) => rejecter(new Error('cancel'))); +}; diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.js b/miniprogram/tcb-shop/pages/usercenter/address/list/index.js new file mode 100644 index 0000000..6eaf6f9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.js @@ -0,0 +1,102 @@ +/* eslint-disable no-param-reassign */ +import { getAllAddress, deleteAddress } from '../../../../services/address/address'; +import Toast from 'tdesign-miniprogram/toast/index'; +import { resolveAddress, rejectAddress } from './util'; +import { shouldFresh, addressListFinishFresh } from '../../../../utils/addressListFresh'; +import { objectToParamString } from '../../../../utils/util'; + +Page({ + data: { + addressList: [], + deleteID: '', + showDeleteConfirm: false, + loading: false, + }, + + setLoading() { + this.setData({ loading: true }); + }, + unsetLoading() { + this.setData({ loading: false }); + }, + + /** + * 如果是 true 的话,点击后会选中并返回上一页;否则点击后会进入编辑页 + */ + selectMode: false, + /** 是否已经选择地址,不置为 true 的话页面离开时会触发取消选择行为 */ + hasSelect: false, + + onLoad(query) { + const { selectMode, id = '' } = query; + this.setData({ + id, + }); + this.selectMode = selectMode === 'true'; + this.init(); + }, + + onShow() { + shouldFresh && this.fresh(); + }, + + init() { + this.fresh(); + }, + onUnload() { + if (this.selectMode && !this.hasSelect) { + rejectAddress(); + } + }, + async fresh() { + this.setLoading(); + try { + await this.getAddressList(); + addressListFinishFresh(); + } catch { + this.toast('拉取地址失败,请稍后再试'); + } finally { + this.unsetLoading(); + } + }, + async getAddressList() { + const addressList = await getAllAddress(); + this.setData({ addressList }); + }, + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + icon: '', + duration: 1000, + }); + }, + async deleteAddressHandle({ detail: { _id } }) { + try { + this.setLoading(); + await deleteAddress({ id: _id }); + const { addressList } = this.data; + this.setData({ addressList: addressList.filter((x) => x._id !== _id) }); + } catch { + this.toast('删除地址失败,请稍后再试'); + } finally { + this.unsetLoading(); + } + }, + editAddressHandle({ detail }) { + wx.navigateTo({ url: `/pages/usercenter/address/edit/index?${objectToParamString(detail)}` }); + }, + selectHandle({ detail }) { + if (this.selectMode) { + this.hasSelect = true; + resolveAddress(detail); + wx.navigateBack({ delta: 1 }); + } else { + this.editAddressHandle({ detail }); + } + }, + createHandle() { + wx.navigateTo({ url: '/pages/usercenter/address/edit/index' }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.json b/miniprogram/tcb-shop/pages/usercenter/address/list/index.json new file mode 100644 index 0000000..0a51b22 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "收货地址", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-address-item": "../../components/ui-address-item/index", + "t-empty": "tdesign-miniprogram/empty/empty", + "loading-dialog": "../../../../components/loading-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml new file mode 100644 index 0000000..77b3b98 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + 新建收货地址 + + + 最多支持添加20个收货地址 + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss new file mode 100644 index 0000000..286227b --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss @@ -0,0 +1,109 @@ +page { + background: #f5f5f5; + height: 100%; +} +.address-container { + display: flex; + flex-direction: column; + align-items: stretch; + padding-bottom: calc(env(safe-area-inset-bottom) + 172rpx); +} +.address-container .address-list { + font-size: 24rpx; + background-color: #ffffff; + -webkit-overflow-scrolling: touch; +} +.address-list .no-address { + width: 750rpx; + padding-top: 30vh; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; +} +.address-list .no-address__icon { + width: 224rpx; + height: 224rpx; +} +.address-list .no-address__text { + font-size: 28rpx; + line-height: 40rpx; + color: #999999; + margin-top: 24rpx; +} +.address-container .bottom-fixed { + border-top: 1rpx solid #e5e5e5; + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + background: #fff; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + padding: 12rpx 32rpx calc(env(safe-area-inset-bottom) + 12rpx) 32rpx; +} +.address-container .btn-wrap { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + font-size: 32rpx; + font-weight: bold; +} +.address-container .btn-wrap .location-btn { + width: 332rpx; + height: 88rpx; + display: flex; + justify-content: center; + align-items: center; + background-color: #ffffff; + color: #333; + position: relative; +} +.address-container .btn-wrap .location-btn::after { + content: ''; + position: absolute; /* 把父视图设置为relative,方便定位*/ + top: 0; + left: 0; + width: 200%; + height: 200%; + transform: scale(0.5); + transform-origin: 0 0; + box-sizing: border-box; + border-radius: 88rpx; + border: #dddddd 2rpx solid; +} +.address-container .btn-wrap .address-btn { + flex-grow: 1; + height: 88rpx; + display: flex; + justify-content: center; + align-items: center; + background-color: #fa4126; + border-radius: 44rpx; + color: #fff; +} +.address-container .btn-wrap .btn-default { + background: #c6c6c6; +} +.address-container .bottom-fixed .footer { + margin-top: 10rpx; + display: inline-block; + width: 100%; + text-align: center; + font-size: 24rpx; + font-weight: 400; + color: #ff2525; + line-height: 60rpx; + height: 60rpx; +} +.address-container .message { + margin-top: 48rpx; +} +.address-container .custom-class { + margin-right: 12rpx; + font-weight: normal; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/util.js b/miniprogram/tcb-shop/pages/usercenter/address/list/util.js new file mode 100644 index 0000000..953f8d0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/util.js @@ -0,0 +1,31 @@ +let addressPromise = []; + +/** 获取一个地址选择Promise */ +export const getAddressPromise = () => { + let resolver; + let rejecter; + const nextPromise = new Promise((resolve, reject) => { + resolver = resolve; + rejecter = reject; + }); + + addressPromise.push({ resolver, rejecter }); + + return nextPromise; +}; + +/** 用户选择了一个地址 */ +export const resolveAddress = (address) => { + const allAddress = [...addressPromise]; + addressPromise = []; + + allAddress.forEach(({ resolver }) => resolver(address)); +}; + +/** 用户没有选择任何地址只是返回上一页了 */ +export const rejectAddress = () => { + const allAddress = [...addressPromise]; + addressPromise = []; + + allAddress.forEach(({ rejecter }) => rejecter(new Error('cancel'))); +}; diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js new file mode 100644 index 0000000..5bfe0f6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js @@ -0,0 +1,37 @@ +Component({ + externalClasses: ['title-class', 'icon-class', 'number-class'], + options: { + multipleSlots: true, + }, + properties: { + orderTagInfos: { + type: Array, + value: [], + }, + title: { + type: String, + value: '我的订单', + }, + desc: { + type: String, + value: '全部订单', + }, + isTop: { + type: Boolean, + value: true, + }, + classPrefix: { + type: String, + value: 'wr', + }, + }, + methods: { + onClickItem(e) { + this.triggerEvent('onClickItem', e.currentTarget.dataset.item); + }, + + onClickTop() { + this.triggerEvent('onClickTop', {}); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json new file mode 100644 index 0000000..c22feda --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-badge": "tdesign-miniprogram/badge/badge", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml new file mode 100644 index 0000000..de56e28 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + {{item.title}} + + + diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss new file mode 100644 index 0000000..59b9aa1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss @@ -0,0 +1,56 @@ +.order-group { + margin-bottom: 24rpx; + background-color: #ffffff; + border-radius: 16rpx 16rpx 0 0; +} +.order-group .order-group__top { + padding: 24rpx 18rpx 24rpx 32rpx; + border-radius: 16rpx 16rpx 0 0; +} +.order-group__top___title { + font-size: 32rpx; + line-height: 48rpx; + font-weight: bold; +} +.order-group__top__note { + font-size: 28rpx; +} +.order-group__content { + overflow: hidden; + width: 100%; + height: 164rpx; + display: flex; + background-color: #fff; + border-radius: 0 0 16rpx 16rpx; +} +.order-group__item { + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; +} +.order-group__item:first-child { + border-radius: 0 0 0 16rpx; +} +.order-group__item:last-child { + border-radius: 0 0 16rpx 0; +} +.order-group__item__title { + font-size: 24rpx; + color: #666; + line-height: 32rpx; +} +.order-group__item__icon { + margin-bottom: 20rpx; + width: 56rpx; + height: 56rpx; + position: relative; +} +.order-group__top__title { + font-weight: bold; +} +.order-group .order-group__left { + margin-right: 0; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js new file mode 100644 index 0000000..467bbf7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js @@ -0,0 +1,46 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, + }, + properties: { + address: { + type: Object, + value: {}, + }, + customIcon: { + type: String, + value: 'edit-1', + }, + extraSpace: { + type: Boolean, + value: true, + }, + isDrawLine: { + type: Boolean, + value: true, + }, + }, + externalClasses: [ + 'item-wrapper-class', + 'title-class', + 'default-tag-class', + 'normal-tag-class', + 'address-info-class', + 'delete-class', + ], + methods: { + onDelete(e) { + const { item } = e.currentTarget.dataset; + this.triggerEvent('onDelete', item); + }, + onSelect(e) { + const { item } = e.currentTarget.dataset; + this.triggerEvent('onSelect', item); + }, + onEdit(e) { + const { item } = e.currentTarget.dataset; + this.triggerEvent('onEdit', item); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json new file mode 100644 index 0000000..0f9fe0f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell" + } +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml new file mode 100644 index 0000000..9cc4bba --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml @@ -0,0 +1,30 @@ + + var toHide = function(array) { var mphone = array.substring(0, 3) + '****' + array.substring(7); return mphone; } + module.exports.toHide = toHide; + + + + + + + + + + {{address.name}} + {{phoneReg.toHide(address.phone || '')}} + + + + {{address.address}} + + + + + + + + + 删除 + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss new file mode 100644 index 0000000..5ce1ea9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss @@ -0,0 +1,103 @@ +.address-item-wrapper { + overflow: hidden; +} +.address-item-wrapper .swipe-out .wr-swiper-cell { + margin-top: 20rpx; +} +.address-item-wrapper .swipe-out .swipe-right-del { + display: flex; + justify-content: center; + align-items: center; + width: 144rpx; + height: 100%; + background-color: #fa4126; + color: #fff; + font-size: 28rpx; + line-height: 40rpx; +} +.address-item-wrapper .draw-line { + position: relative; +} +.address-item-wrapper .draw-line::after { + content: ''; + position: absolute; + bottom: 0; + left: 32rpx; + width: 200%; + height: 2rpx; + transform: scale(0.5); + transform-origin: 0 0; + box-sizing: border-box; + border-bottom: #e5e5e5 2rpx solid; +} +.address-item-wrapper .address { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32rpx; + background-color: #fff; +} +.address-item-wrapper .address .address-edit { + padding: 20rpx 0 20rpx 46rpx; +} +.address-item-wrapper .address .address-left { + width: 80rpx; + display: flex; + justify-content: center; +} +.address-item-wrapper .address .address-content { + display: flex; + flex-direction: column; + flex: 1; +} +.address-item-wrapper .address .address-content .title { + font-size: 32rpx; + line-height: 48rpx; + margin-bottom: 16rpx; + color: #333333; + font-weight: bold; + display: flex; +} +.address-item-wrapper .address .address-content .title .text-style { + margin-right: 8rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 280rpx; +} +.address-item-wrapper .address .address-content .label-adds { + display: flex; +} +.address-item-wrapper .address .address-content .label-adds .adds { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + color: #999999; +} +.address-item-wrapper .address .address-content .label-adds .tag { + display: inline-block; + padding: 0rpx 8rpx; + min-width: 40rpx; + height: 32rpx; + border-radius: 18rpx; + font-size: 20rpx; + line-height: 32rpx; + text-align: center; + margin-right: 8rpx; + vertical-align: text-top; +} +.address-item-wrapper .address .address-content .label-adds .tag-default { + background: #ffece9; + color: #fa4126; +} +.address-item-wrapper .address .address-content .label-adds .tag-primary { + background: #f0f1ff; + color: #5a66ff; +} +.address-item-wrapper .address .address-content .label-adds .address-text { + font-size: 28rpx; + line-height: 40rpx; + color: #999999; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js new file mode 100644 index 0000000..fff68cc --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js @@ -0,0 +1,73 @@ +Component({ + properties: { + show: { + type: Boolean, + observer(show) { + if (!show) return; + this.updateDivisions(); + }, + }, + title: { + type: String, + value: '', + }, + value: { + type: String, + value: '', + observer() { + if (!this.data.show) return; + this.updateDivisions(); + }, + }, + pickerOptions: { + type: Array, + value: [], + observer() { + if (!this.data.show) return; + this.updateDivisions(); + }, + }, + headerVisible: { + type: Boolean, + value: true, + }, + }, + data: { + pickerValue: [], + }, + methods: { + updateDivisions() { + const { pickerOptions, value } = this.data; + const index = (pickerOptions || []).findIndex( + (item) => item.code === value, + ); + + setTimeout(() => { + this.setData({ pickerValue: index >= 0 ? [index] : [0] }); + }, 0); + }, + + getAreaByIndex(indexes) { + const { pickerOptions } = this.data; + return pickerOptions[indexes.toString()]; + }, + + onChange(e) { + const currentValue = e.detail.value; + const target = this.getAreaByIndex(currentValue); + if (target === null) return; + + this.setData({ pickerValue: currentValue }); + this.triggerEvent('change', { value: target.code, target: target }); + }, + + onConfirm() { + const target = this.getAreaByIndex(this.data.pickerValue); + this.triggerEvent('confirm', { value: target?.code, target }); + }, + + onClose() { + this.triggerEvent('close'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json new file mode 100644 index 0000000..6e4c04e --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup" + } +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml new file mode 100644 index 0000000..6e3cddc --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml @@ -0,0 +1,21 @@ + + + + 取消 + {{title}} + 确定 + + + {{title}} + + + + {{ item.name }} + + + + 取消 + 确定 + + + diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss new file mode 100644 index 0000000..3e977be --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss @@ -0,0 +1,102 @@ +.city-picker-container { + opacity: 0; + position: fixed; + top: 100vh; + left: 0; + right: 0; + height: 100vh; + z-index: 100; +} +.city-picker-container.show { + top: 0; + opacity: 1; +} +.city-picker-container.show .city-picker-box { + bottom: 0; +} +.city-picker-shadow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.65); +} +.city-picker-header { + height: 100rpx; + line-height: 100rpx; + text-align: center; + font-size: 32rpx; + color: #333333; +} +.city-picker-more { + display: flex; + justify-content: space-between; + align-items: center; +} +.city-picker-footer { + height: 100rpx; + display: flex; + justify-content: space-between; + align-items: center; +} +.city-picker-footer .btn { + width: 330rpx; + height: 80rpx; + line-height: 80rpx; + text-align: center; + color: #666666; + font-size: 32rpx; + position: relative; +} +.city-picker-footer .btn__active { + opacity: 0.5; +} +.city-picker-footer .btn::after { + display: block; + content: ' '; + position: absolute; + left: -50%; + right: -50%; + top: -50%; + bottom: -50%; + transform: scale(0.5); + border: 1rpx solid #999999; + border-radius: 16rpx; +} +.city-picker-footer .btn.primary { + color: #fa550f; +} +.city-picker-footer .btn.primary::after { + border-color: #fa550f; +} +.picker-column:not(:first-child) { + margin-left: 40rpx; +} +.city-picker-box { + position: absolute; + bottom: -100%; + transition: 0.3s bottom ease-in-out; + left: 0; + right: 0; + z-index: 100; + background-color: #fff; + padding: 0 30rpx; + color: #333333; + font-size: 34rpx; + border-radius: 20rpx 20rpx 0 0; + padding-bottom: env(safe-area-inset-bottom); +} +.show .city-picker-shadow { + display: block; +} +.picker { + height: 300rpx; + margin: 50rpx 0; + line-height: 88rpx; + text-align: center; +} +/* 似乎小程序picker-view的bug,indicator-class仅height生效,其他诸如line-height、text-align等放到父class中设置 */ +.picker-center-row { + height: 88rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js new file mode 100644 index 0000000..47593c4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js @@ -0,0 +1,35 @@ +const AuthStepType = { + ONE: 1, + TWO: 2, + THREE: 3, +}; + +Component({ + options: { + multipleSlots: true, + }, + properties: { + currAuthStep: { + type: Number, + value: AuthStepType.ONE, + }, + userInfo: { + type: Object, + value: {}, + }, + isNeedGetUserInfo: { + type: Boolean, + value: false, + }, + }, + data: { + defaultAvatarUrl: + 'https://cdn-we-retail.ym.tencent.com/miniapp/usercenter/icon-user-center-avatar@2x.png', + AuthStepType, + }, + methods: { + gotoUserEditPage() { + this.triggerEvent('gotoUserEditPage'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json new file mode 100644 index 0000000..e169f6f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-avatar": "tdesign-miniprogram/avatar/avatar" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml new file mode 100644 index 0000000..ad11e7d --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml @@ -0,0 +1,34 @@ + + + + + + {{'请登录'}} + + + + + + + {{userInfo.nickName || '微信用户'}} + + + + + + + + + + + + + {{userInfo.nickName || '微信用户'}} + + + diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss new file mode 100644 index 0000000..2b488c1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss @@ -0,0 +1,48 @@ +.user-center-card { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 480rpx; + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/template/user-center-bg-v1.png'); + background-size: cover; + background-repeat: no-repeat; + padding: 0 24rpx; +} +.user-center-card__header { + margin-top: 192rpx; + margin-bottom: 48rpx; + height: 96rpx; + line-height: 48rpx; + display: flex; + justify-content: flex-start; + align-items: center; + color: #333; + position: relative; +} +.user-center-card__header__avatar { + width: 96rpx; + height: 96rpx; + border-radius: 48rpx; + overflow: hidden; +} + +.user-center-card__header__name { + font-size: 36rpx; + line-height: 48rpx; + color: #333; + font-weight: bold; + margin-left: 24rpx; + margin-right: 16rpx; +} +.user-center-card__header__transparent { + position: absolute; + left: 0; + top: 0; + background-color: transparent; + height: 100%; + width: 100%; +} +.user-center-card__icon { + line-height: 96rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/index.js b/miniprogram/tcb-shop/pages/usercenter/index.js new file mode 100644 index 0000000..cf90eed --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.js @@ -0,0 +1,220 @@ +import { fetchUserCenter } from '../../services/usercenter/fetchUsercenter'; +import { getToPayOrderCount, getToSendOrderCount, getToReceiveOrderCount } from '../../services/order/order'; +import { ORDER_STATUS } from '../../services/order/order'; +import Toast from 'tdesign-miniprogram/toast/index'; + +const menuData = [ + [ + { + title: '收货地址', + tit: '', + url: '', + type: 'address', + }, + ], +]; + +const orderTagInfos = [ + { + title: '待付款', + iconName: 'wallet', + orderNum: 0, + tabType: ORDER_STATUS.TO_PAY, + status: 1, + }, + { + title: '待发货', + iconName: 'deliver', + orderNum: 0, + tabType: ORDER_STATUS.TO_SEND, + status: 1, + }, + { + title: '待收货', + iconName: 'package', + orderNum: 0, + tabType: ORDER_STATUS.TO_RECEIVE, + status: 1, + }, + { + title: '待评价', + iconName: 'comment', + orderNum: 0, + tabType: ORDER_STATUS.FINISHED, + status: 1, + }, + // { + // title: '退款/售后', + // iconName: 'exchang', + // orderNum: 0, + // tabType: 0, + // status: 1, + // }, +]; + +const getDefaultData = () => ({ + showMakePhone: false, + userInfo: { + avatarUrl: '', + nickName: '正在登录...', + phoneNumber: '', + }, + menuData, + orderTagInfos, + customerServiceInfo: {}, + currAuthStep: 1, + showKefu: true, + versionNo: '', + toPayOrderCount: 0, + toSendOrderCount: 0, + toReceiveOrderCount: 0, +}); + +Page({ + data: getDefaultData(), + + onLoad() { + this.getVersionInfo(); + }, + + onShow() { + this.getTabBar().init(); + this.init(); + }, + onPullDownRefresh() { + this.init(); + }, + + init() { + this.fetUseriInfoHandle(); + this.initOrderCount(); + }, + + async initOrderCount() { + const [pay, send, receive] = await Promise.all([ + getToPayOrderCount(), + getToSendOrderCount(), + getToReceiveOrderCount(), + ]); + this.setData({ + 'orderTagInfos[0].orderNum': pay, + 'orderTagInfos[1].orderNum': send, + 'orderTagInfos[2].orderNum': receive, + }); + }, + + fetUseriInfoHandle() { + fetchUserCenter().then(({ userInfo, countsData, customerServiceInfo }) => { + // eslint-disable-next-line no-unused-expressions + menuData?.[0].forEach((v) => { + countsData.forEach((counts) => { + if (counts.type === v.type) { + // eslint-disable-next-line no-param-reassign + v.tit = counts.num; + } + }); + }); + this.setData({ + userInfo, + menuData, + customerServiceInfo, + currAuthStep: 2, + }); + wx.stopPullDownRefresh(); + }); + }, + + onClickCell({ currentTarget }) { + const { type } = currentTarget.dataset; + + switch (type) { + case 'address': { + wx.navigateTo({ url: '/pages/usercenter/address/list/index' }); + break; + } + case 'service': { + this.openMakePhone(); + break; + } + case 'help-center': { + Toast({ + context: this, + selector: '#t-toast', + message: '你点击了帮助中心', + icon: '', + duration: 1000, + }); + break; + } + case 'point': { + Toast({ + context: this, + selector: '#t-toast', + message: '你点击了积分菜单', + icon: '', + duration: 1000, + }); + break; + } + case 'coupon': { + wx.navigateTo({ url: '/pages/coupon/coupon-list/index' }); + break; + } + default: { + Toast({ + context: this, + selector: '#t-toast', + message: '未知跳转', + icon: '', + duration: 1000, + }); + break; + } + } + }, + + jumpNav(e) { + const status = e.detail.tabType; + + if (status === 0) { + wx.navigateTo({ url: '/pages/order/after-service-list/index' }); + } else { + wx.navigateTo({ url: `/pages/order/order-list/index?status=${status}` }); + } + }, + + jumpAllOrder() { + wx.navigateTo({ url: '/pages/order/order-list/index' }); + }, + + openMakePhone() { + this.setData({ showMakePhone: true }); + }, + + closeMakePhone() { + this.setData({ showMakePhone: false }); + }, + + call() { + wx.makePhoneCall({ + phoneNumber: this.data.customerServiceInfo.servicePhone, + }); + }, + + gotoUserEditPage() { + const { currAuthStep } = this.data; + if (currAuthStep === 2) { + wx.navigateTo({ url: '/pages/usercenter/person-info/index' }); + } else { + this.fetUseriInfoHandle(); + } + }, + + getVersionInfo() { + const versionInfo = wx.getAccountInfoSync(); + const { version, envVersion = __wxConfig } = versionInfo.miniProgram; + this.setData({ + versionNo: envVersion === 'release' ? version : envVersion, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/index.json b/miniprogram/tcb-shop/pages/usercenter/index.json new file mode 100644 index 0000000..398a70b --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.json @@ -0,0 +1,14 @@ +{ + "navigationBarTitleText": "个人中心", + "navigationStyle": "custom", + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-user-center-card": "./components/user-center-card/index", + "t-order-group": "./components/order-group/index", + "t-toast": "tdesign-miniprogram/toast/toast" + }, + "enablePullDownRefresh": true +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/index.wxml b/miniprogram/tcb-shop/pages/usercenter/index.wxml new file mode 100644 index 0000000..e42a865 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.wxml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + 服务时间: {{customerServiceInfo.serviceTimeDuration}} + + 电话客服 + + 取消 + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/index.wxss b/miniprogram/tcb-shop/pages/usercenter/index.wxss new file mode 100644 index 0000000..483e6f2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.wxss @@ -0,0 +1,146 @@ +page { + background-color: #f5f5f5; +} + +.content-wrapper { + margin-top: 340rpx; + position: relative; + padding: 0 30rpx; +} + +.main-content { + height: 500rpx; +} + +.order-group-wrapper { + margin-bottom: 16rpx; +} + +.order-group-note { + font-size: 28rpx; +} + +.cell-box { + border-radius: 10rpx; + overflow: hidden; + margin-bottom: 20rpx; +} +.icon-color { + color: #aaa; +} +.cell-class { + height: 100rpx; + display: flex; + align-items: center; +} + +.order-content { + overflow: hidden; + width: 100%; + display: flex; + background-color: #fff; + border-radius: 16rpx; +} + +.order-item { + flex: 1; + height: 180rpx; + overflow: hidden; + position: relative; + text-align: center; +} + +.order-content-box { + margin: auto; + position: absolute; + width: 100%; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} +.order-content-t { + margin-top: 10rpx; + font-size: 24rpx; + color: #333; + letter-spacing: 0; + text-align: center; +} + +.popup-content { + background: #f5f5f5; + margin-bottom: env(safe-area-inset-bottom); + border-radius: 16rpx 16rpx 0 0; +} +.popup-content .popup-title { + background: #fff; + text-align: center; + font-size: 24rpx; + color: #999; + height: 112rpx; + text-align: center; + line-height: 112rpx; + border-radius: 16rpx 16rpx 0 0; +} + +.border-bottom-1px { + position: relative; +} + +.border-bottom-1px::after { + position: absolute; + display: block; + content: ''; + box-sizing: border-box; + top: 0; + left: 0; + width: 200%; + height: 200%; + transform: scale(0.5); + transform-origin: left top; + border-bottom: 2rpx solid #e5e5e5; +} +.popup-content .popup-phone, +.popup-content .popup-close { + background: #fff; + height: 100rpx; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + font-size: 30rpx; + font-family: PingFangSC-Regular, PingFang SC; + font-weight: 400; + color: #333; +} +.popup-content .popup-phone.online { + margin-bottom: 20rpx; +} +.popup-content .popup-phone.online::after { + content: none; +} +.popup-content .popup-close { + color: #333; + border: 0; + margin-top: 16rpx; +} + +.my-order { + border-radius: 10rpx; +} + +.footer__version { + text-align: center; + margin-top: 50rpx; + color: #999; + margin-bottom: 4rpx; + font-size: 24rpx; + line-height: 32rpx; +} +.cell-box .order-group__left { + margin-right: 0; +} +.cell-box .t-cell-padding { + padding: 24rpx 18rpx 24rpx 32rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.js b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.js new file mode 100644 index 0000000..57e4e3d --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.js @@ -0,0 +1,19 @@ +Page({ + data: { + nameValue: '', + }, + onLoad(options) { + const { name } = options; + this.setData({ + nameValue: name, + }); + }, + onSubmit() { + wx.navigateBack({ backRefresh: true }); + }, + clearContent() { + this.setData({ + nameValue: '', + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.json b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.json new file mode 100644 index 0000000..efc7e5f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "昵称", + "usingComponents": { + "t-input": "tdesign-miniprogram/input/input", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml new file mode 100644 index 0000000..2ceb518 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml @@ -0,0 +1,14 @@ + + + 最多可输入15个字 + + 保存 + + diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss new file mode 100644 index 0000000..61674b3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss @@ -0,0 +1,18 @@ +page { + background-color: #f5f5f5; +} +page view { + box-sizing: border-box; +} +.name-edit { + padding-top: 20rpx; +} +.name-edit .name-edit__input--desc { + font-size: 26rpx; + padding: 16rpx 32rpx; + color: #999; + margin-bottom: 200rpx; +} +.name-edit .name-edit__wrapper { + margin: 0 32rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.js b/miniprogram/tcb-shop/pages/usercenter/person-info/index.js new file mode 100644 index 0000000..fb5db98 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.js @@ -0,0 +1,122 @@ +import { fetchPerson } from '../../../services/usercenter/fetchPerson'; +import { phoneEncryption } from '../../../utils/util'; +import Toast from 'tdesign-miniprogram/toast/index'; + +Page({ + data: { + personInfo: { + avatarUrl: '', + nickName: '', + gender: 0, + phoneNumber: '', + }, + showUnbindConfirm: false, + pickerOptions: [ + { + name: '男', + code: '1', + }, + { + name: '女', + code: '2', + }, + ], + typeVisible: false, + genderMap: ['', '男', '女'], + }, + onLoad() { + this.init(); + }, + init() { + this.fetchData(); + }, + fetchData() { + fetchPerson().then((personInfo) => { + this.setData({ + personInfo, + 'personInfo.phoneNumber': phoneEncryption(personInfo.phoneNumber), + }); + }); + }, + onClickCell({ currentTarget }) { + const { dataset } = currentTarget; + const { nickName } = this.data.personInfo; + + switch (dataset.type) { + case 'gender': + this.setData({ + typeVisible: true, + }); + break; + case 'name': + wx.navigateTo({ + url: `/pages/usercenter/name-edit/index?name=${nickName}`, + }); + break; + case 'avatarUrl': + this.toModifyAvatar(); + break; + default: { + break; + } + } + }, + onClose() { + this.setData({ + typeVisible: false, + }); + }, + onConfirm(e) { + const { value } = e.detail; + this.setData( + { + typeVisible: false, + 'personInfo.gender': value, + }, + () => { + Toast({ + context: this, + selector: '#t-toast', + message: '设置成功', + theme: 'success', + }); + }, + ); + }, + async toModifyAvatar() { + try { + const tempFilePath = await new Promise((resolve, reject) => { + wx.chooseImage({ + count: 1, + sizeType: ['compressed'], + sourceType: ['album', 'camera'], + success: (res) => { + const { path, size } = res.tempFiles[0]; + if (size <= 10485760) { + resolve(path); + } else { + reject({ errMsg: '图片大小超出限制,请重新上传' }); + } + }, + fail: (err) => reject(err), + }); + }); + const tempUrlArr = tempFilePath.split('/'); + const tempFileName = tempUrlArr[tempUrlArr.length - 1]; + Toast({ + context: this, + selector: '#t-toast', + message: `已选择图片-${tempFileName}`, + theme: 'success', + }); + } catch (error) { + if (error.errMsg === 'chooseImage:fail cancel') return; + Toast({ + context: this, + selector: '#t-toast', + message: error.errMsg || error.msg || '修改头像出错了', + theme: 'error', + }); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.json b/miniprogram/tcb-shop/pages/usercenter/person-info/index.json new file mode 100644 index 0000000..e63dcc5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "个人资料", + "usingComponents": { + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-button": "tdesign-miniprogram/button/button", + "t-image": "/components/webp-image/index", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-select-picker": "../components/ui-select-picker/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml new file mode 100644 index 0000000..21b8c6f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss new file mode 100644 index 0000000..cb4eb70 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss @@ -0,0 +1,45 @@ +:host { + background-color: #f5f5f5; +} +page view { + box-sizing: border-box; +} +.person-info { + padding-top: 20rpx; +} + +.person-info__btn { + width: 100%; + border: 2rpx solid #ddd; + border-radius: 48rpx; + padding: 18rpx 0; + display: flex; + align-self: center; + justify-content: center; +} +.person-info__wrapper { + width: 100%; + padding: 0 32rpx; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); + position: absolute; + bottom: 0; + left: 0; +} + +.avatarUrl { + width: 80rpx; + height: 80rpx; + border-radius: 50% !important; + overflow: hidden; +} + +.t-class-confirm { + color: #fa550f !important; +} + +.person-info .order-group__left { + margin-right: 0; +} +.person-info .t-cell-class { + height: 112rpx; +} diff --git a/miniprogram/tcb-shop/project.config.json b/miniprogram/tcb-shop/project.config.json new file mode 100644 index 0000000..2a3b8cb --- /dev/null +++ b/miniprogram/tcb-shop/project.config.json @@ -0,0 +1,150 @@ +{ + "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "packOptions": { + "ignore": [], + "include": [] + }, + "setting": { + "urlCheck": true, + "es6": true, + "enhance": true, + "postcss": true, + "preloadBackgroundData": false, + "minified": true, + "newFeature": false, + "coverView": true, + "nodeModules": true, + "autoAudits": false, + "showShadowRootInWxmlPanel": true, + "scopeDataCheck": false, + "uglifyFileName": false, + "checkInvalidKey": true, + "checkSiteMap": true, + "uploadWithSourceMap": true, + "compileHotReLoad": false, + "lazyloadPlaceholderEnable": false, + "useMultiFrameRuntime": true, + "useApiHook": true, + "useApiHostProcess": true, + "ignoreDevUnusedFiles": false, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "enableEngineNative": false, + "useIsolateContext": true, + "userConfirmedBundleSwitch": false, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true, + "disableUseStrict": false, + "minifyWXML": true, + "showES6CompileOption": false, + "useCompilerPlugins": false, + "ignoreUploadUnusedFiles": true, + "useStaticServer": true + }, + "compileType": "miniprogram", + "libVersion": "2.23.1", + "appid": "", + "projectname": "tcb-shop", + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "condition": { + "miniprogram": { + "list": [ + { + "name": "首页入口", + "pathName": "pages/home/home", + "query": "", + "scene": null + }, + { + "name": "示例页-商品分类", + "pathName": "pages/goods/category/index", + "query": "", + "scene": null + }, + { + "name": "示例页-个人中心", + "pathName": "pages/usercenter/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品列表", + "pathName": "pages/goods/list/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品详情", + "pathName": "pages/goods/details/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品评论", + "pathName": "pages/goods/comments/index", + "query": "", + "scene": null + }, + { + "name": "示例页-售后列表", + "pathName": "pages/order/after-service-list/index", + "query": "", + "scene": null + }, + { + "name": "示例页-售后详情", + "pathName": "pages/order/after-service-detail/index", + "query": "rightsNo=123123423", + "scene": null + }, + { + "name": "示例页-搜索页", + "pathName": "pages/goods/search/index", + "query": "", + "scene": null + }, + { + "name": "示例页-搜索结果", + "pathName": "pages/goods/result/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品评价", + "pathName": "pages/goods/comments/create/index", + "query": "", + "scene": null + }, + { + "name": "示例页-选择商品评价", + "pathName": "pages/goods/comments/create-list/index", + "query": "", + "scene": null + }, + { + "name": "示例页-申请售后", + "pathName": "pages/order/apply-service/index", + "query": "orderNo=132222623132329291&skuId=135691625", + "scene": null + }, + { + "name": "示例页-发票详情", + "pathName": "pages/order/invoice/index", + "query": "orderNo=132381532610540875", + "scene": null + } + ] + } + }, + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + }, + "cloudfunctionTemplateRoot": "cloudfunctionTemplate/", + "cloudfunctionRoot": "cloudfunctions/" +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/services/_utils/delay.js b/miniprogram/tcb-shop/services/_utils/delay.js new file mode 100644 index 0000000..da4ad62 --- /dev/null +++ b/miniprogram/tcb-shop/services/_utils/delay.js @@ -0,0 +1,3 @@ +export function delay(ms = 200) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/miniprogram/tcb-shop/services/_utils/model.js b/miniprogram/tcb-shop/services/_utils/model.js new file mode 100644 index 0000000..46329e9 --- /dev/null +++ b/miniprogram/tcb-shop/services/_utils/model.js @@ -0,0 +1,34 @@ +export function model() { + return globalThis.dataModel; +} + +export async function getAll({ filter, select, name }) { + const addSelect = (prop) => (select ? { ...prop, select } : prop); + const pageSize = 200; + const first = await model()[name].list( + addSelect({ + pageNumber: 1, + pageSize, + getCount: true, + filter, + }), + ); + const { + data: { total }, + } = first; + const totalPage = Math.ceil(total / 200); + const lists = await Promise.all( + Array.from({ length: totalPage - 1 }, (_, index) => index + 2).map((pageNumber) => + model()[name].list( + addSelect({ + pageNumber, + pageSize, + filter, + }), + ), + ), + ); + + const ret = lists.reduce((acc, current) => acc.concat(current.data.records), first.data.records); + return ret; +} diff --git a/miniprogram/tcb-shop/services/_utils/timeout.js b/miniprogram/tcb-shop/services/_utils/timeout.js new file mode 100644 index 0000000..870c2bf --- /dev/null +++ b/miniprogram/tcb-shop/services/_utils/timeout.js @@ -0,0 +1,3 @@ +export function timeout(ms = 1000) { + return new Promise((_, reject) => setTimeout(reject, ms)); +} diff --git a/miniprogram/tcb-shop/services/activity/fetchActivity.js b/miniprogram/tcb-shop/services/activity/fetchActivity.js new file mode 100644 index 0000000..e9aefa6 --- /dev/null +++ b/miniprogram/tcb-shop/services/activity/fetchActivity.js @@ -0,0 +1,20 @@ +import { config } from '../../config/index'; + +/** 获取活动列表 */ +function mockFetchActivity(ID = 0) { + const { delay } = require('../_utils/delay'); + const { getActivity } = require('../../model/activity'); + + return delay().then(() => getActivity(ID)); +} + +/** 获取活动列表 */ +export function fetchActivity(ID = 0) { + if (config.useMock) { + return mockFetchActivity(ID); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/activity/fetchActivityList.js b/miniprogram/tcb-shop/services/activity/fetchActivityList.js new file mode 100644 index 0000000..2522aff --- /dev/null +++ b/miniprogram/tcb-shop/services/activity/fetchActivityList.js @@ -0,0 +1,20 @@ +import { config } from '../../config/index'; + +/** 获取活动列表 */ +function mockFetchActivityList(pageIndex = 1, pageSize = 20) { + const { delay } = require('../_utils/delay'); + const { getActivityList } = require('../../model/activities'); + + return delay().then(() => getActivityList(pageIndex, pageSize)); +} + +/** 获取活动列表 */ +export function fetchActivityList(pageIndex = 1, pageSize = 20) { + if (config.useMock) { + return mockFetchActivityList(pageIndex, pageSize); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/address/address.js b/miniprogram/tcb-shop/services/address/address.js new file mode 100644 index 0000000..d0e285e --- /dev/null +++ b/miniprogram/tcb-shop/services/address/address.js @@ -0,0 +1,85 @@ +import { DATA_MODEL_KEY } from '../../config/model'; +import { getAll, model } from '../_utils/model'; + +const DELIVERY_INFO_MODEL_KEY = DATA_MODEL_KEY.DELIVERY_INFO; + +export function getAllAddress() { + return getAll({ + name: DELIVERY_INFO_MODEL_KEY, + select: { + _id: true, + phone: true, + address: true, + name: true, + }, + }); +} + +/** + * + * @param {{ + * name: String, + * address: String, + * phone: String, + * }} param0 + * @returns + */ +export function createAddress({ name, address, phone }) { + return model()[DELIVERY_INFO_MODEL_KEY].create({ + data: { + name, + address, + phone, + }, + }); +} + +/** + * + * @param {{ + * name, + * address, + * phone, + * _id + * }} param0 + */ +export function updateAddress({ name, address, phone, _id }) { + return model()[DELIVERY_INFO_MODEL_KEY].update({ + data: { + name, + address, + phone, + }, + filter: { + where: { + _id: { $eq: _id }, + }, + }, + }); +} + +export function deleteAddress({ id }) { + return model()[DELIVERY_INFO_MODEL_KEY].delete({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + }); +} + +export async function getAddress({ id }) { + return ( + await model()[DELIVERY_INFO_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + }) + ).data; +} diff --git a/miniprogram/tcb-shop/services/attrValue/attrValue.js b/miniprogram/tcb-shop/services/attrValue/attrValue.js new file mode 100644 index 0000000..915e460 --- /dev/null +++ b/miniprogram/tcb-shop/services/attrValue/attrValue.js @@ -0,0 +1,30 @@ +import { getAll } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const ATTR_VALUE_MODEL_KEY = DATA_MODEL_KEY.ATTR_VALUE; + +export async function getAllAttrValues(skuId) { + const res = await getAll({ + name: ATTR_VALUE_MODEL_KEY, + filter: { + relateWhere: { + sku: { + where: { + _id: { + $eq: skuId, + }, + }, + }, + }, + }, + select: { + _id: true, + value: true, + attr_name: { + _id: true, + name: true, + }, + }, + }); + return res; +} diff --git a/miniprogram/tcb-shop/services/cart/cart.js b/miniprogram/tcb-shop/services/cart/cart.js new file mode 100644 index 0000000..fb69231 --- /dev/null +++ b/miniprogram/tcb-shop/services/cart/cart.js @@ -0,0 +1,120 @@ +import { model, getAll } from '../../services/_utils/model'; +import { config } from '../../config/index'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const CATE_ITEM_MODEL_KEY = DATA_MODEL_KEY.CART_ITEM; + +/** 获取购物车mock数据 */ +function mockFetchCartGroupData(params) { + const { delay } = require('../_utils/delay'); + const { genCartGroupData } = require('../../model/cart'); + + return delay().then(() => genCartGroupData(params)); +} + +/** + * + * @param {{id: string}} param0 + * @returns + */ +export async function getCartItem({ id }) { + return model()[CATE_ITEM_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + select: { + _id: true, + count: true, + sku: { + _id: true, + count: true, + description: true, + }, + }, + }); +} + +export async function fetchCartItems() { + return getAll({ + name: CATE_ITEM_MODEL_KEY, + select: { + _id: true, + count: true, + sku: { + _id: true, + count: true, + description: true, + }, + }, + }); +} + +/** + * + * @param {{ + * skuId: String, + * count: Number + * }} param0 + */ +export async function createCartItem({ skuId, count }) { + return await model()[CATE_ITEM_MODEL_KEY].create({ + data: { + count, + sku: { _id: skuId }, + }, + }); +} + +/** + * + * @param {{cartItemId: string}} param0 + */ +export async function deleteCartItem({ cartItemId }) { + return await model()[CATE_ITEM_MODEL_KEY].delete({ + filter: { + where: { + _id: { + $eq: cartItemId, + }, + }, + }, + }); +} + +/** + * + * @param {{ + * cartItemId: String, + * count: Number + * }} param0 + * @returns + */ +export async function updateCartItemCount({ cartItemId, count }) { + return await model()[CATE_ITEM_MODEL_KEY].update({ + data: { + count, + }, + filter: { + where: { + _id: { + $eq: cartItemId, + }, + }, + }, + }); +} + +/** 获取购物车数据 */ +export function fetchCartGroupData(params) { + if (config.useMock) { + return mockFetchCartGroupData(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/cate/cate.js b/miniprogram/tcb-shop/services/cate/cate.js new file mode 100644 index 0000000..331e1c9 --- /dev/null +++ b/miniprogram/tcb-shop/services/cate/cate.js @@ -0,0 +1,60 @@ +/* eslint-disable eqeqeq */ +import { model, getAll } from '../_utils/model'; +import { getCloudImageTempUrl } from '../../utils/cloudImageHandler'; +import { SPU_SELLING_STATUS } from '../../utils/spuStatus'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const CATE_MODEL_KEY = DATA_MODEL_KEY.CATE; + +// TODO: we should do pagination +export async function getAllSpuOfCate(cateId) { + return ( + await model()[CATE_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: cateId, + }, + }, + relateWhere: { + spu: { + where: { + status: { + $eq: SPU_SELLING_STATUS, + }, + }, + }, + }, + }, + select: { + spu: { + $master: true, + }, + }, + }) + ).data; +} + +export async function getCates() { + const cateSelect = { + _id: true, + name: true, + image: true, + }; + + const allCates = ( + await getAll({ + name: CATE_MODEL_KEY, + select: { + ...cateSelect, + child_cate: cateSelect, + }, + }) + ).filter((c) => c.child_cate.length !== 0); + + const childCates = allCates.flatMap((c) => c.child_cate); + const res = await getCloudImageTempUrl(childCates.map((x) => x.image)); + res.forEach((image, index) => (childCates[index].image = image)); + + return allCates; +} diff --git a/miniprogram/tcb-shop/services/comments/comments.js b/miniprogram/tcb-shop/services/comments/comments.js new file mode 100644 index 0000000..a4647fd --- /dev/null +++ b/miniprogram/tcb-shop/services/comments/comments.js @@ -0,0 +1,109 @@ +import { model } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const COMMENT_MODEL_KEY = DATA_MODEL_KEY.COMMENT; + +export async function getGoodsDetailCommentInfo(spuId) { + const firstAndCount = () => + model()[COMMENT_MODEL_KEY].list({ + filter: { + relateWhere: { + spu: { + where: { + _id: { + $eq: spuId, + }, + }, + }, + }, + }, + select: { + $master: true, + spu: { + _id: true, + }, + }, + orderBy: [{ rating: 'desc' }], + pageNumber: 1, + pageSize: 1, + getCount: true, + }); + const goodCount = () => + model()[COMMENT_MODEL_KEY].list({ + filter: { + relateWhere: { + spu: { + where: { + _id: { + $eq: spuId, + }, + }, + }, + }, + where: { + rating: { + $gt: 3, + }, + }, + }, + select: { + $master: true, + }, + pageNumber: 1, + pageSize: 1, + getCount: true, + }); + + return Promise.all([firstAndCount(), goodCount()]); +} + +export async function getCommentsOfSpu({ spuId, pageNumber, pageSize }) { + return ( + await model()[COMMENT_MODEL_KEY].list({ + select: { + $master: true, + order_item: { + _id: true, + }, + }, + filter: { + relateWhere: { + spu: { + where: { + _id: { + $eq: spuId, + }, + }, + }, + }, + }, + pageSize, + pageNumber, + getCount: true, + }) + ).data; +} + +/** + * + * @param {{ + * orderItemId: string, + * content: string, + * rating: number, + * spuId: string + * }} param0 + */ +export function createComment({ orderItemId, content, rating, spuId }) { + return model()[COMMENT_MODEL_KEY].create({ + data: { + content, + rating, + order_item: { + _id: orderItemId, + }, + spu: { + _id: spuId, + }, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/comments/fetchComments.js b/miniprogram/tcb-shop/services/comments/fetchComments.js new file mode 100644 index 0000000..9bd1155 --- /dev/null +++ b/miniprogram/tcb-shop/services/comments/fetchComments.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品评论 */ +function mockFetchComments(parmas) { + const { delay } = require('../_utils/delay'); + const { getGoodsAllComments } = require('../../model/comments'); + return delay().then(() => getGoodsAllComments(parmas)); +} + +/** 获取商品评论 */ +export function fetchComments(parmas) { + if (config.useMock) { + return mockFetchComments(parmas); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/comments/fetchCommentsCount.js b/miniprogram/tcb-shop/services/comments/fetchCommentsCount.js new file mode 100644 index 0000000..cbb09d0 --- /dev/null +++ b/miniprogram/tcb-shop/services/comments/fetchCommentsCount.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品评论数 */ +function mockFetchCommentsCount(ID = 0) { + const { delay } = require('../_utils/delay'); + const { getGoodsCommentsCount } = require('../../model/comments'); + return delay().then(() => getGoodsCommentsCount(ID)); +} + +/** 获取商品评论数 */ +export function fetchCommentsCount(ID = 0) { + if (config.useMock) { + return mockFetchCommentsCount(ID); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/coupon/index.js b/miniprogram/tcb-shop/services/coupon/index.js new file mode 100644 index 0000000..a0010e0 --- /dev/null +++ b/miniprogram/tcb-shop/services/coupon/index.js @@ -0,0 +1,65 @@ +import { config } from '../../config/index'; + +/** 获取优惠券列表 */ +function mockFetchCoupon(status) { + const { delay } = require('../_utils/delay'); + const { getCouponList } = require('../../model/coupon'); + return delay().then(() => getCouponList(status)); +} + +/** 获取优惠券列表 */ +export function fetchCouponList(status = 'default') { + if (config.useMock) { + return mockFetchCoupon(status); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取优惠券 详情 */ +function mockFetchCouponDetail(id, status) { + const { delay } = require('../_utils/delay'); + const { getCoupon } = require('../../model/coupon'); + const { genAddressList } = require('../../model/address'); + + return delay().then(() => { + const result = { + detail: getCoupon(id, status), + storeInfoList: genAddressList(), + }; + + result.detail.useNotes = `1个订单限用1张,除运费券外,不能与其它类型的优惠券叠加使用(运费券除外)\n2.仅适用于各区域正常售卖商品,不支持团购、抢购、预售类商品`; + result.detail.storeAdapt = `商城通用`; + + if (result.detail.type === 'price') { + result.detail.desc = `减免 ${result.detail.value / 100} 元`; + + if (result.detail.base) { + result.detail.desc += `,满${result.detail.base / 100}元可用`; + } + + result.detail.desc += '。'; + } else if (result.detail.type === 'discount') { + result.detail.desc = `${result.detail.value}折`; + + if (result.detail.base) { + result.detail.desc += `,满${result.detail.base / 100}元可用`; + } + + result.detail.desc += '。'; + } + + return result; + }); +} + +/** 获取优惠券 详情 */ +export function fetchCouponDetail(id, status = 'default') { + if (config.useMock) { + return mockFetchCouponDetail(id, status); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js b/miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js new file mode 100644 index 0000000..fce338d --- /dev/null +++ b/miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js @@ -0,0 +1,20 @@ +import { config } from '../../../config/index'; +import { queryCommentDetail } from '../../../model/comments/queryDetail'; +/** 获取商品评价数据 */ +function mockQueryCommentDetail(params) { + const { delay } = require('../../_utils/delay'); + const data = queryCommentDetail(params); + return delay().then(() => { + return data; + }); +} + +/** 获取评价详情 */ +export function getCommentDetail(params) { + if (config.useMock) { + return mockQueryCommentDetail(params); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchCategoryList.js b/miniprogram/tcb-shop/services/good/fetchCategoryList.js new file mode 100644 index 0000000..adf6ba4 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchCategoryList.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchGoodCategory() { + const { delay } = require('../_utils/delay'); + const { getCategoryList } = require('../../model/category'); + return delay().then(() => getCategoryList()); +} + +/** 获取商品列表 */ +export function getCategoryList() { + if (config.useMock) { + return mockFetchGoodCategory(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchGood.js b/miniprogram/tcb-shop/services/good/fetchGood.js new file mode 100644 index 0000000..577801d --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchGood.js @@ -0,0 +1,24 @@ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchGood(ID = 0) { + + const { delay } = require('../_utils/delay'); + const { genGood } = require('../../model/good'); + + return delay().then(() => { + const res = genGood(ID); + console.log("mock fetch goood return res", res); + return res; + }); +} + +/** 获取商品列表 */ +export function fetchGood(ID = 0) { + if (config.useMock) { + return mockFetchGood(ID); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js b/miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js new file mode 100644 index 0000000..95a11c4 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js @@ -0,0 +1,37 @@ +import { config } from '../../config/index'; + +/** 获取商品详情页评论数 */ +function mockFetchGoodDetailsCommentsCount(spuId = 0) { + const { delay } = require('../_utils/delay'); + const { + getGoodsDetailsCommentsCount, + } = require('../../model/detailsComments'); + return delay().then(() => getGoodsDetailsCommentsCount(spuId)); +} + +/** 获取商品详情页评论数 */ +export function getGoodsDetailsCommentsCount(spuId = 0) { + if (config.useMock) { + return mockFetchGoodDetailsCommentsCount(spuId); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取商品详情页评论 */ +function mockFetchGoodDetailsCommentList(spuId = 0) { + const { delay } = require('../_utils/delay'); + const { getGoodsDetailsComments } = require('../../model/detailsComments'); + return delay().then(() => getGoodsDetailsComments(spuId)); +} + +/** 获取商品详情页评论 */ +export function getGoodsDetailsCommentList(spuId = 0) { + if (config.useMock) { + return mockFetchGoodDetailsCommentList(spuId); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchGoodsList.js b/miniprogram/tcb-shop/services/good/fetchGoodsList.js new file mode 100644 index 0000000..1650b9b --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchGoodsList.js @@ -0,0 +1,39 @@ +/* eslint-disable no-param-reassign */ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchGoodsList(params) { + const { delay } = require('../_utils/delay'); + const { getSearchResult } = require('../../model/search'); + + const data = getSearchResult(params); + + if (data.spuList.length) { + data.spuList.forEach((item) => { + item.spuId = item.spuId; + item.thumb = item.primaryImage; + item.title = item.title; + item.price = item.minSalePrice; + item.originPrice = item.maxLinePrice; + item.desc = ''; + if (item.spuTagList) { + item.tags = item.spuTagList.map((tag) => tag.title); + } else { + item.tags = []; + } + }); + } + return delay().then(() => { + return data; + }); +} + +/** 获取商品列表 */ +export function fetchGoodsList(params) { + if (config.useMock) { + return mockFetchGoodsList(params); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchSearchHistory.js b/miniprogram/tcb-shop/services/good/fetchSearchHistory.js new file mode 100644 index 0000000..3a73963 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchSearchHistory.js @@ -0,0 +1,35 @@ +import { config } from '../../config/index'; + +/** 获取搜索历史 */ +function mockSearchHistory() { + const { delay } = require('../_utils/delay'); + const { getSearchHistory } = require('../../model/search'); + return delay().then(() => getSearchHistory()); +} + +/** 获取搜索历史 */ +export function getSearchHistory() { + if (config.useMock) { + return mockSearchHistory(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取搜索历史 */ +function mockSearchPopular() { + const { delay } = require('../_utils/delay'); + const { getSearchPopular } = require('../../model/search'); + return delay().then(() => getSearchPopular()); +} + +/** 获取搜索历史 */ +export function getSearchPopular() { + if (config.useMock) { + return mockSearchPopular(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchSearchResult.js b/miniprogram/tcb-shop/services/good/fetchSearchResult.js new file mode 100644 index 0000000..5b42851 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchSearchResult.js @@ -0,0 +1,38 @@ +/* eslint-disable no-param-reassign */ +import { config } from '../../config/index'; + +/** 获取搜索历史 */ +function mockSearchResult(params) { + const { delay } = require('../_utils/delay'); + const { getSearchResult } = require('../../model/search'); + + const data = getSearchResult(params); + + if (data.spuList.length) { + data.spuList.forEach((item) => { + item.spuId = item.spuId; + item.thumb = item.primaryImage; + item.title = item.title; + item.price = item.minSalePrice; + item.originPrice = item.maxLinePrice; + if (item.spuTagList) { + item.tags = item.spuTagList.map((tag) => ({ title: tag.title })); + } else { + item.tags = []; + } + }); + } + return delay().then(() => { + return data; + }); +} + +/** 获取搜索历史 */ +export function getSearchResult(params) { + if (config.useMock) { + return mockSearchResult(params); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/spu.js b/miniprogram/tcb-shop/services/good/spu.js new file mode 100644 index 0000000..05ae792 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/spu.js @@ -0,0 +1,77 @@ +import { model } from '../_utils/model'; +import { getCloudImageTempUrl } from '../../utils/cloudImageHandler'; +import { SPU_SELLING_STATUS } from '../../utils/spuStatus'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const SPU_MODEL_KEY = DATA_MODEL_KEY.SPU; +const SKU_MODEL_KEY = DATA_MODEL_KEY.SKU; + +/** + * + * @param {{ + * pageSize: Number, + * pageNumber: Number, + * cateId: String, + * search: String + * }}} param0 + * @returns + */ +export async function listGood({ pageSize, pageNumber, search }) { + const filter = { + where: { + status: { $eq: SPU_SELLING_STATUS }, + }, + }; + if (search) { + filter.where.name = { $search: search }; + } + + return ( + await model()[SPU_MODEL_KEY].list({ + filter, + pageSize, + pageNumber, + getCount: true, + orderBy: [{ priority: 'desc' }], + }) + ).data; +} + +export async function getPrice(spuId) { + const { + data: { records }, + } = await model()[SKU_MODEL_KEY].list({ + filter: { + where: { + spu: { + $eq: spuId, + }, + }, + }, + }); + return records[0].price; +} + +export async function handleSpuCloudImage(spu) { + if (spu == null) { + return; + } + const handledImages = await getCloudImageTempUrl([spu.cover_image, ...spu.swiper_images]); + handledImages.forEach((image, index) => { + if (index === 0) { + spu.cover_image = image; + return; + } + spu.swiper_images[index - 1] = image; + }); +} + +export async function getSpu(spuId) { + return ( + await model()[SPU_MODEL_KEY].get({ + filter: { + where: { _id: { $eq: spuId } }, + }, + }) + ).data; +} diff --git a/miniprogram/tcb-shop/services/home/home.js b/miniprogram/tcb-shop/services/home/home.js new file mode 100644 index 0000000..ac3483d --- /dev/null +++ b/miniprogram/tcb-shop/services/home/home.js @@ -0,0 +1,8 @@ +import { model } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const HOME_SWIPER_MODEL_KEY = DATA_MODEL_KEY.HOME_SWIPER; + +export async function getHomeSwiper() { + return (await model()[HOME_SWIPER_MODEL_KEY].list({ select: { images: true } })).data.records[0]; +} diff --git a/miniprogram/tcb-shop/services/order/applyService.js b/miniprogram/tcb-shop/services/order/applyService.js new file mode 100644 index 0000000..c66930a --- /dev/null +++ b/miniprogram/tcb-shop/services/order/applyService.js @@ -0,0 +1,70 @@ +import { config } from '../../config/index'; + +/** 获取售后单mock数据 */ +function mockFetchRightsPreview(params) { + const { delay } = require('../_utils/delay'); + const { genRightsPreview } = require('../../model/order/applyService'); + + return delay().then(() => genRightsPreview(params)); +} + +/** 获取售后单数据 */ +export function fetchRightsPreview(params) { + if (config.useMock) { + return mockFetchRightsPreview(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 确认收货 */ +export function dispatchConfirmReceived() { + if (config.useMock) { + const { delay } = require('../_utils/delay'); + return delay(); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取可选的mock售后原因列表 */ +function mockFetchApplyReasonList(params) { + const { delay } = require('../_utils/delay'); + const { genApplyReasonList } = require('../../model/order/applyService'); + + return delay().then(() => genApplyReasonList(params)); +} + +/** 获取可选的售后原因列表 */ +export function fetchApplyReasonList(params) { + if (config.useMock) { + return mockFetchApplyReasonList(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 发起mock售后申请 */ +function mockDispatchApplyService(params) { + const { delay } = require('../_utils/delay'); + const { applyService } = require('../../model/order/applyService'); + + return delay().then(() => applyService(params)); +} + +/** 发起售后申请 */ +export function dispatchApplyService(params) { + if (config.useMock) { + return mockDispatchApplyService(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/order.js b/miniprogram/tcb-shop/services/order/order.js new file mode 100644 index 0000000..265d882 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/order.js @@ -0,0 +1,171 @@ +import { model, getAll } from '../../services/_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const ORDER_MODEL_KEY = DATA_MODEL_KEY.ORDER; + +const ORDER_STATUS_INFO = { + TO_PAY: { value: 'TO_PAY', label: '待付款' }, + TO_SEND: { value: 'TO_SEND', label: '待发货' }, + TO_RECEIVE: { value: 'TO_RECEIVE', label: '待收货' }, + FINISHED: { value: 'FINISHED', label: '已完成' }, + CANCELED: { value: 'CANCELED', label: '已取消' }, + RETURN_APPLIED: { value: 'RETURN_APPLIED', label: '申请退货' }, + RETURN_REFUSED: { value: 'RETURN_REFUSED', label: '拒绝退货申请' }, + RETURN_FINISH: { value: 'RETURN_FINISH', label: '退货完成' }, + RETURN_MONEY_REFUSED: { value: 'RETURN_MONEY_REFUSED', label: '拒绝退款' }, +}; + +export const ORDER_STATUS = new Proxy(ORDER_STATUS_INFO, { + get(target, prop) { + return target[prop]?.value; + }, +}); + +export const orderStatusToName = (status) => Object.values(ORDER_STATUS_INFO).find((x) => x.value === status)?.label; + +/** + * + * @param {{ + * status: String, + * addressId: String + * }} param0 + * @returns + */ +export async function createOrder({ status, addressId }) { + return ( + await model()[ORDER_MODEL_KEY].create({ + data: { + status, + delivery_info: { + _id: addressId, + }, + }, + }) + ).data; +} + +export function getAllOrder() { + return getAll({ + name: ORDER_MODEL_KEY, + }); +} + +/** + * + * @param {{ + * pageSize: Number, + * pageNumber: Number, + * status?: String + * }}} param0 + * @returns + */ +export async function listOrder({ pageSize, pageNumber, status }) { + if (status != null) { + return ( + await model()[ORDER_MODEL_KEY].list({ + filter: { + where: { + status: { + $eq: status, + }, + }, + }, + pageSize, + pageNumber, + getCount: true, + }) + ).data; + } + return ( + await model()[ORDER_MODEL_KEY].list({ + filter: {}, + pageSize, + pageNumber, + getCount: true, + }) + ).data; +} + +async function getOrderCountOfStatus(status) { + return ( + await model()[ORDER_MODEL_KEY].list({ + filter: { where: { status: { $eq: status } } }, + select: { _id: true }, + getCount: true, + }) + ).data.total; +} + +export async function getToPayOrderCount() { + return getOrderCountOfStatus(ORDER_STATUS.TO_PAY); +} + +export async function getToSendOrderCount() { + return getOrderCountOfStatus(ORDER_STATUS.TO_SEND); +} + +export async function getToReceiveOrderCount() { + return getOrderCountOfStatus(ORDER_STATUS.TO_RECEIVE); +} + +/** + * + * @param {String} orderId + */ +export async function getOrder(orderId) { + return ( + await model()[ORDER_MODEL_KEY].get({ + filter: { + where: { + _id: { $eq: orderId }, + }, + }, + select: { + $master: true, + delivery_info: { + _id: true, + phone: true, + address: true, + name: true, + }, + }, + }) + ).data; +} + +export async function updateOrderDeliveryInfo({ orderId, deliveryInfoId }) { + return model()[ORDER_MODEL_KEY].update({ + data: { + delivery_info: { + _id: deliveryInfoId, + }, + }, + filter: { + where: { + _id: { + $eq: orderId, + }, + }, + }, + }); +} + +/** + * + * @param {{orderId: String, status: String}}} param0 + * @returns + */ +export async function updateOrderStatus({ orderId, status }) { + return await model()[ORDER_MODEL_KEY].update({ + data: { + status, + }, + filter: { + where: { + _id: { + $eq: orderId, + }, + }, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderConfirm.js b/miniprogram/tcb-shop/services/order/orderConfirm.js new file mode 100644 index 0000000..4d34744 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderConfirm.js @@ -0,0 +1,69 @@ +import { config } from '../../config/index'; +import { mockIp, mockReqId } from '../../utils/mock'; + +/** 获取结算mock数据 */ +function mockFetchSettleDetail(params) { + const { delay } = require('../_utils/delay'); + const { genSettleDetail } = require('../../model/order/orderConfirm'); + + return delay().then(() => genSettleDetail(params)); +} + +/** 提交mock订单 */ +function mockDispatchCommitPay() { + const { delay } = require('../_utils/delay'); + + return delay().then(() => ({ + data: { + isSuccess: true, + tradeNo: '350930961469409099', + payInfo: '{}', + code: null, + transactionId: 'E-200915180100299000', + msg: null, + interactId: '15145', + channel: 'wechat', + limitGoodsList: null, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 891, + success: true, + })); +} + +/** 获取结算数据 */ +export function fetchSettleDetail(params) { + if (config.useMock) { + return mockFetchSettleDetail(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/* 提交订单 */ +export function dispatchCommitPay(params) { + if (config.useMock) { + return mockDispatchCommitPay(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 开发票 */ +export function dispatchSupplementInvoice() { + if (config.useMock) { + const { delay } = require('../_utils/delay'); + return delay(); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderDetail.js b/miniprogram/tcb-shop/services/order/orderDetail.js new file mode 100644 index 0000000..3e7d797 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderDetail.js @@ -0,0 +1,39 @@ +import { config } from '../../config/index'; + +/** 获取订单详情mock数据 */ +function mockFetchOrderDetail(params) { + const { delay } = require('../_utils/delay'); + const { genOrderDetail } = require('../../model/order/orderDetail'); + + return delay().then(() => genOrderDetail(params)); +} + +/** 获取订单详情数据 */ +export function fetchOrderDetail(params) { + if (config.useMock) { + return mockFetchOrderDetail(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取客服mock数据 */ +function mockFetchBusinessTime(params) { + const { delay } = require('../_utils/delay'); + const { genBusinessTime } = require('../../model/order/orderDetail'); + + return delay().then(() => genBusinessTime(params)); +} + +/** 获取客服数据 */ +export function fetchBusinessTime(params) { + if (config.useMock) { + return mockFetchBusinessTime(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderItem.js b/miniprogram/tcb-shop/services/order/orderItem.js new file mode 100644 index 0000000..6587585 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderItem.js @@ -0,0 +1,77 @@ +import { model, getAll } from '../../services/_utils/model'; +import { getSkuDetail } from '../sku/sku'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const ORDER_ITEM_MODEL_KEY = DATA_MODEL_KEY.ORDER_ITEM; + +export async function getOrderItem(id) { + return ( + await model()[ORDER_ITEM_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + select: { + sku: { + _id: true, + }, + comment: { + _id: true, + }, + }, + }) + ).data; +} + +export async function createOrderItem({ count, skuId, orderId }) { + return model()[ORDER_ITEM_MODEL_KEY].create({ + data: { + count, + sku: { + _id: skuId, + }, + order: { + _id: orderId, + }, + }, + }); +} + +export function getAllOrderItems() { + return getAll({ name: ORDER_ITEM_MODEL_KEY }); +} + +/** + * + * @param {{orderId: String}} param0 + */ +export async function getAllOrderItemsOfAnOrder({ orderId }) { + const orderItems = await getAll({ + name: ORDER_ITEM_MODEL_KEY, + filter: { + where: { + order: { + $eq: orderId, + }, + }, + }, + select: { + _id: true, + count: true, + sku: { + _id: true, + }, + }, + }); + await Promise.all( + orderItems.map(async (orderItem) => { + const skuId = orderItem.sku._id; + const sku = await getSkuDetail(skuId); + orderItem.sku = sku; + }), + ); + return orderItems; +} diff --git a/miniprogram/tcb-shop/services/order/orderList.js b/miniprogram/tcb-shop/services/order/orderList.js new file mode 100644 index 0000000..f4a9e7a --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderList.js @@ -0,0 +1,39 @@ +import { config } from '../../config/index'; + +/** 获取订单列表mock数据 */ +function mockFetchOrders(params) { + const { delay } = require('../_utils/delay'); + const { genOrders } = require('../../model/order/orderList'); + + return delay(200).then(() => genOrders(params)); +} + +/** 获取订单列表数据 */ +export function fetchOrders(params) { + if (config.useMock) { + return mockFetchOrders(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取订单列表mock数据 */ +function mockFetchOrdersCount(params) { + const { delay } = require('../_utils/delay'); + const { genOrdersCount } = require('../../model/order/orderList'); + + return delay().then(() => genOrdersCount(params)); +} + +/** 获取订单列表统计 */ +export function fetchOrdersCount(params) { + if (config.useMock) { + return mockFetchOrdersCount(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderSubmitComment.js b/miniprogram/tcb-shop/services/order/orderSubmitComment.js new file mode 100644 index 0000000..5e3ea36 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderSubmitComment.js @@ -0,0 +1,22 @@ +import { config } from '../../config/index'; + +/** 获取评价商品 */ +function mockGetGoods(parameter) { + const { delay } = require('../_utils/delay'); + const { getGoods } = require('../../model/submitComment'); + const data = getGoods(parameter); + + return delay().then(() => { + return data; + }); +} + +/** 获取评价商品 */ +export function getGoods(parameter) { + if (config.useMock) { + return mockGetGoods(parameter); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/pay/pay.js b/miniprogram/tcb-shop/services/pay/pay.js new file mode 100644 index 0000000..bcc9552 --- /dev/null +++ b/miniprogram/tcb-shop/services/pay/pay.js @@ -0,0 +1,44 @@ +/** + * + * @param {{_id: String, totalPrice: Number}} order + * @returns + */ +export async function pay(order) { + try { + const res = await wx.cloud.callFunction({ + // 云函数名称 + name: 'wxpayFunctions', + data: { + type: 'wxpay_order', + order, + }, + }); + const paymentData = res.result?.data; + // 唤起微信支付组件,完成支付 + try { + await wx.requestPayment({ + timeStamp: paymentData?.timeStamp, + nonceStr: paymentData?.nonceStr, + package: paymentData?.packageVal, + paySign: paymentData?.paySign, + signType: 'RSA', // 该参数为固定值 + }); + } catch (e) { + return Promise.reject(e); + } + } catch (e) { + return Promise.reject(e); + } +} + +export async function refund(orderId) { + return wx.cloud.callFunction({ + // 云函数名称 + name: 'wxpayFunctions', + data: { + // 调用云函数中的申请退款方法 + type: 'wxpay_refund', + orderId, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/promotion/detail.js b/miniprogram/tcb-shop/services/promotion/detail.js new file mode 100644 index 0000000..841f723 --- /dev/null +++ b/miniprogram/tcb-shop/services/promotion/detail.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchPromotion(ID = 0) { + const { delay } = require('../_utils/delay'); + const { getPromotion } = require('../../model/promotion'); + return delay().then(() => getPromotion(ID)); +} + +/** 获取商品列表 */ +export function fetchPromotion(ID = 0) { + if (config.useMock) { + return mockFetchPromotion(ID); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/sku/sku.js b/miniprogram/tcb-shop/services/sku/sku.js new file mode 100644 index 0000000..60d550d --- /dev/null +++ b/miniprogram/tcb-shop/services/sku/sku.js @@ -0,0 +1,57 @@ +import { model, getAll } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const SKU_MODEL_KEY = DATA_MODEL_KEY.SKU; + +/** + * + * @param {String} skuId + */ +export async function getSkuDetail(skuId) { + const { data } = await model()[SKU_MODEL_KEY].get({ + filter: { + where: { + _id: { $eq: skuId }, + }, + }, + select: { + _id: true, + count: true, + price: true, + image: true, + attr_value: { + value: true, + _id: true, + }, + spu: { + name: true, + }, + }, + }); + return data; +} + +export async function updateSku({ skuId, data }) { + return wx.cloud.callFunction({ + // 云函数名称 + name: 'shop_update_sku', + // 传给云函数的参数 + data: { + skuId, + data, + }, + }); +} + +export async function getAllSku(spuId) { + return getAll({ + name: SKU_MODEL_KEY, + filter: { + where: { + spu: { + $eq: spuId, + }, + }, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/usercenter/fetchPerson.js b/miniprogram/tcb-shop/services/usercenter/fetchPerson.js new file mode 100644 index 0000000..bd31178 --- /dev/null +++ b/miniprogram/tcb-shop/services/usercenter/fetchPerson.js @@ -0,0 +1,28 @@ +import { config } from '../../config/index'; + +/** 获取个人中心信息 */ +function mockFetchPerson() { + const { delay } = require('../_utils/delay'); + const { genSimpleUserInfo } = require('../../model/usercenter'); + const { genAddress } = require('../../model/address'); + const address = genAddress(); + return delay().then(() => ({ + ...genSimpleUserInfo(), + address: { + provinceName: address.provinceName, + provinceCode: address.provinceCode, + cityName: address.cityName, + cityCode: address.cityCode, + }, + })); +} + +/** 获取个人中心信息 */ +export function fetchPerson() { + if (config.useMock) { + return mockFetchPerson(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js b/miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js new file mode 100644 index 0000000..b187644 --- /dev/null +++ b/miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取个人中心信息 */ +function mockFetchUserCenter() { + const { delay } = require('../_utils/delay'); + const { genUsercenter } = require('../../model/usercenter'); + return delay(200).then(() => genUsercenter()); +} + +/** 获取个人中心信息 */ +export function fetchUserCenter() { + if (config.useMock) { + return mockFetchUserCenter(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/sitemap.json b/miniprogram/tcb-shop/sitemap.json new file mode 100644 index 0000000..ca02add --- /dev/null +++ b/miniprogram/tcb-shop/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/style/cart-group.wxss b/miniprogram/tcb-shop/style/cart-group.wxss new file mode 100644 index 0000000..ad585a5 --- /dev/null +++ b/miniprogram/tcb-shop/style/cart-group.wxss @@ -0,0 +1,83 @@ +/* var() css变量适配*/ +.wr-swiper-cell__right { + margin: 20rpx 0; +} +.wr-swiper-cell__right .swiper-right-del { + height: calc(100% - 40rpx); + width: 60px; + background-color: #ff2525; + font-size: 28rpx; + color: white; + display: flex; + justify-content: center; + align-items: center; +} +.goods-stepper .stepper { + border: none; + border-radius: 0; + height: auto; + width: 168rpx; + overflow: visible; +} +.goods-stepper .stepper .stepper__minus, +.goods-stepper .stepper .stepper__plus { + width: 44rpx; + height: 44rpx; + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus--hover, +.goods-stepper .stepper .stepper__plus--hover { + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus .wr-icon, +.goods-stepper .stepper .stepper__plus .wr-icon { + font-size: 24rpx; +} +.goods-stepper .stepper .stepper__minus { + position: relative; +} +.goods-stepper .stepper .stepper__minus::after { + position: absolute; + display: block; + content: ' '; + left: -20rpx; + right: -5rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__plus { + position: relative; +} +.goods-stepper .stepper .stepper__plus::after { + position: absolute; + display: block; + content: ' '; + left: -5rpx; + right: -20rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__input { + width: 72rpx; + height: 44rpx; + background-color: #f5f5f5; + font-size: 24rpx; + color: #222427; + font-weight: 600; + border-left: none; + border-right: none; + min-height: 40rpx; + margin: 0 4rpx; + display: flex; + align-items: center; +} +.invalid-card .invalid-private-mask .wr-goods-card { + background-color: #fff; + padding: 18rpx 32rpx 24rpx 80rpx; +} +.text-primary .wr-icon { + color: #fa550f; + color: var(--color-primary, #fa550f); +} diff --git a/miniprogram/tcb-shop/style/global.wxss b/miniprogram/tcb-shop/style/global.wxss new file mode 100644 index 0000000..0194db1 --- /dev/null +++ b/miniprogram/tcb-shop/style/global.wxss @@ -0,0 +1,960 @@ +/* + * @Author: oliverppeng + * @LastEditors: Please set LastEditors + * @Date: 2021-12-01 17:33:43 + * @LastEditTime: 2021-12-03 15:31:17 + * @Description: + * @FilePath: /retail-mp/style/global.wxss + */ +.text-primary { + color: #fa550f; +} +.text-success { + color: #5fb446; +} +.text-warn { + color: #ec8131; +} +.text-danger { + color: #de1c24; +} +.text-title { + color: #282828; +} +.text-normal { + color: #5d5d5d; +} +.text-small { + color: #9b9b9b; +} +.text-minor { + color: #ececec; +} +.text-border { + color: #eeeeee; +} +.text-white { + color: #fff; +} +.bg-primary { + background-color: #fa550f; + color: #fff; +} +.bg-success { + background-color: #5fb446; + color: #fff; +} +.bg-warn { + background-color: #ec8131; + color: #fff; +} +.bg-danger { + background-color: #de1c24; + color: #fff; +} +.bg-title { + background-color: #282828; + color: #fff; +} +.bg-normal { + background-color: #5d5d5d; + color: #282828; +} +.bg-small { + background-color: #9b9b9b; + color: #5d5d5d; +} +.bg-minor { + background-color: #ececec; + color: #5d5d5d; +} +.bg-border { + background-color: #eeeeee; + color: #5d5d5d; +} +.bd-primary { + color: #fa550f; +} +.bd-success { + color: #5fb446; +} +.bd-warn { + color: #ec8131; +} +.bd-danger { + color: #de1c24; +} +.bd-title { + color: #282828; +} +.bd-normal { + color: #5d5d5d; +} +.bd-small { + color: #9b9b9b; +} +.bd-minor { + color: #ececec; +} +.bd-border { + color: #eeeeee; +} +.ft-super { + font-size: 40rpx; +} +.ft-main { + font-size: 36rpx; +} +.ft-normal { + font-size: 32rpx; +} +.ft-assist { + font-size: 28rpx; +} +.ft-minor { + font-size: 24rpx; +} +.ft-mini { + font-size: 20rpx; +} +.fw-super { + font-weight: 800; +} +.fw-main { + font-weight: 600; +} +.fw-normal { + font-weight: 400; +} +.fw-minor { + font-weight: 300; +} +.mo-border-1rpx { + position: relative; + z-index: 0; +} +.mo-border-1rpx::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 200%; + height: 200%; + border-width: 2rpx; + border-style: solid; + transform: scale(0.5); + transform-origin: 0 0; + z-index: -1; + box-sizing: border-box; +} +/* 层级定义 + @z-index-0: 1; + @z-index-1: 100; + @z-index-2: 200; + @z-index-5: 500; + @z-index-component: 1000; // 通用组件级别 + @z-index-dropdown: @z-index-component; + @z-index-sticky: @z-index-component + 20; + @z-index-fixed: @z-index-component + 30; + @z-index-modal-backdrop:@z-index-component + 40; + @z-index-modal:@z-index-component + 50; + @z-index-popover:@z-index-component + 60; + @z-index-tooltip:@z-index-component + 70; + */ +page { + height: 100%; + font-family: 'Microsoft YaHei', '微软雅黑', 'MicrosoftJhengHei', '华文细黑', Helvetica, Arial, 'sans-serif'; + font-size: 26rpx; + background-color: #f8f8f8; + font-weight: 400; +} +view, +image, +icon, +scroll-view, +text, +button, +checkbox, +form, +input, +label, +navigator, +audio, +video, +canvas { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0; + outline: 0; +} +scroll-view { + height: 100%; +} +form, +image { + display: block; +} +button { + padding: 0; + margin: 0; + border-radius: 0; + height: 100%; + display: block; + line-height: inherit; + font-size: inherit; + color: inherit; + background: none; + -webkit-appearance: none; + border: none; +} +button::after { + content: none; +} +input, +textarea { + font-family: 'Microsoft YaHei', '微软雅黑', 'MicrosoftJhengHei', '华文细黑', Helvetica, Arial, 'sans-serif'; + font-size: 26rpx; + z-index: 0; +} +.price { + color: #ec8131; + font-size: 32rpx; + font-weight: 600; +} +.price-del { + color: #9b9b9b; + font-size: 24rpx; + font-weight: 400; +} +.page { + background: #fff; +} +.color-price { + color: #ec8131; +} +.bg-cart { + background-color: #ffc220; + color: #fff; +} +.market-addcart { + color: #ec8131; + font-size: 42rpx; +} +.ovh { + overflow: hidden; +} +.hidden { + display: none; +} +.show { + display: block; +} +.text { + display: inline-block; +} +.inline { + display: inline; +} +.minHeight { + min-height: 101%; +} +.imgCover { + width: 100%; + padding-bottom: 100%; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + display: block; + position: relative; +} +.imgCover-list { + width: 195rpx; + height: 260rpx; + padding-bottom: 0; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + display: block; + position: relative; +} +.circular { + border-radius: 50%; +} +.text-line1 { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + word-break: break-all; +} +.text-line-1 { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + word-break: break-all; +} +.text-line2 { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + word-break: break-all; +} +.text-line3 { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + word-break: break-all; +} +.flex, +.box { + display: flex; + display: -webkit-flex; +} +.flex-v-center { + align-items: center; + -webkit-align-items: center; +} +.flex-center { + justify-content: center; + -webkit-justify-content: center; + align-items: center; + -webkit-align-items: center; +} +.flex-between { + justify-content: space-between; + -webkit-justify-content: space-between; +} +.flex-v-between { + align-content: space-between; + -webkit-align-content: space-between; +} +.flex-end { + justify-content: flex-end; + -webkit-justify-content: flex-end; +} +.flex-col { + flex-direction: column; + -webkit-flex-direction: column; +} +.flex1 { + flex: 1; + -webkit-flex: 1; +} +.flex0 { + flex: none; + -webkit-flex: none; +} +.flex-start { + justify-content: flex-start; + -webkit-justify-content: flex-start; +} +.border-around, +.border-bottom-1px, +.border-left-1px, +.border-right-1px, +.border-top-1px { + position: relative; + border: 1rpx solid #e9e9e9; +} +.border-top-1px { + border-width: 1rpx 0 0 0; +} +.border-right-1px { + border-width: 0 1rpx 0 0; +} +.border-bottom-1px { + border-width: 0 0 1rpx 0; +} +.border-left-1px { + border-width: 0 0 0 1rpx; +} +.border-t-1px, +.border-r-1px, +.border-b-1px, +.border-l-1px { + position: relative; +} +.border-t-1px::after, +.border-r-1px::after, +.border-b-1px::after, +.border-l-1px::after { + content: ''; + position: absolute; + border-width: 2rpx; + border-color: #efefef; + border-style: solid; +} +.border-t-1px::after { + left: 0; + top: 0; + width: 100%; + transform: scaleY(0.5); +} +.border-b-1px::after { + left: 0; + bottom: 0; + width: 100%; + transform: scaleY(0.5); +} +.border-l-1px::after { + left: 0; + top: 0; + height: 100%; + transform: scaleX(0.5); +} +.border-r-1px::after { + right: 0; + top: 0; + height: 100%; + transform: scaleX(0.5); +} +.arrows { + position: relative; +} +.arrows::after { + content: ''; + display: inline-block; + width: 6px; + height: 6px; + border: 2px solid #c3c3c3; + border-width: 2px 2px 0 0; + position: absolute; + top: 50%; + right: 26rpx; + margin-top: -3px; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); +} +.arrows-inline::after { + position: relative; + left: auto; + top: auto; + right: auto; + bottom: auto; + margin-top: -4px; + margin-left: 5px; +} +.pt-16, +.pt-8 { + padding-top: 16rpx; +} +.pb-16, +.pb-8 { + padding-bottom: 16rpx; +} +.pl-16, +.pl-8 { + padding-left: 16rpx; +} +.pr-16, +.pr-8 { + padding-right: 16rpx; +} +.pl-20, +.pl-10 { + padding-left: 20rpx; +} +.pr-20, +.pr-10 { + padding-right: 20rpx; +} +.pl-30 { + padding-left: 30rpx; +} +.pr-30 { + padding-right: 30rpx; +} +.pl-32, +.pl-15 { + padding-left: 32rpx; +} +.pr-32, +.pr-15 { + padding-right: 32rpx; +} +.pb360 { + padding-bottom: 360rpx; +} +.PriceSwitch { + line-height: 21px; + font-size: 24rpx; + padding: 0 8rpx; +} +.PriceSwitch .i { + font-size: 30rpx; +} +.Original { + font-style: normal; + font-size: 24rpx; + color: #9a9a9a; + text-decoration: line-through; + margin-left: 16rpx; + display: inline-block; +} +.color1, +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: normal; + color: #1a1a1a; + margin: 0; + padding: 0; +} +.color-3 { + color: #333; +} +.color-6 { + color: #666; +} +.color-9 { + color: #999; +} +.color-e9 { + color: #e9e9e9; +} +.color-64 { + color: #646464; +} +.color-b4 { + color: #b4b4b4; +} +.color-97 { + color: #979797; +} +.color-9b { + color: #9b9b9b; +} +.color-white { + color: #fff; +} +.color-black { + color: #000; +} +.color-c { + color: #ccc; +} +.bkg-white { + background-color: #fff; +} +.font10 { + font-size: 20rpx; +} +.font11 { + font-size: 22rpx; +} +.font12 { + font-size: 24rpx; +} +.font13 { + font-size: 26rpx; +} +.font14 { + font-size: 28rpx; +} +.font15 { + font-size: 30rpx; +} +.font16 { + font-size: 32rpx; +} +.font17 { + font-size: 34rpx; +} +.font18 { + font-size: 36rpx; +} +.font19 { + font-size: 38rpx; +} +.font20 { + font-size: 20rpx; +} +.font22 { + font-size: 22rpx; +} +.font24 { + font-size: 24rpx; +} +.font26 { + font-size: 26rpx; +} +.font28 { + font-size: 28rpx; +} +.font30 { + font-size: 30rpx; +} +.font32 { + font-size: 32rpx; +} +.font34 { + font-size: 34rpx; +} +.font36 { + font-size: 36rpx; +} +.font38 { + font-size: 38rpx; +} +.font40 { + font-size: 40rpx; +} +.font46 { + font-size: 46rpx; +} +.font50 { + font-size: 50rpx; +} +.font56 { + font-size: 56rpx; +} +.font82 { + font-size: 82rpx; +} +.bkg-white { + background-color: #fff; +} +.fontWeight-l { + font-weight: 500; +} +.fontWeight-n { + font-weight: 300; +} +.fontWeight-nor { + font-weight: normal; +} +.line-height1 { + line-height: 1; +} +.btn-active { + width: 40rpx; + display: inline-block; + text-align: center; + height: 40rpx; + background: #ff2e45; + border-radius: 8rpx; + color: #fff; + line-height: 40rpx; + font-size: 24rpx; + font-weight: normal; + font-style: normal; + overflow: hidden; +} +.btn-auto { + display: inline-block; + font-size: 20rpx; + border: 1px solid #ff2e45; + border-radius: 8rpx; + height: 36rpx; + line-height: 36rpx; + color: #ff2e45; + padding: 0 12rpx; + margin-right: 16rpx; +} +.btn-lg, +.btn-md, +.btn-sm, +.btn-xs { + text-align: center; + width: 100%; + border-radius: 8rpx; + color: #fff; + line-height: 88rpx; + font-size: 30rpx; +} +.btn-lg, +.btn-md { + background-color: #ff2e45; +} +.btn-sm { + border: 1px solid #999; + color: #666; + height: 60rpx; + line-height: 60rpx; +} +.btn-xs { + width: 100%; + height: 68rpx; + line-height: 68rpx; + border: 1px solid #e2e2e2; + color: #666; +} +.btn-dashed, +.btn-md-dashed { + width: 100%; + height: 80rpx; + line-height: 80rpx; + text-align: center; + color: #ff2e45; + border-radius: 8rpx; + border: 1px solid #ff2e45; + font-size: 26rpx; +} +.btn-md-dashed { + height: 50rpx; + line-height: 50rpx; + border-radius: 8rpx; +} +.btn-bj1 { + background-color: #ff2e45; +} +.btn-bj2 { + background-color: #ff8522; +} +.btn-dis { + background-color: #999; + color: #bbb; +} +.btn-dashed-dis { + border: 1px solid #c8c8c8; + color: #bbbbbb; + border-radius: 8rpx; +} +.titleTag { + line-height: 28rpx; + height: 28rpx; + margin-right: 8rpx; + color: #fff; + padding: 0 8rpx; + font-size: 18rpx; + border-radius: 4rpx; + font-weight: bold; + display: inline-block; +} +.titleTag.memberTag { + margin-top: 2rpx; +} +.amounts { + font-style: normal; + display: inline-block; + height: 24rpx; + min-width: 24rpx; + padding: 0 6rpx; + border-radius: 24rpx; + background-color: #fd1d45 !important; + color: #fff; + text-align: center; + font-size: 20rpx; + font-weight: 600; + position: absolute; + top: 6rpx; + line-height: 24rpx; + box-sizing: border-box; +} +.amounts:empty, +.titleTag:empty { + display: none; +} +.loadMore { + font-size: 26rpx; + color: #c1c1c1; + text-align: center; + height: 80rpx; + line-height: 58rpx; + margin-top: 20rpx; + display: flex; + align-items: center; + justify-content: center; +} +.loadings { + margin-right: 10rpx; + width: 40rpx; + height: 40rpx; + display: inline-block; + vertical-align: sub; + background: url(https://cdn.ghsmpwalmart.com/saas/market/image/loadings.png) no-repeat; + background-size: 240rpx 40rpx; + background-position: 0 0; + animation: circle-loadings 1s steps(6) infinite; +} +@keyframes circle-loadings { + from { + background-position: 0 0; + } + to { + background-position: -240rpx 0; + } +} +.page-loading { + display: inline-block; + width: 72rpx; + height: 75rpx; + background: url('https://cdn.ghsmpwalmart.com/saas/market/image/page-loading-bc.png') no-repeat; + background-position: 0 0; + background-size: 72rpx 75rpx; +} +.page-loading::before { + content: ' '; + display: inline-block; + width: 72rpx; + height: 75rpx; + background: url('https://cdn.ghsmpwalmart.com/saas/market/image/page-loading-spin.png') no-repeat; + background-position: 0 0; + background-size: 720rpx 75rpx; + animation: animate-page-loading 0.4s steps(10) infinite; +} +@keyframes animate-page-loading { + from { + background-position-x: 0rpx; + } + to { + background-position-x: -720rpx; + } +} +.page-loading-wrap { + width: 100%; + height: 1026rpx; + display: flex; + display: -webkit-flex; + justify-content: center; + -webkit-justify-content: center; + padding-top: 46vh; +} +.grooms { + margin: 0 16rpx; + overflow: hidden; + text-align: center; + height: 52rpx; +} +.grooms label { + display: inline-block; + padding: 0 20rpx; + height: 52rpx; + line-height: 52rpx; + position: relative; + color: #3e3e3e; + font-size: 26rpx; +} +.grooms label > p { + overflow: hidden; + max-width: 400rpx; + min-width: 120rpx; + white-space: nowrap; + text-overflow: ellipsis; +} +.grooms label > p::before { + content: ''; + display: inline-block; + position: absolute; + width: 1000rpx; + margin-left: -1000rpx; + height: 0; + left: 0; + top: 50%; + border: 1px solid #e9e9e9; + border-width: 1px 0 0 0; +} +.grooms label > p::after { + content: ''; + display: inline-block; + position: absolute; + width: 1000rpx; + margin-left: -1000rpx; + height: 0; + left: 0; + left: inherit; + top: 50%; + border: 1px solid #e9e9e9; + border-width: 0 0 1px 0; + right: 0; + margin-right: -1000rpx; +} +.navHeight { + height: 100rpx; +} +.widget-mask, +.widget_mask { + position: fixed; + left: 0; + top: 0px; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 200; +} +.theme-color { + color: #f12d22; +} +.theme-bgc { + background-color: #f12d22; +} +.theme-bdc { + border-color: #f12d22; +} +.theme-bgcart { + background-color: #ffb56c; +} +.IphoneX { + padding-bottom: 68rpx !important; +} +.fixIphonex { + bottom: 68rpx !important; +} +.fixIphonex::after { + content: ' '; + position: fixed; + bottom: 0 !important; + height: 68rpx !important; + width: 100%; + background: #ffffff; + left: 0; +} +.iphonexMenus { + bottom: 166rpx !important; +} +.imgCover-oblong-sm .imgCover { + width: 195rpx; + height: 260rpx; +} +.imgCover-oblong-lg .imgCover { + padding-bottom: 133%; +} +.fw-Light { + font-weight: 300; +} +.fw-Regular { + font-weight: 400; +} +.fw-Medium { + font-weight: 600; +} +.fw-Semibold { + font-weight: 800; +} +.icon-xingouwuche { + font-size: 42rpx; +} +.color777 { + color: #777777; +} +.popup-header { + color: #000; + font-size: 28rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + position: relative; + border-bottom: 1rpx solid #dbdbdb; + font-weight: 400; +} +.popup-header .wr-close { + position: absolute; + left: 0; + font-size: 26rpx; + color: #5d5d5d; + width: 60rpx; + height: 60rpx; + text-align: center; + line-height: 60rpx; + top: 20rpx; + left: 10rpx; +} diff --git a/miniprogram/tcb-shop/style/goodsList.wxss b/miniprogram/tcb-shop/style/goodsList.wxss new file mode 100644 index 0000000..be0fcac --- /dev/null +++ b/miniprogram/tcb-shop/style/goodsList.wxss @@ -0,0 +1,169 @@ +/* 层级定义 +@z-index-0: 1; +@z-index-1: 100; +@z-index-2: 200; +@z-index-5: 500; +@z-index-component: 1000; // 通用组件级别 +@z-index-dropdown: @z-index-component; +@z-index-sticky: @z-index-component + 20; +@z-index-fixed: @z-index-component + 30; +@z-index-modal-backdrop:@z-index-component + 40; +@z-index-modal:@z-index-component + 50; +@z-index-popover:@z-index-component + 60; +@z-index-tooltip:@z-index-component + 70; +*/ +/* var() css变量适配*/ +.goods-list-wrap { + padding-left: 24rpx; + background-color: #fff; +} +.goods-list-wrap .wr-goods-card { + padding: 24rpx 24rpx 24rpx 0; + border-bottom: 1rpx solid #e6e6e6; + background-color: #fff; +} +.goods-list-wrap .wr-goods-card.no-border { + border-bottom: none; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__thumb { + width: 200rpx; + height: 200rpx; + margin-right: 24rpx; + border-radius: 8rpx; + overflow: hidden; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content { + position: relative; + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + width: 478rpx; + height: 200rpx; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content .wr-goods-card__title { + overflow: hidden; + margin-bottom: 24rpx; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + display: -webkit-box; + font-size: 26rpx; + line-height: 36rpx; + font-weight: 400; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content .card-index--wr-goods-card__price { + font-size: 32rpx; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content .card-index--wr-goods-card__price .symbol { + font-size: 24rpx; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__tags { + flex-grow: 2; +} +.goods-list-wrap .wr-goods-card .goods-card-tags-wrap { + color: #fa550f; + color: var(--color-primary, #fa550f); + display: flex; + height: 30rpx; + flex-flow: row wrap; + text-align: center; + width: 100%; + flex-shrink: 0; +} +.goods-list-wrap .wr-goods-card .goods-card-tags-wrap .tag { + box-sizing: border-box; + font-size: 20rpx; + border-radius: 4rpx; + flex-shrink: 0; + vertical-align: middle; + margin-right: 8rpx; + background-color: #fff; +} +.goods-list-wrap .wr-goods-card .goods-card-tags-wrap .tag::after { + border-radius: 4rpx; + border: 2rpx solid #fa550f; + border: 2rpx solid var(--color-primary, #fa550f); +} +.goods-list-wrap .wr-goods-card .goods-add-cart { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + right: 0; + bottom: 0; + width: 48rpx; +} +.goods-list-wrap .wr-goods-card .goods-add-cart .goods-add-cart { + line-height: 48rpx; + height: 48rpx; +} +.goods-list-wrap.vertical { + padding: 20rpx 24rpx; + display: flex; + flex-flow: row wrap; + justify-content: space-between; + background-color: transparent; +} +.goods-list-wrap.vertical .wr-goods-card { + width: 340rpx; + height: 574rpx; + overflow: hidden; + padding: 0; + border-bottom: none; + display: flex; + flex-flow: column nowrap; + border-radius: 8px; + margin-bottom: 24rpx; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__thumb { + width: 100%; + height: 340rpx; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content { + width: 100%; + padding: 20rpx; + overflow: hidden; + height: 234rpx; + box-sizing: border-box; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content .wr-goods-card__title { + -webkit-box-orient: horizontal; + -webkit-line-clamp: 1; + line-clamp: 1; + height: 36rpx; + color: #333; + white-space: nowrap; + display: block; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content .card-index--wr-goods-card__origin-price { + position: absolute; + left: 20rpx; + bottom: 72rpx; + margin-left: 0; + font-size: 24rpx; + color: #aaaaaa; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content .wr-goods-card__groupon-price { + position: absolute; + left: 20rpx; + bottom: 72rpx; + margin-left: 0; + font-size: 24rpx; + color: #aaaaaa; +} +.goods-list-wrap.vertical .wr-goods-card .goods-add-cart { + right: 20rpx; + bottom: 20rpx; +} +.goods-list-wrap.vertical .grouponPrice { + margin-bottom: 50rpx; +} +.goods-list-wrap .wr-goods-card__twoLine .wr-goods-card__title { + -webkit-line-clamp: 2 !important; + line-clamp: 2 !important; + display: -webkit-box !important; + white-space: normal !important; + height: auto !important; + -webkit-box-orient: vertical !important; +} diff --git a/miniprogram/tcb-shop/style/iconfont.wxss b/miniprogram/tcb-shop/style/iconfont.wxss new file mode 100644 index 0000000..23df272 --- /dev/null +++ b/miniprogram/tcb-shop/style/iconfont.wxss @@ -0,0 +1,306 @@ +@font-face { + font-family: 'wr'; + src: url('https://cdn3.codesign.qq.com/icons/gqxWyZ1yMJZmVXk/Yyg5Zp2LG8292lK/iconfont.woff?t=cfc62dd36011e60805f5c3ad1a20b642') + format('woff2'); +} + +.wr { + font-family: 'wr' !important; + font-size: 32rpx; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.wr-deliver:before { + content: '\e033'; +} +.wr-indent_close:before { + content: '\e041'; +} +.wr-edit:before { + content: '\e002'; +} +.wr-succeed:before { + content: '\e00d'; +} +.wr-goods_return:before { + content: '\e03c'; +} +.wr-wallet:before { + content: '\e051'; +} +.wr-package:before { + content: '\e047'; +} +.wr-comment:before { + content: '\e037'; +} +.wr-exchang:before { + content: '\e03e'; +} +.wr-credit_card:before { + content: '\e035'; +} +.wr-service:before { + content: '\e04a'; +} +.wr-shop_bag:before { + content: '\e02a'; +} +.wr-goods_refund:before { + content: '\e03d'; +} +.wr-check:before { + content: '\e053'; +} +.wr-wechat:before { + content: '\e065'; +} +.wr-cartAdd:before { + content: '\e05d'; +} +.wr-home:before { + content: '\e020'; +} +.wr-person:before { + content: '\e02c'; +} +.wr-cart:before { + content: '\e023'; +} +.wr-location:before { + content: '\e016'; +} +.wr-arrow_forward:before { + content: '\e012'; +} +.wr-close:before { + content: '\e021'; +} +.wr-search:before { + content: '\e011'; +} +.wr-clear_filled:before { + content: '\e027'; +} +.wr-arrow_drop_up:before { + content: '\e071'; +} +.wr-arrow_drop_down:before { + content: '\e070'; +} +.wr-filter:before { + content: '\e038'; +} +.wr-copy:before { + content: '\e001'; +} +.wr-arrow_back:before { + content: '\e003'; +} +.wr-add_circle:before { + content: '\e004'; +} +.wr-Download:before { + content: '\e006'; +} +.wr-map:before { + content: '\e007'; +} +.wr-store:before { + content: '\e008'; +} +.wr-movie:before { + content: '\e00a'; +} +.wr-done:before { + content: '\e00b'; +} +.wr-minus:before { + content: '\e00c'; +} +.wr-list:before { + content: '\e00e'; +} +.wr-expand_less:before { + content: '\e00f'; +} +.wr-person_add:before { + content: '\e010'; +} +.wr-Photo:before { + content: '\e013'; +} +.wr-preview:before { + content: '\e014'; +} +.wr-remind:before { + content: '\e015'; +} + +.wr-info:before { + content: '\e017'; +} +.wr-expand_less_s:before { + content: '\e018'; +} +.wr-arrow_forward_s:before { + content: '\e019'; +} +.wr-expand_more_s:before { + content: '\e01a'; +} +.wr-share:before { + content: '\e01d'; +} +.wr-notify:before { + content: '\e01e'; +} +.wr-add:before { + content: '\e01f'; +} +.wr-Home:before { + content: '\e020'; +} +.wr-delete:before { + content: '\e022'; +} +.wr-error:before { + content: '\e025'; +} +.wr-sort:before { + content: '\e028'; +} +.wr-sort_filled:before { + content: '\e029'; +} +.wr-shop_bag_filled:before { + content: '\e02b'; +} + +.wr-person_filled:before { + content: '\e02d'; +} +.wr-cart_filled:before { + content: '\e02e'; +} +.wr-home_filled:before { + content: '\e02f'; +} +.wr-add_outline:before { + content: '\e030'; +} + +.wr-compass:before { + content: '\e034'; +} +.wr-goods_exchange:before { + content: '\e03a'; +} +.wr-group_buy:before { + content: '\e03b'; +} +.wr-group:before { + content: '\e03f'; +} +.wr-indent_goods:before { + content: '\e040'; +} +.wr-help:before { + content: '\e042'; +} +.wr-group_takeout:before { + content: '\e043'; +} +.wr-label:before { + content: '\e044'; +} +.wr-indent_wating:before { + content: '\e045'; +} +.wr-member:before { + content: '\e046'; +} + +.wr-scanning:before { + content: '\e04b'; +} +.wr-tv:before { + content: '\e04d'; +} +.wr-to_top:before { + content: '\e04f'; +} +.wr-visibility_off:before { + content: '\e050'; +} +.wr-error-1:before { + content: '\e052'; +} + +.wr-arrow_right:before { + content: '\e054'; +} +.wr-arrow_left:before { + content: '\e056'; +} +.wr-picture_filled:before { + content: '\e057'; +} +.wr-navigation:before { + content: '\e058'; +} +.wr-telephone:before { + content: '\e059'; +} +.wr-indent_time:before { + content: '\e05c'; +} +.wr-cart_add:before { + content: '\e05d'; +} +.wr-classify:before { + content: '\e060'; +} +.wr-place:before { + content: '\e063'; +} +.wr-wechat_pay:before { + content: '\e064'; +} +.wr-security:before { + content: '\e066'; +} +.wr-alarm:before { + content: '\e067'; +} +.wr-person-1:before { + content: '\e068'; +} +.wr-open_in_new:before { + content: '\e069'; +} +.wr-uncheck:before { + content: '\e06b'; +} +.wr-thumb_up:before { + content: '\e06c'; +} +.wr-thumb_up_filled:before { + content: '\e06d'; +} +.wr-star:before { + content: '\e06e'; +} +.wr-star_filled:before { + content: '\e06f'; +} +.wr-cards:before { + content: '\e072'; +} +.wr-picture_error_filled:before { + content: '\e076'; +} +.wr-discount:before { + content: '\e077'; +} diff --git a/miniprogram/tcb-shop/style/theme.wxss b/miniprogram/tcb-shop/style/theme.wxss new file mode 100644 index 0000000..bb1249e --- /dev/null +++ b/miniprogram/tcb-shop/style/theme.wxss @@ -0,0 +1,47 @@ +/* 主题定制 */ +.t-input { + --td-input-placeholder-text-color: #bbbbbb; + --td-input-text-color: #333333; +} + +.t-tab-bar { + --td-tab-bar-color: #bbb; + --td-tab-bar-active-color: #333; +} + +.t-cascader { + --td-cascader-active-color: #fa4126; +} + +.t-switch { + --td-switch-checked-color: #34c759; +} + +.t-button { + --td-button-font-weight: 500; + --td-button-medium-font-size: 32rpx; + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-disabled-color: #fff; + --td-button-default-disabled-bg: #cccccc; + --td-button-default-disabled-border-color: #cccccc; + --td-button-default-active-bg-color: #fa4126; + --td-button-default-active-border-color: #fa4126; +} + +.t-textarea { + --td-textarea-placeholder-color: #bbb; +} + +.t-checkbox { + --td-checkbox-icon-checked-color: #fa4126; +} + +.dialog__button-confirm { + color: #fa4126 !important; +} + +.dialog__button-cancel { + color: #aeb3b7 !important; +} diff --git a/miniprogram/tcb-shop/utils/addressListFresh.js b/miniprogram/tcb-shop/utils/addressListFresh.js new file mode 100644 index 0000000..b643493 --- /dev/null +++ b/miniprogram/tcb-shop/utils/addressListFresh.js @@ -0,0 +1,3 @@ +export let shouldFresh = false; +export const addressListShouldFresh = () => (shouldFresh = true); +export const addressListFinishFresh = () => (shouldFresh = false); diff --git a/miniprogram/tcb-shop/utils/cartFresh.js b/miniprogram/tcb-shop/utils/cartFresh.js new file mode 100644 index 0000000..68e13a4 --- /dev/null +++ b/miniprogram/tcb-shop/utils/cartFresh.js @@ -0,0 +1,3 @@ +export let shouldFresh = false; +export const cartShouldFresh = () => (shouldFresh = true); +export const cartFinishFresh = () => (shouldFresh = false); diff --git a/miniprogram/tcb-shop/utils/cloudImageHandler.js b/miniprogram/tcb-shop/utils/cloudImageHandler.js new file mode 100644 index 0000000..94b2f95 --- /dev/null +++ b/miniprogram/tcb-shop/utils/cloudImageHandler.js @@ -0,0 +1,30 @@ +export async function getCloudImageTempUrl(images) { + const handledIndexToOriginIndex = new Map(); + const shouldBeHandledImages = []; + images.forEach((x, index) => { + if (!x.startsWith('cloud')) return; + + const handleIndex = shouldBeHandledImages.length; + shouldBeHandledImages.push(x); + handledIndexToOriginIndex.set(handleIndex, index); + }); + + const handledImages = ( + await wx.cloud.getTempFileURL({ + fileList: shouldBeHandledImages, + }) + ).fileList.map((x) => x.tempFileURL); + + const ret = [...images]; + handledImages.forEach((image, handleIndex) => (ret[handledIndexToOriginIndex.get(handleIndex)] = image)); + + return ret; +} + +export async function getSingleCloudImageTempUrl(image) { + return ( + await wx.cloud.getTempFileURL({ + fileList: [image], + }) + ).fileList[0].tempFileURL; +} diff --git a/miniprogram/tcb-shop/utils/getPermission.js b/miniprogram/tcb-shop/utils/getPermission.js new file mode 100644 index 0000000..e6a64cb --- /dev/null +++ b/miniprogram/tcb-shop/utils/getPermission.js @@ -0,0 +1,45 @@ +const getPermission = ({ code, name }) => { + return new Promise((resolve, reject) => { + wx.getSetting({ + success: (res) => { + if (res.authSetting[code] === false) { + wx.showModal({ + title: `获取${name}失败`, + content: `获取${name}失败,请在【右上角】-小程序【设置】项中,将【${name}】开启。`, + confirmText: '去设置', + confirmColor: '#FA550F', + cancelColor: '取消', + success(res) { + if (res.confirm) { + wx.openSetting({ + success(settinRes) { + if (settinRes.authSetting[code] === true) { + resolve(); + } else { + console.warn('用户未打开权限', name, code); + reject(); + } + }, + }); + } else { + reject(); + } + }, + fail() { + reject(); + }, + }); + } else { + resolve(); + } + }, + fail() { + reject(); + }, + }); + }); +}; + +module.exports = { + getPermission, +}; diff --git a/miniprogram/tcb-shop/utils/listLoading.js b/miniprogram/tcb-shop/utils/listLoading.js new file mode 100644 index 0000000..10d16e6 --- /dev/null +++ b/miniprogram/tcb-shop/utils/listLoading.js @@ -0,0 +1,9 @@ +/** + * Should be used with `components/load-more`. + */ +export const LIST_LOADING_STATUS = { + READY: 0, + LOADING: 1, + NO_MORE: 2, + FAILED: 3, +}; diff --git a/miniprogram/tcb-shop/utils/mock.js b/miniprogram/tcb-shop/utils/mock.js new file mode 100644 index 0000000..27f13e8 --- /dev/null +++ b/miniprogram/tcb-shop/utils/mock.js @@ -0,0 +1,51 @@ +/** + * 随机打散字符串 + * @param {number} n 长度 + * @param {string} str 字符串 + * @returns + */ +function generateMixed(n, str) { + var res = ''; + for (var i = 0; i < n; i++) { + var id = Math.ceil(Math.random() * 35); + res += str[id]; + } + return res; +} + +/** + * 生成随机数 + * @param {number} min 最小值 + * @param {number} max 最大值 + * @returns + */ +function getRandomNum(min, max) { + var range = max - min; + var rand = Math.random(); + return min + Math.round(rand * range); +} + +/** + * 生成随机IP + * @returns + */ +function mockIp() { + return `10.${getRandomNum(1, 254)}.${getRandomNum(1, 254)}.${getRandomNum( + 1, + 254, + )}`; +} + +function mockReqId() { + return `${getRandomNum(100000, 999999)}.${new Date().valueOf()}${getRandomNum( + 1000, + 9999, + )}.${getRandomNum(10000000, 99999999)}`; +} + +module.exports = { + generateMixed, + mockIp, + mockReqId, + getRandomNum, +}; diff --git a/miniprogram/tcb-shop/utils/orderListFresh.js b/miniprogram/tcb-shop/utils/orderListFresh.js new file mode 100644 index 0000000..3195d87 --- /dev/null +++ b/miniprogram/tcb-shop/utils/orderListFresh.js @@ -0,0 +1,3 @@ +export let shouldFresh = false; +export const orderListShouldFresh = () => (shouldFresh = true); +export const orderListFinishFresh = () => (shouldFresh = false); diff --git a/miniprogram/tcb-shop/utils/orderOperation.js b/miniprogram/tcb-shop/utils/orderOperation.js new file mode 100644 index 0000000..1db5146 --- /dev/null +++ b/miniprogram/tcb-shop/utils/orderOperation.js @@ -0,0 +1,5 @@ +export const OPERATION_TYPE = { + CANCEL: 'cancel', + CONFIRM: 'confirm', + PAY: 'pay', +}; diff --git a/miniprogram/tcb-shop/utils/spuStatus.js b/miniprogram/tcb-shop/utils/spuStatus.js new file mode 100644 index 0000000..40ae401 --- /dev/null +++ b/miniprogram/tcb-shop/utils/spuStatus.js @@ -0,0 +1 @@ +export const SPU_SELLING_STATUS = 'ENABLED'; diff --git a/miniprogram/tcb-shop/utils/util.js b/miniprogram/tcb-shop/utils/util.js new file mode 100644 index 0000000..eb004b6 --- /dev/null +++ b/miniprogram/tcb-shop/utils/util.js @@ -0,0 +1,135 @@ +import dayjs from 'dayjs'; + +const formatTime = (date, template) => dayjs(date).format(template); + +/** + * 格式化价格数额为字符串 + * 可对小数部分进行填充,默认不填充 + * @param price 价格数额,以分为单位! + * @param fill 是否填充小数部分 0-不填充 1-填充第一位小数 2-填充两位小数 + */ +function priceFormat(price, fill = 0) { + if (isNaN(price) || price === null || price === Infinity) { + return price; + } + + let priceFormatValue = Math.round(parseFloat(`${price}`) * 10 ** 8) / 10 ** 8; // 恢复精度丢失 + priceFormatValue = `${Math.ceil(priceFormatValue) / 100}`; // 向上取整,单位转换为元,转换为字符串 + if (fill > 0) { + // 补充小数位数 + if (priceFormatValue.indexOf('.') === -1) { + priceFormatValue = `${priceFormatValue}.`; + } + const n = fill - priceFormatValue.split('.')[1]?.length; + for (let i = 0; i < n; i++) { + priceFormatValue = `${priceFormatValue}0`; + } + } + return priceFormatValue; +} + +/** + * 获取cdn裁剪后链接 + * + * @param {string} url 基础链接 + * @param {number} width 宽度,单位px + * @param {number} [height] 可选,高度,不填时与width同值 + */ +const cosThumb = (url, width, height = width) => { + if (url.indexOf('?') > -1) { + return url; + } + + if (url.indexOf('http://') === 0) { + url = url.replace('http://', 'https://'); + } + + return `${url}?imageMogr2/thumbnail/${~~width}x${~~height}`; +}; + +const get = (source, paths, defaultValue) => { + if (typeof paths === 'string') { + paths = paths.replace(/\[/g, '.').replace(/\]/g, '').split('.').filter(Boolean); + } + const { length } = paths; + let index = 0; + while (source != null && index < length) { + source = source[paths[index++]]; + } + return source === undefined || index === 0 ? defaultValue : source; +}; +let systemWidth = 0; +/** 获取系统宽度,为了减少启动消耗所以在函数里边做初始化 */ +export const loadSystemWidth = () => { + if (systemWidth) { + return systemWidth; + } + + try { + ({ screenWidth: systemWidth, pixelRatio } = wx.getSystemInfoSync()); + } catch (e) { + systemWidth = 0; + } + return systemWidth; +}; + +/** + * 转换rpx为px + * + * @description + * 什么时候用? + * - 布局(width: 172rpx)已经写好, 某些组件只接受px作为style或者prop指定 + * + */ +const rpx2px = (rpx, round = false) => { + loadSystemWidth(); + + // px / systemWidth = rpx / 750 + const result = (rpx * systemWidth) / 750; + + if (round) { + return Math.floor(result); + } + + return result; +}; + +/** + * 手机号码*加密函数 + * @param {string} phone 电话号 + * @returns + */ +const phoneEncryption = (phone) => { + return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); +}; + +// 内置手机号正则字符串 +const innerPhoneReg = '^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[0-8]|8\\d|9\\d)\\d{8}$'; + +/** + * 手机号正则校验 + * @param phone 手机号 + * @param phoneReg 正则字符串 + * @returns true - 校验通过 false - 校验失败 + */ +const phoneRegCheck = (phone) => { + const phoneRegExp = new RegExp(innerPhoneReg); + return phoneRegExp.test(phone); +}; + +function objectToParamString(obj) { + return Object.entries(obj) + .map(([key, val]) => `${key}=${val}`) + .join('&'); +} + +module.exports = { + formatTime, + priceFormat, + cosThumb, + get, + rpx2px, + phoneEncryption, + phoneRegCheck, + objectToParamString, +};