Browse Source

初始化项目

luxf 5 months ago
parent
commit
145069f222
100 changed files with 3508 additions and 0 deletions
  1. 17 0
      App.vue
  2. 5 0
      api/user.js
  3. 44 0
      components/layout/nav-bar.vue
  4. 20 0
      index.html
  5. 89 0
      locales/i18n.js
  6. 3 0
      locales/lang/module/en-us/common.js
  7. 3 0
      locales/lang/module/zh-cn/common.js
  8. 30 0
      main.js
  9. 72 0
      manifest.json
  10. 125 0
      package-lock.json
  11. 58 0
      package.json
  12. 25 0
      pages.json
  13. 75 0
      pages/index/index.vue
  14. BIN
      static/logo.png
  15. 10 0
      store/getters.js
  16. 19 0
      store/index.js
  17. 52 0
      store/modules/app.js
  18. 14 0
      store/modules/index.js
  19. 127 0
      store/modules/user.js
  20. 13 0
      uni.promisify.adaptor.js
  21. 14 0
      utils/auth.js
  22. 6 0
      utils/event-bus.js
  23. 7 0
      utils/index.js
  24. 168 0
      utils/request.js
  25. 1 0
      wxcomponents/vant/action-sheet/index.d.ts
  26. 75 0
      wxcomponents/vant/action-sheet/index.js
  27. 8 0
      wxcomponents/vant/action-sheet/index.json
  28. 70 0
      wxcomponents/vant/action-sheet/index.wxml
  29. 1 0
      wxcomponents/vant/action-sheet/index.wxss
  30. 1 0
      wxcomponents/vant/area/index.d.ts
  31. 220 0
      wxcomponents/vant/area/index.js
  32. 6 0
      wxcomponents/vant/area/index.json
  33. 20 0
      wxcomponents/vant/area/index.wxml
  34. 8 0
      wxcomponents/vant/area/index.wxs
  35. 1 0
      wxcomponents/vant/area/index.wxss
  36. 1 0
      wxcomponents/vant/button/index.d.ts
  37. 64 0
      wxcomponents/vant/button/index.js
  38. 7 0
      wxcomponents/vant/button/index.json
  39. 56 0
      wxcomponents/vant/button/index.wxml
  40. 39 0
      wxcomponents/vant/button/index.wxs
  41. 1 0
      wxcomponents/vant/button/index.wxss
  42. 70 0
      wxcomponents/vant/calendar/calendar.wxml
  43. 1 0
      wxcomponents/vant/calendar/components/header/index.d.ts
  44. 37 0
      wxcomponents/vant/calendar/components/header/index.js
  45. 3 0
      wxcomponents/vant/calendar/components/header/index.json
  46. 16 0
      wxcomponents/vant/calendar/components/header/index.wxml
  47. 1 0
      wxcomponents/vant/calendar/components/header/index.wxss
  48. 6 0
      wxcomponents/vant/calendar/components/month/index.d.ts
  49. 154 0
      wxcomponents/vant/calendar/components/month/index.js
  50. 3 0
      wxcomponents/vant/calendar/components/month/index.json
  51. 39 0
      wxcomponents/vant/calendar/components/month/index.wxml
  52. 71 0
      wxcomponents/vant/calendar/components/month/index.wxs
  53. 1 0
      wxcomponents/vant/calendar/components/month/index.wxss
  54. 1 0
      wxcomponents/vant/calendar/index.d.ts
  55. 360 0
      wxcomponents/vant/calendar/index.js
  56. 10 0
      wxcomponents/vant/calendar/index.json
  57. 27 0
      wxcomponents/vant/calendar/index.wxml
  58. 37 0
      wxcomponents/vant/calendar/index.wxs
  59. 1 0
      wxcomponents/vant/calendar/index.wxss
  60. 12 0
      wxcomponents/vant/calendar/utils.d.ts
  61. 83 0
      wxcomponents/vant/calendar/utils.js
  62. 25 0
      wxcomponents/vant/calendar/utils.wxs
  63. 1 0
      wxcomponents/vant/card/index.d.ts
  64. 49 0
      wxcomponents/vant/card/index.js
  65. 6 0
      wxcomponents/vant/card/index.json
  66. 56 0
      wxcomponents/vant/card/index.wxml
  67. 1 0
      wxcomponents/vant/card/index.wxss
  68. 1 0
      wxcomponents/vant/cascader/index.d.ts
  69. 210 0
      wxcomponents/vant/cascader/index.js
  70. 8 0
      wxcomponents/vant/cascader/index.json
  71. 54 0
      wxcomponents/vant/cascader/index.wxml
  72. 24 0
      wxcomponents/vant/cascader/index.wxs
  73. 1 0
      wxcomponents/vant/cascader/index.wxss
  74. 1 0
      wxcomponents/vant/cell-group/index.d.ts
  75. 11 0
      wxcomponents/vant/cell-group/index.js
  76. 3 0
      wxcomponents/vant/cell-group/index.json
  77. 11 0
      wxcomponents/vant/cell-group/index.wxml
  78. 1 0
      wxcomponents/vant/cell-group/index.wxss
  79. 1 0
      wxcomponents/vant/cell/index.d.ts
  80. 38 0
      wxcomponents/vant/cell/index.js
  81. 6 0
      wxcomponents/vant/cell/index.json
  82. 47 0
      wxcomponents/vant/cell/index.wxml
  83. 17 0
      wxcomponents/vant/cell/index.wxs
  84. 1 0
      wxcomponents/vant/cell/index.wxss
  85. 1 0
      wxcomponents/vant/checkbox-group/index.d.ts
  86. 36 0
      wxcomponents/vant/checkbox-group/index.js
  87. 3 0
      wxcomponents/vant/checkbox-group/index.json
  88. 5 0
      wxcomponents/vant/checkbox-group/index.wxml
  89. 1 0
      wxcomponents/vant/checkbox-group/index.wxss
  90. 1 0
      wxcomponents/vant/checkbox/index.d.ts
  91. 77 0
      wxcomponents/vant/checkbox/index.js
  92. 6 0
      wxcomponents/vant/checkbox/index.json
  93. 31 0
      wxcomponents/vant/checkbox/index.wxml
  94. 20 0
      wxcomponents/vant/checkbox/index.wxs
  95. 1 0
      wxcomponents/vant/checkbox/index.wxss
  96. 4 0
      wxcomponents/vant/circle/canvas.d.ts
  97. 43 0
      wxcomponents/vant/circle/canvas.js
  98. 1 0
      wxcomponents/vant/circle/index.d.ts
  99. 197 0
      wxcomponents/vant/circle/index.js
  100. 0 0
      wxcomponents/vant/circle/index.json

+ 17 - 0
App.vue

