├── src ├── components │ ├── free │ │ ├── .gitkeep │ │ ├── image-ad │ │ │ ├── assets │ │ │ │ └── default.png │ │ │ ├── style.scss │ │ │ └── index.tsx │ │ ├── search-bar │ │ │ ├── style.scss │ │ │ └── index.tsx │ │ ├── image-nav │ │ │ ├── style.scss │ │ │ └── index.tsx │ │ ├── title-text │ │ │ ├── style.scss │ │ │ └── index.tsx │ │ ├── notice-bar │ │ │ └── index.tsx │ │ ├── white-height │ │ │ └── index.tsx │ │ └── video-player │ │ │ └── index.tsx │ ├── action-bar-button │ │ ├── style.scss │ │ └── index.tsx │ ├── action-bar-icon │ │ ├── style.scss │ │ └── index.tsx │ ├── action-bar │ │ ├── style.scss │ │ └── index.tsx │ ├── Counter.vue │ └── sec-killing │ │ ├── style.scss │ │ └── index.tsx ├── styles │ ├── common.scss │ ├── flex.scss │ └── nutui │ │ └── variables.scss ├── assets │ └── images │ │ ├── qrcode.jpg │ │ └── tabbar │ │ ├── flame.png │ │ ├── home.png │ │ ├── user.png │ │ ├── flame-regular.png │ │ ├── home-regular.png │ │ ├── layout-fluid.png │ │ ├── shopping-cart.png │ │ ├── user-regular.png │ │ ├── layout-fluid-regular.png │ │ └── shopping-cart-regular.png ├── pages │ ├── goods │ │ ├── index.config.ts │ │ ├── detail │ │ │ ├── index.config.ts │ │ │ ├── data.ts │ │ │ ├── style.scss │ │ │ └── index.tsx │ │ └── index.tsx │ ├── main │ │ ├── cart │ │ │ ├── index.config.ts │ │ │ └── index.tsx │ │ ├── category │ │ │ ├── index.config.ts │ │ │ ├── style.scss │ │ │ └── index.tsx │ │ ├── index │ │ │ ├── index.config.ts │ │ │ ├── widgets.ts │ │ │ └── index.tsx │ │ └── member │ │ │ ├── index.config.ts │ │ │ └── index.tsx │ └── order │ │ ├── index.config.ts │ │ └── index.tsx ├── constants │ └── user.ts ├── api │ └── decorate │ │ └── decorate.ts ├── custom-tab-bar │ ├── style.scss │ └── index.tsx ├── app.scss ├── app.ts ├── utils │ ├── index.ts │ ├── common.ts │ └── http.ts ├── stores │ └── counter.ts ├── index.html └── app.config.ts ├── .stylelintignore ├── .husky └── pre-commit ├── .gitignore ├── .eslintignore ├── .vscode ├── extensions.json └── settings.json ├── .editorconfig ├── config ├── dev.js ├── prod.js └── index.js ├── project.tt.json ├── README.md ├── project.private.config.json ├── global.d.ts ├── babel.config.js ├── tsconfig.json ├── .eslintrc ├── LICENSE ├── .stylelintrc.js ├── project.config.json ├── .github └── workflows │ └── gh-pages.yml └── package.json /src/components/free/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/common.scss: -------------------------------------------------------------------------------- 1 | @import "./flex"; 2 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | /public/* 3 | public/* 4 | /lib/* 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /src/components/action-bar-button/style.scss: -------------------------------------------------------------------------------- 1 | .action-bar-button { 2 | flex: 1; 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | deploy_versions/ 3 | .temp/ 4 | .rn_temp/ 5 | node_modules/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /src/assets/images/qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/qrcode.jpg -------------------------------------------------------------------------------- /src/pages/goods/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: 'Emesh' 3 | }); 4 | -------------------------------------------------------------------------------- /src/pages/main/cart/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: '购物车' 3 | }); 4 | -------------------------------------------------------------------------------- /src/pages/order/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: 'Emesh' 3 | }); 4 | -------------------------------------------------------------------------------- /src/assets/images/tabbar/flame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/flame.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/home.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/user.png -------------------------------------------------------------------------------- /src/pages/main/category/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: 'Emesh' 3 | }); 4 | -------------------------------------------------------------------------------- /src/pages/main/index/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: 'Emesh' 3 | }); 4 | -------------------------------------------------------------------------------- /src/pages/main/member/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: 'Emesh' 3 | }); 4 | -------------------------------------------------------------------------------- /src/assets/images/tabbar/flame-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/flame-regular.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/home-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/home-regular.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/layout-fluid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/layout-fluid.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/shopping-cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/shopping-cart.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/user-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/user-regular.png -------------------------------------------------------------------------------- /src/components/free/image-ad/assets/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/components/free/image-ad/assets/default.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/layout-fluid-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/layout-fluid-regular.png -------------------------------------------------------------------------------- /src/assets/images/tabbar/shopping-cart-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eamesh/emesh-taro/HEAD/src/assets/images/tabbar/shopping-cart-regular.png -------------------------------------------------------------------------------- /src/constants/user.ts: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN = 'ACCESS_TOKEN'; 2 | export const CURRENT_USER = 'CURRENT_USER'; 3 | export const USER = 'USER'; 4 | -------------------------------------------------------------------------------- /src/components/free/search-bar/style.scss: -------------------------------------------------------------------------------- 1 | .search { 2 | .nut-searchbar__input-bar::placeholder { 3 | color: var(--search-text-color); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/goods/detail/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: 'Emesh', 3 | // navigationStyle: 'custom' 4 | }); 5 | -------------------------------------------------------------------------------- /src/api/decorate/decorate.ts: -------------------------------------------------------------------------------- 1 | import Http from '@/utils/http'; 2 | 3 | export const getHome = (): Promise => { 4 | return Http.request({ 5 | url: '/decorate/home' 6 | }); 7 | }; 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | *.sh 2 | node_modules 3 | *.md 4 | *.woff 5 | *.ttf 6 | .vscode 7 | .idea 8 | dist 9 | lib 10 | /public 11 | .husky 12 | .local 13 | Dockerfile 14 | components.d.ts 15 | components.d.ts -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "johnsoncodehk.volar", 4 | "dbaeumer.vscode-eslint", 5 | "stylelint.vscode-stylelint", 6 | "EditorConfig.EditorConfig" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/components/free/image-ad/style.scss: -------------------------------------------------------------------------------- 1 | .image-ad { 2 | .nut-swiper-item { 3 | line-height: 200px; 4 | } 5 | 6 | .nut-swiper-item .swiper-image { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /config/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"development"', 4 | HOST: '"http://localhost"', 5 | API_PREFIX: '"api"', 6 | API_VERSION: '"v1"' 7 | }, 8 | defineConstants: { 9 | }, 10 | mini: {}, 11 | h5: {} 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/free/image-nav/style.scss: -------------------------------------------------------------------------------- 1 | .image-nav { 2 | .nut-grid-item__content { 3 | background: transparent; 4 | background-color: transparent; 5 | border-color: var(--border-color); 6 | 7 | .nut-grid-item__text { 8 | color: inherit; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/custom-tab-bar/style.scss: -------------------------------------------------------------------------------- 1 | .global-tabbar { 2 | display: flex; 3 | background-color: #fff; 4 | 5 | :global { 6 | .nut-tabbar { 7 | padding-bottom: none; 8 | } 9 | } 10 | 11 | .tabbar { 12 | .nut-tabbar { 13 | padding-bottom: 2px solid red; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /project.tt.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "./", 3 | "projectname": "emesh-taro", 4 | "description": "", 5 | "appid": "touristappid", 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": false, 9 | "postcss": false, 10 | "minified": false 11 | }, 12 | "compileType": "miniprogram" 13 | } 14 | -------------------------------------------------------------------------------- /src/pages/goods/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { defineComponent } from 'vue'; 3 | 4 | export default defineComponent({ 5 | name: 'Goods', 6 | 7 | setup () { 8 | // 9 | }, 10 | 11 | render () { 12 | return ( 13 | 14 | goods 15 | 16 | ); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/pages/order/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { defineComponent } from 'vue'; 3 | 4 | export default defineComponent({ 5 | name: 'Order', 6 | 7 | setup () { 8 | // 9 | }, 10 | 11 | render () { 12 | return ( 13 | 14 | order 15 | 16 | ); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/styles/flex.scss: -------------------------------------------------------------------------------- 1 | .flex { 2 | display: flex; 3 | } 4 | 5 | .column { 6 | flex-direction: column; 7 | } 8 | 9 | .justify-center { 10 | justify-content: center; 11 | } 12 | 13 | .justify-space-between { 14 | justify-content: space-between; 15 | } 16 | 17 | .items-center { 18 | align-items: center; 19 | } 20 | 21 | .flex-1 { 22 | flex: 1; 23 | } 24 | -------------------------------------------------------------------------------- /src/pages/main/member/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { defineComponent } from 'vue'; 3 | 4 | export default defineComponent({ 5 | name: 'Member', 6 | 7 | setup () { 8 | // 9 | }, 10 | 11 | render () { 12 | return ( 13 | 14 | member 15 | 16 | ); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/pages/main/category/style.scss: -------------------------------------------------------------------------------- 1 | .page-category { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100%; 5 | overflow: hidden; 6 | 7 | .page-header { 8 | height: 80px; 9 | flex-shrink: 0; 10 | } 11 | 12 | .page-tabs { 13 | flex: 1; 14 | overflow: hidden; 15 | 16 | .scrollable { 17 | box-sizing: border-box; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app.scss: -------------------------------------------------------------------------------- 1 | @import "./styles/common"; 2 | 3 | page { 4 | height: 100%; 5 | } 6 | 7 | .page { 8 | width: 100%; 9 | min-height: 100%; 10 | } 11 | 12 | .page-wrap { 13 | padding: 10px; 14 | position: relative; 15 | } 16 | 17 | .text-decoration { 18 | text-decoration: line-through; 19 | } 20 | 21 | .afe-area-inset-bottom { 22 | padding-bottom: env(safe-area-inset-bottom); 23 | } 24 | -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import { createPinia } from 'pinia'; 3 | import { Icon, Popup, Price, InputNumber } from '@nutui/nutui-taro'; 4 | 5 | import './app.scss'; 6 | 7 | const App = createApp({ 8 | onShow (options) {}, 9 | // 入口组件不需要实现 render 方法,即使实现了也会被 taro 所覆盖 10 | }); 11 | 12 | App.use(createPinia()).use(Icon).use(Popup).use(Price).use(InputNumber); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /src/components/action-bar-icon/style.scss: -------------------------------------------------------------------------------- 1 | .action-bar-icon { 2 | border: none; 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | line-height: 1; 7 | color: #333; 8 | min-width: 48px; 9 | height: 50px; 10 | border-radius: 0; 11 | padding: 0 12px; 12 | 13 | .nut-icon { 14 | display: flex; 15 | margin: 0 auto 5px; 16 | } 17 | 18 | &__text { 19 | font-size: 10px; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Emesh-taro 2 | 3 | Emesh 多端小程序 4 | 5 | ## Preview 6 | 7 | - [H5预览](https://eamesh.github.io/emesh-taro) 8 | - 微信小程序预览(未发布需提交体验版审核) 9 | 10 | 11 | > 演示数据来自[eamesh/emesh](https://github.com/eamesh/emesh)店铺微页面 12 | ## Feature 13 | - [x] 动态渲染 14 | - [x] 标题文本 15 | - [x] 辅助空白 16 | - [x] 图片广告 17 | - [x] 图文导航 18 | - [x] 搜索 19 | - [x] 公告栏 20 | - [x] 视频播放器 21 | - [ ] 电梯导航 22 | - [ ] 商品卡片 23 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll": true, 4 | }, 5 | "eslint.validate": [ 6 | "javascript", 7 | "javascriptreact", 8 | "typescript", 9 | "typescriptreact" 10 | ], 11 | "eslint.alwaysShowStatus": true, 12 | "stylelint.validate": [ 13 | "css", 14 | "postcss", 15 | "scss", 16 | "vue", 17 | "sass" 18 | ], 19 | "typescript.tsdk": "node_modules/typescript/lib" 20 | } -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { PropType } from 'vue'; 2 | 3 | const commonWidgetDataProps = (defaultData: T) => ( 4 | { 5 | data: { 6 | type: Object as PropType, 7 | default: () => (defaultData) 8 | }, 9 | params: Object 10 | } 11 | ); 12 | 13 | export const widgetDataProps = (defaultData: T) => ( 14 | { 15 | id: { 16 | type: [Number, String], 17 | required: true 18 | }, 19 | ...commonWidgetDataProps(defaultData) 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /project.private.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "condition": { 3 | "miniprogram": { 4 | "list": [ 5 | { 6 | "name": "", 7 | "pathName": "pages/goods/detail/index", 8 | "query": "", 9 | "launchMode": "default", 10 | "scene": null 11 | } 12 | ] 13 | } 14 | }, 15 | "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html" 16 | } -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.png'; 4 | declare module '*.gif'; 5 | declare module '*.jpg'; 6 | declare module '*.jpeg'; 7 | declare module '*.svg'; 8 | declare module '*.css'; 9 | declare module '*.less'; 10 | declare module '*.scss'; 11 | declare module '*.sass'; 12 | declare module '*.styl'; 13 | 14 | declare namespace NodeJS { 15 | interface ProcessEnv { 16 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/pages/main/cart/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { defineComponent } from 'vue'; 3 | import { Button } from '@nutui/nutui-taro'; 4 | import { redirectTo } from '@/utils/common'; 5 | 6 | export default defineComponent({ 7 | name: 'Cart', 8 | 9 | setup () { 10 | // 11 | }, 12 | 13 | render () { 14 | return ( 15 | 16 | 17 | 18 | ); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /src/components/action-bar-icon/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@nutui/nutui-taro'; 2 | import { Text } from '@tarojs/components'; 3 | import { defineComponent } from 'vue'; 4 | 5 | import './style.scss'; 6 | 7 | export default defineComponent({ 8 | name: 'ActionBarIcon', 9 | 10 | render () { 11 | return ( 12 | 16 | ); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/components/action-bar-button/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@nutui/nutui-taro'; 2 | import { Text } from '@tarojs/components'; 3 | import { defineComponent } from 'vue'; 4 | 5 | import './style.scss'; 6 | 7 | export default defineComponent({ 8 | name: 'ActionBarButton', 9 | 10 | render () { 11 | const { 12 | $slots, 13 | $props 14 | } = this; 15 | 16 | return ( 17 | 23 | ); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // babel-preset-taro 更多选项和默认值: 2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md 3 | module.exports = { 4 | presets: [ 5 | ['taro', { 6 | framework: 'vue3', 7 | ts: true 8 | }] 9 | ], 10 | plugins: [ 11 | [ 12 | "import", 13 | { 14 | "libraryName": "@nutui/nutui-taro", 15 | "libraryDirectory": "dist/packages/_es", 16 | "style": (name, file) => name.toLowerCase().replace('_es/', '') + '/index.scss', 17 | "camel2DashComponentName": false 18 | }, 19 | 'nutui3-taro' 20 | ] 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /src/custom-tab-bar/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue'; 2 | import { Tabbar, TabbarItem } from '@nutui/nutui-taro'; 3 | import { View } from '@tarojs/components'; 4 | 5 | import './style.scss'; 6 | 7 | export default defineComponent({ 8 | name: 'CustomTabBar', 9 | 10 | setup () { 11 | 12 | }, 13 | 14 | render () { 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /src/stores/counter.ts: -------------------------------------------------------------------------------- 1 | // https://pinia.esm.dev/introduction.html 2 | import { defineStore } from 'pinia'; 3 | 4 | export const useCounterStore = defineStore('counter', { 5 | state: () => { 6 | return { count: 0 }; 7 | }, 8 | // could also be defined as 9 | // state: () => ({ count: 0 }) 10 | actions: { 11 | increment() { 12 | this.count++; 13 | }, 14 | }, 15 | }); 16 | 17 | // You can even use a function (similar to a component setup()) to define a Store for more advanced use cases: 18 | // export const useCounterStore = defineStore('counter', () => { 19 | // const count = ref(0) 20 | // 21 | // function increment() { 22 | // count.value++ 23 | // } 24 | // 25 | // return {count, increment} 26 | // }) -------------------------------------------------------------------------------- /src/components/action-bar/style.scss: -------------------------------------------------------------------------------- 1 | .action-bar { 2 | display: flex; 3 | align-items: center; 4 | box-sizing: content-box; 5 | height: 50px; 6 | background-color: "#fff"; 7 | 8 | &-button { 9 | &:nth-last-child(2) { 10 | border-bottom-right-radius: 0; 11 | border-top-right-radius: 0; 12 | margin-left: 5px; 13 | background: linear-gradient(to right, #ffd01e, #ff8917); 14 | border: 0; 15 | color: #fff; 16 | } 17 | 18 | &:nth-last-child(1) { 19 | border-bottom-left-radius: 0; 20 | border-top-left-radius: 0; 21 | border: 0; 22 | margin-right: 5px; 23 | background: linear-gradient(to right, #ff6034, #ee0a24); 24 | color: #fff; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/free/title-text/style.scss: -------------------------------------------------------------------------------- 1 | .free-title-text { 2 | padding: 0 16px; 3 | font: inherit; 4 | font-size: 100%; 5 | vertical-align: initial; 6 | background-color: red; 7 | 8 | &__content { 9 | position: relative; 10 | padding: 16px 0; 11 | } 12 | 13 | &__sub { 14 | margin-top: 8px; 15 | } 16 | 17 | .divider { 18 | &::after { 19 | position: absolute; 20 | top: 0; 21 | left: 0; 22 | box-sizing: border-box; 23 | width: 200%; 24 | height: 200%; 25 | transform: scale(.5); 26 | transform-origin: 0 0; 27 | content: " "; 28 | pointer-events: none; 29 | border-bottom: 1px solid #eee; 30 | } 31 | } 32 | 33 | &-center { 34 | text-align: center; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/action-bar/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue'; 2 | import { View } from '@tarojs/components'; 3 | 4 | import './style.scss'; 5 | 6 | const actionBarProps = { 7 | safeAreaInsetBottom: { 8 | type: Boolean, 9 | default: false 10 | } 11 | }; 12 | 13 | export default defineComponent({ 14 | name: 'ActionBar', 15 | 16 | props: actionBarProps, 17 | 18 | setup () { 19 | 20 | }, 21 | 22 | render () { 23 | const { 24 | $slots, 25 | safeAreaInsetBottom 26 | } = this; 27 | 28 | return ( 29 | 35 | {$slots.default()} 36 | 37 | ); 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /src/components/Counter.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | 33 | 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "removeComments": false, 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true, 11 | "outDir": "lib", 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "strictNullChecks": true, 15 | "sourceMap": true, 16 | "baseUrl": ".", 17 | "rootDir": ".", 18 | "jsx": "preserve", 19 | "allowJs": true, 20 | "resolveJsonModule": true, 21 | "typeRoots": [ 22 | "node_modules/@types" 23 | ], 24 | "jsxFactory": "Taro.createElement", 25 | "paths": { 26 | "@/*": [ 27 | "./src/*" 28 | ], 29 | } 30 | }, 31 | "include": ["./src", "global.d.ts"], 32 | "compileOnSave": false 33 | } 34 | -------------------------------------------------------------------------------- /src/pages/main/index/widgets.ts: -------------------------------------------------------------------------------- 1 | import SearchBarWidget from '@/components/free/search-bar'; 2 | import TitleTextWidget from '@/components/free/title-text'; 3 | import WhiteHeightWidget from '@/components/free/white-height'; 4 | import NoticeBarWidget from '@/components/free/notice-bar'; 5 | import VideoPlayerWidget from '@/components/free/video-player'; 6 | import ImageNavWidget from '@/components/free/image-nav'; 7 | import ImageAdWidget from '@/components/free/image-ad'; 8 | 9 | import { Component, defineComponent, markRaw, VNode } from 'vue'; 10 | 11 | const widgets: { 12 | [key: string]: ReturnType | VNode | Component; 13 | } = { 14 | 'title-text': markRaw(TitleTextWidget), 15 | 'white-height': markRaw(WhiteHeightWidget), 16 | 'search': markRaw(SearchBarWidget), 17 | 'search-bar': markRaw(SearchBarWidget), 18 | 'notice-bar': markRaw(NoticeBarWidget), 19 | 'video-player': markRaw(VideoPlayerWidget), 20 | 'image-nav': markRaw(ImageNavWidget), 21 | 'image-ad': markRaw(ImageAdWidget), 22 | }; 23 | 24 | export default widgets; 25 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | // ESLint 检查 .vue 文件需要单独配置编辑器: 2 | // https://eslint.vuejs.org/user-guide/#editor-integrations 3 | { 4 | "extends": [ 5 | "taro/vue3" 6 | ], 7 | "rules": { 8 | "semi": ["error", "always"], 9 | "quotes": ["error", "single"], 10 | "no-useless-catch": "off", 11 | "no-async-promise-executor": "off", 12 | "vue/multi-word-component-names": "off", 13 | 14 | "@typescript-eslint/no-explicit-any": "off", 15 | "@typescript-eslint/ban-ts-ignore": "off", 16 | "@typescript-eslint/explicit-function-return-type": "off", 17 | "@typescript-eslint/no-var-requires": "off", 18 | "@typescript-eslint/no-empty-function": "off", 19 | "@typescript-eslint/no-use-before-define": "off", 20 | "@typescript-eslint/ban-ts-comment": "off", 21 | "@typescript-eslint/ban-types": "off", 22 | "@typescript-eslint/no-non-null-assertion": "off", 23 | "@typescript-eslint/explicit-module-boundary-types": "off", 24 | "@typescript-eslint/no-unused-vars": "off" 25 | }, 26 | "globals": { 27 | "defineAppConfig": "readonly" 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/pages/main/category/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { defineComponent, reactive } from 'vue'; 3 | import { Tabs, TabPane } from '@nutui/nutui-taro'; 4 | 5 | import './style.scss'; 6 | 7 | export default defineComponent({ 8 | name: 'Category', 9 | 10 | setup () { 11 | const model = reactive({ 12 | currentTab: 0 13 | }); 14 | 15 | return { 16 | model, 17 | }; 18 | }, 19 | 20 | render () { 21 | const { 22 | model, 23 | } = this; 24 | 25 | return ( 26 | 27 | 28 | header 29 | 30 | 31 | 32 | {[...Array(20)].map((_, item) => { 33 | return 34 | Tab {{item}} 35 | ; 36 | })} 37 | 38 | 39 | 40 | ); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro'; 2 | import queryString from 'query-string'; 3 | 4 | /** 5 | * 跳转. 6 | * 7 | * @param path 8 | * @param params 9 | * @param mode 10 | */ 11 | export const redirectTo = (path: string, params: Object = {}, mode: 'tabbar' | 'redirectTo' | 'reLaunch' | 'navigateTo' = 'navigateTo') => { 12 | let { url, query } = queryString.parseUrl(path); 13 | // 拼接参数 14 | url += '?' + queryString.stringify({ 15 | ...query, 16 | ...params 17 | }); 18 | 19 | switch (mode) { 20 | // 跳转到tabBar页面 21 | case 'tabbar': 22 | Taro.switchTab({ 23 | url 24 | }); 25 | 26 | break; 27 | 28 | // 关闭当前页面,跳转 29 | case 'redirectTo': 30 | Taro.redirectTo({ 31 | url 32 | }); 33 | 34 | break; 35 | 36 | // 关闭所有页面,打开 37 | case 'reLaunch': 38 | Taro.reLaunch({ 39 | url 40 | }); 41 | 42 | break; 43 | 44 | // 保留当前页面 跳转 45 | case 'navigateTo': 46 | default: 47 | Taro.navigateTo({ 48 | url 49 | }); 50 | 51 | break; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 太年轻 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/components/free/notice-bar/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { defineComponent, ref } from 'vue'; 3 | import { NoticeBar } from '@nutui/nutui-taro'; 4 | import { widgetDataProps } from '@/utils'; 5 | 6 | export interface NutuiNoticeBarProps { 7 | title: string; 8 | color: string; 9 | background: string; 10 | } 11 | 12 | const nutuiNoticeBarProps = widgetDataProps({ 13 | title: '', 14 | color: '#D9500B', 15 | background: 'rgb(255, 248, 233)' 16 | }); 17 | 18 | 19 | export default defineComponent({ 20 | name: 'SearchBar', 21 | 22 | props: nutuiNoticeBarProps, 23 | 24 | setup (props) { 25 | const model = ref(props.data); 26 | 27 | return { 28 | model 29 | }; 30 | }, 31 | 32 | render () { 33 | return ( 34 | 35 | 41 | 42 | 43 | 44 | ); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /src/components/sec-killing/style.scss: -------------------------------------------------------------------------------- 1 | .sec-killing { 2 | background-image: url("http://oss.ease.smhx.net/62ae747801d915b8.png"); 3 | background-repeat: no-repeat; 4 | background-size: contain; 5 | width: 375px; 6 | height: 50px; 7 | 8 | .content { 9 | height: 100%; 10 | display: flex; 11 | flex-flow: row nowrap; 12 | justify-content: space-between; 13 | align-items: center; 14 | } 15 | 16 | .price { 17 | margin-left: 50px; 18 | } 19 | 20 | .nut-price { 21 | color: #fff; 22 | } 23 | 24 | .countdown { 25 | width: 108px; 26 | height: 100%; 27 | flex-shrink: 0; 28 | display: flex; 29 | flex-direction: column; 30 | justify-content: space-around; 31 | align-items: center; 32 | font-size: 10px; 33 | color: #f71471; 34 | } 35 | } 36 | 37 | .countdown-part-box { 38 | display: flex; 39 | align-items: center; 40 | font-size: 10px; 41 | 42 | .part-item { 43 | flex-shrink: 0; 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | width: 18px; 48 | height: 22px; 49 | background: #f71471; 50 | color: #fff; 51 | font-size: 12px; 52 | border-radius: 4px; 53 | } 54 | 55 | .part-item-symbol { 56 | margin: 0 2px; 57 | font-size: 10px; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'stylelint-config-standard', 5 | // 'stylelint-config-html/vue', 6 | 'stylelint-config-standard-scss', 7 | 'stylelint-config-recommended-vue/scss' 8 | ], 9 | plugins: ['stylelint-order'], 10 | rules: { 11 | 'indentation': 2, 12 | 'selector-pseudo-element-no-unknown': [ 13 | true, 14 | { 15 | ignorePseudoElements: [ 16 | 'v-deep', 17 | 'deep', 18 | 'input-placeholder' 19 | ] 20 | } 21 | ], 22 | 'selector-class-pattern': null, 23 | 'at-rule-no-unknown': null, 24 | 'scss/at-rule-no-unknown': null, 25 | 'number-leading-zero': 'never', 26 | 'no-descending-specificity': null, 27 | 'font-family-no-missing-generic-family-keyword': null, 28 | 'selector-type-no-unknown': null, 29 | 'no-duplicate-selectors': null, 30 | 'no-empty-source':null, 31 | 'selector-pseudo-class-no-unknown': [ 32 | true, 33 | { 34 | ignorePseudoClasses: [ 35 | 'global', 36 | 'deep' 37 | ], 38 | } 39 | ], 40 | "number-max-precision": null, 41 | 'scss/dollar-variable-pattern': null, 42 | 'max-line-length': 160 43 | }, 44 | ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'], 45 | }; 46 | -------------------------------------------------------------------------------- /config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"production"', 4 | HOST: '"https://api.v1.emesh.cc"', 5 | API_PREFIX: '"api"', 6 | API_VERSION: '"v1"' 7 | }, 8 | defineConstants: { 9 | }, 10 | mini: {}, 11 | h5: { 12 | webpackChain (chain) { 13 | process.env.GH_PAGES && chain.output.publicPath('./'); 14 | } 15 | /** 16 | * WebpackChain 插件配置 17 | * @docs https://github.com/neutrinojs/webpack-chain 18 | */ 19 | // webpackChain (chain) { 20 | // /** 21 | // * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。 22 | // * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer 23 | // */ 24 | // chain.plugin('analyzer') 25 | // .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) 26 | 27 | // /** 28 | // * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。 29 | // * @docs https://github.com/chrisvfritz/prerender-spa-plugin 30 | // */ 31 | // const path = require('path') 32 | // const Prerender = require('prerender-spa-plugin') 33 | // const staticDir = path.join(__dirname, '..', 'dist') 34 | // chain 35 | // .plugin('prerender') 36 | // .use(new Prerender({ 37 | // staticDir, 38 | // routes: [ '/pages/index/index' ], 39 | // postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') }) 40 | // })) 41 | // } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | pages: [ 3 | 'pages/main/index/index', 4 | 'pages/main/category/index', 5 | 'pages/main/cart/index', 6 | 'pages/main/member/index', 7 | 'pages/goods/detail/index', 8 | ], 9 | window: { 10 | backgroundTextStyle: 'light', 11 | navigationBarBackgroundColor: '#fff', 12 | navigationBarTitleText: 'WeChat', 13 | navigationBarTextStyle: 'black' 14 | }, 15 | tabBar: { 16 | color: '#333333', 17 | selectedColor: '#d34716', 18 | backgroundColor: '#ffffff', 19 | list: [ 20 | { 21 | pagePath: 'pages/main/index/index', 22 | text: '首页', 23 | iconPath: './assets/images/tabbar/home-regular.png', 24 | selectedIconPath: './assets/images/tabbar/home.png' 25 | }, 26 | { 27 | pagePath: 'pages/main/category/index', 28 | text: '分类', 29 | iconPath: './assets/images/tabbar/layout-fluid-regular.png', 30 | selectedIconPath: './assets/images/tabbar/layout-fluid.png' 31 | }, 32 | { 33 | pagePath: 'pages/main/cart/index', 34 | text: '购物车', 35 | iconPath: './assets/images/tabbar/shopping-cart-regular.png', 36 | selectedIconPath: './assets/images/tabbar/shopping-cart.png' 37 | }, 38 | { 39 | pagePath: 'pages/main/member/index', 40 | text: '我的', 41 | iconPath: './assets/images/tabbar/user-regular.png', 42 | selectedIconPath: './assets/images/tabbar/user.png' 43 | }, 44 | ] 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /src/utils/http.ts: -------------------------------------------------------------------------------- 1 | import { ACCESS_TOKEN } from '@/constants/user'; 2 | import Taro, { Chain } from '@tarojs/taro'; 3 | 4 | class Http { 5 | constructor () { 6 | Taro.addInterceptor(this.httpInterceptor); 7 | Taro.addInterceptor(Taro.interceptors.timeoutInterceptor); 8 | } 9 | 10 | httpInterceptor (chain: Chain) { 11 | console.log(chain); 12 | const requestParams = chain.requestParams; 13 | return chain.proceed(requestParams).then((response) => { 14 | console.log(response); 15 | }); 16 | } 17 | 18 | request (option: Taro.request.Option) { 19 | return new Promise((resolve, reject) => { 20 | const header: TaroGeneral.IAnyObject = { 21 | 'content-type': 'application/json', 22 | }; 23 | 24 | // 25 | try { 26 | // 使用临时TOKEN 27 | const token = Taro.getStorageSync(ACCESS_TOKEN) || '4|jzeuroVvWNRvGZzKbBkjnFLjPT1g1j0vuo4tdZ1i'; 28 | // console.log(token); 29 | if (token) { 30 | header.Authorization = `Bearer ${token}`; 31 | } 32 | } catch (error) { 33 | console.log(error); 34 | } 35 | 36 | Taro.request(Object.assign({ 37 | timeout: 30000, 38 | header, 39 | success: (response) => { 40 | resolve(response.data as T); 41 | }, 42 | fail: (error) => { 43 | reject(error); 44 | } 45 | }, option, { 46 | url: `${process.env.HOST}/${process.env.API_PREFIX}/${process.env.API_VERSION}${option.url}`, 47 | })); 48 | }); 49 | } 50 | } 51 | 52 | export default new Http(); 53 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "dist/", 3 | "projectname": "emesh-taro", 4 | "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", 5 | "appid": "wxda60059864ae562b", 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": false, 9 | "enhance": true, 10 | "postcss": false, 11 | "preloadBackgroundData": false, 12 | "minified": false, 13 | "newFeature": false, 14 | "coverView": true, 15 | "nodeModules": false, 16 | "autoAudits": false, 17 | "showShadowRootInWxmlPanel": true, 18 | "scopeDataCheck": false, 19 | "uglifyFileName": false, 20 | "checkInvalidKey": true, 21 | "checkSiteMap": true, 22 | "uploadWithSourceMap": true, 23 | "compileHotReLoad": false, 24 | "lazyloadPlaceholderEnable": false, 25 | "useMultiFrameRuntime": true, 26 | "useApiHook": true, 27 | "useApiHostProcess": true, 28 | "babelSetting": { 29 | "ignore": [], 30 | "disablePlugins": [], 31 | "outputPath": "" 32 | }, 33 | "useIsolateContext": false, 34 | "userConfirmedBundleSwitch": false, 35 | "packNpmManually": false, 36 | "packNpmRelationList": [], 37 | "minifyWXSS": true, 38 | "disableUseStrict": false, 39 | "minifyWXML": true, 40 | "showES6CompileOption": false, 41 | "useCompilerPlugins": false, 42 | "ignoreUploadUnusedFiles": true, 43 | "useStaticServer": true 44 | }, 45 | "compileType": "miniprogram", 46 | "condition": {}, 47 | "libVersion": "2.17.0", 48 | "srcMiniprogramRoot": "dist/", 49 | "packOptions": { 50 | "ignore": [], 51 | "include": [] 52 | }, 53 | "editorSetting": { 54 | "tabIndent": "insertSpaces", 55 | "tabSize": 2 56 | } 57 | } -------------------------------------------------------------------------------- /src/components/free/white-height/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from 'vue'; 2 | import { widgetDataProps } from '@/utils'; 3 | import { View } from '@tarojs/components'; 4 | import Taro from '@tarojs/taro'; 5 | 6 | export type WhiteHeightType = 'empty' | 'line'; 7 | export type WhiteHeightLineStyle = 'solid' | 'dashed' | 'dotted'; 8 | export interface WhiteHeightProps { 9 | type: WhiteHeightType, 10 | empty: { 11 | height: number 12 | }, 13 | line: { 14 | style: WhiteHeightLineStyle 15 | paddingX: number 16 | color: string 17 | } 18 | } 19 | 20 | export default defineComponent({ 21 | name: 'WhiteHeight', 22 | 23 | props: widgetDataProps({ 24 | type: 'line', 25 | empty: { 26 | height: 30 27 | }, 28 | line: { 29 | style: 'solid', 30 | paddingX: 0, 31 | color: '#e5e5e5' 32 | } 33 | }), 34 | 35 | setup (props) { 36 | const model = ref(props.data); 37 | 38 | return { 39 | model 40 | }; 41 | }, 42 | 43 | render () { 44 | const { 45 | model 46 | } = this; 47 | 48 | return ( 49 | 50 | { 51 | model.type === 'empty' 52 | ? ( 53 | 56 | ) 57 | : ( 58 | 62 | 65 | 66 | ) 67 | } 68 | 69 | ); 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /src/components/sec-killing/index.tsx: -------------------------------------------------------------------------------- 1 | import { Text, View } from '@tarojs/components'; 2 | import { defineComponent, reactive } from 'vue'; 3 | import { CountDown } from '@nutui/nutui-taro'; 4 | 5 | import './style.scss'; 6 | 7 | export default defineComponent({ 8 | name: 'SecKilling', 9 | 10 | setup () { 11 | const model = reactive({ 12 | end: Date.now() + 50 * 1000, 13 | resetTime: { 14 | d: '1', 15 | h: '00', 16 | m: '00', 17 | s: '00' 18 | } 19 | }); 20 | 21 | return { 22 | model 23 | }; 24 | }, 25 | 26 | render () { 27 | const { 28 | model 29 | } = this; 30 | 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 | 距结束还剩 39 | 40 | {} 42 | }}> 43 | 44 | { model.resetTime.d }天 45 | { model.resetTime.h } 46 | : 47 | { model.resetTime.m } 48 | : 49 | { model.resetTime.s } 50 | 51 | 52 | 53 | 54 | 55 | 56 | ); 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | const config = { 4 | projectName: 'emesh-taro', 5 | date: '2022-4-13', 6 | designWidth: 375, 7 | deviceRatio: { 8 | 640: 2.34 / 2, 9 | 750: 1, 10 | 828: 1.81 / 2, 11 | 375: 2 / 1 12 | }, 13 | sourceRoot: 'src', 14 | outputRoot: 'dist', 15 | plugins: [ 16 | 'taro-plugin-pinia', 17 | '@tarojs/plugin-html', 18 | ], 19 | defineConstants: { 20 | }, 21 | copy: { 22 | patterns: [ 23 | ], 24 | options: { 25 | } 26 | }, 27 | framework: 'vue3', 28 | sass:{ 29 | data: '@import "./src/styles/nutui/variables.scss";' 30 | }, 31 | mini: { 32 | postcss: { 33 | pxtransform: { 34 | enable: true, 35 | config: { 36 | selectorBlackList: ['nut-'] 37 | } 38 | }, 39 | url: { 40 | enable: true, 41 | config: { 42 | limit: 1024 // 设定转换尺寸上限 43 | } 44 | }, 45 | cssModules: { 46 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 47 | config: { 48 | namingPattern: 'module', // 转换模式,取值为 global/module 49 | generateScopedName: '[name]__[local]___[hash:base64:5]' 50 | } 51 | } 52 | } 53 | }, 54 | h5: { 55 | publicPath: '/', 56 | staticDirectory: 'static', 57 | esnextModules: ['nutui-taro'], 58 | postcss: { 59 | autoprefixer: { 60 | enable: true, 61 | config: { 62 | } 63 | }, 64 | cssModules: { 65 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 66 | config: { 67 | namingPattern: 'module', // 转换模式,取值为 global/module 68 | generateScopedName: '[name]__[local]___[hash:base64:5]' 69 | } 70 | } 71 | } 72 | }, 73 | alias: { 74 | '@': path.resolve(__dirname, '..', 'src'), 75 | } 76 | }; 77 | 78 | module.exports = function (merge) { 79 | if (process.env.NODE_ENV === 'development') { 80 | return merge({}, config, require('./dev')); 81 | } 82 | return merge({}, config, require('./prod')); 83 | }; 84 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ dev ] 9 | pull_request: 10 | branches: [ dev ] 11 | 12 | jobs: 13 | build: 14 | name: Build and deploy gh-pages 15 | env: 16 | MY_SECRET: ${{secrets.gh_pages}} 17 | USER_NAME: eamesh 18 | USER_EMAIL: easeava@gmail.com 19 | BUILD_PATH: ./dist 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | matrix: 24 | node-version: [16.x] 25 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | cache: 'yarn' 33 | 34 | - run: yarn global add @tarojs/cli 35 | - run: yarn install 36 | - run: yarn build:gh-pages 37 | 38 | - name: Commit gh-pages 39 | run: | 40 | cd $BUILD_PATH 41 | git init 42 | git config --local user.name $USER_NAME 43 | git config --local user.email $USER_EMAIL 44 | git status 45 | git remote add origin https://$MY_SECRET@github.com/$GITHUB_REPOSITORY.git 46 | git checkout -b gh-pages 47 | git add --all 48 | git commit -m "deploy to Github pages" 49 | git push origin gh-pages -f 50 | echo 🤘 deploy gh-pages complete. 51 | 52 | # - name: Deploy to Server 53 | # uses: easingthemes/ssh-deploy@main 54 | # env: 55 | # SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} 56 | # ARGS: "-rltgoDzvO --delete" 57 | # SOURCE: "dist/" 58 | # REMOTE_HOST: ${{ secrets.REMOTE_HOST }} 59 | # REMOTE_USER: ${{ secrets.REMOTE_USER }} 60 | # TARGET: ${{ secrets.REMOTE_TARGET }} 61 | # EXCLUDE: "/node_modules/" 62 | -------------------------------------------------------------------------------- /src/components/free/search-bar/index.tsx: -------------------------------------------------------------------------------- 1 | import { widgetDataProps } from '@/utils'; 2 | import { View } from '@tarojs/components'; 3 | import { defineComponent, ref } from 'vue'; 4 | import { SearchBar, Icon } from '@nutui/nutui-taro'; 5 | 6 | import './style.scss'; 7 | 8 | export interface NutuiSearchProps { 9 | text: string, 10 | scan: false, 11 | background: string, 12 | inputBackground: string, 13 | textColor: string 14 | } 15 | 16 | const nutuiSearchProps = widgetDataProps({ 17 | text: '', 18 | scan: false, 19 | background: '#ffffff', 20 | inputBackground: '#f7f7f7', 21 | textColor: '#9f9f9f' 22 | }); 23 | 24 | export default defineComponent({ 25 | name: 'Search', 26 | 27 | props: nutuiSearchProps, 28 | 29 | setup (props) { 30 | const model = ref(props.data); 31 | 32 | return { 33 | model 34 | }; 35 | }, 36 | 37 | render () { 38 | return ( 39 | 42 | { 43 | this.model.text ? ( 44 | 45 | {{ 46 | rightout: () => this.model.text, 47 | leftin: () => { 48 | return ; 49 | }, 50 | rightin: () => { 51 | return this.model.scan ? : null; 52 | } 53 | }} 54 | 55 | ) : ( 56 | 57 | {{ 58 | leftin: () => { 59 | return ; 60 | }, 61 | rightin: () => { 62 | return this.model.scan ? : null; 63 | } 64 | }} 65 | 66 | ) 67 | } 68 | 69 | ); 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /src/pages/main/index/index.tsx: -------------------------------------------------------------------------------- 1 | import { getHome } from '@/api/decorate/decorate'; 2 | import { View } from '@tarojs/components'; 3 | import Taro from '@tarojs/taro'; 4 | import { computed, defineComponent, h, ref } from 'vue'; 5 | import widgets from './widgets'; 6 | 7 | export default defineComponent({ 8 | name: 'Index', 9 | 10 | setup () { 11 | const pageWidgets = ref([]); 12 | const pageStyle = ref({}); 13 | 14 | const pageStyleComputed = computed(() => { 15 | return { 16 | backgroundColor: pageStyle.value.backgroundColor 17 | }; 18 | }); 19 | 20 | // 获取首页 21 | async function handleGetHome () { 22 | try { 23 | const response = await getHome(); 24 | console.log(response); 25 | handleSetPage(response.data); 26 | handleWidgets(response.data.page); 27 | } catch (error) { 28 | console.log(error); 29 | } 30 | } 31 | 32 | function handleWidgets (page: any[]) { 33 | page.forEach((data) => { 34 | Object.prototype.hasOwnProperty.call(widgets, data.key) ? pageWidgets.value.push({ 35 | ...data, 36 | widget: widgets[data.key] 37 | }) : console.log(`Widget =======> ${data.key} 不存在`); 38 | }); 39 | } 40 | 41 | function handleSetPage (pageData: any) { 42 | const freePage = pageData['free-page']; 43 | const freeHeader = pageData['free-header']; 44 | 45 | pageStyle.value = freePage; 46 | 47 | Taro.setBackgroundColor({ 48 | backgroundColor: freePage.backgroundColor 49 | }); 50 | Taro.setNavigationBarColor({ 51 | backgroundColor: freeHeader.backgroundColor, 52 | frontColor: '#000000' 53 | }); 54 | Taro.setNavigationBarTitle({ 55 | title: freePage.title 56 | }); 57 | } 58 | 59 | handleGetHome(); 60 | 61 | return { 62 | pageWidgets, 63 | pageStyleComputed, 64 | }; 65 | }, 66 | 67 | render () { 68 | const { 69 | pageWidgets, 70 | pageStyleComputed, 71 | } = this; 72 | 73 | return ( 74 | 75 | {pageWidgets.map(({ widget, data }, id) => { 76 | return h(widget, { 77 | id, 78 | data 79 | }); 80 | })} 81 | 82 | ); 83 | } 84 | }); 85 | -------------------------------------------------------------------------------- /src/pages/goods/detail/data.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | 'Sku': [{ 3 | 'id': 1, 4 | 'name': '颜色', 5 | 'list': [{ 6 | 'name': '亮黑色', 7 | 'id': 100016015112, 8 | 'active': true, 9 | 'disable': false 10 | }, { 11 | 'name': '釉白色', 12 | 'id': 100016015142, 13 | 'active': false, 14 | 'disable': false 15 | }, { 16 | 'name': '秘银色', 17 | 'id': 100016015078, 18 | 'active': false, 19 | 'disable': false 20 | }, { 21 | 'name': '夏日胡杨', 22 | 'id': 100009064831, 23 | 'active': false, 24 | 'disable': false 25 | }, { 26 | 'name': '秋日胡杨', 27 | 'id': 100009064830, 28 | 'active': false, 29 | 'disable': false 30 | }] 31 | }, { 32 | 'id': 2, 33 | 'name': '版本', 34 | 'list': [{ 35 | 'name': '8GB+128GB', 36 | 'id': 100016015102, 37 | 'active': true, 38 | 'disable': false 39 | }, { 40 | 'name': '8GB+256GB', 41 | 'id': 100016015122, 42 | 'active': false, 43 | 'disable': false 44 | }] 45 | }, { 46 | 'id': 3, 47 | 'name': '版本', 48 | 'list': [{ 49 | 'name': '4G(有充版)', 50 | 'id': 100016015103, 51 | 'active': true, 52 | 'disable': false 53 | }, { 54 | 'name': '5G(有充版)', 55 | 'id': 100016015123, 56 | 'active': false, 57 | 'disable': false 58 | }, { 59 | 'name': '5G(无充版)', 60 | 'id': 100016015104, 61 | 'active': true, 62 | 'disable': true 63 | }, { 64 | 'name': '5G(无充)质保换新版', 65 | 'id': 100016015125, 66 | 'active': false, 67 | 'disable': false 68 | }] 69 | }], 70 | 'Goods': { 71 | 'skuId': '100016015112', 72 | 'price': '4599.00', 73 | 'imagePath': '//m.360buyimg.com/mobilecms/s750x750_jfs/t1/210630/17/8651/208682/618a5bd6Eddc8ea0e/b5e55e1a03bc0126.jpg!q80.dpg.webp' 74 | }, 75 | 'imagePathMap': { 76 | '100016015112': '//m.360buyimg.com/mobilecms/s750x750_jfs/t1/210630/17/8651/208682/618a5bd6Eddc8ea0e/b5e55e1a03bc0126.jpg!q80.dpg.web', 77 | '100016015142': '//img14.360buyimg.com/n4/jfs/t1/216079/14/3895/201095/618a5c0cEe0b9e2ba/cf5b98fb6128a09e.jpg', 78 | '100016015078': '//img14.360buyimg.com/n4/jfs/t1/215845/12/3788/221990/618a5c4dEc71cb4c7/7bd6eb8d17830991.jpg', 79 | '100009064831': '//img14.360buyimg.com/n4/jfs/t1/203247/8/14659/237368/618a5c87Ecc968774/b0bb25331e5e2d1a.jpg', 80 | '100009064830': '//img14.360buyimg.com/n4/jfs/t1/160950/40/25098/234168/618a5cb9E65ba975e/7f8f93ea7767a51b.jpg' 81 | } 82 | }; 83 | -------------------------------------------------------------------------------- /src/components/free/image-nav/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from 'vue'; 2 | import { Grid, GridItem } from '@nutui/nutui-taro'; 3 | import { View } from '@tarojs/components'; 4 | import { widgetDataProps } from '@/utils'; 5 | 6 | import './style.scss'; 7 | 8 | export type ImageNavType = 'image' | 'text'; 9 | export type ImageNavImageType = 'fixed' | 'slide'; 10 | export type ImageNavImageDirection = 'horizontal' | 'vertical'; 11 | 12 | export interface ImageNavStyle { 13 | backgroundColor: string; 14 | color: string; 15 | borderColor: string; 16 | } 17 | 18 | export interface ImageNavItem { 19 | title: string; 20 | imgUrl?: string; 21 | redirect: any; 22 | } 23 | 24 | export interface NutuiImageNavProps { 25 | type: ImageNavType; 26 | imageType: ImageNavImageType; 27 | direction: ImageNavImageDirection; 28 | style: ImageNavStyle; 29 | navs: ImageNavItem[]; 30 | max: number; 31 | columnNum: number; 32 | reverse: boolean; 33 | } 34 | 35 | const nutuiImageNavProps = widgetDataProps({ 36 | type: 'image', 37 | imageType: 'fixed', 38 | direction: 'horizontal', 39 | style: { 40 | backgroundColor: '#FFFFFF', 41 | color: '#333333', 42 | borderColor: '#f5f6f7' 43 | }, 44 | max: 12, 45 | columnNum: 3, 46 | reverse: false, 47 | navs: [ 48 | { 49 | title: '导航一', 50 | imgUrl: '', 51 | redirect: {} 52 | }, 53 | { 54 | title: '导航二', 55 | imgUrl: '', 56 | redirect: {} 57 | }, 58 | { 59 | title: '导航三', 60 | imgUrl: '', 61 | redirect: {} 62 | }, 63 | { 64 | title: '导航四', 65 | imgUrl: '', 66 | redirect: {} 67 | } 68 | ] 69 | }); 70 | 71 | export default defineComponent({ 72 | name: 'ImageNav', 73 | 74 | props: nutuiImageNavProps, 75 | 76 | setup (props) { 77 | const model = ref(props.data); 78 | 79 | return { 80 | model 81 | }; 82 | }, 83 | 84 | render () { 85 | const { 86 | model 87 | } = this; 88 | 89 | return ( 90 | 91 | 97 | { 98 | model.type === 'image' 99 | ? model.navs.map(item => ( 100 | 101 | )) 102 | : model.navs.map(item => ( 103 | 104 | 105 | {item.title} 106 | 107 | 108 | )) 109 | } 110 | 111 | 112 | ); 113 | } 114 | }); 115 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emesh-taro", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "", 6 | "templateInfo": { 7 | "name": "vue3-NutUI", 8 | "typescript": true, 9 | "css": "sass" 10 | }, 11 | "scripts": { 12 | "build:weapp": "taro build --type weapp", 13 | "build:swan": "taro build --type swan", 14 | "build:alipay": "taro build --type alipay", 15 | "build:tt": "taro build --type tt", 16 | "build:h5": "taro build --type h5", 17 | "build:rn": "taro build --type rn", 18 | "build:qq": "taro build --type qq", 19 | "build:quickapp": "taro build --type quickapp", 20 | "dev:weapp": "npm run build:weapp -- --watch", 21 | "dev:swan": "npm run build:swan -- --watch", 22 | "dev:alipay": "npm run build:alipay -- --watch", 23 | "dev:tt": "npm run build:tt -- --watch", 24 | "dev:h5": "npm run build:h5 -- --watch", 25 | "dev:rn": "npm run build:rn -- --watch", 26 | "dev:qq": "npm run build:qq -- --watch", 27 | "dev:quickapp": "npm run build:quickapp -- --watch", 28 | "lint:eslint": "eslint \"src/**/*.{vue,js,ts,tsx}\" --fix", 29 | "lint:stylelint": "stylelint --fix \"src/**/*.{vue,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/", 30 | "prepare": "husky install", 31 | "build:gh-pages": "export GH_PAGES=true && taro build --type h5" 32 | }, 33 | "browserslist": [ 34 | "last 3 versions", 35 | "Android >= 4.1", 36 | "ios >= 8" 37 | ], 38 | "author": "", 39 | "license": "MIT", 40 | "dependencies": { 41 | "@babel/runtime": "^7.7.7", 42 | "@nutui/nutui-taro": "^3.1.18", 43 | "@tarojs/components": "3.4.5", 44 | "@tarojs/plugin-framework-vue3": "3.4.5", 45 | "@tarojs/plugin-html": "3.4.5", 46 | "@tarojs/runtime": "3.4.5", 47 | "@tarojs/taro": "3.4.5", 48 | "pinia": "^2.0.13", 49 | "query-string": "^7.1.1", 50 | "taro-plugin-pinia": "^1.0.0", 51 | "vue": "^3.0.0" 52 | }, 53 | "devDependencies": { 54 | "@babel/core": "^7.8.0", 55 | "@tarojs/cli": "3.4.5", 56 | "@tarojs/mini-runner": "3.4.5", 57 | "@tarojs/webpack-runner": "3.4.5", 58 | "@types/webpack-env": "^1.13.6", 59 | "@typescript-eslint/eslint-plugin": "^2.x", 60 | "@typescript-eslint/parser": "^2.x", 61 | "@vue/compiler-sfc": "^3.0.0", 62 | "babel-plugin-import": "^1.13.3", 63 | "babel-preset-taro": "3.4.5", 64 | "eslint": "^8.13.0", 65 | "eslint-config-taro": "3.4.5", 66 | "eslint-plugin-vue": "^7.0.0", 67 | "husky": ">=7", 68 | "lint-staged": ">=10", 69 | "postcss": "^8.4.12", 70 | "postcss-html": "^1.3.1", 71 | "sass": "^1.50.0", 72 | "stylelint": "^14.6.1", 73 | "stylelint-config-recommended-vue": "^1.4.0", 74 | "stylelint-config-standard": "^25.0.0", 75 | "stylelint-config-standard-scss": "^3.0.0", 76 | "stylelint-order": "^5.0.0", 77 | "typescript": "^3.7.0", 78 | "vue-loader": "^16.0.0-beta.8" 79 | }, 80 | "lint-staged": { 81 | "*.{ts,tsx,js,vue,md}": "yarn lint:eslint", 82 | "*.{vue,scss,css,sass}": "yarn lint:stylelint" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/components/free/image-ad/index.tsx: -------------------------------------------------------------------------------- 1 | import { computed, defineComponent, ref, unref } from 'vue'; 2 | import { Swiper, SwiperItem } from '@nutui/nutui-taro'; 3 | import { View, Image } from '@tarojs/components'; 4 | import { widgetDataProps } from '@/utils'; 5 | import Default from './assets/default.png'; 6 | 7 | import './style.scss'; 8 | import Taro from '@tarojs/taro'; 9 | 10 | export interface AdItemData { 11 | imgUrl: string; 12 | redirect: object; 13 | } 14 | 15 | export interface NutuiImageAdProps { 16 | type: 'default' | 'small' | 'dot' | 'block' | 'rectangle'; 17 | radioType: 'square' | 'round'; 18 | imageType: 'shadow' | 'regular'; 19 | pagePadding: number; 20 | imagePadding: number; 21 | ads: AdItemData[]; 22 | } 23 | 24 | const nutuiImageAdProps = widgetDataProps({ 25 | type: 'default', 26 | imageType: 'regular', 27 | radioType: 'square', 28 | pagePadding: 0, 29 | imagePadding: 0, 30 | ads: [] 31 | }); 32 | 33 | export default defineComponent({ 34 | name: 'ImageAd', 35 | 36 | props: nutuiImageAdProps, 37 | 38 | setup (props) { 39 | const model = ref(props.data); 40 | const modelUnref = unref(model); 41 | 42 | const swiperItemStyle = computed(() => { 43 | return { 44 | paddingLeft: Taro.pxTransform(modelUnref.pagePadding), 45 | paddingRight: Taro.pxTransform(modelUnref.pagePadding), 46 | }; 47 | }); 48 | 49 | const swiperImageStyle = computed(() => { 50 | return { 51 | borderRadius: modelUnref.radioType === 'round' ? Taro.pxTransform(10) : 0 52 | }; 53 | }); 54 | 55 | // Swiper 高度 56 | const swiperContainerHeight = computed(() => { 57 | return Taro.getSystemInfoSync().windowWidth / (375/200); 58 | }); 59 | 60 | return { 61 | model, 62 | swiperItemStyle, 63 | swiperImageStyle, 64 | swiperContainerHeight 65 | }; 66 | }, 67 | 68 | render () { 69 | const { 70 | model, 71 | swiperItemStyle, 72 | swiperImageStyle, 73 | swiperContainerHeight 74 | } = this; 75 | 76 | return ( 77 | 78 | 85 | { 86 | model.ads.length ? model.ads.map(ad => { 87 | return ( 88 | 89 | 90 | 91 | ); 92 | }) : [...Array(3)].map(() => { 93 | return ( 94 | 95 | 96 | 97 | ); 98 | }) 99 | } 100 | 101 | 102 | ); 103 | } 104 | }); 105 | -------------------------------------------------------------------------------- /src/components/free/video-player/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { computed, defineComponent, ref, unref } from 'vue'; 3 | import { Video } from '@nutui/nutui-taro'; 4 | import { widgetDataProps } from '@/utils'; 5 | import Taro from '@tarojs/taro'; 6 | 7 | export interface NutuiVideoPlayerProps { 8 | type: 'resource' | 'network', 9 | coverType: 'default' | 'resource', 10 | radioType: 'square' | 'round', 11 | pagePadding: number, 12 | resource: { 13 | src: string, 14 | type: string 15 | }, 16 | network: { 17 | src: string, 18 | type: string 19 | }, 20 | options: { 21 | controls: boolean, 22 | poster: string, 23 | autoplay: boolean, 24 | muted: boolean, 25 | loop: boolean 26 | } 27 | } 28 | 29 | const nutuiVideoPlayerProps = widgetDataProps({ 30 | type: 'resource', 31 | coverType: 'default', 32 | radioType: 'square', 33 | pagePadding: 0, 34 | resource: { 35 | src: '', 36 | type: 'video/mp4' 37 | }, 38 | network: { 39 | src: '', 40 | type: 'video/mp4' 41 | }, 42 | options: { 43 | controls: true, 44 | poster: '', 45 | autoplay: false, 46 | muted: true, 47 | loop: true 48 | } 49 | }); 50 | 51 | 52 | export default defineComponent({ 53 | name: 'VideoPlayer', 54 | 55 | props: nutuiVideoPlayerProps, 56 | 57 | setup (props) { 58 | const model = ref(props.data); 59 | const modelUnref = unref(model); 60 | 61 | const videoPlayerStyle = computed(() => { 62 | return { 63 | paddingLeft: Taro.pxTransform(modelUnref.pagePadding), 64 | paddingRight: Taro.pxTransform(modelUnref.pagePadding), 65 | // height: Taro.pxTransform(210) 66 | height: `${Taro.getSystemInfoSync().windowWidth / (375/210)}px` 67 | }; 68 | }); 69 | 70 | const videoStyle = computed(() => { 71 | return { 72 | borderRadius: modelUnref.radioType === 'round' ? Taro.pxTransform(8) : 0, 73 | overflow: 'hidden' 74 | }; 75 | }); 76 | 77 | return { 78 | model, 79 | videoStyle, 80 | videoPlayerStyle 81 | }; 82 | }, 83 | 84 | render () { 85 | const { 86 | model, 87 | videoStyle, 88 | videoPlayerStyle 89 | } = this; 90 | 91 | return ( 92 | 93 | { 94 | model.type === 'resource' && model.resource.src ? ( 95 | 101 | ) : model.network.src ? ( 102 | 108 | ) : 113 | } 114 | 115 | ); 116 | } 117 | }); 118 | -------------------------------------------------------------------------------- /src/components/free/title-text/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from '@tarojs/components'; 2 | import { widgetDataProps } from '@/utils'; 3 | import { computed, defineComponent, ref, unref } from 'vue'; 4 | 5 | import './style.scss'; 6 | import Taro from '@tarojs/taro'; 7 | 8 | export type TitleTextTitleAligin = 'left' | 'center'; 9 | export type TitleTextWeight = 'bold' | '400'; 10 | export type TitleTextFontSize = 16 | 14 | 12; 11 | 12 | export interface TitleTextProps { 13 | bottomDivider: boolean, 14 | style: { 15 | backgroundColor: string 16 | }, 17 | title: { 18 | text: string, 19 | aligin: TitleTextTitleAligin, 20 | style: { 21 | fontSize: TitleTextFontSize, 22 | fontWeight: TitleTextWeight, 23 | color: string 24 | } 25 | }, 26 | description: { 27 | text: string, 28 | style: { 29 | fontSize: TitleTextFontSize, 30 | fontWeight: TitleTextWeight, 31 | color: string 32 | } 33 | } 34 | } 35 | 36 | export default defineComponent({ 37 | name: 'title-text', 38 | 39 | props: widgetDataProps({ 40 | bottomDivider: true, 41 | style: { 42 | backgroundColor: '#fff' 43 | }, 44 | title: { 45 | text: '标题', 46 | aligin: 'left', 47 | style: { 48 | fontSize: 16, 49 | fontWeight: 'bold', 50 | color: '#323233' 51 | } 52 | }, 53 | description: { 54 | text: '', 55 | style: { 56 | fontSize: 12, 57 | fontWeight: '400', 58 | color: '#969799' 59 | } 60 | } 61 | }), 62 | 63 | setup (props) { 64 | const model = ref(props.data); 65 | const modelUnref = unref(model); 66 | 67 | const titleStyle = computed(() => { 68 | return { 69 | ...modelUnref.title.style, 70 | fontSize: Taro.pxTransform(modelUnref.title.style.fontSize) 71 | }; 72 | }); 73 | 74 | const descriptionStyle = computed(() => { 75 | return { 76 | ...modelUnref.description.style, 77 | fontSize: Taro.pxTransform(modelUnref.description.style.fontSize) 78 | }; 79 | }); 80 | 81 | return { 82 | model, 83 | titleStyle, 84 | descriptionStyle 85 | }; 86 | }, 87 | 88 | render () { 89 | const { 90 | model, 91 | titleStyle, 92 | descriptionStyle 93 | } = this; 94 | 95 | return ( 96 | 97 | 104 | {model.title.text} 105 | { 106 | model.description.text && ( 107 | 108 | {model.description.text} 109 | 110 | ) 111 | } 112 | 113 | 114 | ); 115 | } 116 | }); 117 | -------------------------------------------------------------------------------- /src/pages/goods/detail/style.scss: -------------------------------------------------------------------------------- 1 | .page-goods { 2 | background-color: #f5f5f5; 3 | display: flex; 4 | flex-direction: column; 5 | height: 100vh; 6 | overflow: hidden; 7 | 8 | &__header { 9 | flex: 1; 10 | overflow: auto; 11 | } 12 | 13 | &__bottom { 14 | height: 50px; 15 | flex-shrink: 0; 16 | background-color: #fff; 17 | padding-bottom: env(safe-area-inset-bottom); 18 | } 19 | } 20 | 21 | .swiper-container { 22 | overflow: hidden; 23 | 24 | .swiper-image { 25 | width: 100%; 26 | height: 100%; 27 | } 28 | 29 | .swiper-page { 30 | position: absolute; 31 | right: 10px; 32 | bottom: 20px; 33 | } 34 | } 35 | 36 | // .goods-video { 37 | // width: 100%; 38 | // height: 280px; 39 | // } 40 | 41 | .goods-slider { 42 | position: relative; 43 | 44 | &__switch { 45 | width: 100%; 46 | position: absolute; 47 | bottom: 20px; 48 | z-index: 1; 49 | } 50 | 51 | video { 52 | display: block; 53 | } 54 | } 55 | 56 | .goods-area { 57 | background-color: #fff; 58 | 59 | .goods-name { 60 | display: flex; 61 | flex-flow: row nowrap; 62 | 63 | .goods-text { 64 | font-size: 16px; 65 | font-weight: 600; 66 | color: #262626; 67 | padding-right: 4px; 68 | } 69 | 70 | .goods-favorite { 71 | width: 50px; 72 | flex-shrink: 0; 73 | display: flex; 74 | flex-direction: column; 75 | align-items: center; 76 | font-size: 10px; 77 | position: relative; 78 | align-self: flex-start; 79 | 80 | .nut-icon { 81 | margin-bottom: 4px; 82 | } 83 | 84 | &::before { 85 | content: " "; 86 | width: 0; 87 | display: block; 88 | border-left: 1px solid #ddd; 89 | position: absolute; 90 | top: 0; 91 | bottom: 0; 92 | left: 0; 93 | } 94 | } 95 | } 96 | } 97 | 98 | .goods-container { 99 | .nut-cell { 100 | &:first-child { 101 | margin-top: 0; 102 | } 103 | } 104 | 105 | .price-wrap { 106 | .price { 107 | margin-right: 6px; 108 | } 109 | 110 | .text-decoration { 111 | color: #ddd; 112 | 113 | view { 114 | text-decoration: line-through; 115 | } 116 | } 117 | } 118 | 119 | .title-wrap { 120 | color: #333; 121 | padding-top: 4px; 122 | } 123 | 124 | .extra-wrap { 125 | font-size: 9px; 126 | } 127 | } 128 | 129 | .goods-cell { 130 | width: 100%; 131 | font-size: 10px; 132 | 133 | .cell-extra { 134 | color: #333; 135 | } 136 | 137 | .cell-title { 138 | margin-right: 10px; 139 | } 140 | } 141 | 142 | .goods-content { 143 | .content-label { 144 | font-size: 10px; 145 | color: #333; 146 | } 147 | } 148 | 149 | .goods-action { 150 | &__bar { 151 | display: flex; 152 | flex-direction: row; 153 | } 154 | 155 | &__icon { 156 | font-size: 10px; 157 | display: flex; 158 | flex-direction: column; 159 | justify-content: center; 160 | align-items: center; 161 | } 162 | } 163 | 164 | .nut-sku-operate { 165 | padding-bottom: env(safe-area-inset-bottom); 166 | } 167 | -------------------------------------------------------------------------------- /src/pages/goods/detail/index.tsx: -------------------------------------------------------------------------------- 1 | import { View, Image, Video, Text } from '@tarojs/components'; 2 | import { computed, defineComponent, onMounted, reactive, ref } from 'vue'; 3 | import { Swiper, SwiperItem, Tag, Row, Col, Cell, CellGroup, Sku } from '@nutui/nutui-taro'; 4 | import Taro from '@tarojs/taro'; 5 | import SecKilling from '@/components/sec-killing'; 6 | 7 | import './style.scss'; 8 | import ActionBar from '@/components/action-bar'; 9 | import ActionBarIcon from '@/components/action-bar-icon'; 10 | import ActionBarButton from '@/components/action-bar-button'; 11 | import jsonData from './data'; 12 | 13 | export default defineComponent({ 14 | name: 'GoodsDetail', 15 | 16 | setup () { 17 | const model = reactive({ 18 | video: { 19 | source: { 20 | src: 'http://oss.ease.smhx.net/CtwiPWJhBLuAdfgOAk6s2Fy-320071.mp4', 21 | type: 'video/mp4' 22 | }, 23 | options: { 24 | poster: 'http://oss.ease.smhx.net/1650784652827.jpg' 25 | }, 26 | } 27 | }); 28 | const swiperData = reactive({ 29 | page: 0, 30 | current: 1 31 | }); 32 | const showMode = ref('video'); 33 | const showModes = [ 34 | { 35 | text: '视频', 36 | key: 'video' 37 | }, 38 | { 39 | text: '图片', 40 | key: 'image' 41 | } 42 | ] as const; 43 | 44 | function handleSwichMode (e: MouseEvent, key: typeof showModes[number]['key']) { 45 | console.log('click', key); 46 | showMode.value = key; 47 | } 48 | 49 | function handleSwiperChange (index: number) { 50 | swiperData.current = index + 1; 51 | } 52 | 53 | // Swiper 高度 54 | const swiperContainerHeight = computed(() => { 55 | return Taro.getSystemInfoSync().windowWidth / (375/280); 56 | }); 57 | 58 | const videoContainerStyle = computed(() => { 59 | return { 60 | width: Taro.pxTransform(375), 61 | height: `${swiperContainerHeight.value}px` 62 | }; 63 | }); 64 | 65 | const base = ref(false); 66 | const data = reactive({ 67 | sku: [], 68 | goods: {} 69 | }); 70 | 71 | onMounted(() => { 72 | const { Sku, Goods } = jsonData; 73 | data.sku = Sku; 74 | data.goods = Goods; 75 | }); 76 | // 切换规格类目 77 | const selectSku = (ss: any) => { 78 | const { sku, parentIndex } = ss; 79 | if (sku.disable) return false; 80 | data.sku[parentIndex].list.forEach((s) => { 81 | s.active = s.id == sku.id; 82 | }); 83 | data.goods = { 84 | skuId: sku.id, 85 | price: '4599.00', 86 | imagePath: 87 | '//img14.360buyimg.com/n4/jfs/t1/215845/12/3788/221990/618a5c4dEc71cb4c7/7bd6eb8d17830991.jpg' 88 | }; 89 | }; 90 | // 底部操作按钮触发 91 | const clickBtnOperate = (op:string)=>{ 92 | console.log('点击了操作按钮',op); 93 | }; 94 | // 关闭商品规格弹框 95 | const close = ()=>{}; 96 | 97 | 98 | return { 99 | model, 100 | swiperData, 101 | showMode, 102 | showModes, 103 | handleSwiperChange, 104 | handleSwichMode, 105 | swiperContainerHeight, 106 | videoContainerStyle, 107 | base, 108 | selectSku, 109 | clickBtnOperate, 110 | close, 111 | data 112 | }; 113 | }, 114 | 115 | render () { 116 | const { 117 | model, 118 | swiperData, 119 | showMode, 120 | showModes, 121 | handleSwiperChange, 122 | handleSwichMode, 123 | swiperContainerHeight, 124 | videoContainerStyle, 125 | selectSku, 126 | clickBtnOperate, 127 | close, 128 | data 129 | } = this; 130 | 131 | return ( 132 | 133 | 134 | 135 | { 136 | showMode === 'image' ? ( 137 | 144 | {{ 145 | default: () => { 146 | return ( 147 | <> 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ); 159 | }, 160 | 161 | page: () => { 162 | return 163 | {swiperData.current} / 3 164 | ; 165 | } 166 | }} 167 | 168 | ) : ( 169 | 170 | ) 171 | } 172 | 173 | { 174 | showModes.map(item => { 175 | return 176 | handleSwichMode(e, item.key)}>{item.text} 177 | ; 178 | }) 179 | } 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 摩托罗拉 edge s pro 旗舰骁龙870 1亿像素 50X潜望式变焦 10亿色144Hz臻彩屏 轻薄5G手机 12GB+256GB 青玉案 189 | 190 | 191 | 收藏 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 精品速冻咖啡【买2盒送吸管杯】 206 | 207 | 208 | 已售卖30500+件 209 | 210 | 211 | 212 | 213 | 214 | 215 | { 216 | console.log(this.base = true); 217 | }}> 218 | 选择 219 | 220 | asd 221 | 222 | 多种可选 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 运费 231 | 232 | 免运费 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 256 | 259 | 260 | 261 | {/* 262 | 263 | 264 | 客服 265 | 266 | 267 | 268 | 购物车 269 | 270 | 271 | 272 | 店铺 273 | 274 | */} 275 | 276 | 277 | 278 | 287 | 288 | 289 | ); 290 | } 291 | }); 292 | -------------------------------------------------------------------------------- /src/styles/nutui/variables.scss: -------------------------------------------------------------------------------- 1 | // color 2 | 3 | // 主色调 4 | $primary-color: #fa2c19 !default; 5 | $primary-color-end: #fa6419 !default; 6 | 7 | // 辅助色 8 | $help-color: #f5f5f5 !default; 9 | 10 | // 标题常规文字 11 | $title-color: #1a1a1a !default; 12 | 13 | // 副标题 14 | $title-color2: #666 !default; 15 | 16 | // 次内容 17 | $text-color: #808080 !default; 18 | 19 | // 特殊禁用色 20 | $disable-color: #ccc !default; 21 | $white: #fff !default; 22 | $black: #000 !default; 23 | 24 | // $font-family: pingfang sc, microsoft yahei, helvetica, hiragino sans gb, simsun, sans-serif !default; 25 | $font-family: 400 .24rem / 1.5 microsoft yahei,arial,lucida grande,verdana; 26 | 27 | // ---- Animation ---- 28 | $animation-duration: .25s !default; 29 | $animation-timing-fun: cubic-bezier(.55, .085, .68, .53) !default; 30 | 31 | // Font 32 | $font-size-0: 10px !default; 33 | $font-size-1: 12px !default; 34 | $font-size-2: 14px !default; 35 | $font-size-3: 16px !default; 36 | $font-size-4: 18px !default; 37 | $font-weight-bold: 400 !default; 38 | $font-size-small: $font-size-1 !default; 39 | $font-size-base: $font-size-2 !default; 40 | $font-size-large: $font-size-3 !default; 41 | $line-height-base: 1.5 !default; 42 | 43 | // button 44 | $button-border-radius: 25px !default; 45 | $button-border-width: 1px !default; 46 | $button-default-bg-color: $white !default; 47 | $button-default-border-color: rgb(204 204 204 / 100%) !default; 48 | $button-default-color: rgb(102 102 102 / 100%) !default; 49 | $button-default-padding: 0 18px !default; 50 | $button-mini-padding: 0 12px !default; 51 | $button-small-padding: 0 12px !default; 52 | $button-small-height: 28px !default; 53 | $button-mini-height: 24px !default; 54 | $button-default-height: 38px !default; 55 | $button-large-height: 48px !default; 56 | $button-large-line-height: 46px !default; 57 | $button-small-line-height: 26px !default; 58 | $button-block-height: 48px !default; 59 | $button-default-line-height: 36px !default; 60 | $button-block-line-height: 46px !default; 61 | $button-default-font-size: $font-size-2 !default; 62 | $button-small-font-size: $font-size-1 !default; 63 | $button-mini-font-size: $font-size-1 !default; 64 | $button-disabled-opacity: .68 !default; 65 | $button-primary-color: $white !default; 66 | $button-primary-border-color: $primary-color !default; 67 | $button-primary-background-color: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 68 | $button-info-color: $white !default; 69 | $button-info-border-color: rgb(73 106 242 / 100%) !default; 70 | $button-info-background-color: linear-gradient(315deg, rgb(73 143 242 / 100%) 0%, rgb(73 101 242 / 100%) 100%) !default; 71 | $button-success-color: $white !default; 72 | $button-success-border-color: rgb(38 191 38 / 100%) !default; 73 | $button-success-background-color: linear-gradient( 74 | 135deg, 75 | rgb(38 191 38 / 100%) 0%, 76 | rgb(39 197 48 / 100%) 45%, 77 | rgb(40 207 63 / 100%) 83%, 78 | rgb(41 212 70 / 100%) 100% 79 | ) !default; 80 | $button-danger-color: $white !default; 81 | $button-danger-border-color: rgb(250 44 25 / 100%) !default; 82 | $button-danger-background-color: rgb(250 44 25 / 100%) !default; 83 | $button-warning-color: $white !default; 84 | $button-warning-border-color: rgb(255 158 13 / 100%) !default; 85 | $button-warning-background-color: linear-gradient( 86 | 135deg, 87 | rgb(255 158 13 / 100%) 0%, 88 | rgb(255 167 13 / 100%) 45%, 89 | rgb(255 182 13 / 100%) 83%, 90 | rgb(255 190 13 / 100%) 100% 91 | ) !default; 92 | $button-plain-background-color: $white !default; 93 | 94 | // cell 95 | 96 | $cell-color: $title-color2 !default; 97 | $cell-title-font: $font-size-2 !default; 98 | $cell-title-desc-font: $font-size-1 !default; 99 | $cell-desc-font: $font-size-2 !default; 100 | $cell-desc-color: $disable-color !default; 101 | $cell-border-radius: 6px !default; 102 | $cell-padding: 13px 16px !default; 103 | $cell-line-height: 20px !default; 104 | $cell-after-right: 16px !default; 105 | $cell-after-border-bottom: 2px solid #f5f6f7 !default; 106 | $cell-default-icon-margin: 0 4px 0 0; 107 | 108 | // cell-group 109 | 110 | $cell-group-title-padding: 0 10px !default; 111 | $cell-group-title-color: #909ca4 !default; 112 | $cell-group-title-font-size: $font-size-2 !default; 113 | $cell-group-title-line-height: 20px !default; 114 | $cell-group-desc-padding: 0 10px !default; 115 | $cell-group-desc-color: #909ca4 !default; 116 | $cell-group-desc-font-size: $font-size-1 !default; 117 | $cell-group-desc-line-height: 16px !default; 118 | $cell-group-background-color: $white !default; 119 | 120 | // divider 121 | 122 | $divider-margin: 16px 0 !default; 123 | $divider-text-font-size: $font-size-2 !default; 124 | $divider-text-color: #909ca4 !default; 125 | $divider-line-height: 2px !default; 126 | $divider-before-margin-right: 16px !default; 127 | $divider-after-margin-left: 16px !default; 128 | 129 | // icon 130 | 131 | $icon-height: 20px !default; 132 | $icon-width: 20px !default; 133 | $icon-line-height: 20px !default; 134 | 135 | // uploader 136 | 137 | $uploader-picture-width: 100px !default; 138 | $uploader-picture-height: 100px !default; 139 | $uploader-background: #f7f8fa !default; 140 | 141 | // picker 142 | $picker-cancel-color: #808080 !default; 143 | $picker-ok-color: $primary-color !default; 144 | $picker-bar-cancel-font-size: 14px !default; 145 | $picker-bar-ok-font-size: 14px !default; 146 | $picker-bar-button-padding: 0 15px !default; 147 | $picker-bar-title-font-size: 16px !default; 148 | $picker-bar-title-color: $title-color !default; 149 | $picker-bar-title-font-weight: normal !default; 150 | $picker-item-height: 36px !default; 151 | $picker-item-text-color: #808080 !default; 152 | $picker-item-active-text-color: inherit !default; 153 | $picker-item-text-font-size: 16px !default; 154 | $picker-item-active-line-border: 1px solid #d8d8d8 !default; 155 | $picker-columns-item-color: $title-color !default; 156 | 157 | // input 158 | $input-border-bottom: #eaf0fb !default; 159 | $input-disabled-color: #c8c9cc !default; 160 | $input-required-color: $primary-color !default; 161 | $input-font-size: $font-size-2 !default; 162 | 163 | // textarea 164 | 165 | $textarea-font: $font-size-2 !default; 166 | $textarea-height: 100px !default; 167 | $textarea-limit-color: $text-color !default; 168 | $textarea-text-color: $title-color !default; 169 | $textarea-disabled-color: $disable-color !default; 170 | 171 | // inputnumber 172 | 173 | $inputnumber-icon-color: $title-color !default; 174 | $inputnumber-icon-void-color: $disable-color !default; 175 | $inputnumber-icon-size: 20px !default; 176 | $inputnumber-input-font-size: 12px !default; 177 | $inputnumber-input-font-color: $title-color !default; 178 | $inputnumber-input-background-color: $help-color !default; 179 | $inputnumber-input-border-radius: 4px !default; 180 | $inputnumber-input-width: 40px !default; 181 | $inputnumber-input-margin: 0 6px !default; 182 | $inputnumber-input-border: 0 !default; 183 | 184 | // actionsheet 185 | $actionsheet-light-color: #f6f6f6 !default; 186 | $actionsheet-item-border-bottom: none !default; 187 | $actionsheet-item-font-size: $font-size-2 !default; 188 | $actionsheet-item-subdesc-font-size: $font-size-1 !default; 189 | $actionsheet-item-cancel-border-top: 1px solid $actionsheet-light-color !default; 190 | $actionsheet-item-line-height: 24px !default; 191 | $actionsheet-item-font-color: $title-color !default; 192 | 193 | // shortpassword 194 | $shortpassword-background-color: rgb(245 245 245 / 100%) !default; 195 | $shortpassword-border-color: #ddd !default; 196 | $shortpassword-error: $primary-color !default; 197 | $shortpassword-forget: rgb(128 128 128 / 100%) !default; 198 | 199 | // numberkeyboard 200 | $numberkeyboard-width: 100% !default; 201 | $numberkeyboard-padding: 0 0 22px 0 !default; 202 | $numberkeyboard-background-color: #f2f3f5 !default; 203 | $numberkeyboard-header-height: 34px !default; 204 | $numberkeyboard-header-padding: 6px 0 0 0 !default; 205 | $numberkeyboard-header-color: #646566 !default; 206 | $numberkeyboard-header-font-size: 16px !default; 207 | $numberkeyboard-header-close-padding: 0 16px !default; 208 | $numberkeyboard-header-close-color: #576b95 !default; 209 | $numberkeyboard-header-close-font-size: 14px !default; 210 | $numberkeyboard-header-close-background-color: transparent !default; 211 | $numberkeyboard-key-background-color: #fff !default; 212 | $numberkeyboard-key-active-background-color: #ebedf0 !default; 213 | $numberkeyboard-key-height: 48px !default; 214 | $numberkeyboard-key-line-height: 1.5 !default; 215 | $numberkeyboard-key-border-radius: 8px !default; 216 | $numberkeyboard-key-font-size: 28px !default; 217 | $numberkeyboard-key-font-size-color: #333 !default; 218 | $numberkeyboard-key-finish-font-size: 16px !default; 219 | $numberkeyboard-key-finish-font-size-color: #fff !default; 220 | $numberkeyboard-key-finish-background-color: #1989fa !default; 221 | $numberkeyboard-key-activeFinsh-background-color: #0570db !default; 222 | 223 | // countdown 224 | $countdown-display: flex !default; 225 | $countdown-color: inherit !default; 226 | $countdown-font-size: initial !default; 227 | 228 | // large price 229 | $price-symbol-big-size: 18px !default; 230 | $price-big-size: 24px !default; 231 | $price-decimal-big-size: 18px !default; 232 | 233 | // normal price 234 | $price-symbol-medium-size: 14px !default; 235 | $price-medium-size: 16px !default; 236 | $price-decimal-medium-size: 14px !default; 237 | 238 | // small price 239 | $price-symbol-small-size: 10px !default; 240 | $price-small-size: 12px !default; 241 | $price-decimal-small-size: 10px !default; 242 | 243 | // avatar 244 | $avatar-square: 5px !default; 245 | $avatar-large-width: 60px !default; 246 | $avatar-large-height: 60px !default; 247 | $avatar-small-width: 32px !default; 248 | $avatar-small-height: 32px !default; 249 | $avatar-normal-width: 40px !default; 250 | $avatar-normal-height: 40px !default; 251 | 252 | // switch 253 | $switch-close-bg-color: #ebebeb !default; 254 | $switch-close--cline-bg-color: #f0f0f0 !default; 255 | $switch-width: 36px !default; 256 | $switch-height: 21px !default; 257 | $switch-line-height: 21px !default; 258 | $switch-border-radius: 21px !default; 259 | $switch-inside-width: 13px !default; 260 | $switch-inside-height: 13px !default; 261 | $switch-inside-open-transform: translateX(146%) !default; 262 | $switch-inside-close-transform: translateX(30%) !default; 263 | 264 | // toast 265 | $toast-title-font-size: 16px !default; 266 | $toast-text-font-size: 14px !default; 267 | $toast-font-color: $white !default; 268 | $toast-inner-padding: 24px 30px !default; 269 | $toast-inner-bg-color: rgb(0 0 0 / 80%) !default; 270 | $toast-inner-border-radius: 12px !default; 271 | $toast-cover-bg-color: rgb(0 0 0 / 0%) !default; 272 | 273 | // backtop 274 | $backtop-border-color: #e0e0e0 !default; 275 | 276 | // calendar 277 | $calendar-primary-color: $primary-color !default; 278 | $calendar-choose-color: rgba($primary-color, .09) !default; 279 | $calendar-choose-font-color: $primary-color !default; 280 | $calendar-base-color: #333 !default; 281 | $calendar-disable-color: #d1d0d0 !default; 282 | $calendar-base-font: $font-size-3 !default; 283 | $calendar-title-font: $font-size-4 !default; 284 | $calendar-title-font-weight: 500 !default; 285 | $calendar-sub-title-font: $font-size-2 !default; 286 | $calendar-text-font: $font-size-1 !default; 287 | $calendar-day-font: 16px !default; 288 | $calendar-day-active-border-radius: 0 !default; 289 | $calendar-day-width: 14.28% !default; 290 | $calendar-day-height: 64px !default; 291 | $calendar-day-font-weight: 500 !default; 292 | $calendar-day67-font-color: $primary-color !default; 293 | 294 | // overlay 295 | $overlay-bg-color: rgb(0 0 0 / 70%) !default; 296 | 297 | // popup 298 | $popup-close-icon-margin: 16px !default; 299 | $popup-border-radius: 20px !default; 300 | 301 | // Notify 302 | $notify-text-color: $white !default; 303 | $notify-padding: 12px 0 !default; 304 | $notify-font-size: 14px !default; 305 | $notify-height: 44px !default; 306 | $notify-line-height: auto !default; 307 | $notify-base-background-color: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 308 | $notify-primary-background-color: linear-gradient( 309 | 315deg, 310 | rgb(73 143 242 / 100%) 0%, 311 | rgb(73 101 242 / 100%) 100% 312 | ) !default; 313 | $notify-success-background-color: linear-gradient( 314 | 135deg, 315 | rgb(38 191 38 / 100%) 0%, 316 | rgb(39 197 48 / 100%) 45%, 317 | rgb(40 207 63 / 100%) 83%, 318 | rgb(41 212 70 / 100%) 100% 319 | ) !default; 320 | $notify-danger-background-color: rgb(250 50 25 / 100%) !default; 321 | $notify-warning-background-color: linear-gradient(135deg, rgb(255 93 13 / 100%) 0%, rgb(255 154 13 / 100%) 100%) !default; 322 | 323 | // rate 324 | $rate-icon-color: $primary-color !default; 325 | $rate-icon-void-color: $disable-color !default; 326 | 327 | // tabbar 328 | $tabbar-active-color: $primary-color !default; 329 | $tabbar-unactive-color: $primary-color !default; 330 | $tabbar-border-top: 1px solid #eee !default; 331 | $tabbar-border-bottom: 1px solid #eee !default; 332 | $tabbar-box-shadow: none !default; 333 | $tabbar-item-text-font-size: $font-size-0 !default; 334 | $tabbar-item-text-line-height: initial !default; 335 | 336 | // infiniteloading 337 | $infiniteloading-bottom-color: #c8c8c8 !default; 338 | 339 | // range 340 | $range-tip-font-color: #333 !default; 341 | $range-bg-color: rgba($primary-color, .5) !default; 342 | $range-bg-color-tick: #fa958c !default; 343 | $range-bar-bg-color: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 344 | $range-bar-btn-bg-color: $white !default; 345 | $range-bar-btn-width: 24px !default; 346 | $range-bar-btn-height: 24px !default; 347 | $range-bar-btn-border: 1px solid $primary-color !default; 348 | 349 | // swiper 350 | $swiper-pagination-item-width: 8px !default; 351 | $swiper-pagination-item-height: 3px !default; 352 | $swiper-pagination-item-margin-right: 7px !default; 353 | $swiper-pagination-item-border-radius: 2px !default; 354 | 355 | // address 356 | $address-region-tab-line: linear-gradient(90deg, $primary-color 0%, rgba($primary-color, .15) 100%) !default; 357 | $address-icon-color: $primary-color !default; 358 | 359 | // steps 360 | $steps-base-icon-width: 25px !default; 361 | $steps-base-icon-height: 25px !default; 362 | $steps-base-icon-line-height: 25px !default; 363 | $steps-base-icon-font-size: 13px !default; 364 | $steps-base-title-font-size: 14px !default; 365 | $steps-base-line-color: #909ca4 !default; 366 | $steps-base-title-color: #909ca4 !default; 367 | $steps-base-title-margin-bottom: 10px !default; 368 | $steps-base-content-font-size: 14px !default; 369 | $steps-base-content-color: #666 !default; 370 | $steps-wait-icon-bg-color: #959fb1 !default; 371 | $steps-wait-icon-color: $white !default; 372 | $steps-wait-head-color: #909ca4 !default; 373 | $steps-wait-head-border-color: #909ca4 !default; 374 | $steps-wait-content-color: #909ca4 !default; 375 | $steps-finish-head-color: $primary-color !default; 376 | $steps-finish-head-border-color: $primary-color !default; 377 | $steps-finish-title-color: $primary-color !default; 378 | $steps-finish-line-background: $primary-color !default; 379 | $steps-finish-icon-text-color: $white !default; 380 | $steps-process-head-color: $white !default; 381 | $steps-process-head-border-color: $primary-color !default; 382 | $steps-process-title-color: $primary-color !default; 383 | $steps-process-icon-text-color: $primary-color !default; 384 | 385 | // dialog 386 | $dialog-width: 296px !default; 387 | 388 | // checkbox 389 | $checkbox-label-color: #1d1e1e !default; 390 | $checkbox-label-disable-color: #999 !default; 391 | $checkbox-icon-disable-color: #d6d6d6 !default; 392 | $checkbox-label-margin-left: 15px !default; 393 | $checkbox-label-font-size: 14px !default; 394 | $checkbox-icon-font-size: 18px !default; 395 | 396 | // radio 397 | $radio-label-font-color: #1d1e1e !default; 398 | $radio-label-font-active-color: $primary-color !default; 399 | $radio-label-disable-color: #999 !default; 400 | $radio-icon-disable-color: #d6d6d6 !default; 401 | $radio-label-button-border-color: $primary-color !default; 402 | $radio-label-button-background: rgba($primary-color, .05) !default; 403 | $radio-label-margin-left: 15px !default; 404 | $radio-button-border-radius: 15px !default; 405 | $radio-label-font-size: 14px !default; 406 | $radio-button-font-size: 12px !default; 407 | $radio-button-padding: 5px 18px !default; 408 | $radio-icon-font-size: 18px !default; 409 | 410 | // fixednav 411 | $fixednav-bg-color: $white !default; 412 | $fixednav-font-color: $black !default; 413 | $fixednav-index: 201 !default; 414 | $fixednav-btn-bg: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 415 | 416 | // NoticeBar 417 | $noticebar-background: rgb(251 248 220 / 100%) !default; 418 | $noticebar-color: #d9500b !default; 419 | $noticebar-font-size: 14px !default; 420 | $noticebar-height: 40px !default; 421 | $noticebar-line-height: 24px !default; 422 | $noticebar-left-icon-width: 16px !default; 423 | $noticebar-right-icon-width: 16px !default; 424 | $noticebar-box-padding: 0 16px !default; 425 | $noticebar-wrapable-padding: 16px !default; 426 | $noticebar-lefticon-margin: 0 10px !default; 427 | $noticebar-righticon-margin: 0 10px !default; 428 | 429 | // TimeSelect 430 | $timeselect-title-font-size: $font-size-2 !default; 431 | $timeselect-title-color: $title-color !default; 432 | $timeselect-title-width: 100% !default; 433 | $timeselect-title-height: 50px !default; 434 | $timeselect-title-line-height: 50px !default; 435 | $timeselect-pannel-bg-color: #f6f7f9 !default; 436 | 437 | // TimePannel 438 | $timeselect-timepannel-text-color: $title-color2 !default; 439 | $timeselect-timepannel-font-size: $font-size-2 !default; 440 | $timeselect-timepannel-cur-bg-color: $white !default; 441 | $timeselect-timepannel-cur-text-color: #333 !default; 442 | $timeselect-timepannel-width: 140px !default; 443 | $timeselect-timepannel-height: 40px !default; 444 | $timeselect-timepannel-padding: 15px !default; 445 | 446 | // TimeDetail 447 | $timeselect-timedetail-padding: 0 5px 50px 13px !default; 448 | $timeselect-timedetail-item-width: 100px !default; 449 | $timeselect-timedetail-item-height: 50px !default; 450 | $timeselect-timedetail-item-line-height: 50px !default; 451 | $timeselect-timedetail-item-bg-color: #f6f7f9 !default; 452 | $timeselect-timedetail-item-border-radius: 5px !default; 453 | $timeselect-timedetail-item-text-color: #333 !default; 454 | $timeselect-timedetail-item-text-font-size: $font-size-2 !default; 455 | $timeselect-timedetail-item-cur-bg-color: rgba($primary-color, .15) !default; 456 | $timeselect-timedetail-item-cur-border: $primary-color !default; 457 | $timeselect-timedetail-item-cur-text-color: $primary-color !default; 458 | $timeselect-timedetail-time-text-color: #999 !default; 459 | $timeselect-timedetail-time-font-size: $font-size-1 !default; 460 | 461 | // tag 462 | $tag-font-size: 12px !default; 463 | $tag-default-border-radius: 4px !default; 464 | $tag-round-border-radius: 8px !default; 465 | $tag-default-background-color: #000 !default; 466 | $tag-primary-background-color: #3460fa !default; 467 | $tag-success-background-color: #4fc08d !default; 468 | $tag-danger-background-color: linear-gradient( 469 | 135deg, 470 | rgb(242 20 12 / 100%) 0%, 471 | rgb(232 34 14 / 100%) 69.83950099728881%, 472 | rgb(242 77 12 / 100%) 100% 473 | ) !default; 474 | $tag-warning-background-color: #f3812e !default; 475 | $tag-default-color: #fff !default; 476 | $tag-border-width: 1px !default; 477 | $tag-plain-background-color: #fff !default; 478 | 479 | // badge 480 | $badge-background-color: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 481 | $badge-color: #fff !default; 482 | $badge-font-size: $font-size-1 !default; 483 | $badge-border-radius: 14px !default; 484 | $badge-padding: 0 5px !default; 485 | $badge-content-transform: translateY(-50%) translateX(100%) !default; 486 | $badge-z-index: 1 !default; 487 | $badge-dot-width: 7px !default; 488 | $badge-dot-height: 7px !default; 489 | $badge-dot-border-radius: 7px !default; 490 | $badge-dot-padding: 0 !default; 491 | 492 | // popover 493 | $popover-white-background-color: rgb(255 255 255 / 100%) !default; 494 | $popover-dark-background-color: rgb(75 76 77 / 100%) !default; 495 | $popover-border-bottom-color: rgb(229 229 229 / 100%) !default; 496 | $popover-primary-text-color: rgb(51 51 51 / 100%) !default; 497 | $popover-disable-color: rgb(154 155 157 / 100%) !default; 498 | 499 | // progress 500 | $progress-inner-background-color: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 501 | $progress-insidetext-background: $progress-inner-background-color !default; 502 | $progress-outer-background-color: #f3f3f3 !default; 503 | $progress-outer-border-radius: 12px !default; 504 | $progress-insidetext-border-radius: 5px !default; 505 | $progress-insidetext-padding: 3px 5px 3px 6px !default; 506 | $progress-insidetext-top: -42% !default; 507 | $progress-small-height: 5px !default; 508 | $progress-small-text-font-size: 7px !default; 509 | $progress-small-text-line-height: 10px !default; 510 | $progress-small-text-padding: 2px 4px !default; 511 | $progress-small-text-top: -100% !default; 512 | $progress-base-height: 10px !default; 513 | $progress-base-text-font-size: 9px !default; 514 | $progress-base-text-line-height: 13px !default; 515 | $progress-base-text-padding: $progress-insidetext-padding !default; 516 | $progress-base-text-top: $progress-insidetext-top !default; 517 | $progress-large-height: 15px !default; 518 | $progress-large-text-font-size: 13px !default; 519 | $progress-large-text-line-height: 18px !default; 520 | $progress-large-text-padding: $progress-insidetext-padding !default; 521 | $progress-large-text-top: $progress-insidetext-top !default; 522 | 523 | // pagination 524 | $pagination-color: $primary-color !default; 525 | $pagination-font-size: $font-size-2 !default; 526 | $pagination-item-border-color: #e4e7eb !default; 527 | $pagination-active-background-color: linear-gradient(135deg, $primary-color 0%, $primary-color-end 100%) !default; 528 | $pagination-disable-color: rgb(116 116 116 / 31%) !default; 529 | $pagination-disable-background-color: #f7f8fa !default; 530 | $pagination-item-border-width: 1px !default; 531 | $pagination-item-border-radius: 2px !default; 532 | $pagination-prev-next-padding: 0 11px !default; 533 | 534 | // tabs 535 | $tabs-tab-smile-color: $primary-color !default; 536 | $tabs-titles-border-radius: 0 !default; 537 | $tabs-titles-item-large-font-size: $font-size-3 !default; 538 | $tabs-titles-item-font-size: $font-size-2 !default; 539 | $tabs-titles-item-small-font-size: $font-size-1 !default; 540 | $tabs-titles-item-color: $title-color !default; 541 | $tabs-titles-item-active-color: $title-color !default; 542 | $tabs-titles-background-color: $help-color !default; 543 | $tabs-horizontal-tab-line-color: linear-gradient(90deg, $primary-color 0%, rgba($primary-color, .15) 100%) !default; 544 | $tabs-horizontal-titles-height: 46px !default; 545 | $tabs-horizontal-titles-item-min-width: 50px !default; 546 | $tabs-horizontal-titles-item-active-line-width: 40px !default; 547 | $tabs-vertical-tab-line-color: linear-gradient(180deg, $primary-color 0%, rgba($primary-color, .15) 100%) !default; 548 | $tabs-vertical-titles-item-height: 40px !default; 549 | $tabs-vertical-titles-item-active-line-height: 14px !default; 550 | $tabs-vertical-titles-width: 100px !default; 551 | 552 | // indicator 553 | $indicator-color: $primary-color !default; 554 | $indicator-dot-color: $disable-color !default; 555 | $indicator-white: $white !default; 556 | $indicator-size: 18px !default; 557 | $indicator-dot-size: calc($indicator-size / 3) !default; 558 | $indicator-border-size: $indicator-size + 2 !default; 559 | $indicator-number-font-size: 10px !default; 560 | 561 | // menu 562 | $menu-bar-line-height: 48px !default; 563 | $menu-item-font-size: $font-size-2 !default; 564 | $menu-item-text-color: $title-color !default; 565 | $menu-item-active-text-color: $primary-color !default; 566 | $menu-bar-border-bottom-color: #eaf0fb !default; 567 | $menu-bar-opened-z-index: 2001 !default; 568 | $menu-item-disabled-color: #969799 !default; 569 | $menu-title-text-padding-left: 8px !default; 570 | $menu-title-text-padding-right: 8px !default; 571 | $menu-item-content-padding: 12px 24px !default; 572 | $menu-item-content-max-height: 214px !default; 573 | $menu-item-option-padding-top: 12px !default; 574 | $menu-item-option-padding-bottom: 12px !default; 575 | $menu-item-option-i-margin-right: 6px !default; 576 | $menu-bar-box-shadow: 0 2px 12px rgb(89 89 89 / 12%) !default; 577 | 578 | // collapse 579 | $collapse-item-padding: 13px 36px 13px 26px !default; 580 | $collapse-item-color: #666 !default; 581 | $collapse-item-disabled-color: #c8c9cc !default; 582 | $collapse-item-icon-color: #666 !default; 583 | $collapse-item-font-size: $font-size-2 !default; 584 | $collapse-item-line-height: 24px !default; 585 | $collapse-item-sub-title-color: #666 !default; 586 | $collapse-wrapper-content-padding: 12px 26px !default; 587 | $collapse-wrapper-empty-content-padding: 0 26px !default; 588 | $collapse-wrapper-content-color: #666 !default; 589 | $collapse-wrapper-content-font-size: $font-size-2 !default; 590 | $collapse-wrapper-content-line-height: 1.5 !default; 591 | $collapse-wrapper-content-background-color: $white !default; 592 | 593 | // searchbar 594 | $searchbar-background: $white !default; 595 | $searchbar-right-out-color: $black !default; 596 | $searchbar-padding: 9px 16px !default; 597 | $searchbar-width: 100% !default; 598 | $searchbar-input-background: #f7f7f7 !default; 599 | $searchbar-input-padding: 0 0 0 13px !default; 600 | $searchbar-input-height: 32px !default; 601 | $searchbar-input-width: 100% !default; 602 | $searchbar-input-border-radius: 16px !default; 603 | $searchbar-input-box-shadow: 0 0 8px 0 rgb(0 0 0 / 4%) !default; 604 | 605 | // empty 606 | $empty-padding: 32px 0 !default; 607 | $empty-image-size: 170px !default; 608 | $empty-description-margin-top: 4px !default; 609 | $empty-description-color: #666 !default; 610 | $empty-description-font-size: 14px !default; 611 | $empty-description-line-height: 20px !default; 612 | $empty-description-padding: 0 40px !default; 613 | 614 | // cascader 615 | $cascader-font-size: $font-size-2 !default; 616 | $cascader-line-height: 22px !default; 617 | $cascader-tabs-item-padding: 0 10px !default; 618 | $cascader-bar-padding: 24px 20px 17px !default; 619 | $cascader-bar-font-size: $font-size-4 !default; 620 | $cascader-bar-line-height: 20px !default; 621 | $cascader-bar-color: $title-color !default; 622 | $cascader-item-padding: 10px 20px !default; 623 | $cascader-item-color: $title-color !default; 624 | $cascader-item-font-size: $font-size-2 !default; 625 | $cascader-item-active-color: $primary-color !default; 626 | 627 | // form 628 | $form-item-error-line-color: $primary-color !default; 629 | $form-item-required-color: $primary-color !default; 630 | $form-item-error-message-color: $primary-color !default; 631 | $form-item-label-font-size: 14px !default; 632 | $form-item-label-width: 90px !default; 633 | $form-item-label-margin-right: 10px !default; 634 | $form-item-label-text-align: left !default; 635 | $form-item-required-margin-right: 4px !default; 636 | $form-item-body-font-size: 14px !default; 637 | $form-item-body-slots-text-align: left !default; 638 | $form-item-body-input-text-align: left !default; 639 | $form-item-tip-font-size: 10px !default; 640 | $form-item-tip-text-align: left !default; 641 | 642 | // sku 643 | $sku-item-border: 1px solid $primary-color !default; 644 | $sku-item-disable-line: line-through !default; 645 | $sku-opetate-bg-default: linear-gradient(90deg, $primary-color, $primary-color-end 100%) !default; 646 | $sku-item-active-bg: rgba($primary-color, .15) !default; 647 | $sku-opetate-bg-buy: linear-gradient( 648 | 135deg, 649 | rgb(255 186 13 / 100%) 0%, 650 | rgb(255 195 13 / 100%) 69%, 651 | rgb(255 207 13 / 100%) 100% 652 | ) !default; 653 | 654 | // card 655 | $card-font-size-0: $font-size-0 !default; 656 | $card-font-size-1: $font-size-1 !default; 657 | $card-font-size-2: $font-size-2 !default; 658 | $card-font-size-3: $font-size-3 !default; 659 | 660 | // grid 661 | $grid-border-color: #f5f6f7 !default; 662 | $grid-item-content-padding: 16px 8px !default; 663 | $grid-item-content-bg-color: $white !default; 664 | $grid-item-text-margin: 8px !default; 665 | $grid-item-text-color: $title-color2 !default; 666 | $grid-item-text-font-size: $font-size-1 !default; 667 | 668 | // table 669 | $table-border-color: #ececec !default; 670 | $table-cols-padding: 10px !default; 671 | $table-tr-even-bg-color: #f3f3f3 !default; 672 | $table-tr-odd-bg-color: $white !default; 673 | 674 | // navbar 675 | $navbar-height: 44px !default; 676 | $navbar-margin-bottom: 20px !default; 677 | $navbar-padding: 13px 16px !default; 678 | $navbar-background: $white !default; 679 | $navbar-box-shadow: 0 1px 7px 0 rgb(237 238 241 / 100%) !default; 680 | $navbar-color: $title-color2 !default; 681 | $navbar-title-base-font: $font-size-2 !default; 682 | $navbar-title-font: $font-size-2 !default; 683 | $navbar-title-font-weight: 0 !default; 684 | $navbar-title-font-color: $navbar-color !default; 685 | $navbar-title-width: 100px !default; 686 | $navbar-title-icon-margin: 0 0 0 13px !default; 687 | 688 | // sidenavbar 689 | $sidenavbar-content-bg-color: $white !default; 690 | 691 | // subsidenavbar 692 | $sidenavbar-sub-title-border-color: #f6f6f6 !default; 693 | $sidenavbar-sub-title-bg-color: #f6f6f6 !default; 694 | $sidenavbar-sub-title-font-size: $font-size-large !default; 695 | $sidenavbar-sub-title-radius: 0 !default; 696 | $sidenavbar-sub-title-border: 0 !default; 697 | $sidenavbar-sub-title-width: 100% !default; 698 | $sidenavbar-sub-title-height: 40px !default; 699 | $sidenavbar-sub-title-text-line-height: 40px !default; 700 | $sidenavbar-sub-title-text-color: $title-color !default; 701 | 702 | // sidenavbaritem 703 | $sidenavbar-item-title-color: #333 !default; 704 | $sidenavbar-item-title-bg-color: $white !default; 705 | $sidenavbar-item-height: 40px !default; 706 | $sidenavbar-item-line-height: 40px !default; 707 | $sidenavbar-item-font-size: 16px !default; 708 | 709 | // elevator 710 | $elevator-list-item-highcolor: $primary-color !default; 711 | $elevator-list-item-font-size: 12px !default; 712 | $elevator-list-item-font-color: #333 !default; 713 | $elevator-list-item-name-padding: 0 20px !default; 714 | $elevator-list-item-name-height: 30px !default; 715 | $elevator-list-item-name-line-height: 30px !default; 716 | $elevator-list-item-code-font-size: 14px !default; 717 | $elevator-list-item-code-font-color: #1a1a1a !default; 718 | $elevator-list-item-code-font-weight: 500 !default; 719 | $elevator-list-item-code-padding: 0 20px !default; 720 | $elevator-list-item-code-height: 35px !default; 721 | $elevator-list-item-code-line-height: 35px !default; 722 | $elevator-list-item-code-after-width: 100% !default; 723 | $elevator-list-item-code-after-height: 1px !default; 724 | $elevator-list-item-code-after-bg-color: #f5f5f5 !default; 725 | $elevator-list-item-code-current-box-shadow: 0 3px 3px 1px rgb(240 240 240 / 100%) !default; 726 | $elevator-list-item-code-current-bg-color: #fff !default; 727 | $elevator-list-item-code-current-border-radius: 50% !default; 728 | $elevator-list-item-code-current-width: 45px !default; 729 | $elevator-list-item-code-current-height: 45px !default; 730 | $elevator-list-item-code-current-line-height: 45px !default; 731 | $elevator-list-item-code-current-position: absolute !default; 732 | $elevator-list-item-code-current-right: 60px !default; 733 | $elevator-list-item-code-current-top: 50% !default; 734 | $elevator-list-item-code-current-transform: translateY(-50%) !default; 735 | $elevator-list-item-code-current-text-align: center !default; 736 | $elevator-list-item-bars-position: absolute !default; 737 | $elevator-list-item-bars-right: 8px !default; 738 | $elevator-list-item-bars-top: 50% !default; 739 | $elevator-list-item-bars-transform: translateY(-50%) !default; 740 | $elevator-list-item-bars-padding: 15px 0 !default; 741 | $elevator-list-item-bars-background-color: #eeeff2 !default; 742 | $elevator-list-item-bars-border-radius: 6px !default; 743 | $elevator-list-item-bars-text-align: center !default; 744 | $elevator-list-item-bars-z-index: 10 !default; 745 | $elevator-list-item-bars-inner-item-padding: 3px !default; 746 | $elevator-list-item-bars-inner-item-font-size: 10px !default; 747 | 748 | // list 749 | $list-item-margin: 0 0 10px 0; 750 | 751 | @import "@nutui/nutui-taro/dist/styles/mixins/index"; 752 | @import "@nutui/nutui-taro/dist/styles/animation/index"; 753 | --------------------------------------------------------------------------------