@@ -0,0 +1,17 @@
+<script>
+	export default {
+		onLaunch: function() {
+			console.log('App Launch')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style>
+	/*每个页面公共css */
+</style>

+ 5 - 0
api/user.js

@@ -0,0 +1,5 @@
+import { get, post } from '@/utils/request'
+
+export const login = post('/api/user/login')
+export const getInfo = get('/api/user/info')
+export const logout = post('/api/user/logout')

+ 44 - 0
components/layout/nav-bar.vue

@@ -0,0 +1,44 @@
+<template>
+	<view class="nav-bar">
+		<view class="nav-bar-status" :style="{ height: statusBarHeight + 'px' }">
+		</view>
+		<view class="nav-bar-title" :style="{ height: titleBarHeight +'px' }">
+			<view></view>
+			<view>慕尼黑展览公司</view>
+			<view></view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {},
+		data() {
+			return {
+				titleBarHeight: 0,
+				statusBarHeight: 0,
+				isNotchScreen: false
+			}
+		},
+		created() {
+			const systemInfo = uni.getSystemInfoSync()
+			this.statusBarHeight = systemInfo.statusBarHeight
+			const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
+			this.titleBarHeight = (menuButtonInfo.top - this.statusBarHeight) * 2 + menuButtonInfo.height
+			console.log(systemInfo)
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.nav-bar {
+		background-color: $primary-color;
+	}
+	
+	.nav-bar-title{
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		color: #ffffff;
+	}
+</style>

+ 20 - 0
index.html

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 89 - 0
locales/i18n.js

@@ -0,0 +1,89 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import store from '@/store'
+
+export const getLocale = function() {
+	let locale = uni.getStorageSync('locale')
+	if (!locale) {
+		locale = uni.getLocale()
+		if (locale.indexOf('zh') === 0) {
+			return 'zh-cn'
+		} else {
+			return 'en-us'
+		}
+	}
+	return locale || 'zh-cn'
+}
+
+export function setLocale(locale) {
+  if (VueI18n.instance && VueI18n.instance.locale === locale) {
+    return
+  }
+  if (VueI18n.instance) {
+    VueI18n.instance.locale = locale
+  }
+  // if (locale !== 'zh-cn') {
+  //   const r = getAppLocale({ 'app_id': 'starify-ui', 'locale': locale.replace('-', '_') })
+  //   if (r.ret === 0) {
+  //     let messages = VueI18n.instance.getLocaleMessage(locale)
+  //     messages = Object.assign(messages, r.data)
+  //     VueI18n.instance.setLocaleMessage(locale, messages)
+  //   }
+  // }
+	const messages = VueI18n.instance.getLocaleMessage(locale)
+	VueI18n.instance.setLocaleMessage(locale, messages)
+  store.commit('SET_LOCALE', locale)
+	uni.setLocale(locale)
+  uni.setStorageSync('locale', locale)
+}
+
+function loadLocaleMessages() {
+  const locales = require.context('./lang', true, /[A-Za-z0-9-_,\s]+\.js$/i)
+  const messages = { 'zh-cn': {}}
+  locales.keys().forEach((key) => {
+      const matched = key.match(/\/module\/([A-Za-z0-9-_]+)\/([^.]*)\./i)
+      if (matched && matched.length > 1) {
+        const locale = matched[1]
+				const module = matched[2]
+				if (!messages[locale]) {
+					messages[locale] = {}
+				}
+        messages[locale][module] = locales(key)['default']
+      }
+  })
+  return messages
+}
+
+export function initLocale() {
+  if (!VueI18n.locateInit) {
+    VueI18n.locateInit = true
+    const locale = getLocale()
+    setLocale(locale)
+  }
+}
+
+if (!VueI18n.instance) {
+  Vue.use(VueI18n)
+	const i18n = new VueI18n({
+		locale: 'zh-cn',
+		fallbackLocale: 'zh-cn',
+		messages: loadLocaleMessages(),
+		silentFallbackWarn: true
+	})
+	VueI18n.instance = i18n
+	i18n.$t = Vue.prototype.$t = (key, text, values) => {
+		let t = i18n.t(key, values)
+		if (t) {
+			t = t.toString()
+		}
+		return t || text
+	}
+	i18n.$tc = Vue.prototype.$tc = (key, text, values) => {
+		let t = i18n.t(key, values)
+		if (t) {
+			t = t.toString()
+		}
+		return t || text
+	}
+}
+export default VueI18n.instance

+ 3 - 0
locales/lang/module/en-us/common.js

@@ -0,0 +1,3 @@
+exports.default = {
+  'close': 'close'
+}

+ 3 - 0
locales/lang/module/zh-cn/common.js

@@ -0,0 +1,3 @@
+exports.default = {
+  'close': '关闭'
+}

+ 30 - 0
main.js

@@ -0,0 +1,30 @@
+import App from './App'
+
+// #ifndef VUE3
+import Vue from 'vue'
+import Store from './store'
+import { initLocale } from '@/locales/i18n'
+
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+App.mpType = 'app'
+const app = new Vue({
+	App,
+	Store
+})
+app.$mount()
+
+initLocale()
+// #endif
+
+// #ifdef VUE3
+import {
+	createSSRApp
+} from 'vue'
+export function createApp() {
+	const app = createSSRApp(App)
+	return {
+		app
+	}
+}
+// #endif

+ 72 - 0
manifest.json

@@ -0,0 +1,72 @@
+{
+    "name" : "mp_exhibitor_miniprog",
+    "appid" : "",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {},
+            /* SDK配置 */
+            "sdkConfigs" : {}
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "2"
+}

+ 125 - 0
package-lock.json

@@ -0,0 +1,125 @@
+{
+	"name": "productronica",
+	"version": "1.0.0",
+	"lockfileVersion": 1,
+	"requires": true,
+	"dependencies": {
+		"@escook/request-miniprogram": {
+			"version": "0.2.1",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/@escook/request-miniprogram/-/request-miniprogram-0.2.1.tgz",
+			"integrity": "sha512-ueWV5YsaEm/ycQZuEjMiA88GFMhfBQSjy9GrP9omy4xAQajkGTbYIlnhzsDfWzRPmRC1fKmAiKMrCVcgS+SHcQ=="
+		},
+		"@intlify/core-base": {
+			"version": "10.0.4",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/@intlify/core-base/-/core-base-10.0.4.tgz",
+			"integrity": "sha512-GG428DkrrWCMhxRMRQZjuS7zmSUzarYcaHJqG9VB8dXAxw4iQDoKVQ7ChJRB6ZtsCsX3Jse1PEUlHrJiyQrOTg==",
+			"requires": {
+				"@intlify/message-compiler": "10.0.4",
+				"@intlify/shared": "10.0.4"
+			}
+		},
+		"@intlify/message-compiler": {
+			"version": "10.0.4",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/@intlify/message-compiler/-/message-compiler-10.0.4.tgz",
+			"integrity": "sha512-AFbhEo10DP095/45EauinQJ5hJ3rJUmuuqltGguvc3WsvezZN+g8qNHLGWKu60FHQVizMrQY7VJ+zVlBXlQQkQ==",
+			"requires": {
+				"@intlify/shared": "10.0.4",
+				"source-map-js": "^1.0.2"
+			}
+		},
+		"@intlify/shared": {
+			"version": "10.0.4",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/@intlify/shared/-/shared-10.0.4.tgz",
+			"integrity": "sha512-ukFn0I01HsSgr3VYhYcvkTCLS7rGa0gw4A4AMpcy/A9xx/zRJy7PS2BElMXLwUazVFMAr5zuiTk3MQeoeGXaJg=="
+		},
+		"@vue/devtools-api": {
+			"version": "6.6.4",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+			"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="
+		},
+		"asynckit": {
+			"version": "0.4.0",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/asynckit/-/asynckit-0.4.0.tgz",
+			"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+		},
+		"axios": {
+			"version": "1.7.7",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/axios/-/axios-1.7.7.tgz",
+			"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
+			"requires": {
+				"follow-redirects": "^1.15.6",
+				"form-data": "^4.0.0",
+				"proxy-from-env": "^1.1.0"
+			}
+		},
+		"combined-stream": {
+			"version": "1.0.8",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/combined-stream/-/combined-stream-1.0.8.tgz",
+			"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+			"requires": {
+				"delayed-stream": "~1.0.0"
+			}
+		},
+		"delayed-stream": {
+			"version": "1.0.0",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/delayed-stream/-/delayed-stream-1.0.0.tgz",
+			"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+		},
+		"follow-redirects": {
+			"version": "1.15.9",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/follow-redirects/-/follow-redirects-1.15.9.tgz",
+			"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
+		},
+		"form-data": {
+			"version": "4.0.0",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/form-data/-/form-data-4.0.0.tgz",
+			"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+			"requires": {
+				"asynckit": "^0.4.0",
+				"combined-stream": "^1.0.8",
+				"mime-types": "^2.1.12"
+			}
+		},
+		"mime-db": {
+			"version": "1.52.0",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/mime-db/-/mime-db-1.52.0.tgz",
+			"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+		},
+		"mime-types": {
+			"version": "2.1.35",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/mime-types/-/mime-types-2.1.35.tgz",
+			"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+			"requires": {
+				"mime-db": "1.52.0"
+			}
+		},
+		"proxy-from-env": {
+			"version": "1.1.0",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+			"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+		},
+		"source-map-js": {
+			"version": "1.2.1",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/source-map-js/-/source-map-js-1.2.1.tgz",
+			"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="
+		},
+		"vue-i18n": {
+			"version": "10.0.4",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/vue-i18n/-/vue-i18n-10.0.4.tgz",
+			"integrity": "sha512-1xkzVxqBLk2ZFOmeI+B5r1J7aD/WtNJ4j9k2mcFcQo5BnOmHBmD7z4/oZohh96AAaRZ4Q7mNQvxc9h+aT+Md3w==",
+			"requires": {
+				"@intlify/core-base": "10.0.4",
+				"@intlify/shared": "10.0.4",
+				"@vue/devtools-api": "^6.5.0"
+			}
+		},
+		"vuex": {
+			"version": "4.1.0",
+			"resolved": "https://mirrors.cloud.tencent.com/npm/vuex/-/vuex-4.1.0.tgz",
+			"integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
+			"requires": {
+				"@vue/devtools-api": "^6.0.0-beta.11"
+			}
+		}
+	}
+}

+ 58 - 0
package.json

@@ -0,0 +1,58 @@
+{
+	"name": "productronica",
+	"version": "1.0.0",
+	"description": "",
+	"main": "main.js",
+	"scripts": {
+		"test": "echo \"Error: no test specified\" && exit 1"
+	},
+	"keywords": [],
+	"author": "",
+	"license": "ISC",
+	"uni-app": {
+		"scripts": {
+			"dev": {
+				"title": "小程序-开发环境",
+				"env": {
+					"UNI_PLATFORM": "mp-weixin",
+					"NAME": "dev",
+					"TOKEN_KEY": "token",
+					"BASE_API": "https://starify-api-dev.matchexpo.cn"
+				},
+				"define": {
+					"MP-CJN": true
+				}
+			},
+			"text": {
+				"title": "小程序-测试环境",
+				"env": {
+					"UNI_PLATFORM": "mp-weixin",
+					"NAME": "test",
+					"TOKEN_KEY": "token",
+					"BASE_API": "https://starify-api-dev.matchexpo.cn"
+				},
+				"define": {
+					"MP-CJN": true
+				}
+			},
+			"prod": {
+				"title": "小程序-生产环境",
+				"env": {
+					"UNI_PLATFORM": "mp-weixin",
+					"NAME": "prod",
+					"TOKEN_KEY": "token",
+					"BASE_API": "https://starify-api-dev.matchexpo.cn"
+				},
+				"define": {
+					"MP-CJN": true
+				}
+			}
+		}
+	},
+	"dependencies": {
+		"@escook/request-miniprogram": "^0.2.1",
+		"axios": "^1.7.7",
+		"vue-i18n": "^10.0.4",
+		"vuex": "^4.1.0"
+	}
+}

+ 25 - 0
pages.json

@@ -0,0 +1,25 @@
+{
+	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "uni-app"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationStyle": "custom",
+		"navigationBarTextStyle": "white",
+		"navigationBarTitleText": "uni-app",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8",
+		"usingComponents": {
+			"van-icon": "/wxcomponents/vant/icon/index",
+			"van-button": "/wxcomponents/vant/button/index",
+			"van-picker": "/wxcomponents/vant/picker/index",
+			"van-tabbar": "/wxcomponents/vant/tabbar/index",
+			"van-tabbar-item": "/wxcomponents/vant/tabbar-item/index"
+		}
+	},
+	"uniIdRouter": {}
+}

+ 75 - 0
pages/index/index.vue

@@ -0,0 +1,75 @@
+<template>
+	<view class="content">
+		<nav-bar></nav-bar>
+		<view class="main">
+			{{ $t('common.close') }}
+		</view>
+		<van-button type="primary" @click="selectLanguage('zh-cn')">中文</van-button>
+		<van-button type="warning" @click="selectLanguage('en-us')">英文</van-button>
+		<van-tabbar :active="active" @change="onChange" :placeholder="true" active-color="#3856ff" inactive-color="#666666">
+			<van-tabbar-item v-for="(item, index) in list" :name="item.name" :key="index" :icon="item.icon">
+				{{ item.text }}
+			</van-tabbar-item>
+		</van-tabbar>
+	</view>
+</template>
+
+<script>
+	import { setLocale } from '../../locales/i18n'
+import {
+		login
+	} from '@/api/user'
+	import NavBar from '@/components/layout/nav-bar.vue'
+
+	export default {
+		components: {
+			NavBar
+		},
+		data() {
+			return {
+				active: 'home',
+				title: 'Hello',
+				list: [{
+						name: 'home',
+						icon: 'home-o',
+						text: '首页',
+						url: '/pages/index'
+					}, {
+						name: 'search',
+						icon: 'search',
+						text: '示例2',
+						url: '/pages/signup/signup'
+					},
+					{
+						name: 'friends',
+						icon: 'friends-o',
+						text: '示例3',
+						url: '/pages/test'
+					},
+					{
+						name: 'setting',
+						icon: 'setting-o',
+						text: '示例3',
+						url: '/pages/test'
+					}
+				]
+			}
+		},
+		created() {
+			const apiUrl = process.env.BASE_API
+			console.log('当前环境的API URL: ', apiUrl)
+			console.log(uni.getLocale())
+		},
+		methods: {
+			onChange(e) {
+				this.active = e.detail
+			},
+			selectLanguage(language) {
+				setLocale(language)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+</style>

BIN
static/logo.png


+ 10 - 0
store/getters.js

@@ -0,0 +1,10 @@
+const getters = {
+  token: state => state.user.token,
+  avatar: state => state.user.avatar || 'https://matchexpo.obs.cn-north-1.myhuaweicloud.com/common/2022/0225/62189585b166b.png',
+  user: state => state.user.user,
+  baseApi: state => process.env.BASE_API,
+  loading: state => state.app.loading,
+  ossDomain: state => state.app.ossDomain,
+  locale: state => state.app.locale
+}
+export default getters

+ 19 - 0
store/index.js

@@ -0,0 +1,19 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import getters from './getters'
+// import { createPersistedState, createSharedMutations } from 'vuex-electron'
+
+import modules from './modules'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  modules,
+  getters,
+  plugins: [
+    // createPersistedState(),
+    // createSharedMutations()
+  ],
+  strict: false
+  // strict: process.env.NODE_ENV !== 'production'
+})

+ 52 - 0
store/modules/app.js

@@ -0,0 +1,52 @@
+const app = {
+  state: {
+    loading: true,
+    device: 'desktop',
+    locale: 'zh-cn'
+  },
+  mutations: {
+    SET_LOCALE: (state, locale) => {
+      state.locale = locale
+    },
+    HIDE_LOADING: (state) => {
+      state.loading = false
+    },
+    GET_DATA: (state, name) => {
+      const item = JSON.parse(JSON.stringify(name))
+      if (state[item]) {
+        return JSON.parse(JSON.stringify(state[item]))
+      }
+    },
+    SET_DATA: (state, res) => {
+      const item = JSON.parse(JSON.stringify(res))
+      if (item.name && !state[item.name]) {
+        state[item.name] = ''
+      }
+      state[item.name] = JSON.parse(JSON.stringify(item.value))
+    },
+    DELETE_DATA: (state, res) => {
+      if (res.name && state[res.name]) {
+        delete state[res.name]
+      }
+    }
+  },
+  actions: {
+    showLoading: ({ commit }) => {
+      commit('SHOW_LOADING')
+    },
+    hideLoading: ({ commit }) => {
+      commit('HIDE_LOADING')
+    },
+    getData: ({ commit }, data) => {
+      commit('GET_DATA', data)
+    },
+    setData: ({ commit }, data) => {
+      commit('SET_DATA', data)
+    },
+    deleteData: ({ commit }, data) => {
+      commit('DELETE_DATA', data)
+    }
+  }
+}
+
+export default app

+ 14 - 0
store/modules/index.js

@@ -0,0 +1,14 @@
+/**
+ * The file enables `@/store/task.js` to import all vuex modules
+ * in a one-shot manner. There should not be any reason to edit this file.
+ */
+
+const files = require.context('.', false, /\.js$/)
+const modules = {}
+
+files.keys().forEach(key => {
+  if (key === './index.js') return
+  modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
+})
+
+export default modules

+ 127 - 0
store/modules/user.js

@@ -0,0 +1,127 @@
+import {
+	getInfo,
+	login,
+	loginPhone,
+	logout
+} from '@/api/user'
+import {
+	getToken,
+	removeToken,
+	setToken
+} from '@/utils/auth'
+import {
+	EventBus,
+	Events
+} from '@/utils/event-bus'
+import store from '@/store'
+
+const user = {
+	state: {
+		token: getToken(),
+		user: {}
+	},
+
+	mutations: {
+		SET_TOKEN: (state, token) => {
+			state.token = token
+			setToken(token)
+		},
+		SET_USER: (state, user) => {
+			state.user = user
+			// 触发菜单更新
+			EventBus.$emit(Events.USER_INFO_UPDATE, {
+				'user': user
+			})
+		},
+		SET_PHONE: (state, phone) => {
+			state.phone = phone
+		}
+	},
+
+	actions: {
+		// 短信登录
+		loginPhone({
+			commit,
+			dispatch
+		}, userInfo) {
+			return new Promise((resolve, reject) => {
+				loginPhone(userInfo).then(response => {
+					const data = response.data
+					commit('SET_TOKEN', data.token)
+					commit('SHOW_LOADING')
+					dispatch('getInfo')
+					resolve()
+				}).catch(error => {
+					reject(error)
+				})
+			})
+		},
+		// 登录
+		login({
+			commit,
+			dispatch,
+			state
+		}, userInfo) {
+			userInfo.username = userInfo.username.trim()
+			return new Promise((resolve, reject) => {
+				login(userInfo).then(response => {
+					const data = response.data
+					commit('SET_TOKEN', data.token)
+					commit('SHOW_LOADING')
+					dispatch('getInfo')
+					resolve()
+				}).catch(error => {
+					reject(error)
+				})
+			})
+		},
+
+		// 获取用户信息
+		getInfo({
+			commit,
+			state
+		}, params) {
+			return new Promise((resolve, reject) => {
+				params = params || {}
+				getInfo(params).then(response => {
+					const data = response.data
+					commit('SET_USER', data)
+					store.commit('HIDE_LOADING')
+					resolve(response)
+				}).catch(error => {
+					reject(error)
+					store.commit('HIDE_LOADING')
+				})
+			})
+		},
+		// 登出
+		logout({
+			commit,
+			dispatch,
+			state
+		}) {
+			return new Promise((resolve, reject) => {
+				logout().then(() => {
+					resolve()
+					dispatch('fedLogOut')
+				}).catch(error => {
+					reject(error)
+					dispatch('fedLogOut')
+				})
+			})
+		},
+
+		// 前端 登出
+		fedLogOut({
+			commit
+		}) {
+			return new Promise(resolve => {
+				removeToken()
+				commit('SET_TOKEN', '')
+				resolve()
+			})
+		}
+	}
+}
+
+export default user

+ 13 - 0
uni.promisify.adaptor.js

@@ -0,0 +1,13 @@
+uni.addInterceptor({
+  returnValue (res) {
+    if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+      return res;
+    }
+    return new Promise((resolve, reject) => {
+      res.then((res) => {
+        if (!res) return resolve(res) 
+        return res[0] ? reject(res[0]) : resolve(res[1])
+      });
+    });
+  },
+});

+ 14 - 0
utils/auth.js

@@ -0,0 +1,14 @@
+const TokenKey = process.env.TOKEN_KEY
+const globalTokenKey = TokenKey + '_7121'
+
+export function getToken() {
+  return uni.getStorageSync(TokenKey)
+}
+
+export function setToken(token) {
+  uni.setStorageSync(TokenKey, token)
+}
+
+export function removeToken() {
+  uni.removeStorageSync(TokenKey)
+}

+ 6 - 0
utils/event-bus.js

@@ -0,0 +1,6 @@
+import Vue from 'vue'
+
+export const EventBus = new Vue()
+export const Events = {
+	USER_INFO_UPDATE: "user_info_update"
+}

+ 7 - 0
utils/index.js

@@ -0,0 +1,7 @@
+/**
+ * 获取语种
+ */
+export const getLocale = function() {
+	const language = uni.getStorageSync('language')
+	return language || 'zh-cn'
+}

+ 168 - 0
utils/request.js

@@ -0,0 +1,168 @@
+import {
+	getToken
+} from '@/utils/auth'
+import {
+	getLocale
+} from '@/utils/index'
+import {
+	$http
+} from '@escook/request-miniprogram'
+import store from '@/store'
+
+class Request {
+	constructor(options = {}) {
+		// 请求的根路径
+		this.baseUrl = options.baseUrl || ''
+		// 请求的 url 地址
+		this.url = options.url || ''
+		// 请求方式
+		this.method = 'GET'
+		// 请求的参数对象
+		this.data = null
+		// header 请求头
+		this.header = options.header || {}
+		this.beforeRequest = null
+		this.afterRequest = null
+	}
+
+	get(url, data = {}) {
+		this.method = 'GET'
+		this.url = this.baseUrl + url
+		this.data = data
+		return this._()
+	}
+
+	post(url, data = {}) {
+		this.method = 'POST'
+		this.url = this.baseUrl + url
+		this.data = data
+		return this._()
+	}
+
+	put(url, data = {}) {
+		this.method = 'PUT'
+		this.url = this.baseUrl + url
+		this.data = data
+		return this._()
+	}
+
+	delete(url, data = {}) {
+		this.method = 'DELETE'
+		this.url = this.baseUrl + url
+		this.data = data
+		return this._()
+	}
+
+	_() {
+		// 清空 header 对象
+		this.header = {}
+		// 请求之前做一些事
+		this.beforeRequest && typeof this.beforeRequest === 'function' && this.beforeRequest(this)
+		// 发起请求
+		return new Promise((resolve, reject) => {
+			let weixin = wx
+			// 适配 uniapp
+			if (typeof uni !== 'undefined') {
+				weixin = uni
+			}
+			weixin.request({
+				url: this.url,
+				method: this.method,
+				data: this.data,
+				header: this.header,
+				success: res => {
+					if (this.afterRequest) {
+						this.afterRequest(res)
+					}
+				},
+				fail: err => {
+					reject(err)
+				},
+				complete: res => {
+					resolve(res.data)
+				},
+			})
+		})
+	}
+}
+
+const request = new Request()
+
+/**
+ * 创建请求
+ * @param baseURL
+ */
+
+request.baseUrl = process.env.BASE_API
+request.config = {
+	retry: 3,
+	retryDelay: 1000,
+	timeout: 50000,
+}
+request.beforeRequest = _request => {
+	_request.header['Language-Set'] = getLocale()
+	_request.header['X-Requested-With'] = 'XMLHttpRequest'
+	const apiToken = getToken()
+	if (apiToken) {
+		_request.header.Authorization = `Bearer ${apiToken}`
+	}
+}
+request.afterRequest = response => {
+	console.log(response)
+	if (response.header && response.header['authorization']) {
+		// 刷新token
+		const token = response.header['authorization'].replace('Bearer ', '')
+		if (token !== store.getters.token) {
+			store.commit('SET_TOKEN', token)
+		}
+	}
+	const res = response.data
+	if (res.ret !== 0) {
+		if (res.ret === 401) {
+			if (response.data.msg === 'Token has expired') {
+				response.data.msg = '登录超时'
+			}
+			store.dispatch('fedLogOut').then(() => {
+				uni.navigateTo({
+					url: '/login-new'
+				})
+			})
+		} else {
+			uni.showModal({
+				content: res.msg || 'Error',
+				success(r) {
+					if (r.confirm) {
+					} else if (r.cancel) {
+					}
+				}
+			})
+		}
+		return Promise.reject(new Error(res.msg || 'Error'))
+	} else {
+		return res
+	}
+}
+
+export default request
+
+/**
+ * GET请求
+ * @param {Object} url
+ * @param {Object} params
+ */
+export const get = (url) => (params) => {
+	return request.get(url, params)
+}
+
+/**
+ * POST请求
+ * @param {Object} url
+ * @param {Object} params
+ */
+export const post = (url) => (params) => {
+	return request.post(url, params)
+}
+
+export const link = (url) => {
+	return process.env.BASE_API + url
+}

+ 1 - 0
wxcomponents/vant/action-sheet/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 75 - 0
wxcomponents/vant/action-sheet/index.js

@@ -0,0 +1,75 @@
+import { VantComponent } from '../common/component';
+import { button } from '../mixins/button';
+VantComponent({
+    classes: ['list-class'],
+    mixins: [button],
+    props: {
+        show: Boolean,
+        title: String,
+        cancelText: String,
+        description: String,
+        round: {
+            type: Boolean,
+            value: true,
+        },
+        zIndex: {
+            type: Number,
+            value: 100,
+        },
+        actions: {
+            type: Array,
+            value: [],
+        },
+        overlay: {
+            type: Boolean,
+            value: true,
+        },
+        closeOnClickOverlay: {
+            type: Boolean,
+            value: true,
+        },
+        closeOnClickAction: {
+            type: Boolean,
+            value: true,
+        },
+        safeAreaInsetBottom: {
+            type: Boolean,
+            value: true,
+        },
+        rootPortal: {
+            type: Boolean,
+            value: false,
+        },
+    },
+    methods: {
+        onSelect(event) {
+            const { index } = event.currentTarget.dataset;
+            const { actions, closeOnClickAction, canIUseGetUserProfile } = this.data;
+            const item = actions[index];
+            if (item) {
+                this.$emit('select', item);
+                if (closeOnClickAction) {
+                    this.onClose();
+                }
+                if (item.openType === 'getUserInfo' && canIUseGetUserProfile) {
+                    wx.getUserProfile({
+                        desc: item.getUserProfileDesc || '  ',
+                        complete: (userProfile) => {
+                            this.$emit('getuserinfo', userProfile);
+                        },
+                    });
+                }
+            }
+        },
+        onCancel() {
+            this.$emit('cancel');
+        },
+        onClose() {
+            this.$emit('close');
+        },
+        onClickOverlay() {
+            this.$emit('click-overlay');
+            this.onClose();
+        },
+    },
+});

+ 8 - 0
wxcomponents/vant/action-sheet/index.json

@@ -0,0 +1,8 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-popup": "../popup/index",
+    "van-loading": "../loading/index"
+  }
+}

+ 70 - 0
wxcomponents/vant/action-sheet/index.wxml

@@ -0,0 +1,70 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<van-popup
+  show="{{ show }}"
+  position="bottom"
+  round="{{ round }}"
+  z-index="{{ zIndex }}"
+  overlay="{{ overlay }}"
+  custom-class="van-action-sheet custom-class"
+  safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
+  close-on-click-overlay="{{ closeOnClickOverlay }}"
+  root-portal="{{ rootPortal }}"
+  bind:close="onClickOverlay"
+>
+  <view wx:if="{{ title }}" class="van-action-sheet__header">
+    {{ title }}
+    <van-icon
+      name="cross"
+      custom-class="van-action-sheet__close"
+      bind:click="onClose"
+    />
+  </view>
+  <view wx:if="{{ description }}" class="van-action-sheet__description van-hairline--bottom">
+    {{ description }}
+  </view>
+  <view wx:if="{{ actions && actions.length }}" class="list-class">
+    <!-- button外包一层view,防止actions动态变化,导致渲染时button被打散 -->
+    <button
+      wx:for="{{ actions }}"
+      wx:key="index"
+      open-type="{{ item.disabled || item.loading || (canIUseGetUserProfile && item.openType === 'getUserInfo') ? '' : item.openType }}"
+      style="{{ item.color ? 'color: ' + item.color : '' }}"
+      class="{{ utils.bem('action-sheet__item', { disabled: item.disabled || item.loading }) }} {{ item.className || '' }}"
+      hover-class="van-action-sheet__item--hover"
+      data-index="{{ index }}"
+      bindtap="{{ item.disabled || item.loading ? '' : 'onSelect' }}"
+      bindgetuserinfo="onGetUserInfo"
+      bindcontact="onContact"
+      bindgetphonenumber="onGetPhoneNumber"
+      binderror="onError"
+      bindlaunchapp="onLaunchApp"
+      bindopensetting="onOpenSetting"
+      lang="{{ lang }}"
+      session-from="{{ sessionFrom }}"
+      send-message-title="{{ sendMessageTitle }}"
+      send-message-path="{{ sendMessagePath }}"
+      send-message-img="{{ sendMessageImg }}"
+      show-message-card="{{ showMessageCard }}"
+      app-parameter="{{ appParameter }}"
+    >
+      <block wx:if="{{ !item.loading }}">
+        {{ item.name }}
+        <view wx:if="{{ item.subname }}" class="van-action-sheet__subname" >{{ item.subname }}</view>
+      </block>
+      <van-loading wx:else custom-class="van-action-sheet__loading" size="22px" />
+    </button>
+  </view>
+  <slot />
+  <block wx:if="{{ cancelText }}">
+    <view class="van-action-sheet__gap" />
+    <view
+      class="van-action-sheet__cancel"
+      hover-class="van-action-sheet__cancel--hover"
+      hover-stay-time="70"
+      bind:tap="onCancel"
+    >
+      {{ cancelText }}
+    </view>
+  </block>
+</van-popup>

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/action-sheet/index.wxss


+ 1 - 0
wxcomponents/vant/area/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 220 - 0
wxcomponents/vant/area/index.js

@@ -0,0 +1,220 @@
+import { VantComponent } from '../common/component';
+import { pickerProps } from '../picker/shared';
+import { requestAnimationFrame } from '../common/utils';
+const EMPTY_CODE = '000000';
+VantComponent({
+    classes: ['active-class', 'toolbar-class', 'column-class'],
+    props: Object.assign(Object.assign({}, pickerProps), { showToolbar: {
+            type: Boolean,
+            value: true,
+        }, value: {
+            type: String,
+            observer(value) {
+                this.code = value;
+                this.setValues();
+            },
+        }, areaList: {
+            type: Object,
+            value: {},
+            observer: 'setValues',
+        }, columnsNum: {
+            type: null,
+            value: 3,
+        }, columnsPlaceholder: {
+            type: Array,
+            observer(val) {
+                this.setData({
+                    typeToColumnsPlaceholder: {
+                        province: val[0] || '',
+                        city: val[1] || '',
+                        county: val[2] || '',
+                    },
+                });
+            },
+        } }),
+    data: {
+        columns: [{ values: [] }, { values: [] }, { values: [] }],
+        typeToColumnsPlaceholder: {},
+    },
+    mounted() {
+        requestAnimationFrame(() => {
+            this.setValues();
+        });
+    },
+    methods: {
+        getPicker() {
+            if (this.picker == null) {
+                this.picker = this.selectComponent('.van-area__picker');
+            }
+            return this.picker;
+        },
+        onCancel(event) {
+            this.emit('cancel', event.detail);
+        },
+        onConfirm(event) {
+            const { index } = event.detail;
+            let { value } = event.detail;
+            value = this.parseValues(value);
+            this.emit('confirm', { value, index });
+        },
+        emit(type, detail) {
+            detail.values = detail.value;
+            delete detail.value;
+            this.$emit(type, detail);
+        },
+        parseValues(values) {
+            const { columnsPlaceholder } = this.data;
+            return values.map((value, index) => {
+                if (value &&
+                    (!value.code || value.name === columnsPlaceholder[index])) {
+                    return Object.assign(Object.assign({}, value), { code: '', name: '' });
+                }
+                return value;
+            });
+        },
+        onChange(event) {
+            var _a;
+            const { index, picker, value } = event.detail;
+            this.code = value[index].code;
+            (_a = this.setValues()) === null || _a === void 0 ? void 0 : _a.then(() => {
+                this.$emit('change', {
+                    picker,
+                    values: this.parseValues(picker.getValues()),
+                    index,
+                });
+            });
+        },
+        getConfig(type) {
+            const { areaList } = this.data;
+            return (areaList && areaList[`${type}_list`]) || {};
+        },
+        getList(type, code) {
+            if (type !== 'province' && !code) {
+                return [];
+            }
+            const { typeToColumnsPlaceholder } = this.data;
+            const list = this.getConfig(type);
+            let result = Object.keys(list).map((code) => ({
+                code,
+                name: list[code],
+            }));
+            if (code != null) {
+                // oversea code
+                if (code[0] === '9' && type === 'city') {
+                    code = '9';
+                }
+                result = result.filter((item) => item.code.indexOf(code) === 0);
+            }
+            if (typeToColumnsPlaceholder[type] && result.length) {
+                // set columns placeholder
+                const codeFill = type === 'province'
+                    ? ''
+                    : type === 'city'
+                        ? EMPTY_CODE.slice(2, 4)
+                        : EMPTY_CODE.slice(4, 6);
+                result.unshift({
+                    code: `${code}${codeFill}`,
+                    name: typeToColumnsPlaceholder[type],
+                });
+            }
+            return result;
+        },
+        getIndex(type, code) {
+            let compareNum = type === 'province' ? 2 : type === 'city' ? 4 : 6;
+            const list = this.getList(type, code.slice(0, compareNum - 2));
+            // oversea code
+            if (code[0] === '9' && type === 'province') {
+                compareNum = 1;
+            }
+            code = code.slice(0, compareNum);
+            for (let i = 0; i < list.length; i++) {
+                if (list[i].code.slice(0, compareNum) === code) {
+                    return i;
+                }
+            }
+            return 0;
+        },
+        setValues() {
+            const picker = this.getPicker();
+            if (!picker) {
+                return;
+            }
+            let code = this.code || this.getDefaultCode();
+            const provinceList = this.getList('province');
+            const cityList = this.getList('city', code.slice(0, 2));
+            const stack = [];
+            const indexes = [];
+            const { columnsNum } = this.data;
+            if (columnsNum >= 1) {
+                stack.push(picker.setColumnValues(0, provinceList, false));
+                indexes.push(this.getIndex('province', code));
+            }
+            if (columnsNum >= 2) {
+                stack.push(picker.setColumnValues(1, cityList, false));
+                indexes.push(this.getIndex('city', code));
+                if (cityList.length && code.slice(2, 4) === '00') {
+                    [{ code }] = cityList;
+                }
+            }
+            if (columnsNum === 3) {
+                stack.push(picker.setColumnValues(2, this.getList('county', code.slice(0, 4)), false));
+                indexes.push(this.getIndex('county', code));
+            }
+            return Promise.all(stack)
+                .catch(() => { })
+                .then(() => picker.setIndexes(indexes))
+                .catch(() => { });
+        },
+        getDefaultCode() {
+            const { columnsPlaceholder } = this.data;
+            if (columnsPlaceholder.length) {
+                return EMPTY_CODE;
+            }
+            const countyCodes = Object.keys(this.getConfig('county'));
+            if (countyCodes[0]) {
+                return countyCodes[0];
+            }
+            const cityCodes = Object.keys(this.getConfig('city'));
+            if (cityCodes[0]) {
+                return cityCodes[0];
+            }
+            return '';
+        },
+        getValues() {
+            const picker = this.getPicker();
+            if (!picker) {
+                return [];
+            }
+            return this.parseValues(picker.getValues().filter((value) => !!value));
+        },
+        getDetail() {
+            const values = this.getValues();
+            const area = {
+                code: '',
+                country: '',
+                province: '',
+                city: '',
+                county: '',
+            };
+            if (!values.length) {
+                return area;
+            }
+            const names = values.map((item) => item.name);
+            area.code = values[values.length - 1].code;
+            if (area.code[0] === '9') {
+                area.country = names[1] || '';
+                area.province = names[2] || '';
+            }
+            else {
+                area.province = names[0] || '';
+                area.city = names[1] || '';
+                area.county = names[2] || '';
+            }
+            return area;
+        },
+        reset(code) {
+            this.code = code || '';
+            return this.setValues();
+        },
+    },
+});

+ 6 - 0
wxcomponents/vant/area/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-picker": "../picker/index"
+  }
+}

+ 20 - 0
wxcomponents/vant/area/index.wxml

@@ -0,0 +1,20 @@
+<wxs src="./index.wxs" module="computed" />
+
+<van-picker
+  class="van-area__picker"
+  active-class="active-class"
+  toolbar-class="toolbar-class"
+  column-class="column-class"
+  show-toolbar="{{ showToolbar }}"
+  value-key="name"
+  title="{{ title }}"
+  loading="{{ loading }}"
+  columns="{{ computed.displayColumns(columns, columnsNum) }}"
+  item-height="{{ itemHeight }}"
+  visible-item-count="{{ visibleItemCount }}"
+  cancel-button-text="{{ cancelButtonText }}"
+  confirm-button-text="{{ confirmButtonText }}"
+  bind:change="onChange"
+  bind:confirm="onConfirm"
+  bind:cancel="onCancel"
+/>

+ 8 - 0
wxcomponents/vant/area/index.wxs

@@ -0,0 +1,8 @@
+/* eslint-disable */
+function displayColumns(columns, columnsNum) {
+  return columns.slice(0, +columnsNum);
+}
+
+module.exports = {
+  displayColumns: displayColumns,
+};

+ 1 - 0
wxcomponents/vant/area/index.wxss

@@ -0,0 +1 @@
+@import '../common/index.wxss';

+ 1 - 0
wxcomponents/vant/button/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 64 - 0
wxcomponents/vant/button/index.js

@@ -0,0 +1,64 @@
+import { VantComponent } from '../common/component';
+import { button } from '../mixins/button';
+import { canIUseFormFieldButton } from '../common/version';
+const mixins = [button];
+if (canIUseFormFieldButton()) {
+    mixins.push('wx://form-field-button');
+}
+VantComponent({
+    mixins,
+    classes: ['hover-class', 'loading-class'],
+    data: {
+        baseStyle: '',
+    },
+    props: {
+        formType: String,
+        icon: String,
+        classPrefix: {
+            type: String,
+            value: 'van-icon',
+        },
+        plain: Boolean,
+        block: Boolean,
+        round: Boolean,
+        square: Boolean,
+        loading: Boolean,
+        hairline: Boolean,
+        disabled: Boolean,
+        loadingText: String,
+        customStyle: String,
+        loadingType: {
+            type: String,
+            value: 'circular',
+        },
+        type: {
+            type: String,
+            value: 'default',
+        },
+        dataset: null,
+        size: {
+            type: String,
+            value: 'normal',
+        },
+        loadingSize: {
+            type: String,
+            value: '20px',
+        },
+        color: String,
+    },
+    methods: {
+        onClick(event) {
+            this.$emit('click', event);
+            const { canIUseGetUserProfile, openType, getUserProfileDesc, lang, } = this.data;
+            if (openType === 'getUserInfo' && canIUseGetUserProfile) {
+                wx.getUserProfile({
+                    desc: getUserProfileDesc || '  ',
+                    lang: lang || 'en',
+                    complete: (userProfile) => {
+                        this.$emit('getuserinfo', userProfile);
+                    },
+                });
+            }
+        },
+    },
+});

+ 7 - 0
wxcomponents/vant/button/index.json

@@ -0,0 +1,7 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-loading": "../loading/index"
+  }
+}

+ 56 - 0
wxcomponents/vant/button/index.wxml

@@ -0,0 +1,56 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+<wxs src="./index.wxs" module="computed" />
+
+<button
+  id="{{ id || buttonId }}"
+  data-detail="{{ dataset }}"
+  class="custom-class {{ utils.bem('button', [type, size, { block, round, plain, square, loading, disabled, hairline, unclickable: disabled || loading }]) }} {{ hairline ? 'van-hairline--surround' : '' }}"
+  hover-class="{{ disabled || loading ? '' : 'van-button--active hover-class'}}"
+  lang="{{ lang }}"
+  form-type="{{ formType }}"
+  style="{{ computed.rootStyle({ plain, color, customStyle }) }}"
+  open-type="{{ disabled || loading || (canIUseGetUserProfile && openType === 'getUserInfo') ? '' : openType }}"
+  business-id="{{ businessId }}"
+  session-from="{{ sessionFrom }}"
+  send-message-title="{{ sendMessageTitle }}"
+  send-message-path="{{ sendMessagePath }}"
+  send-message-img="{{ sendMessageImg }}"
+  show-message-card="{{ showMessageCard }}"
+  app-parameter="{{ appParameter }}"
+  aria-label="{{ ariaLabel }}"
+  bindtap="{{ disabled || loading ? '' : 'onClick' }}"
+  bindgetuserinfo="onGetUserInfo"
+  bindcontact="onContact"
+  bindgetphonenumber="onGetPhoneNumber"
+  bindgetrealtimephonenumber="onGetRealTimePhoneNumber"
+  bindagreeprivacyauthorization="onAgreePrivacyAuthorization"
+  binderror="onError"
+  bindlaunchapp="onLaunchApp"
+  bindopensetting="onOpenSetting"
+  bindchooseavatar="onChooseAvatar"
+>
+  <block wx:if="{{ loading }}">
+    <van-loading
+      custom-class="loading-class"
+      size="{{ loadingSize }}"
+      type="{{ loadingType }}"
+      color="{{ computed.loadingColor({ type, color, plain }) }}"
+    />
+    <view wx:if="{{ loadingText }}" class="van-button__loading-text">
+      {{ loadingText }}
+    </view>
+  </block>
+  <block wx:else>
+    <van-icon
+      wx:if="{{ icon }}"
+      size="1.2em"
+      name="{{ icon }}"
+      class-prefix="{{ classPrefix }}"
+      class="van-button__icon"
+      custom-style="line-height: inherit;"
+    />
+    <view class="van-button__text">
+      <slot />
+    </view>
+  </block>
+</button>

+ 39 - 0
wxcomponents/vant/button/index.wxs

@@ -0,0 +1,39 @@
+/* eslint-disable */
+var style = require('../wxs/style.wxs');
+
+function rootStyle(data) {
+  if (!data.color) {
+    return data.customStyle;
+  }
+
+  var properties = {
+    color: data.plain ? data.color : '#fff',
+    background: data.plain ? null : data.color,
+  };
+
+  // hide border when color is linear-gradient
+  if (data.color.indexOf('gradient') !== -1) {
+    properties.border = 0;
+  } else {
+    properties['border-color'] = data.color;
+  }
+
+  return style([properties, data.customStyle]);
+}
+
+function loadingColor(data) {
+  if (data.plain) {
+    return data.color ? data.color : '#c9c9c9';
+  }
+
+  if (data.type === 'default') {
+    return '#c9c9c9';
+  }
+
+  return '#fff';
+}
+
+module.exports = {
+  rootStyle: rootStyle,
+  loadingColor: loadingColor,
+};

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/button/index.wxss


+ 70 - 0
wxcomponents/vant/calendar/calendar.wxml

@@ -0,0 +1,70 @@
+<wxs src="./index.wxs" module="computed" />
+<wxs src="../wxs/utils.wxs" module="utils" />
+<view class="van-calendar">
+  <header
+    title="{{ title }}"
+    showTitle="{{ showTitle }}"
+    subtitle="{{ subtitle }}"
+    showSubtitle="{{ showSubtitle }}"
+    firstDayOfWeek="{{ firstDayOfWeek }}"
+    bind:click-subtitle="onClickSubtitle"
+  >
+    <slot name="title" slot="title"></slot>
+  </header>
+
+  <scroll-view
+    class="van-calendar__body"
+    scroll-y
+    scroll-into-view="{{ scrollIntoView }}"
+  >
+    <month
+      wx:for="{{ computed.getMonths(minDate, maxDate) }}"
+      wx:key="index"
+      id="month{{ index }}"
+      class="month"
+      data-date="{{ item }}"
+      date="{{ item }}"
+      type="{{ type }}"
+      color="{{ color }}"
+      minDate="{{ minDate }}"
+      maxDate="{{ maxDate }}"
+      showMark="{{ showMark }}"
+      formatter="{{ formatter }}"
+      rowHeight="{{ rowHeight }}"
+      currentDate="{{ currentDate }}"
+      showSubtitle="{{ showSubtitle }}"
+      allowSameDay="{{ allowSameDay }}"
+      showMonthTitle="{{ index !== 0 || !showSubtitle }}"
+      firstDayOfWeek="{{ firstDayOfWeek }}"
+      bind:click="onClickDay"
+    />
+  </scroll-view>
+
+  <view
+    class="{{ utils.bem('calendar__footer', { safeAreaInsetBottom }) }}"
+  >
+    <slot name="footer"></slot>
+  </view>
+
+  <view
+    class="{{ utils.bem('calendar__footer', { safeAreaInsetBottom }) }}"
+  >
+    <van-button
+      wx:if="{{ showConfirm }}"
+      round
+      block
+      type="danger"
+      color="{{ color }}"
+      custom-class="van-calendar__confirm"
+      disabled="{{ computed.getButtonDisabled(type, currentDate, minRange) }}"
+      nativeType="text"
+      bind:click="onConfirm"
+    >
+      {{
+        computed.getButtonDisabled(type, currentDate, minRange)
+          ? confirmDisabledText
+          : confirmText
+      }}
+    </van-button>
+  </view>
+</view>

+ 1 - 0
wxcomponents/vant/calendar/components/header/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 37 - 0
wxcomponents/vant/calendar/components/header/index.js

@@ -0,0 +1,37 @@
+import { VantComponent } from '../../../common/component';
+VantComponent({
+    props: {
+        title: {
+            type: String,
+            value: '日期选择',
+        },
+        subtitle: String,
+        showTitle: Boolean,
+        showSubtitle: Boolean,
+        firstDayOfWeek: {
+            type: Number,
+            observer: 'initWeekDay',
+        },
+    },
+    data: {
+        weekdays: [],
+    },
+    created() {
+        this.initWeekDay();
+    },
+    methods: {
+        initWeekDay() {
+            const defaultWeeks = ['日', '一', '二', '三', '四', '五', '六'];
+            const firstDayOfWeek = this.data.firstDayOfWeek || 0;
+            this.setData({
+                weekdays: [
+                    ...defaultWeeks.slice(firstDayOfWeek, 7),
+                    ...defaultWeeks.slice(0, firstDayOfWeek),
+                ],
+            });
+        },
+        onClickSubtitle(event) {
+            this.$emit('click-subtitle', event);
+        },
+    },
+});

+ 3 - 0
wxcomponents/vant/calendar/components/header/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 16 - 0
wxcomponents/vant/calendar/components/header/index.wxml

@@ -0,0 +1,16 @@
+<view class="van-calendar__header">
+  <block wx:if="{{ showTitle }}">
+    <view class="van-calendar__header-title"><slot name="title"></slot></view>
+    <view class="van-calendar__header-title">{{ title }}</view>
+  </block>
+
+  <view wx:if="{{ showSubtitle }}" class="van-calendar__header-subtitle" bind:tap="onClickSubtitle">
+    {{ subtitle }}
+  </view>
+
+  <view class="van-calendar__weekdays">
+    <view wx:for="{{ weekdays }}" wx:key="index" class="van-calendar__weekday">
+      {{ item }}
+    </view>
+  </view>
+</view>

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/calendar/components/header/index.wxss


+ 6 - 0
wxcomponents/vant/calendar/components/month/index.d.ts

@@ -0,0 +1,6 @@
+export interface Day {
+    date: Date;
+    type: string;
+    text: number;
+    bottomInfo?: string;
+}

+ 154 - 0
wxcomponents/vant/calendar/components/month/index.js

@@ -0,0 +1,154 @@
+import { VantComponent } from '../../../common/component';
+import { getMonthEndDay, compareDay, getPrevDay, getNextDay, } from '../../utils';
+VantComponent({
+    props: {
+        date: {
+            type: null,
+            observer: 'setDays',
+        },
+        type: {
+            type: String,
+            observer: 'setDays',
+        },
+        color: String,
+        minDate: {
+            type: null,
+            observer: 'setDays',
+        },
+        maxDate: {
+            type: null,
+            observer: 'setDays',
+        },
+        showMark: Boolean,
+        rowHeight: null,
+        formatter: {
+            type: null,
+            observer: 'setDays',
+        },
+        currentDate: {
+            type: null,
+            observer: 'setDays',
+        },
+        firstDayOfWeek: {
+            type: Number,
+            observer: 'setDays',
+        },
+        allowSameDay: Boolean,
+        showSubtitle: Boolean,
+        showMonthTitle: Boolean,
+    },
+    data: {
+        visible: true,
+        days: [],
+    },
+    methods: {
+        onClick(event) {
+            const { index } = event.currentTarget.dataset;
+            const item = this.data.days[index];
+            if (item.type !== 'disabled') {
+                this.$emit('click', item);
+            }
+        },
+        setDays() {
+            const days = [];
+            const startDate = new Date(this.data.date);
+            const year = startDate.getFullYear();
+            const month = startDate.getMonth();
+            const totalDay = getMonthEndDay(startDate.getFullYear(), startDate.getMonth() + 1);
+            for (let day = 1; day <= totalDay; day++) {
+                const date = new Date(year, month, day);
+                const type = this.getDayType(date);
+                let config = {
+                    date,
+                    type,
+                    text: day,
+                    bottomInfo: this.getBottomInfo(type),
+                };
+                if (this.data.formatter) {
+                    config = this.data.formatter(config);
+                }
+                days.push(config);
+            }
+            this.setData({ days });
+        },
+        getMultipleDayType(day) {
+            const { currentDate } = this.data;
+            if (!Array.isArray(currentDate)) {
+                return '';
+            }
+            const isSelected = (date) => currentDate.some((item) => compareDay(item, date) === 0);
+            if (isSelected(day)) {
+                const prevDay = getPrevDay(day);
+                const nextDay = getNextDay(day);
+                const prevSelected = isSelected(prevDay);
+                const nextSelected = isSelected(nextDay);
+                if (prevSelected && nextSelected) {
+                    return 'multiple-middle';
+                }
+                if (prevSelected) {
+                    return 'end';
+                }
+                return nextSelected ? 'start' : 'multiple-selected';
+            }
+            return '';
+        },
+        getRangeDayType(day) {
+            const { currentDate, allowSameDay } = this.data;
+            if (!Array.isArray(currentDate)) {
+                return '';
+            }
+            const [startDay, endDay] = currentDate;
+            if (!startDay) {
+                return '';
+            }
+            const compareToStart = compareDay(day, startDay);
+            if (!endDay) {
+                return compareToStart === 0 ? 'start' : '';
+            }
+            const compareToEnd = compareDay(day, endDay);
+            if (compareToStart === 0 && compareToEnd === 0 && allowSameDay) {
+                return 'start-end';
+            }
+            if (compareToStart === 0) {
+                return 'start';
+            }
+            if (compareToEnd === 0) {
+                return 'end';
+            }
+            if (compareToStart > 0 && compareToEnd < 0) {
+                return 'middle';
+            }
+            return '';
+        },
+        getDayType(day) {
+            const { type, minDate, maxDate, currentDate } = this.data;
+            if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
+                return 'disabled';
+            }
+            if (type === 'single') {
+                return compareDay(day, currentDate) === 0 ? 'selected' : '';
+            }
+            if (type === 'multiple') {
+                return this.getMultipleDayType(day);
+            }
+            /* istanbul ignore else */
+            if (type === 'range') {
+                return this.getRangeDayType(day);
+            }
+            return '';
+        },
+        getBottomInfo(type) {
+            if (this.data.type === 'range') {
+                if (type === 'start') {
+                    return '开始';
+                }
+                if (type === 'end') {
+                    return '结束';
+                }
+                if (type === 'start-end') {
+                    return '开始/结束';
+                }
+            }
+        },
+    },
+});

+ 3 - 0
wxcomponents/vant/calendar/components/month/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 39 - 0
wxcomponents/vant/calendar/components/month/index.wxml

@@ -0,0 +1,39 @@
+<wxs src="./index.wxs" module="computed"></wxs>
+<wxs src="../../../wxs/utils.wxs" module="utils" />
+
+<view class="van-calendar__month" style="{{ computed.getMonthStyle(visible, date, rowHeight) }}">
+  <view wx:if="{{ showMonthTitle }}" class="van-calendar__month-title">
+    {{ computed.formatMonthTitle(date) }}
+  </view>
+
+  <view wx:if="{{ visible }}" class="van-calendar__days">
+    <view wx:if="{{ showMark }}" class="van-calendar__month-mark">
+      {{ computed.getMark(date) }}
+    </view>
+
+    <view
+      wx:for="{{ days }}"
+      wx:key="index"
+      style="{{ computed.getDayStyle(item.type, index, date, rowHeight, color, firstDayOfWeek) }}"
+      class="{{ utils.bem('calendar__day', [item.type]) }} {{ item.className }}"
+      data-index="{{ index }}"
+      bindtap="onClick"
+    >
+      <view wx:if="{{ item.type === 'selected' }}" class="van-calendar__selected-day" style="width: {{ rowHeight }}px; height: {{ rowHeight }}px; background: {{ color }}">
+        <view wx:if="{{ item.topInfo }}" class="van-calendar__top-info">{{ item.topInfo }}</view>
+        {{ item.text }}
+        <view wx:if="{{ item.bottomInfo }}" class="van-calendar__bottom-info">
+          {{ item.bottomInfo }}
+        </view>
+      </view>
+
+      <view wx:else>
+        <view wx:if="{{ item.topInfo }}" class="van-calendar__top-info">{{ item.topInfo }}</view>
+        {{ item.text }}
+        <view wx:if="{{ item.bottomInfo }}" class="van-calendar__bottom-info">
+          {{ item.bottomInfo }}
+        </view>
+      </view>
+    </view>
+  </view>
+</view>

+ 71 - 0
wxcomponents/vant/calendar/components/month/index.wxs

@@ -0,0 +1,71 @@
+/* eslint-disable */
+var utils = require('../../utils.wxs');
+
+function getMark(date) {
+  return getDate(date).getMonth() + 1;
+}
+
+var ROW_HEIGHT = 64;
+
+function getDayStyle(type, index, date, rowHeight, color, firstDayOfWeek) {
+  var style = [];
+  var current = getDate(date).getDay() || 7;
+  var offset = current < firstDayOfWeek ? (7 - firstDayOfWeek + current) :
+               current === 7 && firstDayOfWeek === 0 ? 0 :
+               (current - firstDayOfWeek);
+
+  if (index === 0) {
+    style.push(['margin-left', (100 * offset) / 7 + '%']);
+  }
+
+  if (rowHeight !== ROW_HEIGHT) {
+    style.push(['height', rowHeight + 'px']);
+  }
+
+  if (color) {
+    if (
+      type === 'start' ||
+      type === 'end' ||
+      type === 'start-end' ||
+      type === 'multiple-selected' ||
+      type === 'multiple-middle'
+    ) {
+      style.push(['background', color]);
+    } else if (type === 'middle') {
+      style.push(['color', color]);
+    }
+  }
+
+  return style
+    .map(function(item) {
+      return item.join(':');
+    })
+    .join(';');
+}
+
+function formatMonthTitle(date) {
+  date = getDate(date);
+  return date.getFullYear() + '年' + (date.getMonth() + 1) + '月';
+}
+
+function getMonthStyle(visible, date, rowHeight) {
+  if (!visible) {
+    date = getDate(date);
+
+    var totalDay = utils.getMonthEndDay(
+      date.getFullYear(),
+      date.getMonth() + 1
+    );
+    var offset = getDate(date).getDay();
+    var padding = Math.ceil((totalDay + offset) / 7) * rowHeight;
+
+    return 'padding-bottom:' + padding + 'px';
+  }
+}
+
+module.exports = {
+  getMark: getMark,
+  getDayStyle: getDayStyle,
+  formatMonthTitle: formatMonthTitle,
+  getMonthStyle: getMonthStyle
+};

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/calendar/components/month/index.wxss


+ 1 - 0
wxcomponents/vant/calendar/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 360 - 0
wxcomponents/vant/calendar/index.js

@@ -0,0 +1,360 @@
+import { VantComponent } from '../common/component';
+import { ROW_HEIGHT, getPrevDay, getNextDay, getToday, compareDay, copyDates, calcDateNum, formatMonthTitle, compareMonth, getMonths, getDayByOffset, } from './utils';
+import Toast from '../toast/toast';
+import { requestAnimationFrame } from '../common/utils';
+const initialMinDate = getToday().getTime();
+const initialMaxDate = (() => {
+    const now = getToday();
+    return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate()).getTime();
+})();
+const getTime = (date) => date instanceof Date ? date.getTime() : date;
+VantComponent({
+    props: {
+        title: {
+            type: String,
+            value: '日期选择',
+        },
+        color: String,
+        show: {
+            type: Boolean,
+            observer(val) {
+                if (val) {
+                    this.initRect();
+                    this.scrollIntoView();
+                }
+            },
+        },
+        formatter: null,
+        confirmText: {
+            type: String,
+            value: '确定',
+        },
+        confirmDisabledText: {
+            type: String,
+            value: '确定',
+        },
+        rangePrompt: String,
+        showRangePrompt: {
+            type: Boolean,
+            value: true,
+        },
+        defaultDate: {
+            type: null,
+            value: getToday().getTime(),
+            observer(val) {
+                this.setData({ currentDate: val });
+                this.scrollIntoView();
+            },
+        },
+        allowSameDay: Boolean,
+        type: {
+            type: String,
+            value: 'single',
+            observer: 'reset',
+        },
+        minDate: {
+            type: Number,
+            value: initialMinDate,
+        },
+        maxDate: {
+            type: Number,
+            value: initialMaxDate,
+        },
+        position: {
+            type: String,
+            value: 'bottom',
+        },
+        rowHeight: {
+            type: null,
+            value: ROW_HEIGHT,
+        },
+        round: {
+            type: Boolean,
+            value: true,
+        },
+        poppable: {
+            type: Boolean,
+            value: true,
+        },
+        showMark: {
+            type: Boolean,
+            value: true,
+        },
+        showTitle: {
+            type: Boolean,
+            value: true,
+        },
+        showConfirm: {
+            type: Boolean,
+            value: true,
+        },
+        showSubtitle: {
+            type: Boolean,
+            value: true,
+        },
+        safeAreaInsetBottom: {
+            type: Boolean,
+            value: true,
+        },
+        closeOnClickOverlay: {
+            type: Boolean,
+            value: true,
+        },
+        maxRange: {
+            type: null,
+            value: null,
+        },
+        minRange: {
+            type: Number,
+            value: 1,
+        },
+        firstDayOfWeek: {
+            type: Number,
+            value: 0,
+        },
+        readonly: Boolean,
+        rootPortal: {
+            type: Boolean,
+            value: false,
+        },
+    },
+    data: {
+        subtitle: '',
+        currentDate: null,
+        scrollIntoView: '',
+    },
+    watch: {
+        minDate() {
+            this.initRect();
+        },
+        maxDate() {
+            this.initRect();
+        },
+    },
+    created() {
+        this.setData({
+            currentDate: this.getInitialDate(this.data.defaultDate),
+        });
+    },
+    mounted() {
+        if (this.data.show || !this.data.poppable) {
+            this.initRect();
+            this.scrollIntoView();
+        }
+    },
+    methods: {
+        reset() {
+            this.setData({ currentDate: this.getInitialDate(this.data.defaultDate) });
+            this.scrollIntoView();
+        },
+        initRect() {
+            if (this.contentObserver != null) {
+                this.contentObserver.disconnect();
+            }
+            const contentObserver = this.createIntersectionObserver({
+                thresholds: [0, 0.1, 0.9, 1],
+                observeAll: true,
+            });
+            this.contentObserver = contentObserver;
+            contentObserver.relativeTo('.van-calendar__body');
+            contentObserver.observe('.month', (res) => {
+                if (res.boundingClientRect.top <= res.relativeRect.top) {
+                    // @ts-ignore
+                    this.setData({ subtitle: formatMonthTitle(res.dataset.date) });
+                }
+            });
+        },
+        limitDateRange(date, minDate = null, maxDate = null) {
+            minDate = minDate || this.data.minDate;
+            maxDate = maxDate || this.data.maxDate;
+            if (compareDay(date, minDate) === -1) {
+                return minDate;
+            }
+            if (compareDay(date, maxDate) === 1) {
+                return maxDate;
+            }
+            return date;
+        },
+        getInitialDate(defaultDate = null) {
+            const { type, minDate, maxDate, allowSameDay } = this.data;
+            if (!defaultDate)
+                return [];
+            const now = getToday().getTime();
+            if (type === 'range') {
+                if (!Array.isArray(defaultDate)) {
+                    defaultDate = [];
+                }
+                const [startDay, endDay] = defaultDate || [];
+                const startDate = getTime(startDay || now);
+                const start = this.limitDateRange(startDate, minDate, allowSameDay ? startDate : getPrevDay(new Date(maxDate)).getTime());
+                const date = getTime(endDay || now);
+                const end = this.limitDateRange(date, allowSameDay ? date : getNextDay(new Date(minDate)).getTime());
+                return [start, end];
+            }
+            if (type === 'multiple') {
+                if (Array.isArray(defaultDate)) {
+                    return defaultDate.map((date) => this.limitDateRange(date));
+                }
+                return [this.limitDateRange(now)];
+            }
+            if (!defaultDate || Array.isArray(defaultDate)) {
+                defaultDate = now;
+            }
+            return this.limitDateRange(defaultDate);
+        },
+        scrollIntoView() {
+            requestAnimationFrame(() => {
+                const { currentDate, type, show, poppable, minDate, maxDate } = this.data;
+                if (!currentDate)
+                    return;
+                // @ts-ignore
+                const targetDate = type === 'single' ? currentDate : currentDate[0];
+                const displayed = show || !poppable;
+                if (!targetDate || !displayed) {
+                    return;
+                }
+                const months = getMonths(minDate, maxDate);
+                months.some((month, index) => {
+                    if (compareMonth(month, targetDate) === 0) {
+                        this.setData({ scrollIntoView: `month${index}` });
+                        return true;
+                    }
+                    return false;
+                });
+            });
+        },
+        onOpen() {
+            this.$emit('open');
+        },
+        onOpened() {
+            this.$emit('opened');
+        },
+        onClose() {
+            this.$emit('close');
+        },
+        onClosed() {
+            this.$emit('closed');
+        },
+        onClickDay(event) {
+            if (this.data.readonly) {
+                return;
+            }
+            let { date } = event.detail;
+            const { type, currentDate, allowSameDay } = this.data;
+            if (type === 'range') {
+                // @ts-ignore
+                const [startDay, endDay] = currentDate;
+                if (startDay && !endDay) {
+                    const compareToStart = compareDay(date, startDay);
+                    if (compareToStart === 1) {
+                        const { days } = this.selectComponent('.month').data;
+                        days.some((day, index) => {
+                            const isDisabled = day.type === 'disabled' &&
+                                getTime(startDay) < getTime(day.date) &&
+                                getTime(day.date) < getTime(date);
+                            if (isDisabled) {
+                                ({ date } = days[index - 1]);
+                            }
+                            return isDisabled;
+                        });
+                        this.select([startDay, date], true);
+                    }
+                    else if (compareToStart === -1) {
+                        this.select([date, null]);
+                    }
+                    else if (allowSameDay) {
+                        this.select([date, date], true);
+                    }
+                }
+                else {
+                    this.select([date, null]);
+                }
+            }
+            else if (type === 'multiple') {
+                let selectedIndex;
+                // @ts-ignore
+                const selected = currentDate.some((dateItem, index) => {
+                    const equal = compareDay(dateItem, date) === 0;
+                    if (equal) {
+                        selectedIndex = index;
+                    }
+                    return equal;
+                });
+                if (selected) {
+                    // @ts-ignore
+                    const cancelDate = currentDate.splice(selectedIndex, 1);
+                    this.setData({ currentDate });
+                    this.unselect(cancelDate);
+                }
+                else {
+                    // @ts-ignore
+                    this.select([...currentDate, date]);
+                }
+            }
+            else {
+                this.select(date, true);
+            }
+        },
+        unselect(dateArray) {
+            const date = dateArray[0];
+            if (date) {
+                this.$emit('unselect', copyDates(date));
+            }
+        },
+        select(date, complete) {
+            if (complete && this.data.type === 'range') {
+                const valid = this.checkRange(date);
+                if (!valid) {
+                    // auto selected to max range if showConfirm
+                    if (this.data.showConfirm) {
+                        this.emit([
+                            date[0],
+                            getDayByOffset(date[0], this.data.maxRange - 1),
+                        ]);
+                    }
+                    else {
+                        this.emit(date);
+                    }
+                    return;
+                }
+            }
+            this.emit(date);
+            if (complete && !this.data.showConfirm) {
+                this.onConfirm();
+            }
+        },
+        emit(date) {
+            this.setData({
+                currentDate: Array.isArray(date) ? date.map(getTime) : getTime(date),
+            });
+            this.$emit('select', copyDates(date));
+        },
+        checkRange(date) {
+            const { maxRange, rangePrompt, showRangePrompt } = this.data;
+            if (maxRange && calcDateNum(date) > maxRange) {
+                if (showRangePrompt) {
+                    Toast({
+                        context: this,
+                        message: rangePrompt || `选择天数不能超过 ${maxRange} 天`,
+                    });
+                }
+                this.$emit('over-range');
+                return false;
+            }
+            return true;
+        },
+        onConfirm() {
+            if (this.data.type === 'range' &&
+                !this.checkRange(this.data.currentDate)) {
+                return;
+            }
+            wx.nextTick(() => {
+                // @ts-ignore
+                this.$emit('confirm', copyDates(this.data.currentDate));
+            });
+        },
+        onClickSubtitle(event) {
+            this.$emit('click-subtitle', event);
+        },
+    },
+});

+ 10 - 0
wxcomponents/vant/calendar/index.json

@@ -0,0 +1,10 @@
+{
+  "component": true,
+  "usingComponents": {
+    "header": "./components/header/index",
+    "month": "./components/month/index",
+    "van-button": "../button/index",
+    "van-popup": "../popup/index",
+    "van-toast": "../toast/index"
+  }
+}

+ 27 - 0
wxcomponents/vant/calendar/index.wxml

@@ -0,0 +1,27 @@
+<wxs src="./index.wxs" module="computed" />
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<import src="./calendar.wxml" />
+
+<van-popup
+  wx:if="{{ poppable }}"
+  custom-class="van-calendar__popup--{{ position }}"
+  close-icon-class="van-calendar__close-icon"
+  show="{{ show }}"
+  round="{{ round }}"
+  position="{{ position }}"
+  closeable="{{ showTitle || showSubtitle }}"
+  close-on-click-overlay="{{ closeOnClickOverlay }}"
+  safe-area-inset-bottom="{{ safeAreaInsetBottom }}"
+  root-portal="{{ rootPortal }}"
+  bind:enter="onOpen"
+  bind:close="onClose"
+  bind:after-enter="onOpened"
+  bind:after-leave="onClosed"
+>
+  <include src="./calendar.wxml" />
+</van-popup>
+
+<include wx:else src="./calendar.wxml" />
+
+<van-toast id="van-toast" />

+ 37 - 0
wxcomponents/vant/calendar/index.wxs

@@ -0,0 +1,37 @@
+/* eslint-disable */
+var utils = require('./utils.wxs');
+
+function getMonths(minDate, maxDate) {
+  var months = [];
+  var cursor = getDate(minDate);
+
+  cursor.setDate(1);
+
+  do {
+    months.push(cursor.getTime());
+    cursor.setMonth(cursor.getMonth() + 1);
+  } while (utils.compareMonth(cursor, getDate(maxDate)) !== 1);
+
+  return months;
+}
+
+function getButtonDisabled(type, currentDate, minRange) {
+  if (currentDate == null) {
+    return true;
+  }
+
+  if (type === 'range') {
+    return !currentDate[0] || !currentDate[1];
+  }
+
+  if (type === 'multiple') {
+    return currentDate.length < minRange;
+  }
+
+  return !currentDate;
+}
+
+module.exports = {
+  getMonths: getMonths,
+  getButtonDisabled: getButtonDisabled
+};

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/calendar/index.wxss


+ 12 - 0
wxcomponents/vant/calendar/utils.d.ts

@@ -0,0 +1,12 @@
+export declare const ROW_HEIGHT = 64;
+export declare function formatMonthTitle(date: Date): string;
+export declare function compareMonth(date1: Date | number, date2: Date | number): 0 | 1 | -1;
+export declare function compareDay(day1: Date | number, day2: Date | number): 0 | 1 | -1;
+export declare function getDayByOffset(date: Date, offset: number): Date;
+export declare function getPrevDay(date: Date): Date;
+export declare function getNextDay(date: Date): Date;
+export declare function getToday(): Date;
+export declare function calcDateNum(date: [Date, Date]): number;
+export declare function copyDates(dates: Date | Date[]): Date | Date[];
+export declare function getMonthEndDay(year: number, month: number): number;
+export declare function getMonths(minDate: number, maxDate: number): number[];

+ 83 - 0
wxcomponents/vant/calendar/utils.js

@@ -0,0 +1,83 @@
+export const ROW_HEIGHT = 64;
+export function formatMonthTitle(date) {
+    if (!(date instanceof Date)) {
+        date = new Date(date);
+    }
+    return `${date.getFullYear()}年${date.getMonth() + 1}月`;
+}
+export function compareMonth(date1, date2) {
+    if (!(date1 instanceof Date)) {
+        date1 = new Date(date1);
+    }
+    if (!(date2 instanceof Date)) {
+        date2 = new Date(date2);
+    }
+    const year1 = date1.getFullYear();
+    const year2 = date2.getFullYear();
+    const month1 = date1.getMonth();
+    const month2 = date2.getMonth();
+    if (year1 === year2) {
+        return month1 === month2 ? 0 : month1 > month2 ? 1 : -1;
+    }
+    return year1 > year2 ? 1 : -1;
+}
+export function compareDay(day1, day2) {
+    if (!(day1 instanceof Date)) {
+        day1 = new Date(day1);
+    }
+    if (!(day2 instanceof Date)) {
+        day2 = new Date(day2);
+    }
+    const compareMonthResult = compareMonth(day1, day2);
+    if (compareMonthResult === 0) {
+        const date1 = day1.getDate();
+        const date2 = day2.getDate();
+        return date1 === date2 ? 0 : date1 > date2 ? 1 : -1;
+    }
+    return compareMonthResult;
+}
+export function getDayByOffset(date, offset) {
+    date = new Date(date);
+    date.setDate(date.getDate() + offset);
+    return date;
+}
+export function getPrevDay(date) {
+    return getDayByOffset(date, -1);
+}
+export function getNextDay(date) {
+    return getDayByOffset(date, 1);
+}
+export function getToday() {
+    const today = new Date();
+    today.setHours(0, 0, 0, 0);
+    return today;
+}
+export function calcDateNum(date) {
+    const day1 = new Date(date[0]).getTime();
+    const day2 = new Date(date[1]).getTime();
+    return (day2 - day1) / (1000 * 60 * 60 * 24) + 1;
+}
+export function copyDates(dates) {
+    if (Array.isArray(dates)) {
+        return dates.map((date) => {
+            if (date === null) {
+                return date;
+            }
+            return new Date(date);
+        });
+    }
+    return new Date(dates);
+}
+export function getMonthEndDay(year, month) {
+    return 32 - new Date(year, month - 1, 32).getDate();
+}
+export function getMonths(minDate, maxDate) {
+    const months = [];
+    const cursor = new Date(minDate);
+    cursor.setDate(1);
+    do {
+        months.push(cursor.getTime());
+        cursor.setMonth(cursor.getMonth() + 1);
+    } while (compareMonth(cursor, maxDate) !== 1);
+    return months;
+}

+ 25 - 0
wxcomponents/vant/calendar/utils.wxs

@@ -0,0 +1,25 @@
+/* eslint-disable */
+function getMonthEndDay(year, month) {
+  return 32 -  getDate(year, month - 1, 32).getDate();
+}
+
+function compareMonth(date1, date2) {
+  date1 = getDate(date1);
+  date2 = getDate(date2);
+
+  var year1 = date1.getFullYear();
+  var year2 = date2.getFullYear();
+  var month1 = date1.getMonth();
+  var month2 = date2.getMonth();
+
+  if (year1 === year2) {
+    return month1 === month2 ? 0 : month1 > month2 ? 1 : -1;
+  }
+
+  return year1 > year2 ? 1 : -1;
+}
+
+module.exports = {
+  getMonthEndDay: getMonthEndDay,
+  compareMonth: compareMonth
+};

+ 1 - 0
wxcomponents/vant/card/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 49 - 0
wxcomponents/vant/card/index.js

@@ -0,0 +1,49 @@
+import { link } from '../mixins/link';
+import { VantComponent } from '../common/component';
+VantComponent({
+    classes: [
+        'num-class',
+        'desc-class',
+        'thumb-class',
+        'title-class',
+        'price-class',
+        'origin-price-class',
+    ],
+    mixins: [link],
+    props: {
+        tag: String,
+        num: String,
+        desc: String,
+        thumb: String,
+        title: String,
+        price: {
+            type: String,
+            observer: 'updatePrice',
+        },
+        centered: Boolean,
+        lazyLoad: Boolean,
+        thumbLink: String,
+        originPrice: String,
+        thumbMode: {
+            type: String,
+            value: 'aspectFit',
+        },
+        currency: {
+            type: String,
+            value: '¥',
+        },
+    },
+    methods: {
+        updatePrice() {
+            const { price } = this.data;
+            const priceArr = price.toString().split('.');
+            this.setData({
+                integerStr: priceArr[0],
+                decimalStr: priceArr[1] ? `.${priceArr[1]}` : '',
+            });
+        },
+        onClickThumb() {
+            this.jumpLink('thumbLink');
+        },
+    },
+});

+ 6 - 0
wxcomponents/vant/card/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-tag": "../tag/index"
+  }
+}

+ 56 - 0
wxcomponents/vant/card/index.wxml

@@ -0,0 +1,56 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<view class="custom-class van-card">
+  <view class="{{ utils.bem('card__header', { center: centered }) }}">
+    <view class="van-card__thumb" bind:tap="onClickThumb">
+      <image
+        wx:if="{{ thumb }}"
+        src="{{ thumb }}"
+        mode="{{ thumbMode }}"
+        lazy-load="{{ lazyLoad }}"
+        class="van-card__img thumb-class"
+      />
+      <slot wx:else name="thumb" />
+      <van-tag
+        wx:if="{{ tag }}"
+        mark
+        type="danger"
+        custom-class="van-card__tag"
+      >
+        {{ tag }}
+      </van-tag>
+      <slot wx:else name="tag" />
+    </view>
+
+    <view class="van-card__content {{ utils.bem('card__content', { center: centered }) }}">
+      <view>
+        <view wx:if="{{ title }}" class="van-card__title title-class">{{ title }}</view>
+        <slot wx:else name="title" />
+
+        <view wx:if="{{ desc }}" class="van-card__desc desc-class">{{ desc }}</view>
+        <slot wx:else name="desc" />
+
+        <slot name="tags" />
+      </view>
+
+      <view class="van-card__bottom">
+        <slot name="price-top" />
+        <view wx:if="{{ price || price === 0 }}" class="van-card__price price-class">
+          <text>{{ currency }}</text>
+          <text class="van-card__price-integer">{{ integerStr }}</text>
+          <text class="van-card__price-decimal">{{ decimalStr }}</text>
+        </view>
+        <slot wx:else name="price" />
+        <view wx:if="{{ originPrice || originPrice === 0 }}" class="van-card__origin-price origin-price-class">{{ currency }} {{ originPrice }}</view>
+        <slot wx:else name="origin-price" />
+        <view wx:if="{{ num }}" class="van-card__num num-class">x {{ num }}</view>
+        <slot wx:else  name="num" />
+        <slot name="bottom" />
+      </view>
+    </view>
+  </view>
+
+  <view class="van-card__footer">
+    <slot name="footer" />
+  </view>
+</view>

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/card/index.wxss


+ 1 - 0
wxcomponents/vant/cascader/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 210 - 0
wxcomponents/vant/cascader/index.js

@@ -0,0 +1,210 @@
+import { VantComponent } from '../common/component';
+var FieldName;
+(function (FieldName) {
+    FieldName["TEXT"] = "text";
+    FieldName["VALUE"] = "value";
+    FieldName["CHILDREN"] = "children";
+})(FieldName || (FieldName = {}));
+const defaultFieldNames = {
+    text: FieldName.TEXT,
+    value: FieldName.VALUE,
+    children: FieldName.CHILDREN,
+};
+VantComponent({
+    props: {
+        title: String,
+        value: {
+            type: String,
+        },
+        placeholder: {
+            type: String,
+            value: '请选择',
+        },
+        activeColor: {
+            type: String,
+            value: '#1989fa',
+        },
+        options: {
+            type: Array,
+            value: [],
+        },
+        swipeable: {
+            type: Boolean,
+            value: false,
+        },
+        closeable: {
+            type: Boolean,
+            value: true,
+        },
+        showHeader: {
+            type: Boolean,
+            value: true,
+        },
+        closeIcon: {
+            type: String,
+            value: 'cross',
+        },
+        fieldNames: {
+            type: Object,
+            value: defaultFieldNames,
+            observer: 'updateFieldNames',
+        },
+        useTitleSlot: Boolean,
+    },
+    data: {
+        tabs: [],
+        activeTab: 0,
+        textKey: FieldName.TEXT,
+        valueKey: FieldName.VALUE,
+        childrenKey: FieldName.CHILDREN,
+        innerValue: '',
+    },
+    watch: {
+        options() {
+            this.updateTabs();
+        },
+        value(newVal) {
+            this.updateValue(newVal);
+        },
+    },
+    created() {
+        this.updateTabs();
+    },
+    methods: {
+        updateValue(val) {
+            if (val !== undefined) {
+                const values = this.data.tabs.map((tab) => tab.selected && tab.selected[this.data.valueKey]);
+                if (values.indexOf(val) > -1) {
+                    return;
+                }
+            }
+            this.innerValue = val;
+            this.updateTabs();
+        },
+        updateFieldNames() {
+            const { text = 'text', value = 'value', children = 'children', } = this.data.fieldNames || defaultFieldNames;
+            this.setData({
+                textKey: text,
+                valueKey: value,
+                childrenKey: children,
+            });
+        },
+        getSelectedOptionsByValue(options, value) {
+            for (let i = 0; i < options.length; i++) {
+                const option = options[i];
+                if (option[this.data.valueKey] === value) {
+                    return [option];
+                }
+                if (option[this.data.childrenKey]) {
+                    const selectedOptions = this.getSelectedOptionsByValue(option[this.data.childrenKey], value);
+                    if (selectedOptions) {
+                        return [option, ...selectedOptions];
+                    }
+                }
+            }
+        },
+        updateTabs() {
+            const { options } = this.data;
+            const { innerValue } = this;
+            if (!options.length) {
+                return;
+            }
+            if (innerValue !== undefined) {
+                const selectedOptions = this.getSelectedOptionsByValue(options, innerValue);
+                if (selectedOptions) {
+                    let optionsCursor = options;
+                    const tabs = selectedOptions.map((option) => {
+                        const tab = {
+                            options: optionsCursor,
+                            selected: option,
+                        };
+                        const next = optionsCursor.find((item) => item[this.data.valueKey] === option[this.data.valueKey]);
+                        if (next) {
+                            optionsCursor = next[this.data.childrenKey];
+                        }
+                        return tab;
+                    });
+                    if (optionsCursor) {
+                        tabs.push({
+                            options: optionsCursor,
+                            selected: null,
+                        });
+                    }
+                    this.setData({
+                        tabs,
+                    });
+                    wx.nextTick(() => {
+                        this.setData({
+                            activeTab: tabs.length - 1,
+                        });
+                    });
+                    return;
+                }
+            }
+            this.setData({
+                tabs: [
+                    {
+                        options,
+                        selected: null,
+                    },
+                ],
+                activeTab: 0,
+            });
+        },
+        onClose() {
+            this.$emit('close');
+        },
+        onClickTab(e) {
+            const { index: tabIndex, title } = e.detail;
+            this.$emit('click-tab', { title, tabIndex });
+            this.setData({
+                activeTab: tabIndex,
+            });
+        },
+        // 选中
+        onSelect(e) {
+            const { option, tabIndex } = e.currentTarget.dataset;
+            if (option && option.disabled) {
+                return;
+            }
+            const { valueKey, childrenKey } = this.data;
+            let { tabs } = this.data;
+            tabs[tabIndex].selected = option;
+            if (tabs.length > tabIndex + 1) {
+                tabs = tabs.slice(0, tabIndex + 1);
+            }
+            if (option[childrenKey]) {
+                const nextTab = {
+                    options: option[childrenKey],
+                    selected: null,
+                };
+                if (tabs[tabIndex + 1]) {
+                    tabs[tabIndex + 1] = nextTab;
+                }
+                else {
+                    tabs.push(nextTab);
+                }
+                wx.nextTick(() => {
+                    this.setData({
+                        activeTab: tabIndex + 1,
+                    });
+                });
+            }
+            this.setData({
+                tabs,
+            });
+            const selectedOptions = tabs.map((tab) => tab.selected).filter(Boolean);
+            const value = option[valueKey];
+            const params = {
+                value,
+                tabIndex,
+                selectedOptions,
+            };
+            this.innerValue = value;
+            this.$emit('change', params);
+            if (!option[childrenKey]) {
+                this.$emit('finish', params);
+            }
+        },
+    },
+});

+ 8 - 0
wxcomponents/vant/cascader/index.json

@@ -0,0 +1,8 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index",
+    "van-tab":  "../tab/index",
+    "van-tabs": "../tabs/index"
+  }
+}

+ 54 - 0
wxcomponents/vant/cascader/index.wxml

@@ -0,0 +1,54 @@
+<wxs src="./index.wxs" module="utils" />
+
+<view wx:if="{{ showHeader }}" class="van-cascader__header">
+  <slot name="title" wx:if="{{ useTitleSlot }}"></slot>
+  <text class="van-cascader__title" wx:else>{{ title }}</text>
+  <van-icon
+    wx:if="{{ closeable }}"
+    name="{{ closeIcon }}"
+    class="van-cascader__close-icon"
+    bind:tap="onClose"
+  />
+</view>
+
+<van-tabs
+  active="{{ activeTab }}"
+  custom-class="van-cascader__tabs"
+  wrap-class="van-cascader__tabs-wrap"
+  tab-class="van-cascader__tab"
+  color="{{ activeColor }}"
+  border="{{ false }}"
+  swipeable="{{ swipeable }}"
+  bind:click="onClickTab"
+>
+  <van-tab
+    wx:for="{{ tabs }}"
+    wx:for-item="tab"
+    wx:for-index="tabIndex"
+    wx:key="tabIndex"
+    title="{{ tab.selected ? tab.selected[textKey] : placeholder }}"
+    style="width: 100%;"
+    title-style="{{ !tab.selected ? 'color: #969799;font-weight:normal;' : '' }}"
+  >
+    <!-- 暂不支持 -->
+    <!-- <slot name="options-top"></slot> -->
+
+    <view class="van-cascader__options">
+      <view
+        wx:for="{{ tab.options }}"
+        wx:for-item="option"
+        wx:key="index"
+        class="{{ option.className }} {{ utils.optionClass(tab, valueKey, option) }}"
+        style="{{ utils.optionStyle({ tab, valueKey, option, activeColor }) }}"
+        data-option="{{ option }}"
+        data-tab-index="{{ tabIndex }}"
+        bind:tap="onSelect"
+      >
+        <text>{{ option[textKey] }}</text>
+        <van-icon wx:if="{{ utils.isSelected(tab, valueKey, option) }}" name="success" size="18" />
+      </view>
+    </view>
+    <!-- 暂不支持 -->
+    <!-- <slot name="options-bottom"></slot> -->
+  </van-tab>
+</van-tabs>

+ 24 - 0
wxcomponents/vant/cascader/index.wxs

@@ -0,0 +1,24 @@
+var utils = require('../wxs/utils.wxs');
+var style = require('../wxs/style.wxs');
+
+function isSelected(tab, valueKey, option) {
+  return tab.selected && tab.selected[valueKey] === option[valueKey]
+}
+
+function optionClass(tab, valueKey, option) {
+  return utils.bem('cascader__option', { selected: isSelected(tab, valueKey, option), disabled: option.disabled })
+}
+
+function optionStyle(data) {
+  var color = data.option.color || (isSelected(data.tab, data.valueKey, data.option) ? data.activeColor : undefined);
+  return style({
+    color
+  });
+}
+
+
+module.exports = {
+  isSelected: isSelected,
+  optionClass: optionClass,
+  optionStyle: optionStyle,
+};

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/cascader/index.wxss


+ 1 - 0
wxcomponents/vant/cell-group/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 11 - 0
wxcomponents/vant/cell-group/index.js

@@ -0,0 +1,11 @@
+import { VantComponent } from '../common/component';
+VantComponent({
+    props: {
+        title: String,
+        border: {
+            type: Boolean,
+            value: true,
+        },
+        inset: Boolean,
+    },
+});

+ 3 - 0
wxcomponents/vant/cell-group/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 11 - 0
wxcomponents/vant/cell-group/index.wxml

@@ -0,0 +1,11 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<view
+  wx:if="{{ title }}"
+  class="{{ utils.bem('cell-group__title', { inset }) }}"
+>
+  {{ title }}
+</view>
+<view class="custom-class {{ utils.bem('cell-group', { inset }) }} {{ border ? 'van-hairline--top-bottom' : '' }}">
+  <slot />
+</view>

+ 1 - 0
wxcomponents/vant/cell-group/index.wxss

@@ -0,0 +1 @@
+@import '../common/index.wxss';.van-cell-group--inset{border-radius:var(--cell-group-inset-border-radius,8px);margin:var(--cell-group-inset-padding,0 16px);overflow:hidden}.van-cell-group__title{color:var(--cell-group-title-color,#969799);font-size:var(--cell-group-title-font-size,14px);line-height:var(--cell-group-title-line-height,16px);padding:var(--cell-group-title-padding,16px 16px 8px)}.van-cell-group__title--inset{padding:var(--cell-group-inset-title-padding,16px 16px 8px 32px)}

+ 1 - 0
wxcomponents/vant/cell/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 38 - 0
wxcomponents/vant/cell/index.js

@@ -0,0 +1,38 @@
+import { link } from '../mixins/link';
+import { VantComponent } from '../common/component';
+VantComponent({
+    classes: [
+        'title-class',
+        'label-class',
+        'value-class',
+        'right-icon-class',
+        'hover-class',
+    ],
+    mixins: [link],
+    props: {
+        title: null,
+        value: null,
+        icon: String,
+        size: String,
+        label: String,
+        center: Boolean,
+        isLink: Boolean,
+        required: Boolean,
+        clickable: Boolean,
+        titleWidth: String,
+        customStyle: String,
+        arrowDirection: String,
+        useLabelSlot: Boolean,
+        border: {
+            type: Boolean,
+            value: true,
+        },
+        titleStyle: String,
+    },
+    methods: {
+        onClick(event) {
+            this.$emit('click', event.detail);
+            this.jumpLink();
+        },
+    },
+});

+ 6 - 0
wxcomponents/vant/cell/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index"
+  }
+}

+ 47 - 0
wxcomponents/vant/cell/index.wxml

@@ -0,0 +1,47 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+<wxs src="./index.wxs" module="computed" />
+
+<view
+  class="custom-class {{ utils.bem('cell', [size, { center, required, borderless: !border, clickable: isLink || clickable }]) }}"
+  hover-class="van-cell--hover hover-class"
+  hover-stay-time="70"
+  style="{{ customStyle }}"
+  bind:tap="onClick"
+>
+  <van-icon
+    wx:if="{{ icon }}"
+    name="{{ icon }}"
+    class="van-cell__left-icon-wrap"
+    custom-class="van-cell__left-icon"
+  />
+  <slot wx:else name="icon" />
+
+  <view
+    style="{{ computed.titleStyle({ titleWidth, titleStyle }) }}"
+    class="van-cell__title title-class"
+  >
+
+    <block wx:if="{{ title }}">{{ title }}</block>
+    <slot wx:else name="title" />
+
+    <view wx:if="{{ label || useLabelSlot }}" class="van-cell__label label-class">
+      <slot wx:if="{{ useLabelSlot }}" name="label" />
+      <block wx:elif="{{ label }}">{{ label }}</block>
+    </view>
+  </view>
+
+  <view class="van-cell__value value-class">
+    <block wx:if="{{ value || value === 0 }}">{{ value }}</block>
+    <slot wx:else />
+  </view>
+
+  <van-icon
+    wx:if="{{ isLink }}"
+    name="{{ arrowDirection ? 'arrow' + '-' + arrowDirection : 'arrow' }}"
+    class="van-cell__right-icon-wrap right-icon-class"
+    custom-class="van-cell__right-icon"
+  />
+  <slot wx:else name="right-icon" />
+
+  <slot name="extra" />
+</view>

+ 17 - 0
wxcomponents/vant/cell/index.wxs

@@ -0,0 +1,17 @@
+/* eslint-disable */
+var style = require('../wxs/style.wxs');
+var addUnit = require('../wxs/add-unit.wxs');
+
+function titleStyle(data) {
+  return style([
+    {
+      'max-width': addUnit(data.titleWidth),
+      'min-width': addUnit(data.titleWidth),
+    },
+    data.titleStyle,
+  ]);
+}
+
+module.exports = {
+  titleStyle: titleStyle,
+};

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/cell/index.wxss


+ 1 - 0
wxcomponents/vant/checkbox-group/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 36 - 0
wxcomponents/vant/checkbox-group/index.js

@@ -0,0 +1,36 @@
+import { useChildren } from '../common/relation';
+import { VantComponent } from '../common/component';
+VantComponent({
+    field: true,
+    relation: useChildren('checkbox', function (target) {
+        this.updateChild(target);
+    }),
+    props: {
+        max: Number,
+        value: {
+            type: Array,
+            observer: 'updateChildren',
+        },
+        disabled: {
+            type: Boolean,
+            observer: 'updateChildren',
+        },
+        direction: {
+            type: String,
+            value: 'vertical',
+        },
+    },
+    methods: {
+        updateChildren() {
+            this.children.forEach((child) => this.updateChild(child));
+        },
+        updateChild(child) {
+            const { value, disabled, direction } = this.data;
+            child.setData({
+                value: value.indexOf(child.data.name) !== -1,
+                parentDisabled: disabled,
+                direction,
+            });
+        },
+    },
+});

+ 3 - 0
wxcomponents/vant/checkbox-group/index.json

@@ -0,0 +1,3 @@
+{
+  "component": true
+}

+ 5 - 0
wxcomponents/vant/checkbox-group/index.wxml

@@ -0,0 +1,5 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+
+<view class="{{ utils.bem('checkbox-group', [{ horizontal: direction === 'horizontal' }]) }}">
+  <slot />
+</view>

+ 1 - 0
wxcomponents/vant/checkbox-group/index.wxss

@@ -0,0 +1 @@
+@import '../common/index.wxss';.van-checkbox-group--horizontal{display:flex;flex-wrap:wrap}

+ 1 - 0
wxcomponents/vant/checkbox/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 77 - 0
wxcomponents/vant/checkbox/index.js

@@ -0,0 +1,77 @@
+import { useParent } from '../common/relation';
+import { VantComponent } from '../common/component';
+function emit(target, value) {
+    target.$emit('input', value);
+    target.$emit('change', value);
+}
+VantComponent({
+    field: true,
+    relation: useParent('checkbox-group'),
+    classes: ['icon-class', 'label-class'],
+    props: {
+        value: Boolean,
+        disabled: Boolean,
+        useIconSlot: Boolean,
+        checkedColor: String,
+        labelPosition: {
+            type: String,
+            value: 'right',
+        },
+        labelDisabled: Boolean,
+        shape: {
+            type: String,
+            value: 'round',
+        },
+        iconSize: {
+            type: null,
+            value: 20,
+        },
+    },
+    data: {
+        parentDisabled: false,
+        direction: 'vertical',
+    },
+    methods: {
+        emitChange(value) {
+            if (this.parent) {
+                this.setParentValue(this.parent, value);
+            }
+            else {
+                emit(this, value);
+            }
+        },
+        toggle() {
+            const { parentDisabled, disabled, value } = this.data;
+            if (!disabled && !parentDisabled) {
+                this.emitChange(!value);
+            }
+        },
+        onClickLabel() {
+            const { labelDisabled, parentDisabled, disabled, value } = this.data;
+            if (!disabled && !labelDisabled && !parentDisabled) {
+                this.emitChange(!value);
+            }
+        },
+        setParentValue(parent, value) {
+            const parentValue = parent.data.value.slice();
+            const { name } = this.data;
+            const { max } = parent.data;
+            if (value) {
+                if (max && parentValue.length >= max) {
+                    return;
+                }
+                if (parentValue.indexOf(name) === -1) {
+                    parentValue.push(name);
+                    emit(parent, parentValue);
+                }
+            }
+            else {
+                const index = parentValue.indexOf(name);
+                if (index !== -1) {
+                    parentValue.splice(index, 1);
+                    emit(parent, parentValue);
+                }
+            }
+        },
+    },
+});

+ 6 - 0
wxcomponents/vant/checkbox/index.json

@@ -0,0 +1,6 @@
+{
+  "component": true,
+  "usingComponents": {
+    "van-icon": "../icon/index"
+  }
+}

+ 31 - 0
wxcomponents/vant/checkbox/index.wxml

@@ -0,0 +1,31 @@
+<wxs src="../wxs/utils.wxs" module="utils" />
+<wxs src="./index.wxs" module="computed" />
+
+<view class="{{ utils.bem('checkbox', [{ horizontal: direction === 'horizontal' }]) }} custom-class">
+  <view
+    wx:if="{{ labelPosition === 'left' }}"
+    class="label-class {{ utils.bem('checkbox__label', [labelPosition, { disabled: disabled || parentDisabled }]) }}"
+    bindtap="onClickLabel"
+  >
+    <slot />
+  </view>
+  <view class="van-checkbox__icon-wrap" bindtap="toggle">
+    <slot wx:if="{{ useIconSlot }}" name="icon" />
+    <van-icon
+      wx:else
+      name="success"
+      size="0.8em"
+      class="{{ utils.bem('checkbox__icon', [shape, { disabled: disabled || parentDisabled, checked: value }]) }}"
+      style="{{ computed.iconStyle(checkedColor, value, disabled, parentDisabled, iconSize) }}"
+      custom-class="icon-class"
+      custom-style="line-height: 1.25em;"
+    />
+  </view>
+  <view
+    wx:if="{{ labelPosition === 'right' }}"
+    class="label-class {{ utils.bem('checkbox__label', [labelPosition, { disabled: disabled || parentDisabled }]) }}"
+    bindtap="onClickLabel"
+  >
+    <slot />
+  </view>
+</view>

+ 20 - 0
wxcomponents/vant/checkbox/index.wxs

@@ -0,0 +1,20 @@
+/* eslint-disable */
+var style = require('../wxs/style.wxs');
+var addUnit = require('../wxs/add-unit.wxs');
+
+function iconStyle(checkedColor, value, disabled, parentDisabled, iconSize) {
+  var styles = {
+    'font-size': addUnit(iconSize),
+  };
+
+  if (checkedColor && value && !disabled && !parentDisabled) {
+    styles['border-color'] = checkedColor;
+    styles['background-color'] = checkedColor;
+  }
+
+  return style(styles);
+}
+
+module.exports = {
+  iconStyle: iconStyle,
+};

File diff suppressed because it is too large
+ 1 - 0
wxcomponents/vant/checkbox/index.wxss


+ 4 - 0
wxcomponents/vant/circle/canvas.d.ts

@@ -0,0 +1,4 @@
+/// <reference types="miniprogram-api-typings" />
+type CanvasContext = WechatMiniprogram.CanvasContext;
+export declare function adaptor(ctx: CanvasContext & Record<string, unknown>): CanvasContext;
+export {};

+ 43 - 0
wxcomponents/vant/circle/canvas.js

@@ -0,0 +1,43 @@
+export function adaptor(ctx) {
+    // @ts-ignore
+    return Object.assign(ctx, {
+        setStrokeStyle(val) {
+            ctx.strokeStyle = val;
+        },
+        setLineWidth(val) {
+            ctx.lineWidth = val;
+        },
+        setLineCap(val) {
+            ctx.lineCap = val;
+        },
+        setFillStyle(val) {
+            ctx.fillStyle = val;
+        },
+        setFontSize(val) {
+            ctx.font = String(val);
+        },
+        setGlobalAlpha(val) {
+            ctx.globalAlpha = val;
+        },
+        setLineJoin(val) {
+            ctx.lineJoin = val;
+        },
+        setTextAlign(val) {
+            ctx.textAlign = val;
+        },
+        setMiterLimit(val) {
+            ctx.miterLimit = val;
+        },
+        setShadow(offsetX, offsetY, blur, color) {
+            ctx.shadowOffsetX = offsetX;
+            ctx.shadowOffsetY = offsetY;
+            ctx.shadowBlur = blur;
+            ctx.shadowColor = color;
+        },
+        setTextBaseline(val) {
+            ctx.textBaseline = val;
+        },
+        createCircularGradient() { },
+        draw() { },
+    });
+}

+ 1 - 0
wxcomponents/vant/circle/index.d.ts

@@ -0,0 +1 @@
+export {};

+ 197 - 0
wxcomponents/vant/circle/index.js

@@ -0,0 +1,197 @@
+import { BLUE, WHITE } from '../common/color';
+import { VantComponent } from '../common/component';
+import { getSystemInfoSync } from '../common/utils';
+import { isObj } from '../common/validator';
+import { canIUseCanvas2d } from '../common/version';
+import { adaptor } from './canvas';
+function format(rate) {
+    return Math.min(Math.max(rate, 0), 100);
+}
+const PERIMETER = 2 * Math.PI;
+const BEGIN_ANGLE = -Math.PI / 2;
+const STEP = 1;
+VantComponent({
+    props: {
+        text: String,
+        lineCap: {
+            type: String,
+            value: 'round',
+        },
+        value: {
+            type: Number,
+            value: 0,
+            observer: 'reRender',
+        },
+        speed: {
+            type: Number,
+            value: 50,
+        },
+        size: {
+            type: Number,
+            value: 100,
+            observer() {
+                this.drawCircle(this.currentValue);
+            },
+        },
+        fill: String,
+        layerColor: {
+            type: String,
+            value: WHITE,
+        },
+        color: {
+            type: null,
+            value: BLUE,
+            observer() {
+                this.setHoverColor().then(() => {
+                    this.drawCircle(this.currentValue);
+                });
+            },
+        },
+        type: {
+            type: String,
+            value: '',
+        },
+        strokeWidth: {
+            type: Number,
+            value: 4,
+        },
+        clockwise: {
+            type: Boolean,
+            value: true,
+        },
+    },
+    data: {
+        hoverColor: BLUE,
+    },
+    methods: {
+        getContext() {
+            const { type, size } = this.data;
+            if (type === '' || !canIUseCanvas2d()) {
+                const ctx = wx.createCanvasContext('van-circle', this);
+                return Promise.resolve(ctx);
+            }
+            const dpr = getSystemInfoSync().pixelRatio;
+            return new Promise((resolve) => {
+                wx.createSelectorQuery()
+                    .in(this)
+                    .select('#van-circle')
+                    .node()
+                    .exec((res) => {
+                    const canvas = res[0].node;
+                    const ctx = canvas.getContext(type);
+                    if (!this.inited) {
+                        this.inited = true;
+                        canvas.width = size * dpr;
+                        canvas.height = size * dpr;
+                        ctx.scale(dpr, dpr);
+                    }
+                    resolve(adaptor(ctx));
+                });
+            });
+        },
+        setHoverColor() {
+            const { color, size } = this.data;
+            if (isObj(color)) {
+                return this.getContext().then((context) => {
+                    if (!context)
+                        return;
+                    const LinearColor = context.createLinearGradient(size, 0, 0, 0);
+                    Object.keys(color)
+                        .sort((a, b) => parseFloat(a) - parseFloat(b))
+                        .map((key) => LinearColor.addColorStop(parseFloat(key) / 100, color[key]));
+                    this.hoverColor = LinearColor;
+                });
+            }
+            this.hoverColor = color;
+            return Promise.resolve();
+        },
+        presetCanvas(context, strokeStyle, beginAngle, endAngle, fill) {
+            const { strokeWidth, lineCap, clockwise, size } = this.data;
+            const position = size / 2;
+            const radius = position - strokeWidth / 2;
+            context.setStrokeStyle(strokeStyle);
+            context.setLineWidth(strokeWidth);
+            context.setLineCap(lineCap);
+            context.beginPath();
+            context.arc(position, position, radius, beginAngle, endAngle, !clockwise);
+            context.stroke();
+            if (fill) {
+                context.setFillStyle(fill);
+                context.fill();
+            }
+        },
+        renderLayerCircle(context) {
+            const { layerColor, fill } = this.data;
+            this.presetCanvas(context, layerColor, 0, PERIMETER, fill);
+        },
+        renderHoverCircle(context, formatValue) {
+            const { clockwise } = this.data;
+            // 结束角度
+            const progress = PERIMETER * (formatValue / 100);
+            const endAngle = clockwise
+                ? BEGIN_ANGLE + progress
+                : 3 * Math.PI - (BEGIN_ANGLE + progress);
+            this.presetCanvas(context, this.hoverColor, BEGIN_ANGLE, endAngle);
+        },
+        drawCircle(currentValue) {
+            const { size } = this.data;
+            this.getContext().then((context) => {
+                if (!context)
+                    return;
+                context.clearRect(0, 0, size, size);
+                this.renderLayerCircle(context);
+                const formatValue = format(currentValue);
+                if (formatValue !== 0) {
+                    this.renderHoverCircle(context, formatValue);
+                }
+                context.draw();
+            });
+        },
+        reRender() {
+            // tofector 动画暂时没有想到好的解决方案
+            const { value, speed } = this.data;
+            if (speed <= 0 || speed > 1000) {
+                this.drawCircle(value);
+                return;
+            }
+            this.clearMockInterval();
+            this.currentValue = this.currentValue || 0;
+            const run = () => {
+                this.interval = setTimeout(() => {
+                    if (this.currentValue !== value) {
+                        if (Math.abs(this.currentValue - value) < STEP) {
+                            this.currentValue = value;
+                        }
+                        else if (this.currentValue < value) {
+                            this.currentValue += STEP;
+                        }
+                        else {
+                            this.currentValue -= STEP;
+                        }
+                        this.drawCircle(this.currentValue);
+                        run();
+                    }
+                    else {
+                        this.clearMockInterval();
+                    }
+                }, 1000 / speed);
+            };
+            run();
+        },
+        clearMockInterval() {
+            if (this.interval) {
+                clearTimeout(this.interval);
+                this.interval = null;
+            }
+        },
+    },
+    mounted() {
+        this.currentValue = this.data.value;
+        this.setHoverColor().then(() => {
+            this.drawCircle(this.currentValue);
+        });
+    },
+    destroyed() {
+        this.clearMockInterval();
+    },
+});

+ 0 - 0
wxcomponents/vant/circle/index.json


Some files were not shown because too many files changed in this diff