Browse Source

init

master
kola-web 1 month ago
parent
commit
506c1d430b
  1. 12
      .editorconfig
  2. 5
      .gitignore
  3. 10
      .neoconf.json
  4. 28
      .prettierrc
  5. 2
      .trae/rules/project_rules.md
  6. 11
      babel.config.json
  7. 5
      dist.nu
  8. 8
      dist.ps1
  9. 5
      dist.sh
  10. 29
      eslint.config.mjs
  11. 32
      package.json
  12. 3339
      pnpm-lock.yaml
  13. 75
      project.config.json
  14. 771
      project.private.config.json
  15. 72
      src/api/request.ts
  16. 173
      src/app.json
  17. 87
      src/app.scss
  18. 643
      src/app.ts
  19. 144
      src/components/calendar/core.js
  20. 12
      src/components/calendar/helper.js
  21. 1
      src/components/calendar/index.d.ts
  22. 258
      src/components/calendar/index.js
  23. 3
      src/components/calendar/index.json
  24. 116
      src/components/calendar/index.wxml
  25. 247
      src/components/calendar/index.wxss
  26. 212
      src/components/calendar/plugins/holidays/holidays-map.js
  27. 1
      src/components/calendar/plugins/holidays/index.d.ts
  28. 201
      src/components/calendar/plugins/holidays/index.js
  29. 1
      src/components/calendar/plugins/index.d.ts
  30. 18
      src/components/calendar/plugins/index.js
  31. 277
      src/components/calendar/plugins/preset/base.js
  32. 69
      src/components/calendar/plugins/preset/get-calendar-data.js
  33. 1
      src/components/calendar/plugins/preset/index.d.ts
  34. 9
      src/components/calendar/plugins/preset/index.js
  35. 1
      src/components/calendar/plugins/selectable.d.ts
  36. 221
      src/components/calendar/plugins/selectable.js
  37. 1036
      src/components/calendar/plugins/solarLunar/convertSolarLunar.js
  38. 1
      src/components/calendar/plugins/solarLunar/index.d.ts
  39. 59
      src/components/calendar/plugins/solarLunar/index.js
  40. 305
      src/components/calendar/plugins/time-range.js
  41. 1
      src/components/calendar/plugins/todo.d.ts
  42. 135
      src/components/calendar/plugins/todo.js
  43. 432
      src/components/calendar/plugins/week.js
  44. 49
      src/components/calendar/render.js
  45. 29
      src/components/calendar/theme/iconfont.wxss
  46. 61
      src/components/calendar/theme/theme-default.wxss
  47. 58
      src/components/calendar/theme/theme-elegant.wxss
  48. 90
      src/components/calendar/theme/theme-nuohe.wxss
  49. 275
      src/components/calendar/utils/index.js
  50. 23
      src/components/calendar/utils/logger.js
  51. 30
      src/components/calendar/utils/wxData.js
  52. 160
      src/components/customPoster/README.md
  53. 101
      src/components/customPoster/index.js
  54. 3
      src/components/customPoster/index.json
  55. 53
      src/components/customPoster/index.scss
  56. 43
      src/components/customPoster/index.wxml
  57. 0
      src/components/customPoster/wxml2canvas/README.md
  58. 1692
      src/components/customPoster/wxml2canvas/index.js
  59. 1
      src/components/customPoster/wxml2canvas/index.js.map
  60. 160
      src/components/customTable/README.md
  61. 240
      src/components/customTable/index.js
  62. 3
      src/components/customTable/index.json
  63. 141
      src/components/customTable/index.scss
  64. 55
      src/components/customTable/index.wxml
  65. 0
      src/components/customTable/wxml2canvas/README.md
  66. 1691
      src/components/customTable/wxml2canvas/index.js
  67. 1
      src/components/customTable/wxml2canvas/index.js.map
  68. 267
      src/components/ec-canvas/ec-canvas.js
  69. 4
      src/components/ec-canvas/ec-canvas.json
  70. 4
      src/components/ec-canvas/ec-canvas.wxml
  71. 5
      src/components/ec-canvas/ec-canvas.wxss
  72. 111
      src/components/ec-canvas/wx-canvas.js
  73. 158
      src/components/freeAudio/free-audio.vue
  74. 176
      src/components/freeAudio/index.js
  75. 5
      src/components/freeAudio/index.json
  76. 54
      src/components/freeAudio/index.scss
  77. 26
      src/components/freeAudio/index.wxml
  78. 7
      src/components/loginNavbar/index.json
  79. 24
      src/components/loginNavbar/index.scss
  80. 23
      src/components/loginNavbar/index.ts
  81. 7
      src/components/loginNavbar/index.wxml
  82. 250
      src/components/navBar/navBar.js
  83. 4
      src/components/navBar/navBar.json
  84. 185
      src/components/navBar/navBar.scss
  85. 33
      src/components/navBar/navBar.wxml
  86. 7
      src/components/pageNavbar/index.json
  87. 16
      src/components/pageNavbar/index.scss
  88. 23
      src/components/pageNavbar/index.ts
  89. 7
      src/components/pageNavbar/index.wxml
  90. 28
      src/components/pagination/index.js
  91. 6
      src/components/pagination/index.json
  92. 11
      src/components/pagination/index.scss
  93. 12
      src/components/pagination/index.wxml
  94. 7
      src/components/pickerArea/index.json
  95. 144
      src/components/pickerArea/index.scss
  96. 325
      src/components/pickerArea/index.ts
  97. 87
      src/components/pickerArea/index.wxml
  98. 6
      src/components/referralFrom/index.json
  99. 95
      src/components/referralFrom/index.scss
  100. 139
      src/components/referralFrom/index.ts
  101. Some files were not shown because too many files have changed in this diff Show More

12
.editorconfig

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

5
.gitignore vendored

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
.svn
.vscode
node_modules
.idea
.DS_Store

10
.neoconf.json

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
{
"emmet_language_server": {
"init_options": {
"preferences": {
"css.intUnit": "rpx",
"css.floatUnitr": "rpx"
}
}
}
}

28
.prettierrc

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
{
"$schema": "http://json.schemastore.org/prettierrc",
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"bracketSpacing": true,
"trailingComma": "all",
"arrowParens": "always",
"endOfLine": "lf",
"htmlWhitespaceSensitivity": "ignore",
"singleAttributePerLine": false,
"overrides": [
{
"files": "*.wxml",
"options": { "parser": "html" }
},
{
"files": "*.wxss",
"options": { "parser": "css" }
},
{
"files": "*.wxs",
"options": { "parser": "babel" }
}
]
}

2
.trae/rules/project_rules.md

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
1. 本项目使用 TypeScript + scss 进行开发
2. 不使用测试框架

11
babel.config.json

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
{
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"runtime": "automatic",
"importSource": "@antv/f2"
}
]
]
}

5
dist.nu

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
cd ./src/images/
svn add . --no-ignore --force
svn ci -m "版本更新"
cd ..
cd ..

8
dist.ps1

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# Change to the src/images directory, or exit if the directory doesn't exist
Set-Location -Path ./src/images/ -ErrorAction Stop
# Add all files to svn, including ignored files and forced additions
svn add . --no-ignore --force
# Commit the changes with a message
svn ci -m "版本更新"

5
dist.sh

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
#!/bin/bash
cd ./src/images/ || exit
svn add . --no-ignore --force
svn ci -m "版本更新"
exit

29
eslint.config.mjs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
// eslint.config.mjs
import antfu from '@antfu/eslint-config'
import prettierConfig from 'eslint-config-prettier'
export default antfu(
{
env: {
es6: true,
},
stylistic: false,
parserOptions: { project: ['./tsconfig.json'] },
globals: {
wx: true,
App: true,
Page: true,
getCurrentPages: true,
getApp: true,
Component: true,
requirePlugin: true,
requireMiniProgram: true,
},
rules: {
'eslint-comments/no-unlimited-disable': 'off',
'ts/no-require-imports': 'off',
eqeqeq: 'off',
},
},
prettierConfig,
)

32
package.json

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
{
"name": "nuohe",
"version": "1.0.0",
"description": "",
"author": "",
"license": "",
"keywords": [],
"scripts": {
"beforeCompile": "babel src/pages --out-dir src/pages --only **/*.jsx",
"lint:fix": "eslint . --fix"
},
"dependencies": {
"@miniprogram-component-plus/video-swiper": "^1.0.1",
"@vant/weapp": "^1.11.2",
"dayjs": "^1.11.10",
"echarts": "^5.4.3",
"miniprogram-licia": "^1.39.2",
"mp-html": "^2.4.2",
"number-precision": "^1.6.0",
"typescript": "^5.3.3"
},
"devDependencies": {
"@antfu/eslint-config": "^3.2.0",
"@babel/cli": "^7.23.4",
"@babel/core": "^7.23.6",
"@babel/plugin-transform-react-jsx": "^7.23.4",
"eslint": "^9.9.1",
"eslint-config-prettier": "^9.1.0",
"miniprogram-api-typings": "^3.12.2",
"prettier": "^3.3.3"
}
}

3339
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

75
project.config.json

@ -0,0 +1,75 @@ @@ -0,0 +1,75 @@
{
"description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"miniprogramRoot": "./src",
"compileType": "miniprogram",
"setting": {
"useCompilerPlugins": [
"typescript",
"sass"
],
"urlCheck": true,
"coverView": true,
"es6": true,
"postcss": true,
"lazyloadPlaceholderEnable": false,
"preloadBackgroundData": false,
"minified": true,
"autoAudits": false,
"uglifyFileName": false,
"uploadWithSourceMap": true,
"enhance": true,
"useMultiFrameRuntime": true,
"showShadowRootInWxmlPanel": true,
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "package.json",
"miniprogramNpmDistDir": "./src"
}
],
"minifyWXSS": true,
"useStaticServer": true,
"showES6CompileOption": false,
"checkInvalidKey": true,
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"disableUseStrict": false,
"minifyWXML": true,
"localPlugins": false,
"condition": false,
"ignoreUploadUnusedFiles": true
},
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
},
"packOptions": {
"ignore": [
{
"value": "images/*.*",
"type": "glob"
},
{
"value": "images/**/*.*",
"type": "glob"
},
{
"value": "images/**/**/*.*",
"type": "glob"
}
],
"include": []
},
"appid": "wxc3cdb3c4d4f62cea",
"scripts": {
"beforeCompile": "pnpm run beforeCompile",
"beforePreview": "pnpm run beforeCompile",
"beforeUpload": "pnpm run beforeCompile"
}
}

771
project.private.config.json

@ -0,0 +1,771 @@ @@ -0,0 +1,771 @@
{
"projectname": "zaiding-miniprogram",
"setting": {
"compileHotReLoad": true,
"urlCheck": true
},
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"condition": {
"miniprogram": {
"list": [
{
"name": "腾讯健康药箱",
"pathName": "pages/demo/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "知识库-视频详情",
"pathName": "pages/repositoryVideoDetail/index",
"query": "id=3",
"launchMode": "default",
"scene": null
},
{
"name": "医生-转诊医生详情",
"pathName": "doctor/pages/d_transferDetail/index",
"query": "type=1",
"launchMode": "default",
"scene": null
},
{
"name": "医生-转诊推荐记录",
"pathName": "doctor/pages/d_transferLog/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生-转诊",
"pathName": "doctor/pages/d_transfer/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/adlResult/index",
"pathName": "pages/adlResult/index",
"query": "id=849&reset=1&m_d=undefined&nrdl=undefined&wy=0&idafa=1",
"launchMode": "default",
"scene": null
},
{
"name": "adl结果",
"pathName": "pages/adlResult/index",
"query": "id=658&reset=1&m_d=undefined&nrdl=undefined",
"launchMode": "default",
"scene": null
},
{
"name": "个人信息及隐私保护政策",
"pathName": "doc/pages/doc1/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "公共-知情录入结果",
"pathName": "public/pages/informedResult/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "公共-知情录入",
"pathName": "public/pages/informed/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "公共-知情同意书",
"pathName": "public/pages/informedStart/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端-患者列表",
"pathName": "doctor/pages/d_patientList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "就诊地图",
"pathName": "pages/doctor/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "doctor/pages/d_patientDetail/index",
"pathName": "doctor/pages/d_patientDetail/index",
"query": "id=1123",
"launchMode": "default",
"scene": null
},
{
"name": "复诊记录",
"pathName": "pages/referral/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端-自定义导出",
"pathName": "doctor/pages/d_customExport/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端-报告中心",
"pathName": "doctor/pages/d_exportList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "test",
"pathName": "pages/webview/index",
"query": "es=201503",
"launchMode": "default",
"scene": null
},
{
"name": "外部-首页",
"pathName": "pages/index/index",
"query": "es=201501",
"launchMode": "default",
"scene": null
},
{
"name": "外部-webview",
"pathName": "pages/webview/index",
"query": "es=201503",
"launchMode": "default",
"scene": null
},
{
"name": "外部-adl",
"pathName": "pages/adl/index",
"query": "m_d=1&es=201502",
"launchMode": "default",
"scene": null
},
{
"name": "皮下剂型-video",
"pathName": "gift/pages/cutaneousVideo/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "皮下剂型",
"pathName": "gift/pages/cutaneous/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_任务详情",
"pathName": "doctor/pages/d_taskDetail/index",
"query": "id=10",
"launchMode": "default",
"scene": null
},
{
"name": "医生端adl结果页",
"pathName": "octor/pages/d_adlResult/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_患者详情",
"pathName": "doctor/pages/d_patientDetail/index",
"query": "id=940",
"launchMode": "default",
"scene": null
},
{
"name": "会议详情",
"pathName": "pages/liveDetail/index",
"query": "id=95",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_互动页_医生视角",
"pathName": "doctor/pages/d_interactiveDoctor/index",
"query": "patientId=940",
"launchMode": "default",
"scene": null
},
{
"name": "bind",
"pathName": "pages/start/index",
"query": "scene=doctorId%3D2%26inviteChan%3D1",
"launchMode": "default",
"scene": 1047
},
{
"name": "医生端_中转页",
"pathName": "doctor/pages/d_trans/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_切换医生",
"pathName": "doctor/pages/d_changeDoctor/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "互动页_患者视角",
"pathName": "pages/interactivePatient/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_新建任务",
"pathName": "doctor/pages/d_createTask/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_任务列表",
"pathName": "doctor/pages/d_taskList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_首页",
"pathName": "doctor/pages/d_home/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_邀约患者",
"pathName": "doctor/pages/d_invite/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_更换手机号",
"pathName": "doctor/pages/d_changePhone/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_个人信息",
"pathName": "doctor/pages/d_userInfo/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_我的",
"pathName": "doctor/pages/d_my/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_患者列表",
"pathName": "doctor/pages/d_patient/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端-互动页",
"pathName": "doctor/pages/d_interactive/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生端_登录页",
"pathName": "doctor/pages/d_login/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的-用药患者审核失败",
"pathName": "gift/pages/vipReject/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的-用药患者审核中(已关注)",
"pathName": "gift/pages/vipStartPending/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的-用药患者审核中",
"pathName": "gift/pages/vipPending/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的->用药患者",
"pathName": "gift/pages/vipCert/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的->用药患者失败",
"pathName": "gift/pages/vipReject/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医院详情",
"pathName": "pages/hospital/index",
"query": "id=3",
"launchMode": "default",
"scene": null
},
{
"name": "能量规则",
"pathName": "gift/pages/scoreRule/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "有奖征文",
"pathName": "pages/storyGuide/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "订单详情",
"pathName": "gift/pages/orderDetail/index",
"query": "id=4",
"launchMode": "default",
"scene": null
},
{
"name": "下单成功",
"pathName": "gift/pages/orderEnd/index",
"query": "id=4",
"launchMode": "default",
"scene": null
},
{
"name": "确认订单",
"pathName": "gift/pages/conformOrder/index",
"query": "id=4",
"launchMode": "default",
"scene": null
},
{
"name": "礼品详情",
"pathName": "gift/pages/giftDetail/index",
"query": "id=3",
"launchMode": "default",
"scene": null
},
{
"name": "nrdl详情",
"pathName": "pages/nrdlDetail/index",
"query": "id=2",
"launchMode": "default",
"scene": null
},
{
"name": "能量明细",
"pathName": "gift/pages/priceDetail/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的礼品",
"pathName": "gift/pages/myGift/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "地址编辑",
"pathName": "gift/pages/siteEdit/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "地址管理",
"pathName": "gift/pages/siteList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "礼品中心",
"pathName": "gift/pages/giftList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "城市列表",
"pathName": "pages/nrdlTable/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "dtp药房",
"pathName": "gift/pages/dtpDurg/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "输液中心",
"pathName": "pages/infusionCenter/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "首页",
"pathName": "pages/index/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "nrdl",
"pathName": "pages/nrdl/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "随访知情同意书",
"pathName": "pages/taskAgreement/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "登录",
"pathName": "pages/login/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "知情同意书",
"pathName": "pages/privacyAgreement/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "录入信息",
"pathName": "pages/enterInfo/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "上传诊断证明",
"pathName": "pages/uploadCert/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "初始进入-审核失败",
"pathName": "pages/startReject/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "初始进入-审核中",
"pathName": "pages/startPending/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "诊断证明成功",
"pathName": "pages/certReslove/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "诊断证明审核中",
"pathName": "pages/certPending/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "专属服务用户进入",
"pathName": "pages/vipLogin/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "奇迹故事",
"pathName": "pages/story/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "奇迹故事详情",
"pathName": "pages/publishStoryDetail/index",
"query": "id=5",
"launchMode": "default",
"scene": null
},
{
"name": "奇迹故事编辑",
"pathName": "pages/storyEnter/index",
"query": "storyleadin=",
"launchMode": "default",
"scene": null
},
{
"name": "我的",
"pathName": "pages/my/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "知识库",
"pathName": "pages/repository/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "会议课堂",
"pathName": "pages/live/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "会议结果",
"pathName": "pages/liveResult/index",
"query": "id=19",
"launchMode": "default",
"scene": null
},
{
"name": "签到",
"pathName": "pages/signIn/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的故事",
"pathName": "pages/storyList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的故事详情",
"pathName": "pages/storyDetail/index",
"query": "id=88",
"launchMode": "default",
"scene": null
},
{
"name": "我的收藏",
"pathName": "pages/mySave/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我报名的会议",
"pathName": "pages/myLive/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "知识库详情",
"pathName": "pages/repositoryDetail/index",
"query": "id=16",
"launchMode": "default",
"scene": null
},
{
"name": "公众号关注",
"pathName": "pages/thePublic/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "adl",
"pathName": "pages/adl/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "adl分享",
"pathName": "pages/adlShare/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "adl测评",
"pathName": "pages/adlTest/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "健康报告",
"pathName": "gift/pages/myHealthRecord/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "奇迹故事编辑结果",
"pathName": "pages/storyEnterResult/index",
"query": "id=2",
"launchMode": "default",
"scene": null
},
{
"name": "配置头像",
"pathName": "pages/getUserInfo/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "个人信息",
"pathName": "pages/personalInformation/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "我的亲友",
"pathName": "pages/family/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "修改手机号",
"pathName": "pages/changePhone/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "切换账号",
"pathName": "pages/changeUser/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "家庭成员",
"pathName": "pages/familyList/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "亲友扫码",
"pathName": "pages/familyScan/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "微页面",
"pathName": "pages/smallPage/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "签到",
"pathName": "pages/signIn/index",
"query": "scene=id%3D46",
"launchMode": "default",
"scene": 1047
},
{
"name": "健康报告图表",
"pathName": "gift/pages/myHealthRecordChart/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "医生详情",
"pathName": "pages/doctorDetail/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "用药记录",
"pathName": "pages/drugRecord/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "商保项目",
"pathName": "pages/comIns/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "微医-webview",
"pathName": "pages/webview/index",
"query": "",
"launchMode": "default",
"scene": null
}
]
}
},
"libVersion": "3.5.8"
}

72
src/api/request.ts

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
interface IGlobalParams {
gUrl: string;
version: string;
}
export const request = function (
{ gUrl, version }: IGlobalParams,
{ url, method, data, header, showMsg = true, loading = false, isJSON = false, ...options }: IAgaxParams,
): Promise<any> {
return new Promise((resolve, reject) => {
if (loading) {
wx.showLoading({
title: "加载中...",
mask: true,
});
}
wx.request({
header: {
loginState: getApp().globalData.loginState,
...header,
},
url: gUrl + url,
method: method,
data: {
loginState: getApp().globalData.loginState,
...(data as object),
},
...options,
success(res: any) {
const { code, data } = res.data;
if (isJSON) {
resolve(res.data);
} else if (code === 0) {
resolve(data);
} else if (showMsg) {
const msg = errPicker(res.data);
if (loading) {
setTimeout(() => {
wx.showToast({
title: msg,
icon: "none",
});
}, 30);
} else {
wx.showToast({
title: msg,
icon: "none",
});
reject(res);
}
} else {
reject(res);
}
},
fail(err) {
reject(err);
},
complete() {
if (loading) {
wx.hideLoading();
}
},
});
});
};
function errPicker(err) {
if (typeof err === "string") {
return err;
}
return err.data || err.msg || err.errMsg || (err.detail && err.detail.errMsg) || "未知错误";
}

173
src/app.json

@ -0,0 +1,173 @@ @@ -0,0 +1,173 @@
{
"$schema": "https://dldir1.qq.com/WechatWebDev/editor-extension/wx-json/app.schema.json",
"pages": [
"pages/start/index",
"pages/index/index",
"pages/login/index",
"pages/vipLogin/index",
"pages/vipLoginReject/index",
"pages/startReject/index",
"pages/startPending/index",
"pages/story/index",
"pages/publishStoryDetail/index",
"pages/storyEnter/index",
"pages/storyEnterResult/index",
"pages/storyList/index",
"pages/storyGuide/index",
"pages/storyDetail/index",
"pages/my/index",
"pages/mySave/index",
"pages/myLive/index",
"pages/getUserInfo/index",
"pages/personalInformation/index",
"pages/changePhone/index",
"pages/changeUser/index",
"pages/cancellation/index",
"pages/family/index",
"pages/familyList/index",
"pages/familyScan/index",
"pages/thePublic/index",
"pages/privacyAgreement/index",
"pages/taskAgreement/index",
"pages/enterInfo/index",
"pages/uploadCert/index",
"pages/certReslove/index",
"pages/certPending/index",
"pages/repository/index",
"pages/repositoryDetail/index",
"pages/repositoryVideoDetail/index",
"pages/live/index",
"pages/liveDetail/index",
"pages/liveResult/index",
"pages/signIn/index",
"pages/adl/index",
"pages/adlShare/index",
"pages/adlTest/index",
"pages/adlResult/index",
"pages/smallPage/index",
"pages/hospital/index",
"pages/doctor/index",
"pages/doctorDetail/index",
"pages/drugRecord/index",
"pages/comIns/index",
"pages/webview/index",
"pages/nrdl/index",
"pages/nrdlDetail/index",
"pages/infusionCenter/index",
"pages/nrdlTable/index",
"pages/interactivePatient/index",
"pages/referral/index",
"pages/demo/index"
],
"subpackages": [
{
"root": "gift",
"pages": [
"pages/giftList/index",
"pages/giftDetail/index",
"pages/conformOrder/index",
"pages/siteList/index",
"pages/siteEdit/index",
"pages/orderEnd/index",
"pages/orderDetail/index",
"pages/myGift/index",
"pages/priceDetail/index",
"pages/scoreRule/index",
"pages/dtpDurg/index",
"pages/vipCert/index",
"pages/vipReject/index",
"pages/vipPending/index",
"pages/vipStartPending/index",
"pages/myHealthRecord/index",
"pages/myHealthRecordChart/index",
"pages/cutaneous/index",
"pages/cutaneousDetail/index",
"pages/cutaneousVideo/index"
]
},
{
"root": "doctor",
"pages": [
"pages/d_login/index",
"pages/d_home/index",
"pages/d_patient/index",
"pages/d_interactive/index",
"pages/d_my/index",
"pages/d_userInfo/index",
"pages/d_changePhone/index",
"pages/d_invite/index",
"pages/d_taskDetail/index",
"pages/d_taskList/index",
"pages/d_interactiveDoctor/index",
"pages/d_patientDetail/index",
"pages/d_createTask/index",
"pages/d_changeDoctor/index",
"pages/d_trans/index",
"pages/d_exportList/index",
"pages/d_customExport/index",
"pages/d_customExportMiddle/index",
"pages/d_patientList/index",
"pages/d_transfer/index",
"pages/d_transferLog/index",
"pages/d_transferDetail/index"
]
},
{
"root": "public",
"pages": ["pages/informedStart/index", "pages/informed/index", "pages/informedResult/index"]
},
{
"root": "doc",
"pages": ["pages/doc1/index", "pages/doc2/index"]
},
{
"root": "resource",
"independent": true,
"pages": []
}
],
"tabBar": {
"custom": true,
"list": [
{
"pagePath": "pages/index/index",
"text": "首页"
},
{
"pagePath": "pages/repository/index",
"text": "知识库"
},
{
"pagePath": "pages/live/index",
"text": "会议课堂"
},
{
"pagePath": "pages/story/index",
"text": "奇迹故事"
},
{
"pagePath": "pages/my/index",
"text": "我的"
}
]
},
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json",
"resolveAlias": {
"@/*": "/*"
},
"usingComponents": {
"pagination": "/components/pagination/index"
},
"requiredPrivateInfos": ["getFuzzyLocation"],
"permission": {
"scope.userFuzzyLocation": {
"desc": "获取您的当前位置,用于模糊匹配您附近的活动"
}
}
}

87
src/app.scss

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
.page-meat {
background-repeat: no-repeat;
}
.van-tabbar {
background: rgba(255, 255, 255, 0.8);
box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(0, 0, 0, 0.12);
}
.van-tabbar-item__icon {
margin-bottom: 0 !important;
}
.clearfix:after {
content: '020';
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix {
/* 触发 hasLayout */
zoom: 1;
}
.scroll::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
.theme1 {
--name: #fff;
}
.theme2 {
--name: #000;
}
.van-hairline--bottom:after {
border-bottom-width: 0px !important;
}
.extend-via-pseudo-elem {
position: relative;
overflow: visible;
&::before {
content: '';
position: absolute;
top: -20rpx;
right: -20rpx;
bottom: -20rpx;
left: -20rpx;
}
}
view {
word-break: break-all;
}
.mp-html {
video {
// width: 100% !important;
// object-fit: contain !important;
// max-height: 350rpx;
}
}
.van-tabbar {
display: flex;
justify-content: space-around;
.van-tabbar-item {
}
[is='miniprogram_npm/@vant/weapp/tabbar-item/index'] {
flex: none !important;
}
}
// .tab-item:first-of-type,
// .tab-item:last-of-type {
// .van-tabbar-item {
// width: 2em;
// }
// }
.pre-line {
white-space: pre-line;
}

643
src/app.ts

@ -0,0 +1,643 @@ @@ -0,0 +1,643 @@
/* eslint-disable */
// app.ts
import { request } from './api/request'
import { parseScene } from './utils/util'
const licia = require('miniprogram-licia')
const dayjs = require('dayjs')
require('/utils/dayjs/day-zh-cn.js')
const relativeTime = require('/utils/dayjs/relativeTime.js')
dayjs.locale('zh-cn') // 全局使用
dayjs.extend(relativeTime)
// page
import page from '@/utils/page'
App<IAppOption>({
globalData: {
// dev
// appid:wxc3cdb3c4d4f62cea
url: 'https://m.igg4.hbraas.com',
upFileUrl: 'https://m.igg4.hbraas.com/',
imageUrl: 'https://m.igg4.hbraas.com/igg4/',
// pro
// appid:wx96f45ca4f1fa36ec
// url: 'https://m.igg4.hbsaas.com',
// upFileUrl: 'https://m.igg4.hbsaas.com/',
// imageUrl: 'https://m.igg4.hbsaas.com/igg4/',
//login
registrationSource: 0,
registChannel: 0,
regBusinessId: '',
IsAliQiWei: 0,
scene: {},
listeners: [],
Timestamp: new Date().getTime(),
loginState: '',
isLogin: 0, // 0 未登录 1 已登录
isReg: '0',
WorkerId: '',
loginType: '', // 1:患者 2:医生
first: true,
// anyWhere: true,
anyWhere: false,
userInfo: {},
DiagnoseType: [
{
id: 1,
name: 'Ⅰ型 眼肌无力,可伴闭眼无力,其它肌群肌力正常',
},
{
id: 2,
name: 'Ⅱa 型 主要累及四肢肌或(和)躯干肌,可有较轻的咽喉肌受累',
},
{
id: 3,
name: 'Ⅱb 型 主要累及咽喉肌或(和)呼吸肌,可有轻度或相同的四肢肌或(和)躯干肌受累',
},
{
id: 4,
name: 'Ⅲa 型 主要累及四肢肌或(和)躯干肌,可有较轻的咽喉肌受累',
},
{
id: 5,
name: 'Ⅲb 型 主要累及咽喉肌或(和)呼吸肌,可有轻度或相同的四肢肌或(和)躯干肌受累',
},
{
id: 6,
name: 'Ⅳa 型 主要累及四肢肌或(和)躯干肌受累,可有较轻的咽喉肌受累',
},
{
id: 7,
name: 'Ⅳb 型 主要累及咽喉肌或(和)呼吸肌,可有轻度或相同的四肢肌或(和)躯干肌受累',
},
{
id: 8,
name: 'Ⅴ型 气管插管,伴或不伴机械通气(除外术后常规使用);仅鼻饲而不进行气管插管的病例为Ⅳb 型',
},
{
id: 9,
name: '其它',
},
],
storyStatus: {
1: '已提交待审核',
2: '审核未通过',
3: '审核通过待校正',
4: '校正完成',
5: '已发布',
100: '审核未通过',
},
liveStatus: {
1: '待开始',
2: '会议中',
3: '已结束',
99: '取消',
100: '已删除',
},
activityStatus: {
1: '待开始',
2: '进行中',
3: '已结束',
99: '取消',
100: '已删除',
},
},
onLaunch() {
// if (options.path) {
// this.globalData.anyWhere = true;
// }
Page = page as WechatMiniprogram.Page.Constructor
wx.ajax = licia.curry(request)({ gUrl: this.globalData.url })
this.autoUpdate()
wx.setInnerAudioOption({
obeyMuteSwitch: false,
mixWithOther: false,
})
},
onShow(options) {
if (options.query.scene) {
this.globalData.anyWhere = false
this.globalData.scene = parseScene(options.query.scene) as { workerId: string }
console.log('DEBUGPRINT[317]: app.ts:129: this.globalData.scene=', this.globalData.scene)
}
this.startLogin()
},
startLogin() {
const that = this
wx.login({
success(res) {
wx.ajax({
method: 'GET',
url: '?r=igg4/user/init-login',
data: {
code: res.code,
},
}).then((res) => {
that.globalData.loginState = res.loginState
that.globalData.isReg = res.isReg
that.globalData.isLogin = res.isLogin
that.globalData.loginType = res.loginType
if (!Object.keys(that.globalData.scene)?.length) {
that.globalData.anyWhere = res.anyWhere
}
if (wx.getLaunchOptionsSync()?.query?.activity === 'NRDL') {
that.clickPush()
}
that.getUserInfo()
})
},
})
},
waitLogin({ isReg = true, loginPage = false, pub = false } = { isReg: true, loginPage: false, pub: false }) {
let time: number
const regFun = (resolve) => {
if (this.verifySys(pub)) {
if (this.globalData.loginType === 1) {
this.registrationVerification(() => resolve(), loginPage)
} else {
resolve()
}
}
}
const unRegFun = (resolve) => {
if (!this.verifySys(pub)) return
resolve()
}
return new Promise((resolve: (value?) => void) => {
if (isReg) {
if (Object.keys(this.globalData.userInfo).length) {
regFun(resolve)
return
}
time = setInterval(() => {
if (Object.keys(this.globalData.userInfo).length) {
clearInterval(time)
regFun(resolve)
}
}, 500)
return
}
this.globalData.anyWhere = true
if (this.globalData.loginState) {
unRegFun(resolve)
return
}
time = setInterval(() => {
if (this.globalData.loginState) {
clearInterval(time)
unRegFun(resolve)
}
}, 500)
})
},
registrationVerification(callback: () => void, loginPage = false) {
// 1-空白用户,2-注册用户,3-疾病患者,4-用药患者
const { PatientId, AuditStatus, isFollow, UserType } = this.globalData.userInfo
const { anyWhere, first } = this.globalData
if (loginPage || (anyWhere && first)) {
callback()
return
}
if (UserType == 4) {
this.globalData.first = false
callback()
return
}
if (UserType == 1) {
this.globalData.registChannel = 0
this.globalData.first = false
wx.reLaunch({
url: '/pages/login/index',
})
return
}
let urlKey = ''
if (!PatientId) {
urlKey = 'enterInfo'
} else if (AuditStatus == 0) {
urlKey = 'noCert'
} else if (AuditStatus == 1) {
urlKey = isFollow ? 'nopending' : 'pending'
} else if (AuditStatus == 2) {
urlKey = 'reject'
}
let navUrl = {
enterInfo: '/pages/enterInfo/index',
noCert: '/pages/enterInfo/index',
reject: '/pages/enterInfo/index',
pending: '/pages/enterInfo/index',
nopending: '/pages/enterInfo/index',
}[urlKey]
if (navUrl && first) {
this.globalData.registChannel = 0
this.globalData.first = false
wx.reLaunch({
url: navUrl,
})
this.globalData.first = false
return
}
this.globalData.first = false
callback()
},
verifySys(pub = false) {
// 1:患者 2:医生
const { loginType, isLogin, anyWhere } = this.globalData
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const url = currentPage.route
const options = currentPage.options
const ignorePath = ['pages/start/index']
// 未登录用户
if (!isLogin) {
// 医生端页面重定向到医生端登录页
if (url.includes('doctor/pages')) {
wx.reLaunch({
url: '/doctor/pages/d_login/index',
})
return false
}
// 患者端随便看看
if (anyWhere) {
return true
}
wx.reLaunch({
url: '/pages/login/index',
})
return false
}
if (ignorePath.includes(url)) return true
if (loginType === 1) {
if (url.includes('doctor/pages')) {
wx.reLaunch({
url: '/pages/index/index',
})
return false
} else {
return true
}
}
if (loginType === 2 && !pub) {
if (url.includes('doctor/pages')) {
return true
} else {
const params = Object.entries(options)
.map(([key, value]) => `${key}=${value}`)
.join('&')
wx.reLaunch({
url: '/doctor/pages/d_trans/index?path=' + encodeURIComponent(`/${url}?${params}`),
})
return false
}
}
return true
},
permissionVerification(grade = 1, registChannel = 0, backPage = null, regBusinessId = '') {
// 1-空白用户,2-注册用户,3-疾病患者,4-用药患者
const { PatientId, AuditStatus, isFollow, UserType } = this.globalData.userInfo
const that = this
this.globalData.registChannel = registChannel
this.globalData.regBusinessId = regBusinessId
let isReject = false
return new Promise((resolve, reject) => {
if (grade == 2 && Number(UserType) < 2) {
wx.reLaunch({
url: '/pages/login/index',
})
isReject = true
}
if (grade == 3 && Number(UserType) < 3) {
if (UserType == 1) {
wx.reLaunch({
url: '/pages/login/index',
})
isReject = true
}
if (UserType == 2) {
let urlKey = ''
if (!PatientId) {
urlKey = 'enterInfo'
} else if (AuditStatus == 0) {
urlKey = 'noCert'
} else if (AuditStatus == 1) {
if (backPage && (backPage as string).includes('liveResult')) {
urlKey = 'liveResult'
} else {
urlKey = isFollow ? 'nopending' : 'pending'
}
} else if (AuditStatus == 2) {
urlKey = 'reject'
}
let navUrl = {
enterInfo: '/pages/enterInfo/index',
noCert: '/pages/enterInfo/index',
reject: '/pages/enterInfo/index',
pending: '/pages/enterInfo/index',
nopending: '/pages/enterInfo/index',
liveResult: backPage,
}[urlKey]
if (urlKey == 'liveResult') {
wx.navigateTo({
url: navUrl as string,
})
} else {
wx.reLaunch({
url: navUrl as string,
})
}
isReject = true
}
}
if (grade == 4 && Number(UserType) < 4) {
wx.reLaunch({
url: '/pages/vipLogin/index',
})
isReject = true
}
if (isReject) {
that.globalData.backPage = backPage
reject(null)
return
}
resolve(null)
})
},
getUserInfo(self, update = false, callback = (_userInfo) => {}) {
if (this.globalData.userInfo?.UserId && !update) {
self?.setData({
userInfo: this.globalData.userInfo,
})
callback(this.globalData.userInfo)
return
}
wx.ajax({
method: 'GET',
url: '?r=igg4/account/info',
showMsg: false,
}).then((res) => {
res.UserType = res.UserType || 1
if (res.UserType > 2) {
this.globalData.anyWhere = false
}
this.globalData.userInfo = res
this.triggerListeners()
self?.setData({
userInfo: res,
})
const { doctorId, inviteChan, recDoctorId: recommendDoctorId } = this.globalData.scene
if (
doctorId &&
res.InviteDoctorId != doctorId &&
this.globalData.isLogin &&
res.PatientId &&
!recommendDoctorId
) {
wx.ajax({
method: 'POST',
url: '?r=igg4/account/update-doctor',
data: {
inviteDoctorId: doctorId,
inviteChannel: inviteChan,
},
})
}
if (recommendDoctorId && doctorId) {
wx.ajax({
method: 'POST',
url: '?r=igg4/doctor-recommend/add-record',
data: {
recommendDoctorId: doctorId,
beRecommendDoctorId: recommendDoctorId,
},
})
this.globalData.scene.recDoctorId = ''
this.globalData.scene.doctorId = ''
}
callback(res)
})
},
mpBehavior(data: { PageName: string; doctor?: boolean }) {
let url = '?r=igg4/mp-behavior/add'
if (data.doctor) {
url = '?r=igg4/doctor/mp-behavior/add'
}
delete data.doctor
wx.ajax({
method: 'POST',
url,
data: data,
loading: false,
showMsg: false,
}).then((res) => {
console.log('res: ', res)
})
},
// 更新用户信息
async updateUserInfo(encryptedData, iv) {
let loginState = this.globalData.loginState
const encodeEncryptedData = encodeURIComponent(encryptedData)
const encodeIv = encodeURIComponent(iv)
const {
data: { code, msg },
} = await wx.ajax({
method: 'POST',
url: '?r=eyecare/user/update-wx-user-info',
data: {
loginState,
encryptedData: encodeEncryptedData,
iv: encodeIv,
},
})
if (code !== 0) {
wx.showToast({
duration: 1500,
title: msg,
icon: 'none',
})
}
return code
},
getCode() {
return new Promise((resolve) => {
wx.login({
success: (res) => {
resolve(res.code)
},
fail: () => {
wx.showToast({
duration: 10000,
title: '发生错误请重试',
icon: 'none',
})
},
})
})
},
// 微信登录
doLogin() {
return new Promise((resolve) => {
wx.showLoading({
title: '加载中...',
mask: true,
})
this.getCode().then((code) => {
wx.ajax({
url: '?r=eyecare/user/init-login',
data: {
code,
},
}).then((res) => {
if (res.data.code === 0) {
resolve(res)
}
})
})
})
},
clickPush() {
wx.ajax({
method: 'POST',
url: '?r=igg4/nrdl/add-push-click-record',
})
},
watch: function (key, method) {
var obj = this.globalData
//加个前缀生成隐藏变量,防止死循环发生
let ori = obj[key] //obj[key]这个不能放在Object.defineProperty里
if (ori) {
//处理已经声明的变量,绑定处理
method(ori)
}
Object.defineProperty(obj, key, {
configurable: true,
enumerable: true,
set: function (value) {
this['_' + key] = value
console.log('是否会被执行2')
method(value)
},
get: function () {
// 在其它界面调用key值的时候,这里就会执行。
if (typeof this['_' + key] == 'undefined') {
if (ori) {
//这里读取数据的时候隐藏变量和 globalData设置不一样,所以要做同步处理
this['_' + key] = ori
return ori
} else {
return undefined
}
} else {
return this['_' + key]
}
},
})
},
// 注册监听器函数的方法
registerListener: function (listener: () => {}) {
this.globalData.listeners.push(listener)
},
// 触发监听器函数的方法
triggerListeners: function () {
var listeners = this.globalData.listeners
for (var i = 0; i < listeners.length; i++) {
listeners[i]()
}
},
autoUpdate: function () {
var self = this
// 获取小程序更新机制兼容
if (wx.canIUse('getUpdateManager')) {
const updateManager = wx.getUpdateManager()
//1. 检查小程序是否有新版本发布
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
if (res.hasUpdate) {
//检测到新版本,需要更新,给出提示
wx.showModal({
title: '更新提示',
content: '检测到新版本,是否下载新版本并重启小程序?',
success: function (res) {
if (res.confirm) {
//2. 用户确定下载更新小程序,小程序下载及更新静默进行
self.downLoadAndUpdate(updateManager)
} else if (res.cancel) {
//用户点击取消按钮的处理,如果需要强制更新,则给出二次弹窗,如果不需要,则这里的代码都可以删掉了
wx.showModal({
title: '温馨提示~',
content: '本次版本更新涉及到新的功能添加,旧版本无法正常访问的哦~',
showCancel: false, //隐藏取消按钮
confirmText: '确定更新', //只保留确定更新按钮
success: function (res) {
if (res.confirm) {
//下载新版本,并重新应用
self.downLoadAndUpdate(updateManager)
}
},
})
}
},
})
}
})
} else {
// 如果希望用户在最新版本的客户端上体验您的小程序,可以这样子提示
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。',
})
}
},
downLoadAndUpdate: function (updateManager) {
//静默下载更新小程序新版本
updateManager.onUpdateReady(function () {
wx.hideLoading()
//新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
})
updateManager.onUpdateFailed(function () {
// 新的版本下载失败
wx.showModal({
title: '新版本更新失败',
content: '您可删除当前小程序,重新打开尝试',
})
})
},
getTheme() {
return new Promise((resolve) => {
const checkUserType = () => {
const userInfo = this.globalData.userInfo
if (userInfo.UserType) {
// resolve(userInfo.UserType >= 4 ? 'DRUG' : 'PATIENT')
resolve('PATIENT')
return true
}
return false
}
const tryResolve = () => {
if (!checkUserType()) {
setTimeout(tryResolve, 30)
}
}
tryResolve()
})
},
})

144
src/components/calendar/core.js

@ -0,0 +1,144 @@ @@ -0,0 +1,144 @@
import { dateUtil, getCalendarConfig } from './utils/index'
/**
* 计算当前月份前后两月应占的格子
* @param {number} year 年份
* @param {number} month 月份
*/
function calculateEmptyGrids(year, month, config) {
const prevMonthGrids = calculatePrevMonthGrids(year, month, config)
const nextMonthGrids = calculateNextMonthGrids(year, month, config)
return {
prevMonthGrids,
nextMonthGrids
}
}
/**
* 计算上月应占的格子
* @param {number} year 年份
* @param {number} month 月份
*/
function calculatePrevMonthGrids(year, month, config) {
let emptyGrids = []
const prevMonthDays = dateUtil.getDatesCountOfMonth(year, month - 1)
let firstDayOfWeek = dateUtil.firstDayOfWeek(year, month)
if (config.firstDayOfWeek === 'Mon') {
if (firstDayOfWeek === 0) {
firstDayOfWeek = 6
} else {
firstDayOfWeek -= 1
}
}
if (firstDayOfWeek > 0) {
const len = prevMonthDays - firstDayOfWeek
const { onlyShowCurrentMonth } = config
const YMInfo = dateUtil.getPrevMonthInfo({ year, month })
for (let i = prevMonthDays; i > len; i--) {
if (onlyShowCurrentMonth) {
emptyGrids.push('')
} else {
const week = dateUtil.getDayOfWeek(+year, +month, i)
emptyGrids.push({
...YMInfo,
date: i,
week
})
}
}
emptyGrids.reverse()
}
return emptyGrids
}
/**
* 计算下一月日期是否需要多展示的日期
* 某些月份日期为5排某些月份6排统一为6排
* @param {number} year
* @param {number} month
* @param {object} config
*/
function calculateExtraEmptyDate(year, month, config) {
let extDate = 0
if (+month === 2) {
extDate += 7
let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1)
if (config.firstDayOfWeek === 'Mon') {
if (+firstDayofMonth === 1) extDate += 7
} else {
if (+firstDayofMonth === 0) extDate += 7
}
} else {
let firstDayofMonth = dateUtil.getDayOfWeek(year, month, 1)
if (config.firstDayOfWeek === 'Mon') {
if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
extDate += 7
}
} else {
if (firstDayofMonth <= 5) {
extDate += 7
}
}
}
return extDate
}
/**
* 计算下月应占的格子
* @param {number} year 年份
* @param {number} month 月份
*/
function calculateNextMonthGrids(year, month, config) {
let emptyGrids = []
const datesCount = dateUtil.getDatesCountOfMonth(year, month)
let lastDayWeek = dateUtil.getDayOfWeek(year, month, datesCount)
if (config.firstDayOfWeek === 'Mon') {
if (lastDayWeek === 0) {
lastDayWeek = 6
} else {
lastDayWeek -= 1
}
}
let len = 7 - (lastDayWeek + 1)
const { onlyShowCurrentMonth } = config
if (!onlyShowCurrentMonth) {
len = len + calculateExtraEmptyDate(year, month, config)
}
const YMInfo = dateUtil.getNextMonthInfo({ year, month })
for (let i = 1; i <= len; i++) {
const week = dateUtil.getDayOfWeek(+year, +month, i)
if (onlyShowCurrentMonth) {
emptyGrids.push('')
} else {
emptyGrids.push({
id: i - 1,
...YMInfo,
date: i,
week: week || 7
})
}
}
return emptyGrids
}
/**
* 设置日历面板数据
* @param {number} year 年份
* @param {number} month 月份
* @param {number} curDate 日期
*/
function calculateCurrentMonthDates(year, month) {
return dateUtil.calcDates(year, month)
}
export function calcJumpData({ dateInfo, config, component }) {
dateInfo = dateInfo || dateUtil.todayFMD()
const { year, month, date } = dateInfo
const calendarConfig = config || getCalendarConfig(component)
const emptyGrids = calculateEmptyGrids(year, month, calendarConfig)
const calendar = {
curYear: year,
curMonth: month,
curDate: date,
dates: calculateCurrentMonthDates(year, month),
...emptyGrids
}
return calendar
}

12
src/components/calendar/helper.js

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
import { dateUtil } from './utils/index'
export function calcTargetYMInfo() {
return {
right: dateUtil.getPrevMonthInfo,
left: dateUtil.getNextMonthInfo,
prev_month: dateUtil.getPrevMonthInfo,
next_month: dateUtil.getNextMonthInfo,
prev_year: dateUtil.getPrevYearInfo,
next_year: dateUtil.getNextYearInfo
}
}

1
src/components/calendar/index.d.ts vendored

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

258
src/components/calendar/index.js

@ -0,0 +1,258 @@ @@ -0,0 +1,258 @@
import plugins from './plugins/index'
import { calcJumpData } from './core'
import { renderCalendar } from './render'
import { calcTargetYMInfo } from './helper'
import { dateUtil, calendarGesture, logger } from './utils/index'
Component({
options: {
styleIsolation: 'apply-shared',
multipleSlots: true // 在组件定义时的选项中启用多slot支持
},
properties: {
config: {
type: Object,
value: {}
}
},
lifetimes: {
attached: function() {
this.initComp()
}
},
methods: {
initComp() {
const calendarConfig = this.setDefaultDisableDate()
this.setConfig(calendarConfig)
},
// 禁用某天日期配置默认为今天
setDefaultDisableDate() {
const calendarConfig = this.properties.config || {}
if (calendarConfig.disableMode && !calendarConfig.disableMode.date) {
calendarConfig.disableMode.date = dateUtil.toTimeStr(
dateUtil.todayFMD()
)
}
return calendarConfig
},
initCalendar(config) {
const { defaultDate } = config
let date = dateUtil.todayFMD()
if (defaultDate && typeof defaultDate === 'string') {
const dateInfo = defaultDate.split('-')
if (dateInfo.length < 3) {
return logger.warn('defaultDate配置格式应为: 2018-4-2 或 2018-04-02')
} else {
date = {
year: +dateInfo[0],
month: +dateInfo[1],
date: +dateInfo[2]
}
}
}
const waitRenderData = calcJumpData({
dateInfo: date,
config
})
const timestamp = dateUtil.todayTimestamp()
if (config.autoChoosedWhenJump) {
const target = waitRenderData.dates.filter(
item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(date)
)
if (target && target.length) {
if (!waitRenderData.selectedDates) {
waitRenderData.selectedDates = target
} else {
waitRenderData.selectedDates.push(target[0])
}
}
}
return {
...waitRenderData,
todayTimestamp: timestamp,
weeksCh: dateUtil.getWeekHeader(config.firstDayOfWeek)
}
},
setConfig(config) {
if (config.markToday && typeof config.markToday === 'string') {
config.highlightToday = true
}
config.theme = config.theme || 'default'
this.setData(
{
config
},
() => {
for (let plugin of plugins.installed) {
const [, p] = plugin
if (typeof p.install === 'function') {
p.install(this)
}
if (typeof p.methods === 'function') {
const methods = p.methods(this)
for (let fnName in methods) {
if (fnName.startsWith('__')) continue
const fn = methods[fnName]
if (typeof fn === 'function') {
if (!this.calendar) this.calendar = {}
this.calendar[fnName] = fn
}
}
}
}
const initData = this.initCalendar(config)
renderCalendar.call(this, initData, config)
}
)
},
tapDate(e) {
const { info } = e.currentTarget.dataset
const { date, disable } = info || {}
if (disable || !date) return
const { calendar, config } = this.data
let calendarData = calendar
let calendarConfig = config
if (config.takeoverTap) {
return this.triggerEvent('takeoverTap', info)
}
for (let plugin of plugins.installed) {
const [, p] = plugin
if (typeof p.onTapDate === 'function') {
const {
calendarData: __calendarData,
calendarConfig: __calendarConfig
} = p.onTapDate(info, calendarData, calendarConfig)
calendarData = __calendarData
calendarConfig = __calendarConfig
}
}
renderCalendar.call(this, calendarData, calendarConfig).then(() => {
this.triggerEvent('afterTapDate', info)
})
},
/**
* 日历滑动开始
* @param {object} e
*/
calendarTouchstart(e) {
const t = e.touches[0]
const startX = t.clientX
const startY = t.clientY
this.swipeLock = true
this.setData({
'gesture.startX': startX,
'gesture.startY': startY
})
},
/**
* 日历滑动中
* @param {object} e
*/
calendarTouchmove(e) {
const { gesture } = this.data
const { preventSwipe } = this.properties.config
if (!this.swipeLock || preventSwipe) return
if (calendarGesture.isLeft(gesture, e.touches[0])) {
this.handleSwipe('left')
this.swipeLock = false
}
if (calendarGesture.isRight(gesture, e.touches[0])) {
this.handleSwipe('right')
this.swipeLock = false
}
},
calendarTouchend(e) {
this.setData({
'calendar.leftSwipe': 0,
'calendar.rightSwipe': 0
})
},
handleSwipe(direction) {
let swipeKey = 'calendar.leftSwipe'
if (direction === 'right') {
swipeKey = 'calendar.rightSwipe'
}
this.setData({
[swipeKey]: 1
})
const { calendar } = this.data
let calendarData = calendar
const { curYear, curMonth } = calendarData
const getMonthInfo = calcTargetYMInfo()[direction]
const target = getMonthInfo({
year: +curYear,
month: +curMonth
})
target.direction = direction
this.renderCalendar(target)
},
changeDate(e) {
const { type } = e.currentTarget.dataset
const { calendar: calendarData } = this.data
const { curYear, curMonth } = calendarData
const getMonthInfo = calcTargetYMInfo()[type]
const target = getMonthInfo({
year: +curYear,
month: +curMonth
})
target.direction = type
this.renderCalendar(target)
},
renderCalendar(target) {
let { calendar: calendarData, config } = this.data
const { curYear, curMonth } = calendarData || {}
for (let plugin of plugins.installed) {
const [, p] = plugin
if (typeof p.onSwitchCalendar === 'function') {
calendarData = p.onSwitchCalendar(target, calendarData, this)
}
}
return renderCalendar.call(this, calendarData, config).then(() => {
let triggerEventName = 'whenChangeMonth'
if (config.weekMode) {
triggerEventName = 'whenChangeWeek'
}
this.triggerEvent(triggerEventName, {
current: {
year: +curYear,
month: +curMonth
},
next: target
})
this.triggerEvent('onSwipe', {
current: {
year: +curYear,
month: +curMonth
},
next: target,
type: triggerEventName
})
})
},
doubleClickJumpToToday() {
const { multi, weekMode } = this.calendar.getCalendarConfig() || {}
if (multi || weekMode) return
if (this.count === undefined) {
this.count = 1
} else {
this.count += 1
}
if (this.lastClick) {
const difference = new Date().getTime() - this.lastClick
if (
difference < 500 &&
this.count >= 2 &&
typeof this.calendar.jump === 'function'
) {
const today = dateUtil.todayFMD()
this.calendar.jump(today)
}
this.count = undefined
this.lastClick = undefined
} else {
this.lastClick = new Date().getTime()
}
this.triggerEvent('jumpToToday')
}
}
})

3
src/components/calendar/index.json

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

116
src/components/calendar/index.wxml

@ -0,0 +1,116 @@ @@ -0,0 +1,116 @@
<view class="flex b tb ac" wx:if="{{calendar}}">
<view class="calendar b tb">
<!-- 头部操作栏 -->
<view
wx:if="{{!config.hideHeader}}"
class="handle {{config.theme}}_handle-color fs32 b lr ac pc"
>
<view class="prev fs36" wx:if="{{!config.weekMode}}">
<text
wx:if="{{!config.hideSelectYear}}"
class="prev-handle iconfont icon-doubleleft"
bindtap="changeDate"
data-type="prev_year"
></text>
<text
class="prev-handle iconfont icon-left"
bindtap="changeDate"
data-type="prev_month"
></text>
</view>
<view class="flex date-in-handle b lr cc blod" bindtap="doubleClickJumpToToday"
>{{calendar.curYear || "--"}} 年 {{calendar.curMonth || "--"}} 月</view
>
<view class="next fs36" wx:if="{{!config.weekMode}}">
<text
class="next-handle iconfont icon-right"
bindtap="changeDate"
data-type="next_month"
></text>
<text
wx:if="{{!config.hideSelectYear}}"
class="next-handle iconfont icon-doubleright"
bindtap="changeDate"
data-type="next_year"
></text>
</view>
</view>
<!-- 星期栏 -->
<view class="weeks b lr ac {{config.theme}}_week-color">
<view class="week fs28" wx:for="{{calendar.weeksCh}}" wx:key="index" data-idx="{{index}}">
{{item}}
</view>
</view>
<!-- 日历面板主体 -->
<view
class="b lr wrap"
bindtouchstart="calendarTouchstart"
catchtouchmove="calendarTouchmove"
catchtouchend="calendarTouchend"
>
<!-- 上月日期格子 -->
<view
class="grid b ac pc {{config.theme}}_prev-month-date"
wx:for="{{calendar.prevMonthGrids}}"
wx:key="index"
data-idx="{{index}}"
>
<view class="date-wrap b cc">
<view class="date">
{{item.date}}
</view>
</view>
</view>
<!-- 本月日期格子 -->
<view
wx:for="{{calendar.dates}}"
wx:key="index"
data-idx="{{index}}"
data-info="{{item}}"
bindtap="tapDate"
class="grid {{item.class ? item.class : ''}} {{config.theme}}_normal-date b ac pc"
>
<view
class="date-wrap b cc {{config.emphasisWeek && (item.week === 0 || item.week === 6) ? config.theme + '_weekend-color' : ''}}"
>
<view
class="date b ac pc {{item.class ? item.class : ''}} {{item.isToday && config.highlightToday ? config.theme + '_today' : ''}} {{item.choosed ? config.theme + '_choosed' : ''}} {{item.disable ? config.theme + '_date-disable' : ''}} {{config.chooseAreaMode ? 'date-area-mode' : ''}} {{calendar.todoLabelCircle && item.showTodoLabel && !item.choosed ? config.theme + '_todo-circle todo-circle' : '' }}"
>
{{config.markToday && item.isToday ? config.markToday : item.date}}
<view
wx:if="{{(config.showLunar && item.lunar && !item.showTodoLabel) || (item.showTodoLabel && calendar.todoLabelPos !== 'bottom') || config.showHolidays}}"
class="date-desc {{config.theme}}_date-desc date-desc-bottom {{(item.choosed || item.isToday) ? 'date-desc-bottom-always' : ''}} {{item.disable ? config.theme + '_date-desc-disable' : ''}}"
>
<text
class="{{config.showHolidays && !item.showTodoLabel && item.label && !item.choosed ? config.theme + '_date-desc-lunar' : ''}} {{item.type === 'festival' ? config.theme + '_festival' : ''}}"
>{{item.label || item.lunar.Term || item.lunar.IDayCn}}</text
>
</view>
<block wx:for="{{item.color}}" wx:for-item="col" wx:for-index="colIndex" wx:key="*this">
<view
wx:if="{{item.showTodoLabel && !calendar.todoLabelCircle}}"
class="{{item.todoText ? 'date-desc' : config.theme + '_todo-dot todo-dot'}} {{config.showLunar ? config.theme + '_date-desc-lunar' : ''}} {{calendar.todoLabelPos === 'bottom' ? 'date-desc-bottom todo-dot-bottom' : 'date-desc-top todo-dot-top'}} {{calendar.showLabelAlways && item.choosed && calendar.todoLabelPos === 'bottom' ? 'date-desc-bottom-always todo-dot-bottom-always' : ''}} {{calendar.showLabelAlways && item.choosed && calendar.todoLabelPos === 'top' ? 'date-desc-top-always todo-dot-top-always' : ''}}"
style="background-color: {{item.todoText ? '' : col || calendar.todoLabelColor}}; color: {{col}};left:{{(item.color.length>1 ? 35 : 50)*(colIndex+1)}}%"
>
{{item.todoText}}
</view>
</block>
</view>
</view>
</view>
<!-- 下月日期格子 -->
<view
class="grid b ac pc {{config.theme}}_next-month-date"
wx:for="{{calendar.nextMonthGrids}}"
wx:key="index"
data-idx="{{index}}"
>
<view class="date-wrap b cc">
<view class="date">
{{item.date}}
</view>
</view>
</view>
</view>
</view>
</view>

247
src/components/calendar/index.wxss

@ -0,0 +1,247 @@ @@ -0,0 +1,247 @@
@import "./theme/iconfont.wxss";
@import "./theme/theme-default.wxss";
@import "./theme/theme-elegant.wxss";
@import "./theme/theme-nuohe.wxss";
.blod {
font-weight: bold;
}
.b {
display: flex;
}
.lr {
flex-direction: row;
}
.tb {
flex-direction: column;
}
.pc {
justify-content: center;
}
.ac {
align-items: center;
}
.cc {
align-items: center;
justify-content: center;
}
.wrap {
flex-wrap: wrap;
}
.flex {
flex-grow: 1;
}
.bg {
background-image: linear-gradient(to bottom, #faefe7, #ffcbd7);
overflow: hidden;
}
.white-color {
color: #fff;
}
.fs24 {
font-size: 24rpx;
}
.fs28 {
font-size: 28rpx;
}
.fs32 {
font-size: 32rpx;
}
.fs36 {
font-size: 36rpx;
}
.calendar {
width: 100%;
box-sizing: border-box;
}
/* 日历操作栏 */
.handle {
height: 80rpx;
}
.prev-handle,
.next-handle {
padding: 20rpx;
}
.date-in-handle {
height: 80rpx;
}
/* 星期栏 */
.weeks {
height: 50rpx;
line-height: 50rpx;
opacity: 0.5;
}
.week {
text-align: center;
}
.grid,
.week {
width: 14.286014285714286%;
}
.date-wrap {
width: 100%;
height: 80rpx;
position: relative;
left: 0;
top: 0;
}
.date {
position: relative;
left: 0;
top: 0;
width: 55rpx;
height: 55rpx;
text-align: center;
line-height: 55rpx;
font-size: 28rpx;
font-weight: 200;
border-radius: 50%;
transition: all 0.3s;
font-weight: bold;
animation-name: choosed;
animation-duration: 0.5s;
animation-timing-function: linear;
animation-iteration-count: 1;
}
.date-area-mode {
width: 100%;
border-radius: 0;
}
.date-desc {
width: 150%;
height: 32rpx;
font-size: 20rpx;
line-height: 32rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
white-space: nowrap;
-webkit-line-clamp: 1;
text-align: center;
}
@keyframes choosed {
from {
transform: scale(1);
}
50% {
transform: scale(0.9);
}
to {
transform: scale(1);
}
}
/* 日期圆圈标记 */
.todo-circle {
border-width: 1rpx;
border-style: solid;
box-sizing: border-box;
}
/* 待办点标记相关样式 */
.todo-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
position: absolute;
left: 50%;
bottom: -10rpx !important;
transform: translateX(-50%);
}
.todo-dot-top {
top: 3rpx;
}
.todo-dot.todo-dot-top-always {
top: -8rpx;
}
.todo-dot.todo-dot-bottom {
bottom: 0;
}
.todo-dot.todo-dot-bottom-always {
bottom: -10rpx;
}
/* 日期描述文字(待办文字/农历)相关样式 */
.date-desc.date-desc-top {
top: -6rpx;
}
.date-desc.date-desc-top-always {
top: -20rpx;
}
.date-desc.date-desc-bottom {
bottom: -14rpx;
}
.todo-circle .date-desc.date-desc-bottom {
bottom: -30rpx;
}
.date-desc.date-desc-bottom-always {
bottom: -28rpx;
}

212
src/components/calendar/plugins/holidays/holidays-map.js

@ -0,0 +1,212 @@ @@ -0,0 +1,212 @@
/* *
@Author: drfu*
@Description: 数据来源于国务院办公厅关于2020年部分节假日安排的通知国办发明电201916_政府信息公开专栏http://www.gov.cn/zhengce/content/2019-11/21/content_5454164.htm
@Date: 2020-10-12 14:29:45*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-16 17:38:08
*/
// 节日列表
export const festival = {
// 农历固定日期节日
lunar: {
1: {
1: {
type: 'festival',
name: '春节',
label: '春节'
},
8: {
type: 'festival',
name: '腊八节',
label: '腊八'
},
15: {
type: 'festival',
name: '元宵节',
label: '元宵'
}
},
7: {
7: {
type: 'festival',
name: '七夕节',
label: '七夕'
},
15: {
type: 'festival',
name: '中元节',
label: '中元节'
}
},
9: {
9: {
type: 'festival',
name: '重阳节',
label: '重阳节'
}
}
},
// 阳历固定日期节日
solar: {
2: {
14: {
type: 'festival',
name: '情人节',
label: '情人节'
}
},
3: {
12: {
type: 'festival',
name: '植树节',
label: '植树节'
}
},
4: {
1: {
type: 'festival',
name: '愚人节',
label: '愚人节'
},
5: {
type: 'festival',
name: '清明节',
label: '清明节'
}
},
5: {
1: {
type: 'festival',
name: '劳动节',
label: '劳动节'
}
},
6: {
1: {
type: 'festival',
name: '儿童节',
label: '儿童节'
}
},
7: {
1: {
type: 'festival',
name: '建党节',
label: '建党节'
}
},
8: {
1: {
type: 'festival',
name: '建军节',
label: '建军节'
}
},
9: {
10: {
type: 'festival',
name: '教师节',
label: '教师节'
}
},
10: {
1: {
type: 'festival',
name: '国庆节',
label: '国庆节'
}
},
12: {
25: {
type: 'festival',
name: '圣诞节',
label: '圣诞节'
}
}
}
}
export const holidays = {
2020: {
1: {
1: {
type: 'holiday',
name: '元旦',
label: '休'
},
19: {
type: 'work',
name: '调班',
label: '班'
},
'24-30': {
type: 'holiday',
name: '春节',
label: '休'
}
},
2: {
1: {
type: 'work',
name: '调班',
label: '班'
}
},
4: {
'4-6': {
type: 'holiday',
name: '清明节',
label: '休'
},
26: {
type: 'work',
name: '调班',
label: '班'
}
},
5: {
'1-5': {
type: 'holiday',
name: '劳动节',
label: '休'
},
9: {
type: 'work',
name: '调班',
label: '班'
}
},
6: {
'25-27': {
type: 'holiday',
name: '端午节',
label: '休'
},
28: {
type: 'work',
name: '调班',
label: '班'
}
},
9: {
27: {
type: 'work',
name: '调班',
label: '班'
}
},
10: {
'1-8': {
type: 'holiday',
name: '国庆节/中秋节',
label: '休'
},
10: {
type: 'work',
name: '调班',
label: '班'
}
}
}
}

1
src/components/calendar/plugins/holidays/index.d.ts vendored

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

201
src/components/calendar/plugins/holidays/index.js

@ -0,0 +1,201 @@ @@ -0,0 +1,201 @@
/* *
@Author: drfu*
@Description: 显示法定节假日班/休情况
@Date: 2020-10-12 14:29:45*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-16 17:34:13
*/
import { holidays, festival } from './holidays-map'
import { dateUtil, getCalendarData, logger } from '../../utils/index'
/**
* 当前是否在休假期内
* @param {object} { year, month }
* @param {object} { start, end, current }
* @returns
*/
function inHolidays({ year, month }, { start, end, current }) {
const getTimeStamp = dateUtil.getTimeStamp
const startTimestamp = getTimeStamp({
year,
month,
date: start
})
const endTimestamp = getTimeStamp({
year,
month,
date: end
})
const currentDateTimestamp = getTimeStamp({
year,
month,
date: current
})
if (
currentDateTimestamp >= startTimestamp &&
currentDateTimestamp <= endTimestamp
) {
return true
}
return false
}
function addSpecialFestival(date, component) {
const { convertlLunar2Solar, convertSolarLunar } = component.calendar || {}
const lunarDateInfo = convertSolarLunar(date)
const { lYear, lMonth } = lunarDateInfo || {}
// 春节
const info = {
type: 'festival',
name: '除夕',
label: '除夕'
}
if (lMonth === 12) {
if (!festival.lunar['12']) festival.lunar['12'] = {}
if (convertlLunar2Solar(`${lYear}-12-30`) === -1) {
festival.lunar['12']['29'] = info
} else {
festival.lunar['12']['30'] = info
}
}
}
/**
* 是否匹配到节日
* @param {object} [dateInfo={}]
* @param {object} [component={}]
* @returns {object|boolean} 匹配到的节日数据或者false
*/
function hasFestivalDate(dateInfo = {}, component = {}) {
const { month, date } = dateInfo
let festivalDate = festival.solar[month] && festival.solar[month][date]
if (!festivalDate) {
const { convertSolarLunar } = component.calendar || {}
const lunarDateInfo = convertSolarLunar(dateInfo)
const { lMonth, lDay } = lunarDateInfo
festivalDate = festival.lunar[lMonth] && festival.lunar[lMonth][lDay]
if (!festivalDate) {
const festivalOfMonth = festival.lunar[lMonth] || {}
const festivalDateKey = Object.keys(festivalOfMonth).find(item =>
item.match(new RegExp(`\\b${lDay}\\b`))
)
if (!festivalDateKey) {
festivalDate = false
} else {
const festivalInfo = festival.lunar[lMonth][festivalDateKey]
if (!festivalInfo) {
festivalDate = false
} else {
const { condition } = festivalInfo
if (typeof condition === 'function') {
festivalDate = condition(lunarDateInfo)
} else {
festivalDate = false
}
}
}
}
}
return festivalDate
}
export default () => {
return {
name: 'holidays',
beforeRender(calendarData = {}, calendarConfig = {}, component) {
let { dates = [] } = calendarData
if (calendarConfig.showHolidays || calendarConfig.showFestival) {
dates = dates.map(d => {
let item = { ...d }
const { year, month, date } = item
const holidaysOfMonth =
(holidays[year] && holidays[year][month]) || {}
const holidayDate = holidaysOfMonth[date]
if (holidayDate) {
item = {
...item,
...holidayDate
}
} else {
const holidayKeys = Object.keys(holidaysOfMonth).filter(item =>
item.includes('-')
)
let target = ''
for (let v of holidayKeys) {
const [start, end] = v.split('-')
if (+d.date >= +start && +d.date <= +end) {
target = v
break
}
}
const [start, end] = target.split('-')
const isInHolidays = inHolidays(
{
year,
month
},
{
start,
end,
current: date
}
)
if (isInHolidays) {
item = {
...item,
...holidaysOfMonth[target]
}
} else if (calendarConfig.showFestival) {
const { convertSolarLunar, convertlLunar2Solar } =
component.calendar || {}
if (
typeof convertSolarLunar !== 'function' ||
typeof convertlLunar2Solar !== 'function'
) {
return logger.warn(
'农历节日显示需要引入农历插件(/component/v2/plugins/solarLunar)'
)
}
addSpecialFestival(item, component)
const festivalDate = hasFestivalDate(item, component)
if (festivalDate) {
item = {
...item,
...festivalDate
}
}
}
}
return item
})
}
return {
calendarData: {
...calendarData,
dates: dates
},
calendarConfig
}
},
methods(component) {
return {
getHolidaysOfCurrentYear() {
const calendar = getCalendarData('calendar', component)
const { curYear } = calendar
return this.methods(component).getHolidaysOfYear(curYear)
},
getHolidaysOfYear(year) {
if (!year) return logger.warn('getHolidaysOfCurrentYear() 入参错误')
if (!holidays[year]) {
logger.warn('未匹配到当前年份节假日信息,请自行补充')
return {
err: 'not match'
}
}
return holidays[year]
}
}
}
}
}

1
src/components/calendar/plugins/index.d.ts vendored

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

18
src/components/calendar/plugins/index.js

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
import preset from './preset/index'
export default {
installed: [...preset],
use(plugin) {
if (typeof plugin !== 'function') return
const info = plugin() || {}
const { name } = info
if (
name &&
name !== 'methods' &&
!this.installed.some(p => p[0] === name)
) {
this.installed.unshift([name, info])
}
return this
}
}

277
src/components/calendar/plugins/preset/base.js

@ -0,0 +1,277 @@ @@ -0,0 +1,277 @@
/**
* @Author: drfu*
* @Description: 基础功能
* @Date: 2020-10-08 21:22:09*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-11 13:28:52
* */
import { calcJumpData } from '../../core'
import { renderCalendar } from '../../render'
import {
dateUtil,
getCalendarData,
setCalendarData,
getCalendarConfig
} from '../../utils/index'
export default () => {
return {
name: 'base',
beforeRender(calendarData = {}, calendarConfig) {
const calendar = calendarData
const { selectedDates = [], dates } = calendar
let _dates = [...dates]
if (selectedDates.length) {
const selectedDatesStr = selectedDates.map(date =>
dateUtil.toTimeStr(date)
)
_dates.forEach(date => {
const dateStr = dateUtil.toTimeStr(date)
if (selectedDatesStr.includes(dateStr)) {
date.choosed = true
}
})
}
return {
calendarData: {
...calendarData,
dates: _dates
},
calendarConfig
}
},
onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
const calendar = {
...calendarData
}
const dateIndex = dateUtil.findDateIndexInArray(
tapedDate,
calendarData.dates
)
const { multi, inverse } = calendarConfig
let dates = [...calendar.dates]
const { selectedDates = [] } = calendar
if (!multi) {
let preSelectedDate = {}
if (selectedDates.length) {
preSelectedDate = [...selectedDates].pop() || {}
}
const timeStr = dateUtil.toTimeStr
if (!inverse && timeStr(preSelectedDate) === timeStr(tapedDate)) {
return calendar
}
let _tapedDate = { ...tapedDate, choosed: !tapedDate.choosed }
dates[dateIndex] = _tapedDate
if (preSelectedDate.date) {
const idx = dateUtil.findDateIndexInArray(preSelectedDate, dates)
const date = dates[idx]
if (date) {
date.choosed = false
}
}
if (dates[dateIndex].choosed) {
calendar.selectedDates = [dates[dateIndex]]
} else {
calendar.selectedDates = []
}
} else {
dates[dateIndex] = {
...dates[dateIndex],
choosed: !dates[dateIndex].choosed
}
if (!calendar.selectedDates) {
calendar.selectedDates = []
}
if (dates[dateIndex].choosed) {
calendar.selectedDates.push(dates[dateIndex])
} else {
calendar.selectedDates = calendar.selectedDates.filter(
date =>
dateUtil.toTimeStr(date) !== dateUtil.toTimeStr(dates[dateIndex])
)
}
}
return {
calendarData: {
...calendar,
dates
},
calendarConfig
}
},
onSwitchCalendar(date, calendarData = {}, component) {
const calendarConfig = getCalendarConfig(component)
if (calendarConfig.weekMode) {
return calendarData
}
const updatedRenderData = calcJumpData({
dateInfo: date,
config: calendarConfig
})
return {
...calendarData,
...updatedRenderData
}
},
methods(component) {
return {
jump: dateInfo => {
if (Object.prototype.toString.call(dateInfo) !== '[object Object]')
return
const updatedRenderData = calcJumpData({
dateInfo,
component
})
const existCalendarData = getCalendarData('calendar', component)
const config = getCalendarConfig(component)
if (config.autoChoosedWhenJump) {
const target = updatedRenderData.dates[dateInfo.date - 1]
if (!updatedRenderData.selectedDates) {
updatedRenderData.selectedDates = [target]
} else {
updatedRenderData.selectedDates.push(target)
}
}
return renderCalendar.call(component, {
...existCalendarData,
...updatedRenderData
})
},
getCalendarConfig() {
return getCalendarConfig(component)
},
setCalendarConfig(config) {
return new Promise((resolve, reject) => {
if (!component || !component.data.config) {
reject('异常:未找到组件配置信息')
return
}
let conf = { ...component.config, ...config }
component.config = conf
setCalendarData({ config: conf }, component)
.then(resolve)
.catch(reject)
})
},
cancelSelectedDates(cancelDates = []) {
const existCalendarData = getCalendarData('calendar', component) || {}
const { dates = [], selectedDates = [] } = existCalendarData
let updatedRenderData = {}
const config = getCalendarConfig(component)
let chooseAreaData = {}
if (config.chooseAreaMode) {
chooseAreaData = {
chooseAreaTimestamp: [],
tempChooseAreaTimestamp: []
}
}
if (!cancelDates.length) {
dates.forEach(item => {
item.choosed = false
})
updatedRenderData = {
dates,
selectedDates: []
}
} else {
const cancelDatesStr = cancelDates.map(date =>
dateUtil.toTimeStr(date)
)
const filterSelectedDates = selectedDates.filter(
date => !cancelDatesStr.includes(dateUtil.toTimeStr(date))
)
dates.forEach(date => {
if (cancelDatesStr.includes(dateUtil.toTimeStr(date))) {
date.choosed = false
}
})
updatedRenderData = {
dates,
selectedDates: filterSelectedDates
}
}
return renderCalendar.call(component, {
...existCalendarData,
...updatedRenderData,
...chooseAreaData
})
},
setSelectedDates: targetDates => {
const existCalendarData = getCalendarData('calendar', component)
let { dates, selectedDates = [] } = existCalendarData || {}
let __selectedDates = []
let __dates = dates
if (!targetDates) {
__dates = dates.map(item => {
const date = { ...item }
date.choosed = true
if (existCalendarData.showLabelAlways && date.showTodoLabel) {
date.showTodoLabel = true
} else {
date.showTodoLabel = false
}
return date
})
__selectedDates = dates
} else if (targetDates && targetDates.length) {
const allSelected = dateUtil.uniqueArrayByDate(
selectedDates.concat(targetDates)
)
const allSelectedDateStr = allSelected.map(d =>
dateUtil.toTimeStr(d)
)
__dates = dates.map(item => {
const date = { ...item }
if (allSelectedDateStr.includes(dateUtil.toTimeStr(date))) {
date.choosed = true
__selectedDates.push(date)
}
if (existCalendarData.showLabelAlways && date.showTodoLabel) {
date.showTodoLabel = true
} else {
date.showTodoLabel = false
}
return date
})
}
return renderCalendar.call(component, {
...existCalendarData,
dates: __dates,
selectedDates: __selectedDates
})
},
setDateStyle: toSetDates => {
if (!Array.isArray(toSetDates)) return Promise.reject()
const existCalendarData = getCalendarData('calendar', component)
const { dates = [], specialStyleDates } = existCalendarData || {}
if (Array.isArray(specialStyleDates)) {
toSetDates = dateUtil.uniqueArrayByDate([
...specialStyleDates,
...toSetDates
])
}
const toSetDatesStr = toSetDates.map(item => dateUtil.toTimeStr(item))
const _dates = dates.map(item => {
const idx = toSetDatesStr.indexOf(dateUtil.toTimeStr(item))
if (idx > -1) {
return {
...item,
class: toSetDates[idx].class
}
} else {
return item
}
})
return renderCalendar.call(component, {
...existCalendarData,
dates: _dates,
specialStyleDates: toSetDates
})
}
}
}
}
}

69
src/components/calendar/plugins/preset/get-calendar-data.js

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
/**
* @Author: drfu*
* @Description: 获取日历数据
* @Date: 2020-10-08 21:22:09*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-11 13:42:37
* */
import { getCalendarData, logger, getCalendarConfig } from '../../utils/index'
function wrapDateWithLunar(dates = [], convertFn) {
const datesWithLunar = JSON.parse(JSON.stringify(dates)).map(date => ({
...date,
lunar: convertFn(date)
}))
return datesWithLunar
}
export default () => {
return {
name: 'getData',
methods(component) {
return {
getCurrentYM: () => {
const { curYear, curMonth } = getCalendarData('calendar', component)
return {
year: curYear,
month: curMonth
}
},
getSelectedDates: (options = {}) => {
const dates =
getCalendarData('calendar.selectedDates', component) || []
const config = getCalendarConfig(component) || {}
if (options.lunar && !config.showLunar) {
const injectedFns = component.calendar || {}
if (typeof injectedFns.convertSolarLunar === 'function') {
return wrapDateWithLunar(dates, injectedFns.convertSolarLunar)
} else {
logger.warn('获取农历信息需引入农历插件')
}
} else {
return dates
}
},
getCalendarDates: (options = {}) => {
const config = getCalendarConfig(component) || {}
const dates = getCalendarData('calendar.dates', component)
if (options.lunar && !config.showLunar) {
const injectedFns = component.calendar || {}
if (typeof injectedFns.convertSolarLunar === 'function') {
return wrapDateWithLunar(dates, injectedFns.convertSolarLunar)
} else {
logger.warn('获取农历信息需引入农历插件')
}
} else {
return dates
}
},
getCalendarAllData: () => {
return {
data: getCalendarData('calendar', component) || {},
config: getCalendarConfig(component) || {}
}
}
}
}
}
}

1
src/components/calendar/plugins/preset/index.d.ts vendored

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

9
src/components/calendar/plugins/preset/index.js

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
import base from './base'
import getCalendarData from './get-calendar-data'
const preset = [
['base', base()],
['get-calendar-data', getCalendarData()]
]
export default preset

1
src/components/calendar/plugins/selectable.d.ts vendored

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

221
src/components/calendar/plugins/selectable.js

@ -0,0 +1,221 @@ @@ -0,0 +1,221 @@
/**
* @Author: drfu*
* @Description: 禁用启用日期选择
* @Date: 2020-10-08 21:22:09*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-08 21:25:00
* */
import { getCalendarData, dateUtil, logger } from '../utils/index'
import { renderCalendar } from '../render'
function convertEnableAreaToTimestamp(timearea = []) {
const start = timearea[0].split('-')
const end = timearea[1].split('-')
if (start.length !== 3 || end.length !== 3) {
logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
return {}
}
const startTimestamp = dateUtil
.newDate(start[0], start[1], start[2])
.getTime()
const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
return {
start,
end,
startTimestamp,
endTimestamp
}
}
function isValiditeOfDateArea(dateArea) {
const {
start,
end,
startTimestamp,
endTimestamp
} = convertEnableAreaToTimestamp(dateArea)
if (!start || !end) return
const datesCountOfStart = dateUtil.getDatesCountOfMonth(start[0], start[1])
const datesCountOfEnd = dateUtil.getDatesCountOfMonth(end[0], end[1])
if (start[2] > datesCountOfStart || start[2] < 1) {
logger.warn('enableArea() 开始日期错误,指定日期不在指定月份天数范围内')
return false
} else if (start[1] > 12 || start[1] < 1) {
logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
return false
} else if (end[2] > datesCountOfEnd || end[2] < 1) {
logger.warn('enableArea() 截止日期错误,指定日期不在指定月份天数范围内')
return false
} else if (end[1] > 12 || end[1] < 1) {
logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
return false
} else if (startTimestamp > endTimestamp) {
logger.warn('enableArea()参数最小日期大于了最大日期')
return false
} else {
return true
}
}
function handleDisableMode(calendarConfig) {
const { disableMode } = calendarConfig
if (!disableMode) return {}
const disableBound =
dateUtil.getTimeStamp(disableMode.date) || dateUtil.todayTimestamp()
return {
disableBound,
disableType: disableMode.type
}
}
function disabledByConfig(dateInfo, currentDate, calendarConfig) {
const date = { ...dateInfo }
const { disableType, disableBound } = handleDisableMode(calendarConfig)
if (
(disableType === 'before' && disableBound && currentDate < disableBound) ||
(disableType === 'after' && disableBound && currentDate > disableBound)
) {
date.disable = true
} else {
date.disable = false
}
return date
}
export default () => {
return {
name: 'enable',
beforeRender(calendarData = {}, calendarConfig = {}) {
const {
dates,
enableArea,
enableDates,
disableDates,
renderCausedBy
} = calendarData
const _dates = [...dates].map(date => {
let item = { ...date }
const timeStr = dateUtil.toTimeStr(date)
const timestamp = +dateUtil.getTimeStamp(item)
if (renderCausedBy === 'enableDates') {
if (enableDates && enableDates.length) {
if (enableDates.includes(timeStr)) {
item.disable = false
} else {
item.disable = true
}
return item
}
} else if (renderCausedBy === 'enableArea') {
if (enableArea && enableArea.length) {
const [startTimestamp, endTimestamp] = enableArea || []
const ifOutofArea =
+startTimestamp > timestamp || timestamp > +endTimestamp
item.disable = ifOutofArea
return item
}
} else if (renderCausedBy === 'disableDates') {
if (disableDates && disableDates.length) {
if (disableDates && disableDates.includes(timeStr)) {
item.disable = true
} else {
item.disable = false
}
return item
}
}
return disabledByConfig(item, timestamp, calendarConfig)
})
return {
calendarData: {
...calendarData,
dates: _dates
},
calendarConfig
}
},
methods(component) {
return {
enableArea: (dateArea = []) => {
if (dateArea.length === 2) {
const validate = isValiditeOfDateArea(dateArea)
if (validate) {
const existCalendarData = getCalendarData('calendar', component)
const {
startTimestamp,
endTimestamp
} = convertEnableAreaToTimestamp(dateArea)
return renderCalendar.call(component, {
...existCalendarData,
renderCausedBy: 'enableArea',
enableArea: [startTimestamp, endTimestamp]
})
}
} else {
return Promise.inject(
'enableArea()参数需为时间范围数组,形如:["2018-8-4" , "2018-8-24"]'
)
}
},
enableDates: (toSet = []) => {
if (!toSet.length) return
const existCalendarData = getCalendarData('calendar', component)
const { enableDates = [] } = existCalendarData || {}
let toSetDates = toSet.map(item => {
if (typeof item === 'string') {
return dateUtil.transformDateRow2Dict(item)
}
return item
})
if (enableDates.length) {
toSetDates = dateUtil.uniqueArrayByDate([
...toSetDates,
...enableDates.map(d => dateUtil.transformDateRow2Dict(d))
])
}
return renderCalendar.call(component, {
...existCalendarData,
renderCausedBy: 'enableDates',
enableDates: toSetDates.map(date => {
if (typeof date !== 'string') {
return dateUtil.toTimeStr(date)
}
return date
})
})
},
disableDates: toSet => {
const existCalendarData = getCalendarData('calendar', component)
const { disableDates = [], dates = [] } = existCalendarData || {}
let toSetDates = toSet.map(item => {
let date = { ...item }
if (typeof date === 'string') {
return dateUtil.transformDateRow2Dict(item)
}
return item
})
if (disableDates && disableDates.length) {
toSetDates = dateUtil.uniqueArrayByDate([
...toSetDates,
...disableDates.map(d => dateUtil.transformDateRow2Dict(d))
])
}
return renderCalendar.call(component, {
...existCalendarData,
renderCausedBy: 'disableDates',
dates,
disableDates: toSetDates.map(date => {
if (typeof date !== 'string') {
return dateUtil.toTimeStr(date)
}
return date
})
})
}
}
}
}
}

1036
src/components/calendar/plugins/solarLunar/convertSolarLunar.js

File diff suppressed because it is too large Load Diff

1
src/components/calendar/plugins/solarLunar/index.d.ts vendored

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

59
src/components/calendar/plugins/solarLunar/index.js

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
import { dateUtil } from '../../utils/index'
import convertSolarLunar from './convertSolarLunar'
function getDateRow2Dict(dateInfo) {
if (!dateInfo) return dateInfo
if (typeof dateInfo === 'string' && dateInfo.includes('-')) {
dateInfo = dateUtil.transformDateRow2Dict(dateInfo)
}
return dateInfo
}
export default () => {
return {
name: 'convertSolarLunar',
beforeRender(calendarData = {}, calendarConfig = {}) {
let { dates = [], selectedDates = [] } = calendarData
if (calendarConfig.showLunar) {
dates = dates.map(dataInfo => {
const { year, month, date } = dataInfo
return {
...dataInfo,
lunar: convertSolarLunar.solar2lunar(year, month, date)
}
})
selectedDates = selectedDates.map(dataInfo => {
const { year, month, date } = dataInfo
return {
...dataInfo,
lunar: convertSolarLunar.solar2lunar(year, month, date)
}
})
}
return {
calendarData: {
...calendarData,
dates: dates,
selectedDates: selectedDates
},
calendarConfig
}
},
methods() {
return {
convertSolarLunar: dateInfo => {
dateInfo = getDateRow2Dict(dateInfo)
if (!dateInfo) return dateInfo
const { year, month, date } = dateInfo
return convertSolarLunar.solar2lunar(year, month, date)
},
convertlLunar2Solar: (dateInfo, isLeapMonth) => {
dateInfo = getDateRow2Dict(dateInfo)
if (!dateInfo) return dateInfo
const { year, month, date } = dateInfo
return convertSolarLunar.lunar2solar(year, month, date, isLeapMonth)
}
}
}
}
}

305
src/components/calendar/plugins/time-range.js

@ -0,0 +1,305 @@ @@ -0,0 +1,305 @@
/**
* @Author: drfu*
* @Description: 时间区域选择
* @Date: 2020-10-08 21:22:09*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-11 13:56:32
* */
import { renderCalendar } from '../render'
import {
logger,
dateUtil,
getCalendarConfig,
getCalendarData
} from '../utils/index'
function pusheNextMonthDateArea(
dateInfo = {},
startTimestamp,
endTimestamp,
selectedDates = []
) {
let tempOfSelectedDate = [...selectedDates]
const dates = dateUtil.calcDates(dateInfo.year, dateInfo.month)
let datesLen = dates.length
for (let i = 0; i < datesLen; i++) {
const date = dates[i]
const timeStamp = dateUtil.getTimeStamp(date)
if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
tempOfSelectedDate.push({
...date,
choosed: true
})
}
if (i === datesLen - 1 && timeStamp < endTimestamp) {
pusheNextMonthDateArea(
dateUtil.getNextMonthInfo(date),
startTimestamp,
endTimestamp,
tempOfSelectedDate
)
}
}
return tempOfSelectedDate
}
function pushPrevMonthDateArea(
dateInfo = {},
startTimestamp,
endTimestamp,
selectedDates = []
) {
let tempOfSelectedDate = [...selectedDates]
const dates = dateUtil.sortDatesByTime(
dateUtil.calcDates(dateInfo.year, dateInfo.month),
'desc'
)
let datesLen = dates.length
let firstDate = dateUtil.getTimeStamp(dates[0])
for (let i = 0; i < datesLen; i++) {
const date = dates[i]
const timeStamp = dateUtil.getTimeStamp(date)
if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
tempOfSelectedDate.push({
...date,
choosed: true
})
}
if (i === datesLen - 1 && firstDate > startTimestamp) {
pushPrevMonthDateArea(
dateUtil.getPrevMonthInfo(date),
startTimestamp,
endTimestamp,
tempOfSelectedDate
)
}
}
return tempOfSelectedDate
}
/**
* 当设置日期区域非当前时保存其它月份的日期至已选日期数组
* @param {object} info
*/
function calcDateWhenNotInOneMonth(info) {
const { firstDate, lastDate, startTimestamp, endTimestamp } = info
let { selectedDate } = info
if (dateUtil.getTimeStamp(firstDate) > startTimestamp) {
selectedDate = pushPrevMonthDateArea(
dateUtil.getPrevMonthInfo(firstDate),
startTimestamp,
endTimestamp,
selectedDate
)
}
if (dateUtil.getTimeStamp(lastDate) < endTimestamp) {
selectedDate = pusheNextMonthDateArea(
dateUtil.getNextMonthInfo(lastDate),
startTimestamp,
endTimestamp,
selectedDate
)
}
return [...selectedDate]
}
/**
* 指定日期区域转时间戳
* @param {array} timearea 时间区域
*/
export function convertTimeRangeToTimestamp(timearea = []) {
const start = timearea[0].split('-')
const end = timearea[1].split('-')
if (start.length !== 3 || end.length !== 3) {
logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
return {}
}
const startTimestamp = dateUtil
.newDate(start[0], start[1], start[2])
.getTime()
const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
return {
start,
end,
startTimestamp,
endTimestamp
}
}
/**
* 校验时间区域是否合法
* @param {array} dateArea 时间区域
*/
function validateTimeRange(dateArea) {
const {
start,
end,
startTimestamp,
endTimestamp
} = convertTimeRangeToTimestamp(dateArea)
if (!start || !end) return
const startMonthDays = dateUtil.getDatesCountOfMonth(start[0], start[1])
const endMonthDays = dateUtil.getDatesCountOfMonth(end[0], end[1])
if (start[2] > startMonthDays || start[2] < 1) {
logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
return false
} else if (start[1] > 12 || start[1] < 1) {
logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
return false
} else if (end[2] > endMonthDays || end[2] < 1) {
logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
return false
} else if (end[1] > 12 || end[1] < 1) {
logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
return false
} else if (startTimestamp > endTimestamp) {
logger.warn('enableArea()参数最小日期大于了最大日期')
return false
} else {
return true
}
}
export default () => {
return {
name: 'timeRange',
beforeRender(calendarData = {}, calendarConfig = {}) {
const {
chooseAreaTimestamp = [],
dates = [],
selectedDates = []
} = calendarData
let __dates = dates
let __selectedDates = selectedDates
const [startDateTimestamp, endDateTimestamp] = chooseAreaTimestamp
if (chooseAreaTimestamp.length === 2) {
__selectedDates = []
__dates = dates.map(d => {
const date = { ...d }
const dateTimeStamp = dateUtil.getTimeStamp(date)
if (
dateTimeStamp >= startDateTimestamp &&
endDateTimestamp >= dateTimeStamp
) {
date.choosed = true
__selectedDates.push(date)
} else {
date.choosed = false
__selectedDates = __selectedDates.filter(
item => dateUtil.getTimeStamp(item) !== dateTimeStamp
)
}
return date
})
const monthOfStartDate = new Date(startDateTimestamp).getMonth()
const monthOfEndDate = new Date(endDateTimestamp).getMonth()
if (monthOfStartDate !== monthOfEndDate) {
__selectedDates = calcDateWhenNotInOneMonth({
firstDate: __dates[0],
lastDate: __dates[__dates.length - 1],
startTimestamp: startDateTimestamp,
endTimestamp: endDateTimestamp,
selectedDate: __selectedDates
})
}
}
return {
calendarData: {
...calendarData,
dates: __dates,
selectedDates: dateUtil.sortDatesByTime(
dateUtil.uniqueArrayByDate(__selectedDates)
)
},
calendarConfig
}
},
onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
if (!calendarConfig.chooseAreaMode) {
return {
calendarData,
calendarConfig
}
}
let {
tempChooseAreaTimestamp = [],
chooseAreaTimestamp: existChooseAreaTimestamp = [],
selectedDates = [],
dates = []
} = calendarData
const timestamp = dateUtil.getTimeStamp(tapedDate)
let __dates = [...dates]
let __selectedDates = [...selectedDates]
if (
tempChooseAreaTimestamp.length === 2 ||
existChooseAreaTimestamp.length === 2
) {
tempChooseAreaTimestamp = [tapedDate]
__selectedDates = []
__dates.forEach(d => (d.choosed = false))
} else if (tempChooseAreaTimestamp.length === 1) {
const preChoosedDate = tempChooseAreaTimestamp[0]
const preTimestamp = dateUtil.getTimeStamp(preChoosedDate)
if (preTimestamp <= timestamp) {
tempChooseAreaTimestamp.push(tapedDate)
} else if (preTimestamp > timestamp) {
tempChooseAreaTimestamp.unshift(tapedDate)
}
} else {
tempChooseAreaTimestamp = [tapedDate]
}
let chooseAreaTimestamp = []
if (tempChooseAreaTimestamp.length === 2) {
const [startDate, endDate] = tempChooseAreaTimestamp
const startDateTimestamp = dateUtil.getTimeStamp(startDate)
const endDateTimestamp = dateUtil.getTimeStamp(endDate)
chooseAreaTimestamp = [startDateTimestamp, endDateTimestamp]
}
return {
calendarData: {
...calendarData,
chooseAreaTimestamp,
tempChooseAreaTimestamp,
dates: __dates,
selectedDates: __selectedDates
},
calendarConfig: {
...calendarConfig,
multi: true
}
}
},
methods(component) {
return {
/**
* 设置连续日期选择区域
* @param {array} dateArea 区域开始结束日期数组
*/
chooseDateArea: (dateArea = []) => {
if (dateArea.length === 1) {
dateArea = dateArea.concat(dateArea)
}
if (dateArea.length !== 2) return
const isRight = validateTimeRange(dateArea)
if (!isRight) return
const config = getCalendarConfig(component) || {}
const { startTimestamp, endTimestamp } = convertTimeRangeToTimestamp(
dateArea
)
const existCalendarData = getCalendarData('calendar', component)
return renderCalendar.call(
component,
{
...existCalendarData,
chooseAreaTimestamp: [startTimestamp, endTimestamp]
},
{
...config,
multi: true,
chooseAreaMode: true
}
)
}
}
}
}
}

1
src/components/calendar/plugins/todo.d.ts vendored

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

135
src/components/calendar/plugins/todo.js

@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
/**
* @Author: drfu*
* @Description: 代办事项
* @Date: 2020-10-08 21:22:09*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-11 14:23:02
* */
import { getCalendarData, dateUtil } from '../utils/index'
import { renderCalendar } from '../render'
function updateDatePropertyOfTodoLabel(todos, dates, showLabelAlways) {
const datesInfo = [...dates]
for (let todo of todos) {
let targetIdx = datesInfo.findIndex(
item => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(todo)
)
let target = datesInfo[targetIdx]
if (!target) continue
if (showLabelAlways) {
target.showTodoLabel = true
} else {
target.showTodoLabel = !target.choosed
}
if (target.showTodoLabel) {
target.todoText = todo.todoText
}
target.color = todo.color
}
return datesInfo
}
export default () => {
return {
name: 'todo',
beforeRender(calendarData = {}, calendarConfig = {}, component) {
const { todos = [], dates = [], showLabelAlways } = calendarData
const dateWithTodoInfo = updateDatePropertyOfTodoLabel(
todos,
dates,
showLabelAlways
)
return {
calendarData: {
...calendarData,
dates: dateWithTodoInfo
},
calendarConfig
}
},
methods(component) {
return {
setTodos: (options = {}) => {
const calendar = getCalendarData('calendar', component)
if (!calendar || !calendar.dates) {
return Promise.reject('请等待日历初始化完成后再调用该方法')
}
const {
circle,
dotColor = '',
pos = 'bottom',
showLabelAlways,
dates: todoDates = []
} = options
const { todos = [] } = calendar
const tranformStr2NumOfTodo = todoDates.map(date =>
dateUtil.tranformStr2NumOfDate(date)
)
const calendarData = {
dates: calendar.dates,
todos: dateUtil.uniqueArrayByDate(
todos.concat(tranformStr2NumOfTodo)
)
}
if (!circle) {
calendarData.todoLabelPos = pos
calendarData.todoLabelColor = dotColor
}
calendarData.todoLabelCircle = circle || false
calendarData.showLabelAlways = showLabelAlways || false
const existCalendarData = getCalendarData('calendar', component)
return renderCalendar.call(component, {
...existCalendarData,
...calendarData
})
},
deleteTodos(todos = []) {
if (!(todos instanceof Array) || !todos.length)
return Promise.reject('deleteTodos()应为入参为非空数组')
const existCalendarData = getCalendarData('calendar', component)
const allTodos = existCalendarData.todos || []
const toDeleteTodos = todos.map(item => dateUtil.toTimeStr(item))
const remainTodos = allTodos.filter(
item => !toDeleteTodos.includes(dateUtil.toTimeStr(item))
)
const { dates, curYear, curMonth } = existCalendarData
const _dates = [...dates]
const currentMonthTodos = dateUtil.filterDatesByYM(
{
year: curYear,
month: curMonth
},
remainTodos
)
_dates.forEach(item => {
item.showTodoLabel = false
})
currentMonthTodos.forEach(item => {
_dates[item.date - 1].showTodoLabel = !_dates[item.date - 1].choosed
})
return renderCalendar.call(component, {
...existCalendarData,
dates: _dates,
todos: remainTodos
})
},
clearTodos() {
const existCalendarData = getCalendarData('calendar', component)
const _dates = [...existCalendarData.dates]
_dates.forEach(item => {
item.showTodoLabel = false
})
return renderCalendar.call(component, {
...existCalendarData,
dates: _dates,
todos: []
})
},
getTodos() {
return getCalendarData('calendar.todos', component) || []
}
}
}
}
}

432
src/components/calendar/plugins/week.js

@ -0,0 +1,432 @@ @@ -0,0 +1,432 @@
/**
* @Author: drfu*
* @Description: 周视图
* @Date: 2020-10-08 21:22:09*
* @Last Modified by: drfu
* @Last Modified time: 2020-10-12 14:39:45
* */
import { renderCalendar } from '../render'
import {
getCalendarConfig,
getCalendarData,
logger,
dateUtil
} from '../utils/index'
import { calcJumpData } from '../core'
/**
* 当月第一周所有日期
*/
function firstWeekInMonth(
target = {},
calendarDates = [],
calendarConfig = {}
) {
const { firstDayOfWeek } = calendarConfig
const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
const { year, month } = target
let firstDay = dateUtil.getDayOfWeek(year, month, 1)
if (firstDayOfWeekIsMon && firstDay === 0) {
firstDay = 7
}
const [, end] = [0, 7 - firstDay]
return calendarDates.slice(0, firstDayOfWeekIsMon ? end + 1 : end)
}
/**
* 当月最后一周所有日期
*/
function lastWeekInMonth(target = {}, calendarDates = [], calendarConfig = {}) {
const { firstDayOfWeek } = calendarConfig
const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
const { year, month } = target
const lastDay = dateUtil.getDatesCountOfMonth(year, month)
let lastDayWeek = dateUtil.getDayOfWeek(year, month, lastDay)
if (firstDayOfWeekIsMon && lastDayWeek === 0) {
lastDayWeek = 7
}
const [start, end] = [lastDay - lastDayWeek, lastDay]
return calendarDates.slice(firstDayOfWeekIsMon ? start : start - 1, end)
}
/**
* 判断目标日期是否在某些指定日历内
*/
function dateIsInDatesRange(target, dates) {
if (!target || !dates || !dates.length) return false
const targetDateStr = dateUtil.toTimeStr(target)
let rst = false
for (let date of dates) {
const dateStr = dateUtil.toTimeStr(date)
if (dateStr === targetDateStr) {
rst = true
return rst
}
rst = false
}
return rst
}
function getDatesWhenTargetInFirstWeek(target, firstWeekDates) {
const { year, month } = target
const prevMonthInfo = dateUtil.getPrevMonthInfo({ year, month })
let lastMonthDatesCount = dateUtil.getDatesCountOfMonth(
prevMonthInfo.year,
prevMonthInfo.month
)
let dates = firstWeekDates
let firstWeekCount = firstWeekDates.length
for (let i = 0; i < 7 - firstWeekCount; i++) {
const week = dateUtil.getDayOfWeek(+year, +month, lastMonthDatesCount)
dates.unshift({
year: prevMonthInfo.year,
month: prevMonthInfo.month,
date: lastMonthDatesCount,
week
})
lastMonthDatesCount -= 1
}
return dates
}
function getDatesWhenTargetInLastWeek(target, lastWeekDates) {
const { year, month } = target
const prevMonthInfo = dateUtil.getNextMonthInfo({ year, month })
let dates = lastWeekDates
let lastWeekCount = lastWeekDates.length
for (let i = 0; i < 7 - lastWeekCount; i++) {
const week = dateUtil.getDayOfWeek(+year, +month, i + 1)
dates.push({
year: prevMonthInfo.year,
month: prevMonthInfo.month,
date: i + 1,
week
})
}
return dates
}
function getDates(target, calendarDates = [], calendarConfig = {}) {
const { year, month, date } = target
const targetDay = dateUtil.getDayOfWeek(year, month, date)
const { firstDayOfWeek } = calendarConfig
const firstDayOfWeekIsMon = firstDayOfWeek === 'Mon'
if (firstDayOfWeekIsMon) {
const startIdx = date - (targetDay || 7)
return calendarDates.splice(startIdx, 7)
} else {
const startIdx = date - targetDay - 1
return calendarDates.splice(startIdx, 7)
}
}
function getTargetWeekDates(target, calendarConfig) {
if (!target) return
const { year, month } = target
const calendarDates = dateUtil.calcDates(year, month)
const firstWeekDates = firstWeekInMonth(target, calendarDates, calendarConfig)
const lastWeekDates = lastWeekInMonth(target, calendarDates, calendarConfig)
if (dateIsInDatesRange(target, firstWeekDates)) {
return getDatesWhenTargetInFirstWeek(target, firstWeekDates)
} else if (dateIsInDatesRange(target, lastWeekDates)) {
return getDatesWhenTargetInLastWeek(target, lastWeekDates)
} else {
return getDates(target, calendarDates, calendarConfig)
}
}
/**
* 计算周视图下当前这一周最后一天
*/
function calculateLastDateOfCurrentWeek(calendarData = {}) {
const { dates = [] } = calendarData
return dates[dates.length - 1]
}
/**
* 计算周视图下当前这一周第一天
*/
function calculateFirstDateOfCurrentWeek(calendarData = {}) {
const { dates } = calendarData
return dates[0]
}
/**
* 计算下一周的日期
*/
function calculateNextWeekDates(calendarData = {}) {
let { curYear, curMonth } = calendarData
let calendarDates = []
let lastDateInThisWeek = calculateLastDateOfCurrentWeek(calendarData)
const { year: LYear, month: LMonth } = lastDateInThisWeek
if (curYear !== LYear || curMonth !== LMonth) {
calendarDates = dateUtil.calcDates(LYear, LMonth)
curYear = LYear
curMonth = LMonth
} else {
calendarDates = dateUtil.calcDates(curYear, curMonth)
}
const lastDateInThisMonth = dateUtil.getDatesCountOfMonth(curYear, curMonth)
const count = lastDateInThisMonth - lastDateInThisWeek.date
const lastDateIdx = calendarDates.findIndex(
date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(lastDateInThisWeek)
)
const startIdx = lastDateIdx + 1
if (count >= 7) {
return {
dates: calendarDates.splice(startIdx, 7),
year: curYear,
month: curMonth
}
} else {
const nextMonth = dateUtil.getNextMonthInfo({
year: curYear,
month: curMonth
})
const { year, month } = nextMonth || {}
const calendarDatesOfNextMonth = dateUtil.calcDates(year, month)
const remainDatesOfThisMonth = calendarDates.splice(startIdx)
const patchDatesOfNextMonth = calendarDatesOfNextMonth.splice(
0,
7 - remainDatesOfThisMonth.length
)
return {
dates: [...remainDatesOfThisMonth, ...patchDatesOfNextMonth],
...nextMonth
}
}
}
/**
* 计算上一周的日期
*/
function calculatePrevWeekDates(calendarData = {}) {
let { curYear, curMonth } = calendarData
let firstDateInThisWeek = calculateFirstDateOfCurrentWeek(calendarData)
let calendarDates = []
const { year: FYear, month: FMonth } = firstDateInThisWeek
if (curYear !== FYear || curMonth !== FMonth) {
calendarDates = dateUtil.calcDates(FYear, FMonth)
curYear = FYear
curMonth = FMonth
} else {
calendarDates = dateUtil.calcDates(curYear, curMonth)
}
const firstDateIdx = calendarDates.findIndex(
date => dateUtil.toTimeStr(date) === dateUtil.toTimeStr(firstDateInThisWeek)
)
if (firstDateIdx - 7 >= 0) {
const startIdx = firstDateIdx - 7
return {
dates: calendarDates.splice(startIdx, 7),
year: curYear,
month: curMonth
}
} else {
const prevMonth = dateUtil.getPrevMonthInfo({
year: curYear,
month: curMonth
})
const { year, month } = prevMonth || {}
const calendarDatesOfPrevMonth = dateUtil.calcDates(year, month)
const remainDatesOfThisMonth = calendarDates.splice(
0,
firstDateInThisWeek.date - 1
)
const patchDatesOfPrevMonth = calendarDatesOfPrevMonth.splice(
-(7 - remainDatesOfThisMonth.length)
)
return {
dates: [...patchDatesOfPrevMonth, ...remainDatesOfThisMonth],
...prevMonth
}
}
}
export default () => {
return {
name: 'week',
beforeRender(calendarData = {}, calendarConfig = {}, component) {
const { initializedWeekMode, selectedDates } = calendarData
if (calendarConfig.weekMode && !initializedWeekMode) {
const { defaultDate } = calendarConfig
const target =
(selectedDates && selectedDates[0]) ||
(defaultDate && dateUtil.transformDateRow2Dict(defaultDate)) ||
dateUtil.todayFMD()
const waitRenderData = this.methods(
component
).__calcDatesWhenSwitchView('week', target)
const { data, config } = waitRenderData || {}
const setSelectDates = this.methods(
component
).__selectTargetDateWhenJump(target, data.dates, config)
return {
calendarData: {
...data,
...setSelectDates,
weeksCh: dateUtil.getWeekHeader(calendarConfig.firstDayOfWeek),
initializedWeekMode: true
},
calendarConfig
}
}
return {
calendarData,
calendarConfig
}
},
onSwitchCalendar(target = {}, calendarData = {}, component) {
const { direction } = target
const { curYear, curMonth } = calendarData
const calendarConfig = getCalendarConfig(component)
let waitRenderData = {}
if (calendarConfig.weekMode) {
if (direction === 'left') {
waitRenderData = calculateNextWeekDates(calendarData)
} else {
waitRenderData = calculatePrevWeekDates(calendarData)
}
const { dates, year, month } = waitRenderData
return {
...calendarData,
dates,
curYear: year || curYear,
curMonth: month || curMonth
}
}
return calendarData
},
methods(component) {
return {
__selectTargetDateWhenJump: (target = {}, dates = [], config = {}) => {
let selectedDate = target
const weekDates = dates.map((date, idx) => {
const tmp = { ...date }
tmp.id = idx
const isTarget =
dateUtil.toTimeStr(target) === dateUtil.toTimeStr(tmp)
if (isTarget && !target.choosed && config.autoChoosedWhenJump) {
tmp.choosed = true
selectedDate = tmp
}
return tmp
})
return {
dates: weekDates,
selectedDates: [selectedDate]
}
},
__calcDatesForWeekMode(target, config = {}, calendarData = {}) {
const { year, month } = target || {}
const weekDates = getTargetWeekDates(target, config)
weekDates.forEach((date, idx) => (date.id = idx))
return {
data: {
...calendarData,
prevMonthGrids: null,
nextMonthGrids: null,
dates: weekDates,
curYear: year,
curMonth: month
},
config: {
...config,
weekMode: true
}
}
},
__calcDatesForMonthMode(target, config = {}, calendarData = {}) {
const { year, month } = target || {}
const waitRenderData = calcJumpData({
dateInfo: target,
config
})
return {
data: {
...calendarData,
...waitRenderData,
curYear: year,
curMonth: month
},
config: {
...config,
weekMode: false
}
}
},
/**
* 月视图切换
* @param {string} view 视图 [week, month]
* @param {object} target
*/
__calcDatesWhenSwitchView: (view, target) => {
const calendarConfig = getCalendarConfig(component)
if (calendarConfig.multi)
return logger.warn('多选模式不能切换周月视图')
const existCalendarData = getCalendarData('calendar', component) || {}
const {
selectedDates = [],
dates = [],
curYear,
curMonth
} = existCalendarData
const currentMonthSelected = selectedDates.filter(
item => curYear === +item.year || curMonth === +item.month
)
let jumpTarget = {}
if (target) {
jumpTarget = target
} else {
if (currentMonthSelected.length) {
jumpTarget = currentMonthSelected.pop()
} else {
jumpTarget = dates[0]
}
}
if (view === 'week') {
return this.methods(component).__calcDatesForWeekMode(
jumpTarget,
calendarConfig,
existCalendarData
)
} else {
return this.methods(component).__calcDatesForMonthMode(
jumpTarget,
calendarConfig,
existCalendarData
)
}
},
weekModeJump: dateInfo => {
const target = dateInfo || dateUtil.todayFMD()
const existCalendarData = getCalendarData('calendar', component) || {}
const waitRenderData = this.methods(
component
).__calcDatesWhenSwitchView('week', target)
const { data, config } = waitRenderData || {}
const setSelectDates = this.methods(
component
).__selectTargetDateWhenJump(target, data.dates, config)
return renderCalendar.call(
component,
{
...existCalendarData,
...data,
...setSelectDates
},
config
)
},
switchView: (view, target) => {
const waitRenderData = this.methods(
component
).__calcDatesWhenSwitchView(view, target)
const { data, config } = waitRenderData || {}
if (!data) return logger.warn('当前状态不能切换为周视图')
return renderCalendar.call(component, data, config)
}
}
}
}
}

49
src/components/calendar/render.js

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
import plugins from "./plugins/index";
import { getCalendarConfig } from "./utils/index";
/**
* 渲染日历
*/
export function renderCalendar(calendarData, calendarConfig) {
return new Promise((resolve) => {
const Component = this;
if (Component.firstRender === void 0) {
Component.firstRender = true;
} else {
Component.firstRender = false;
}
const exitData = Component.data.calendar || {};
for (let plugin of plugins.installed) {
const [, p] = plugin;
if (typeof p.beforeRender === "function") {
const { calendarData: newData, calendarConfig: newConfig } =
p.beforeRender(
{ ...exitData, ...calendarData },
calendarConfig || getCalendarConfig(Component),
Component
);
calendarData = newData;
calendarConfig = newConfig;
}
}
Component.setData(
{
config: calendarConfig,
calendar: calendarData,
},
() => {
const rst = {
calendar: calendarData,
config: calendarConfig,
firstRender: Component.firstRender,
};
resolve(rst);
if (Component.firstRender) {
Component.triggerEvent("afterCalendarRender", rst);
Component.firstRender = false;
}
}
);
});
}

29
src/components/calendar/theme/iconfont.wxss

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
@font-face {
font-family: 'iconfont';
src: url(data:font/truetype;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTYda3jUAAAfEAAAAHEdERUYAKQANAAAHpAAAAB5PUy8yPllJ4AAAAVgAAABWY21hcAAP65kAAAHIAAABQmdhc3D//wADAAAHnAAAAAhnbHlmLotR3AAAAxwAAAGkaGVhZBTU+ykAAADcAAAANmhoZWEHKwOFAAABFAAAACRobXR4DasB4gAAAbAAAAAWbG9jYQC0AR4AAAMMAAAAEG1heHABEwAyAAABOAAAACBuYW1lKeYRVQAABMAAAAKIcG9zdEoLnOYAAAdIAAAAUgABAAAAAQAAiPM8al8PPPUACwQAAAAAANjbW5YAAAAA2NtblgCzAAQDTQL8AAAACAACAAAAAAAAAAEAAAOA/4AAXAQAAAAAAANNAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAAHACYAAgAAAAAAAgAAAAoACgAAAP8AAAAAAAAAAQQAAZAABQAAAokCzAAAAI8CiQLMAAAB6wAyAQgAAAIABQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUGZFZABA5+vn7gOA/4AAXAOAAIAAAAABAAAAAAAABAAAAAAAAAAEAAAABAABLgD4ALQAswAAAAAAAwAAAAMAAAAcAAEAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAA5+7//wAA5+v//xgYAAEAAAAAAAABBgAAAQAAAAAAAAABAgAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJgBMAI4A0gABAS4ABAMKAvwAEgAACQEmBh0BFBcJAQYdARQWNwE2NAL+/j0ECQYBaP6YBgkEAcMMAZkBYAMEBU0IBf7n/ucFCE0FBAMBYAoeAAAAAQD4AAQC1AL8ABIAAAE1NCYHAQYUFwEWNj0BNCcJATYC1AkE/j0MDAHDBAkG/pgBaAYCpk0FBAP+oAoeCv6gAwQFTQgFARkBGQUAAAIAtAAgA00C4AASACUAAAkBNiYrASIHAwYUFwEWOwEyNicTATYmKwEiBwMGFBcBFjsBMjYnAREBCQMEBU0IBf8HBwD/BQhNBQQDJwEJAwQFTQgF/wcHAP8FCE0FBAMBgAFTBAkG/roJFgn+ugYJBAFTAVMECQb+ugkWCf66BgkEAAAAAAIAswAgA0wC4AASACUAAAEDJisBIgYXCQEGFjsBMjcBNjQlAyYrASIGFwkBBhY7ATI3ATY0AhX/BQhNBQQDAQn+9wMEBU0IBQD/BwEp/wUITQUEAwEJ/vcDBAVNCAUA/wcBlAFGBgkE/q3+rQQJBgFGCRYJAUYGCQT+rf6tBAkGAUYJFgAAAAAAABIA3gABAAAAAAAAABUALAABAAAAAAABAAgAVAABAAAAAAACAAcAbQABAAAAAAADAAgAhwABAAAAAAAEAAgAogABAAAAAAAFAAsAwwABAAAAAAAGAAgA4QABAAAAAAAKACsBQgABAAAAAAALABMBlgADAAEECQAAACoAAAADAAEECQABABAAQgADAAEECQACAA4AXQADAAEECQADABAAdQADAAEECQAEABAAkAADAAEECQAFABYAqwADAAEECQAGABAAzwADAAEECQAKAFYA6gADAAEECQALACYBbgAKAEMAcgBlAGEAdABlAGQAIABiAHkAIABpAGMAbwBuAGYAbwBuAHQACgAACkNyZWF0ZWQgYnkgaWNvbmZvbnQKAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABSAGUAZwB1AGwAYQByAABSZWd1bGFyAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAABpAGMAbwBuAGYAbwBuAHQAAGljb25mb250AABHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAABHZW5lcmF0ZWQgYnkgc3ZnMnR0ZiBmcm9tIEZvbnRlbGxvIHByb2plY3QuAABoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAABodHRwOi8vZm9udGVsbG8uY29tAAACAAAAAAAAAAoAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAcAAAABAAIBAgEDAQQBBQVyaWdodARsZWZ0CmRvdWJsZWxlZnQLZG91YmxlcmlnaHQAAAAAAAH//wACAAEAAAAMAAAAFgAAAAIAAQADAAYAAQAEAAAAAgAAAAAAAAABAAAAANWkJwgAAAAA2NtblgAAAADY21uW) format('truetype');
font-weight: normal;
font-style: normal;
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
}
.icon-right::before {
content: "\e7eb";
}
.icon-left::before {
content: "\e7ec";
}
.icon-doubleleft::before {
content: "\e7ed";
}
.icon-doubleright::before {
content: "\e7ee";
}

61
src/components/calendar/theme/theme-default.wxss

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
/* 日历主要颜色相关样式 */
.default_color,
.default_weekend-color,
.default_handle-color,
.default_week-color {
color: #ff629a;
}
.default_today {
color: #fff;
background-color: #874fb4;
}
.default_choosed {
color: #fff;
background-color: #ff629a;
}
.default_date-disable {
color: #c7c7c7;
}
.default_choosed.default_date-disable {
color: #e2e2e2;
background-color: #c2afb6;
}
.default_prev-month-date,
.default_next-month-date {
color: #e2e2e2;
}
.default_normal-date {
color: #88d2ac;
}
.default_todo-circle {
border-color: #88d2ac;
}
.default_todo-dot {
background-color: #e54d42;
}
.default_date-desc {
color: #c2c2c2;
}
.default_date-desc-lunar {
color: #e54d42;
}
.default_date-desc-disable {
color: #e2e2e2;
}
.default_festival {
color: #c2c2c2;
}

58
src/components/calendar/theme/theme-elegant.wxss

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
.elegant_color,
.elegant_weekend-color,
.elegant_handle-color,
.elegant_week-color {
color: #333;
}
.elegant_today {
color: #000;
background-color: #e1e7f5;
}
.elegant_choosed {
color: #000;
background-color: #e2e2e2;
}
.elegant_date-disable {
color: #c7c7c7;
}
.elegant_choosed.elegant_date-disable {
color: #999;
background-color: #ebebeb;
}
.elegant_prev-month-date,
.elegant_next-month-date {
color: #e2e2e2;
}
.elegant_normal-date {
color: #777;
}
.elegant_todo-circle {
border-color: #161035;
}
.elegant_todo-dot {
background-color: #161035;
}
.elegant_date-desc {
color: #c2c2c2;
}
.elegant_date-desc-lunar {
color: #161035;
}
.elegant_date-desc-disable {
color: #e2e2e2;
}
.elegant_festival {
color: #c2c2c2;
}

90
src/components/calendar/theme/theme-nuohe.wxss

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
/* 日历主要颜色相关样式 */
.nuohe_color,
.nuohe_weekend-color,
.nuohe_handle-color,
.nuohe_week-color {
color: rgba(51, 51, 51, 1);
}
.nuohe_today {
color: #fff;
background-color: #874fb4;
}
.nuohe_choosed {
color: rgba(51, 51, 51, 1);
background-color: rgba(189, 189, 189, 0.3);
}
.nuohe_date-disable {
color: #c7c7c7;
}
.nuohe_choosed.nuohe_date-disable {
color: #e2e2e2;
background-color: #c2afb6;
}
.nuohe_prev-month-date,
.nuohe_next-month-date {
color: #e2e2e2;
}
.nuohe_normal-date {
color: rgba(51, 51, 51, 1);
}
.nuohe_todo-circle {
border-color: #88d2ac;
}
.nuohe_todo-dot {
background-color: #e54d42;
}
.nuohe_date-desc {
color: #c2c2c2;
}
.nuohe_date-desc-lunar {
color: #e54d42;
}
.nuohe_date-desc-disable {
color: #e2e2e2;
}
.nuohe_festival {
color: #c2c2c2;
}
.date.doc {
position: relative;
background-color: #e5e5e5;
}
.date.doc::after {
position: absolute;
z-index: 1;
top: -5rpx;
right: -5rpx;
content: "";
width: 17rpx;
height: 24rpx;
background-repeat: no-repeat;
background-size: cover;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAABACAYAAABhspUgAAAAAXNSR0IArs4c6QAACAVJREFUaEPtmltsHFcZx/9nZu92bMdJSMitzoWkoXVKUrVV8wKk4gHxgJTwgKA8lhfgCZBoUJEqUYGA0AhRQUSpFJQIHlyqogZFIipUoKiAKJAYQkp9qR1ir9fe6+zuXM4FfWf2MrOJY+/aS0zlkRxZzsw5v/nP/3znO+c7DItcl4vFTWVl/7wg5EctKSK2knCVBFcKcrGH7vL3MvfwVnYeGbsaustgAAODCSDCDLU1Hp/Ym+o7fnrvB9+9U3NssT5ezWe+k5X8qwvCQ0FwVKSEswJg6sfmHH+cm2l0SZ3Tj8F84ChjiDID+1I9ly4ceuTjbQFfyM2MpDk/meEu8oKjJIUG9khh1YHEtUeupgPAWl3AAENEwzLEmIGd8dToq8OPDbcF/OLCrZFZ7p5Mcxc5wVEUAmQLT8mOLFHvfCwzF7ZEC3CcGdiRSIxeHD7WHvDZhZsjM56ngbMELDlsWVO4c4ExOd8E9i1R969vhzhj2BFPjv76cJvApPCtkMK8YYkVOALLUXh7PNE+8M/Iw553MiN8S1ii6eGVAI+mZ0PfhzHoAWfWBlyMMWyPJ0dfG368PUu8nJ8bWRD8ZPYOUWIlwH+YmQ4Dg8FoAPu22J5Ijb7ywKPtAV8uZZ/LC36qIH11q7UIIZRCp8CW5+Ly7M3bgVkzUlC0ONC74VcvHTj6ybaiBN38RmVhV9YxTBs2nrl08VTRrjy1gvEGZRh+HGu5epOp808ceehMFFEkDYN/f++Df1+sn0UnjtYHjB9+63tQ6ssrAV70WcZOyy8+/ZXltL0o8Hfzk3u4jc94Bh7nSm0uOZWNruvFozBMkzEG+iF3KGm5XJTAZFVIxRXCjnGkNB0pIwXPTWYcu6/MeYLAgh1HozHeE48VE6Y5vyFiXknFoufO73/4nWVZ4kwmc9BG5QVLiONcKSYYEFU0z/vd1P3rQaEiBYiwnSvr2Jgo5mFxTz9Gcbj+AjTjmczPKbbF41fej9jnzz5w9J/B9kMKP58e/1xO4ayjZNKlwaUUkqYJJZsDjRKfBcH1dN3pRa3dKpUwX61oqRs5RS1amLWpOmWYzv5k7xfO3X/0p/W+GsBnMtNPLQjvx1UlDUcKQDEkDKanYRJR6W+tMM0dzPPOYYMvmauWkStXfOBA1kYxWecWMJAwDHV/T9+Xzh088kLDSmfmpx9b8Lw3qpDxqv7MCilmaFBJ/9aMmZYcY05VK78aFw2DrGXBdp2AymSLQDIEAynD9B4c3PKRnwwduqIV/ubc5N+Kgj9EnqQEhxIQ+iwyEHPJIm+WSxBMj7TV4NX+lVIiXyzURGCg/NjP3ijdNEAzH/FsjcX/dWn42CH2g+zMJ9Ju5bWyFDrnFQB6GMGSur4N6Bpzq5hwnVUBbW2kWq3CcXyV6QoMvgZwghk43Dtwgn17burlrHBPaGAlEQFDnN68Bku41NDvrQLsVVK2FZhzAcsqNYCpP/rCZA2tsGGAgIeSvRfZs3Pj0wUhdpZr028vo/cj4OaHJ+WvVIpdUbfeaKHg26KpMoU3P7cgaALeHIun2TPpcaskeE9ZSp0v9DPTjwoB4Cytx+xyV4FLxSKE9FeL9TDnZ3D+KoSA+yIRm52aGVOWEiBLVKXEoBHRdtDRoXbNcQ9XuwxMliBrhIBr8VgDGwxJZoI9TcCSo6wkHCmxUQOHV8Zz3MVVu9JVha2SBR6YjCjlrK/1tCUMwwf+WgDYrQGLlsC1VoDJFiHgoMLBSLsWgClSJOvAJdncd6hbYi0B08DzLVFTeB24g+F5t0G3rnAHgt72yLrC93LieO952ARHVDWXQQVuYbqaWQ2rLtrGVJmhEljR6pyRFqS1fIKS+LjB/JmuNQ7vlrMYUs1dxp3udRwrvdJV4GedJ3BDbl6yj1UFdgsJVNMbQp0mt5UQ67OXBLknwOO/OILi2KYQXN++Bez99F/XJvA75x+G9e7GEFzvfTnsf/Iv68CNKhFla50OujWtsD3fA2tyMPSpM3/eBSebCv0tPljBlkfCm9i9Q1kkNofXiF0fdJO/HEb++tYlvXmnGwYOpTF04lrov7oOPDFyGIUb7+sIuP9gBns+Fd6zXgdunen+7xT+z28OIPOn3R1ZYsujU9jxsbf/tx6m3pQMVxzGLhyFNdUycezOYd9n3wrBMeP23c+ue/hO0q7pOLwOfC/Sy1uvfwDZa9tC4g8Oz2L78X8vOTjviYeXpLrLDSsC3qqy2KZyjeYH3EnsKf92JTxLPvti9UOYkv233eefWKEqE52rWGSJFNx9pxbu9Wbge2/VvOYVru/Ar+UN7cb+8FosGTRrz35RpulhE+zrs1TjoKKMf5BuIzN1cTFYRcpwD6NOd2scxRIVZZqbN/q0la7sN6tIumTwjdlxXUWqCAIW6KeiTMtxxXnh4boTPoK4ZJxq84ZssQgvAEwl3HpVXyusK0kG2HNzk/mSEP0VJfS5NCosKjq7USuAU15lKYkbXa4i3czlwOkUQe0KAtfrzf2RqMVOZ6Z+VxD8w1RUJEtElV9JDx4Ipd8nXFtX+btxOULg7ay/d9eohDL/tFX9PCZB70qk3mQ/ys0M5T3nWlXKXqrYcyaRpFtrRw7qiPQyRa3Aso8JLevdqAg/mptHwW1uZ9XPYxIwiUc/GyLRyuH+LcO6d4KuCv5SRYojrpQDFIcjMLTp60dcdMJel6D1YM+y0MI3uUIg57n4RyGLoueGE/xar2SLHsO0NsXi13b1DDz5/H0Hxv8Lo3UyF0Y2DrcAAAAASUVORK5CYII=");
}
.date.error {
background-color: rgba(207, 83, 117, 1);
border-radius: 50%;
color: #fff;
}
.date.primary {
background-color: rgba(37, 217, 200, 1);
color: #fff;
border-radius: 50%;
}

275
src/components/calendar/utils/index.js

@ -0,0 +1,275 @@ @@ -0,0 +1,275 @@
import Logger from "./logger";
import WxData from "./wxData";
let systemInfo;
export function getSystemInfo() {
if (systemInfo) return systemInfo;
systemInfo = wx.getSystemInfoSync();
return systemInfo;
}
export function isIos() {
const sys = getSystemInfo();
return /iphone|ios/i.test(sys.platform);
}
class Gesture {
/**
* 左滑
* @param {object} e 事件对象
* @returns {boolean} 布尔值
*/
isLeft(gesture = {}, touche = {}) {
const { startX, startY } = gesture;
const deltaX = touche.clientX - startX;
const deltaY = touche.clientY - startY;
if (deltaX < -60 && deltaY < 20 && deltaY > -20) {
return true;
} else {
return false;
}
}
/**
* 右滑
* @param {object} e 事件对象
* @returns {boolean} 布尔值
*/
isRight(gesture = {}, touche = {}) {
const { startX, startY } = gesture;
const deltaX = touche.clientX - startX;
const deltaY = touche.clientY - startY;
if (deltaX > 60 && deltaY < 20 && deltaY > -20) {
return true;
} else {
return false;
}
}
}
class DateUtil {
newDate(year, month, date) {
let cur = `${+year}-${+month}-${+date}`;
if (isIos()) {
cur = `${+year}/${+month}/${+date}`;
}
return new Date(cur);
}
/**
* 计算指定日期时间戳
* @param {object} date
*/
getTimeStamp(dateInfo) {
if (typeof dateInfo === "string") {
dateInfo = this.transformDateRow2Dict(dateInfo);
}
if (Object.prototype.toString.call(dateInfo) !== "[object Object]") return;
const dateUtil = new DateUtil();
return dateUtil.newDate(dateInfo.year, dateInfo.month, dateInfo.date).getTime();
}
/**
* 计算指定月份共多少天
* @param {number} year 年份
* @param {number} month 月份
*/
getDatesCountOfMonth(year, month) {
return new Date(Date.UTC(year, month, 0)).getUTCDate();
}
/**
* 计算指定月份第一天星期几
* @param {number} year 年份
* @param {number} month 月份
*/
firstDayOfWeek(year, month) {
return new Date(Date.UTC(year, month - 1, 1)).getUTCDay();
}
/**
* 计算指定日期星期几
* @param {number} year 年份
* @param {number} month 月份
* @param {number} date 日期
*/
getDayOfWeek(year, month, date) {
return new Date(Date.UTC(year, month - 1, date)).getUTCDay();
}
todayFMD() {
const _date = new Date();
const year = _date.getFullYear();
const month = _date.getMonth() + 1;
const date = _date.getDate();
return {
year: +year,
month: +month,
date: +date,
};
}
todayTimestamp() {
const { year, month, date } = this.todayFMD();
const timestamp = this.newDate(year, month, date).getTime();
return timestamp;
}
toTimeStr(dateInfo = {}) {
return `${+dateInfo.year}-${+dateInfo.month}-${+dateInfo.date}`;
}
transformDateRow2Dict(dateStr) {
if (typeof dateStr === "string" && dateStr.includes("-")) {
const [year, month, date] = dateStr.split("-");
return this.tranformStr2NumOfDate({
year,
month,
date,
});
}
return {};
}
tranformStr2NumOfDate(date = {}) {
const target = { ...date };
// 可能传入字符串
target.year = +target.year;
target.month = +target.month;
target.date = +target.date;
return target;
}
sortDatesByTime(dates = [], sortType) {
return dates.sort((a, b) => {
const at = this.getTimeStamp(a);
const bt = this.getTimeStamp(b);
if (at < bt && sortType !== "desc") {
return -1;
} else {
return 1;
}
});
}
getPrevMonthInfo(date = {}) {
const prevMonthInfo =
Number(date.month) > 1
? {
year: +date.year,
month: Number(date.month) - 1,
}
: {
year: Number(date.year) - 1,
month: 12,
};
return prevMonthInfo;
}
getNextMonthInfo(date = {}) {
const nextMonthInfo =
Number(date.month) < 12
? {
year: +date.year,
month: Number(date.month) + 1,
}
: {
year: Number(date.year) + 1,
month: 1,
};
return nextMonthInfo;
}
getPrevYearInfo(date = {}) {
return {
year: Number(date.year) - 1,
month: +date.month,
};
}
getNextYearInfo(date = {}) {
return {
year: Number(date.year) + 1,
month: +date.month,
};
}
findDateIndexInArray(target, dates) {
return dates.findIndex((item) => dateUtil.toTimeStr(item) === dateUtil.toTimeStr(target));
}
calcDates(year, month) {
const datesCount = this.getDatesCountOfMonth(year, month);
const dates = [];
const today = dateUtil.todayFMD();
for (let i = 1; i <= datesCount; i++) {
const week = dateUtil.getDayOfWeek(+year, +month, i);
const date = {
year: +year,
id: i - 1,
month: +month,
date: i,
week,
isToday: +today.year === +year && +today.month === +month && i === +today.date,
};
dates.push(date);
}
return dates;
}
/**
* 日期数组根据日期去重
* @param {array} array 数组
*/
uniqueArrayByDate(array = []) {
let uniqueObject = {};
let uniqueArray = [];
array.forEach((item) => {
uniqueObject[dateUtil.toTimeStr(item)] = item;
});
for (let i in uniqueObject) {
uniqueArray.push(uniqueObject[i]);
}
return uniqueArray;
}
/**
* 筛选指定年月日期
* @param {object} target 指定年月
* @param {array} dates 待筛选日期
*/
filterDatesByYM(target, dates) {
if (target) {
const { year, month } = target;
const _dates = dates.filter((item) => +item.year === +year && +item.month === +month);
return _dates;
}
return dates;
}
getWeekHeader(firstDayOfWeek) {
let weeksCh = ["日", "一", "二", "三", "四", "五", "六"];
if (firstDayOfWeek === "Mon") {
weeksCh = ["一", "二", "三", "四", "五", "六", "日"];
}
return weeksCh;
}
}
/**
* 获取当前页面实例
*/
export function getCurrentPage() {
const pages = getCurrentPages() || [];
const last = pages.length - 1;
return pages[last] || {};
}
export function getComponentById(componentId) {
const logger = new Logger();
let page = getCurrentPage() || {};
if (page.selectComponent && typeof page.selectComponent === "function") {
if (componentId) {
return page.selectComponent(componentId);
} else {
logger.warn("请传入组件ID");
}
} else {
logger.warn("该基础库暂不支持多个小程序日历组件");
}
}
export const logger = new Logger();
export const calendarGesture = new Gesture();
export const dateUtil = new DateUtil();
export const getCalendarData = (key, component) => new WxData(component).getData(key);
export const setCalendarData = (data, component) => new WxData(component).setData(data);
export const getCalendarConfig = (component) => getCalendarData("config", component);
export const setCalendarConfig = (config, component) =>
setCalendarData(
{
config,
},
component,
);

23
src/components/calendar/utils/logger.js

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
export default class Logger {
info(msg) {
console.log(
'%cInfo: %c' + msg,
'color:#FF0080;font-weight:bold',
'color: #FF509B'
)
}
warn(msg) {
console.log(
'%cWarn: %c' + msg,
'color:#FF6600;font-weight:bold',
'color: #FF9933'
)
}
tips(msg) {
console.log(
'%cTips: %c' + msg,
'color:#00B200;font-weight:bold',
'color: #00CC33'
)
}
}

30
src/components/calendar/utils/wxData.js

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
class WxData {
constructor(component) {
this.Component = component
}
getData(key) {
const data = this.Component.data
if (!key) return data
if (key.includes('.')) {
let keys = key.split('.')
const tmp = keys.reduce((prev, next) => {
return prev[next]
}, data)
return tmp
} else {
return this.Component.data[key]
}
}
setData(data) {
return new Promise((resolve, reject) => {
if (!data) return reject('no data to set')
if (typeof data === 'object') {
this.Component.setData(data, () => {
resolve(data)
})
}
})
}
}
export default WxData

160
src/components/customPoster/README.md

@ -0,0 +1,160 @@ @@ -0,0 +1,160 @@
# 海报插件
## 基于[wxml2canvas](https://github.com/wg-front/wxml2canvas)插件
### 插件引入
```json
{
"customPoster": "/components/customPoster/index"
}
```
```wxml
<customPoster params="{{params}}"></customPoster>
```
### 参数示例
```js
const data = {
params: {
info: { id: 'circle-note_mp', title: '动态页', desc: '指定动态的页面,mp' },
body: {
bgImg:
'https://picsissiok-10049618.cos.ap-shanghai.myqcloud.com/a74018e725861534f1644cff04780f4e_16273677601587.png',
width: '750',
height: '1254',
},
elements: [
{
id: 'circle-name',
desc: '圈名称',
type: 1,
text: '治疗讨论',
font: '宋体',
'font-size': '42',
color: '#FFFFFF',
'single-line': true,
halign: 'center',
top: '106',
},
{
id: 'master-name',
desc: '专家名称',
type: 1,
text: '健康小贴士',
font: '宋体',
'font-size': '38',
color: '#FFFFFF',
'single-line': true,
halign: 'center',
top: '237',
},
{
id: 'master-avatar',
desc: '专家头像',
type: 0,
picType: 'avatar',
src: 'https://picsissiok-10049618.cos.ap-shanghai.myqcloud.com/ceb31ac6137e873225b8cc826389a267_16655329078962.jpg',
width: '120',
height: '120',
halign: 'center',
shape: 'circle',
top: '328',
},
{
id: 'note-content',
desc: '动态内容',
type: 1,
text: '11月5日上海组织进口博览会CIIE大会,邀请大家来我们的展馆现场交流。',
font: '宋体',
'font-size': '45',
color: '#18191A',
width: '630',
height: '348',
left: '60',
top: '495',
},
{
id: 'note-title',
desc: '长文标题',
type: 1,
text: '',
font: '宋体',
'font-size': '42',
color: '#18191A',
width: '659',
height: '116',
left: '46',
top: '495',
'font-weight': 'bold',
},
{
id: 'note-summary',
desc: '长文摘要 ',
type: 1,
text: '',
font: '宋体',
'font-size': '42',
color: '#18191A',
width: '672',
height: '232',
left: '39',
top: '646',
},
{
id: 'note-more',
desc: '还有',
type: 1,
text: '还有3条精彩附件>',
font: '宋体',
'font-size': '31',
color: '#666666',
'single-line': true,
width: '292',
height: '46',
left: '60',
top: '1024',
},
{
id: 'scanText',
desc: '扫描二维码查看详情',
type: 1,
text: '扫描二维码查看详情',
font: '宋体',
'font-size': '31',
color: '#666666',
'single-line': true,
width: '279',
height: '43',
left: '60',
top: '1087',
},
{
id: 'note-qrcode',
desc: '专家列表的二维码',
type: 0,
picType: 'qrcode',
src: 'https://family.devred.hbraas.com/proxy/mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQHJ8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyUWhDdGhTWVNlTjMxMDAwMDAwN3gAAgSonPxjAwQAAAAA',
width: '130',
height: '130',
left: '554',
top: '1012',
},
{
id: 'brand',
desc: 'brand×华秉科技',
type: 1,
text: '做企业数字化转型的亲密伙伴×华秉科技',
font: '宋体',
'font-size': '24',
color: '#B2B3B4',
'single-line': true,
halign: 'center',
top: '1192',
},
],
},
}
```

101
src/components/customPoster/index.js

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
import Wxml2Canvas from "./wxml2canvas/index.js"; // 根据具体路径修改,node_modules会被忽略
Component({
properties: {
params: {
type: Object,
observer(newVal, _olVal) {
if (Object.keys(newVal).length > 0) {
this.paramsFormat(newVal);
this.setData({
width: newVal.body.width,
height: newVal.body.height,
bgImg: newVal.body.bgImg,
elementsMp: newVal.elements,
});
this.drawImage();
}
},
},
},
data: {
imgUrl: "",
width: "",
height: "",
imgheight: "",
bgImg: "",
elementsMp: [],
},
lifetimes: {
attached() {},
},
methods: {
paramsFormat(params) {
params.elements.forEach((item) => {
if (item.type === 0) {
// 图片居中
if (item.halign === "center") {
item.left = (params.body.width - item.width) / 2;
}
} else {
item.text = item.text.replace(/[\r\n]/g, "");
// 文字居中(使文字标签宽度等于画布宽度,文字加上居中的className)
if (item.halign === "center") {
item.width = item.width || params.body.width;
item.left = (params.body.width - item.width) / 2;
}
if (item.halign === "right") {
item.width = params.body.width;
}
if (item.id === "shareText" && item.text.length > 30) {
item.text = item.text.slice(0, 30) + "...";
}
if (item.id === "note-title" && item.text.length > 28) {
item.text = item.text.slice(0, 28) + "...";
}
if (item.id === "note-content" && item.text.length > 90) {
item.text = item.text.slice(0, 90) + "...";
}
let len = params.elements.some((x) => x.id === "note-title" && x.text.length);
if (len && item.id === "note-summary" && item.text.length > 62) {
item.text = item.text.slice(0, 62) + "...";
} else if (item.id === "note-summary" && item.text.length > 76) {
item.text = item.text.slice(0, 76) + "...";
}
}
});
},
drawImage() {
let self = this;
this.drawImage1 = new Wxml2Canvas({
obj: self,
width: this.data.width, // 宽, 以iphone6为基准,传具体数值,其它机型自动适配
height: this.data.height, // 高
element: "canvas1",
background: "transparent",
progress(percent) {},
finish(url) {
self.setData({
imgUrl: url,
});
self.triggerEvent("finish", url);
},
error(res) {},
});
let data = {
list: [
{
type: "wxml",
class: "#canvas-bill-body-mp .draw_canvas",
limit: "#canvas-bill-body-mp",
x: 0,
y: 0,
},
],
};
this.drawImage1.draw(data);
},
},
});

3
src/components/customPoster/index.json

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
{
"usingComponents": {}
}

53
src/components/customPoster/index.scss

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
.canvas-box {
position: fixed;
left: 1000000rpx;
top: -1000000rpx;
}
.canvas-body {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-color: #fff;
}
.draw_canvas {
position: absolute;
}
.canvas-bill {
position: fixed;
width: 750rpx;
left: 1000000rpx;
top: -1000000rpx;
visibility: hidden;
}
.lineOne {
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
}
.textCenter {
text-align: center;
}
.textRight {
text-align: right;
}
.billImg-box {
position: fixed;
left: 0;
top: 0;
z-index: 10;
width: 750rpx;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.75);
}
.image-load-text {
width: 750rpx;
text-align: center;
color: #fff;
line-height: 50rpx;
margin-top: 20rpx;
font-size: 28rpx;
letter-spacing: 2rpx;
}

43
src/components/customPoster/index.wxml

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
<view id="canvas-bill-body-mp" class="canvas-box" style="height: {{ height }}rpx; width: {{ width }}rpx;">
<view
data-type="{{bgImg ? 'background-image' : 'text'}}"
style="height: {{ height }}rpx; width: {{ width }}rpx;background-image: url({{bgImg}})"
class="canvas-body draw_canvas"
>
<view wx:for="{{elementsMp}}" wx:key="index" wx:for-item="item">
<image
class="draw_canvas"
data-type="{{item.shape === 'circle' ? 'radius-image' : 'image'}}"
data-url="{{item.src}}"
style="height: {{ item.height }}rpx; width: {{ item.width? item.width + 'rpx' : 'auto'}};left: {{item.left}}rpx;right: {{item.right}}rpx;top: {{item.top}}rpx;{{item.shape === 'circle'?'border-radius:50%;':''}}"
src="{{item.src}}"
wx:if="{{item.type === 0}}"
data-delay="{{item.delay}}"
></image>
<view
class="draw_canvas {{item['single-line'] ? 'lineOne' : ''}} {{item['halign'] === 'center' ? 'textCenter' : ''}} {{item['halign'] === 'right' ? 'textRight' : ''}}"
wx:if="{{item.type === 1}}"
data-background="{{item.bgcolor}}"
style="font-style: {{item['font-style']}};font-weight: {{item['font-weight']}};background-color: {{item.bgcolor}};color: {{item.color}};font-size: {{item['font-size']}}rpx;font-family: {{item.font}};height: {{ item.height }}rpx; width: {{ item.width? item.width + 'rpx' : 'auto'}};left: {{item.left}}rpx;right: {{item.right}}rpx;top: {{item.top}}rpx;line-height:{{item.lineHeight}}rpx;"
data-type="text"
data-text="{{item.text}}"
>
{{item.text}}
</view>
</view>
</view>
</view>
<canvas
canvas-id="canvas1"
class="canvas-bill"
style="height: {{ height }}rpx; width: {{ width }}rpx;transform: scale({{scale}})"
></canvas>
<!-- <image -->
<!-- wx:if="{{imgUrl}}" -->
<!-- src="{{imgUrl}}" -->
<!-- mode="aspectFit" -->
<!-- style="height: {{ height }}rpx; width: {{ width }}rpx;" -->
<!-- show-menu-by-longpress -->
<!-- ></image> -->

0
src/components/customPoster/wxml2canvas/README.md

1692
src/components/customPoster/wxml2canvas/index.js

File diff suppressed because it is too large Load Diff

1
src/components/customPoster/wxml2canvas/index.js.map

File diff suppressed because one or more lines are too long

160
src/components/customTable/README.md

@ -0,0 +1,160 @@ @@ -0,0 +1,160 @@
# 海报插件
## 基于[wxml2canvas](https://github.com/wg-front/wxml2canvas)插件
### 插件引入
```json
{
"customPoster": "/components/customPoster/index"
}
```
```wxml
<customPoster params="{{params}}"></customPoster>
```
### 参数示例
```js
const data = {
params: {
info: { id: 'circle-note_mp', title: '动态页', desc: '指定动态的页面,mp' },
body: {
bgImg:
'https://picsissiok-10049618.cos.ap-shanghai.myqcloud.com/a74018e725861534f1644cff04780f4e_16273677601587.png',
width: '750',
height: '1254',
},
elements: [
{
id: 'circle-name',
desc: '圈名称',
type: 1,
text: '治疗讨论',
font: '宋体',
'font-size': '42',
color: '#FFFFFF',
'single-line': true,
halign: 'center',
top: '106',
},
{
id: 'master-name',
desc: '专家名称',
type: 1,
text: '健康小贴士',
font: '宋体',
'font-size': '38',
color: '#FFFFFF',
'single-line': true,
halign: 'center',
top: '237',
},
{
id: 'master-avatar',
desc: '专家头像',
type: 0,
picType: 'avatar',
src: 'https://picsissiok-10049618.cos.ap-shanghai.myqcloud.com/ceb31ac6137e873225b8cc826389a267_16655329078962.jpg',
width: '120',
height: '120',
halign: 'center',
shape: 'circle',
top: '328',
},
{
id: 'note-content',
desc: '动态内容',
type: 1,
text: '11月5日上海组织进口博览会CIIE大会,邀请大家来我们的展馆现场交流。',
font: '宋体',
'font-size': '45',
color: '#18191A',
width: '630',
height: '348',
left: '60',
top: '495',
},
{
id: 'note-title',
desc: '长文标题',
type: 1,
text: '',
font: '宋体',
'font-size': '42',
color: '#18191A',
width: '659',
height: '116',
left: '46',
top: '495',
'font-weight': 'bold',
},
{
id: 'note-summary',
desc: '长文摘要 ',
type: 1,
text: '',
font: '宋体',
'font-size': '42',
color: '#18191A',
width: '672',
height: '232',
left: '39',
top: '646',
},
{
id: 'note-more',
desc: '还有',
type: 1,
text: '还有3条精彩附件>',
font: '宋体',
'font-size': '31',
color: '#666666',
'single-line': true,
width: '292',
height: '46',
left: '60',
top: '1024',
},
{
id: 'scanText',
desc: '扫描二维码查看详情',
type: 1,
text: '扫描二维码查看详情',
font: '宋体',
'font-size': '31',
color: '#666666',
'single-line': true,
width: '279',
height: '43',
left: '60',
top: '1087',
},
{
id: 'note-qrcode',
desc: '专家列表的二维码',
type: 0,
picType: 'qrcode',
src: 'https://family.devred.hbraas.com/proxy/mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQHJ8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyUWhDdGhTWVNlTjMxMDAwMDAwN3gAAgSonPxjAwQAAAAA',
width: '130',
height: '130',
left: '554',
top: '1012',
},
{
id: 'brand',
desc: 'brand×华秉科技',
type: 1,
text: '做企业数字化转型的亲密伙伴×华秉科技',
font: '宋体',
'font-size': '24',
color: '#B2B3B4',
'single-line': true,
halign: 'center',
top: '1192',
},
],
},
}
```

240
src/components/customTable/index.js

@ -0,0 +1,240 @@ @@ -0,0 +1,240 @@
const app = getApp();
import Wxml2Canvas from "./wxml2canvas/index.js"; // 根据具体路径修改,node_modules会被忽略
Component({
properties: {
params: {
type: Object,
observer(newVal, _olVal) {
if (Object.keys(newVal).length > 0) {
this.drawImage();
}
},
},
},
data: {
imageUrl: app.globalData.imageUrl,
imgUrl: "",
width: "626",
height: "980",
imgheight: "",
bgImg: "",
elementsMp: [],
tableData: [
{
title: "说话",
key: "TalkingScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "间或有点含糊或有鼻音",
},
{
id: "3",
title: "经常含糊不清或者有鼻音,但是别人还能听懂",
},
{
id: "4",
title: "别人听不太懂",
},
],
},
{
title: "咀嚼",
key: "ChewScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "咀嚼固体食物会感到疲劳",
},
{
id: "3",
title: "咀嚼松软食物会感到疲劳",
},
{
id: "4",
title: "使用喂食管进食,如鼻胃管胃肠管等",
},
],
},
{
title: "吞咽",
key: "SwallowScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "偶尔会噎到",
},
{
id: "3",
title: "经常会噎到,因而需要改变饮食习惯",
},
{
id: "4",
title: "使用喂食管进食,如鼻胃管胃肠管等",
},
],
},
{
title: "呼吸",
key: "BreathScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "劳累后感到气促或呼吸不畅",
},
{
id: "3",
title: "静止时感到气促或呼吸不畅",
},
{
id: "4",
title: "依赖呼吸机",
},
],
},
{
title: "刷牙梳头能力受损",
key: "BrushTeethAndCombHairScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "需要多费些力气但是不需要停下来",
},
{
id: "3",
title: "需要停下来休息才能完成",
},
{
id: "4",
title: "无法自己完成",
},
],
},
{
title: "从椅子上起身能力受损",
key: "GetUpFromChairScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "轻度受损,有时需要用手帮忙",
},
{
id: "3",
title: "中度受损,总是需要用手帮忙",
},
{
id: "4",
title: "严重受损,需要他人帮助",
},
],
},
{
title: "复视/重影",
key: "DoubleVisionScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "有,但不是每天",
},
{
id: "3",
title: "每天,但不是持续一整天",
},
{
id: "4",
title: "持续有",
},
],
},
{
title: "眼睑下垂",
key: "DroopyEyelidsScore",
list: [
{
id: "1",
title: "正常",
},
{
id: "2",
title: "有,但不是每天",
},
{
id: "3",
title: "每天,但不是持续一整天",
},
{
id: "4",
title: "持续有",
},
],
},
],
},
lifetimes: {
attached() {},
},
methods: {
drawImage() {
let self = this;
// destZoom: 10,
this.drawImage1 = new Wxml2Canvas({
obj: self,
width: this.data.width, // 宽, 以iphone6为基准,传具体数值,其它机型自动适配
height: this.data.height, // 高
element: "canvas1",
background: "#ffffff",
progress(_percent) {},
finish(url) {
self.setData({
imgUrl: url,
});
self.triggerEvent("finish", url);
},
error(_res) {},
});
let data = {
list: [
{
type: "wxml",
class: "#canvas-bill-body-mp .draw_canvas",
limit: "#canvas-bill-body-mp",
x: 0,
y: 0,
},
],
};
this.drawImage1.draw(data);
},
},
});

3
src/components/customTable/index.json

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
{
"usingComponents": {}
}

141
src/components/customTable/index.scss

@ -0,0 +1,141 @@ @@ -0,0 +1,141 @@
.canvas-box {
position: fixed;
left: 1000000rpx;
top: -1000000rpx;
}
.canvas-body {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
background-color: #fff;
.detail {
width: 622rpx;
border: 1rpx solid rgba(0, 0, 0, 0.1);
background-color: rgba(0, 0, 0, 0.08);
.d-header {
display: flex;
height: 40rpx;
.none {
flex-shrink: 0;
width: 150rpx;
background-color: rgba(228, 228, 228, 1);
}
.bar {
flex: 1;
display: flex;
.num-normal {
width: 64rpx;
text-align: center;
font-size: 24rpx;
color: #fff;
line-height: 40rpx;
}
.num {
flex: 1;
text-align: center;
font-size: 24rpx;
color: #fff;
line-height: 40rpx;
}
}
}
.d-body {
display: flex;
.d-aside {
width: 28rpx;
flex-shrink: 0;
.da-item {
text-align: center;
font-size: 18rpx;
color: rgba(102, 102, 102, 1);
line-height: 1;
background-color: #e5e5e5;
}
.da-item1 {
padding-top: 150rpx;
display: flex;
height: 200rpx;
margin-bottom: 1px;
}
.da-item2 {
padding-top: 43rpx;
height: 75rpx;
margin-bottom: 1px;
}
.da-item3 {
padding-top: 100rpx;
height: 135rpx;
margin-bottom: 1px;
}
.da-item4 {
padding-top: 100rpx;
height: 135rpx;
}
}
.d-container {
flex: 1;
.dc-row {
display: flex;
height: 117rpx;
line-height: 24rpx;
margin-bottom: 1px;
.dc-title {
padding: 12rpx 12rpx 0;
width: 100rpx;
font-size: 18rpx;
color: rgba(102, 102, 102, 1);
}
.dc-sub-title {
width: 64rpx;
text-align: center;
font-size: 18rpx;
color: rgba(102, 102, 102, 1);
padding: 12rpx 0;
margin-left: 1px;
}
.dc-col {
margin-left: 1px;
padding: 12rpx;
flex: 1;
font-size: 18rpx;
color: rgba(102, 102, 102, 1);
}
.active {
color: #fff !important;
background-color: rgba(207, 83, 117, 1) !important;
}
}
.dc-row:nth-of-type(2n-1) {
.dc-title {
background-color: #f6f6f6;
}
.dc-sub-title {
background-color: #f6f6f6;
}
.dc-col {
background-color: #f6f6f6;
}
}
.dc-row:nth-of-type(2n-2) {
.dc-title {
background-color: #fff;
}
.dc-sub-title {
background-color: #fff;
}
.dc-col {
background-color: #fff;
}
}
}
}
}
}
.canvas-bill {
position: fixed;
width: 750rpx;
left: 1000000rpx;
top: -1000000rpx;
visibility: hidden;
}

55
src/components/customTable/index.wxml

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
<view id="canvas-bill-body-mp" class="canvas-box" style="width: 750rpx">
<view class="canvas-body">
<view class="detail draw_canvas" data-type="text">
<view class="d-header draw_canvas" data-type="text">
<view class="none draw_canvas" data-type="text"></view>
<view
class="bar draw_canvas"
style="background: url({{imageUrl}}/1/adl-scurt.png?t={{Timestamp}}) no-repeat center / 100% 100% ;"
data-type="background-image"
>
<view class="num-normal draw_canvas" data-type="text" data-text="0分">0分</view>
<view class="num draw_canvas" data-type="text" data-text="1分">1分</view>
<view class="num draw_canvas" data-type="text" data-text="2分">2分</view>
<view class="num draw_canvas" data-type="text" data-text="3分">3分</view>
</view>
</view>
<view class="d-body draw_canvas" data-type="text">
<view class="d-aside draw_canvas" data-type="text">
<view class="da-item da-item1 draw_canvas" data-type="text" data-text="延髓">延髓</view>
<view class="da-item da-item2 draw_canvas" data-type="text" data-text="呼吸">呼吸</view>
<view class="da-item da-item3 draw_canvas" data-type="text" data-text="四肢">四肢</view>
<view class="da-item da-item4 draw_canvas" data-type="text" data-text="眼肌">眼肌</view>
</view>
<view class="d-container draw_canvas" data-type="text">
<view class="dc-row draw_canvas" wx:for="{{tableData}}" wx:key="index" data-type="text">
<view class="dc-title draw_canvas" data-type="text" data-text="{{item.title}}">{{item.title}}</view>
<block wx:for="{{item.list}}" wx:for-item="lItem" wx:for-index="lIndex" wx:key="lIndex">
<view
class="{{lIndex==0 ? 'dc-sub-title' :'dc-col'}} {{params[item.key] == lIndex && 'active'}} draw_canvas"
data-type="text"
data-text="{{lItem.title}}"
>
{{lItem.title}}
</view>
</block>
</view>
</view>
</view>
</view>
</view>
</view>
<canvas
canvas-id="canvas1"
class="canvas-bill"
style="height: {{ height }}rpx; width: {{ width }}rpx;transform: scale({{scale}})"
></canvas>
<image
wx:if="{{imgUrl && false}}"
src="{{imgUrl}}"
mode="aspectFit"
style="height: {{ height }}rpx; width: {{ width }}rpx;"
></image>
<!-- show-menu-by-longpress -->

0
src/components/customTable/wxml2canvas/README.md

1691
src/components/customTable/wxml2canvas/index.js

File diff suppressed because it is too large Load Diff

1
src/components/customTable/wxml2canvas/index.js.map

File diff suppressed because one or more lines are too long

267
src/components/ec-canvas/ec-canvas.js

@ -0,0 +1,267 @@ @@ -0,0 +1,267 @@
import WxCanvas from './wx-canvas';
let ctx;
let echarts
function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
Component({
properties: {
canvasId: {
type: String,
value: 'ec-canvas'
},
ec: {
type: Object
},
forceUseOldCanvas: {
type: Boolean,
value: false
}
},
data: {
isUseNewCanvas: false
},
ready:async function () {
echarts = await require.async('../../resource/components/echart/echarts.js')
// Disable prograssive because drawImage doesn't support DOM as parameter
// See https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.drawImage.html
echarts.registerPreprocessor(option => {
if (option && option.series) {
if (option.series.length > 0) {
option.series.forEach(series => {
series.progressive = 0;
});
}
else if (typeof option.series === 'object') {
option.series.progressive = 0;
}
}
});
if (!this.data.ec) {
console.warn('组件需绑定 ec 变量,例:<ec-canvas id="mychart-dom-bar" '
+ 'canvas-id="mychart-bar" ec="{{ ec }}"></ec-canvas>');
return;
}
if (!this.data.ec.lazyLoad) {
this.init();
}
},
methods: {
init: async function (callback) {
echarts = await require.async('../../resource/components/echart/echarts.js')
const version = wx.getSystemInfoSync().SDKVersion
const canUseNewCanvas = compareVersion(version, '2.9.0') >= 0;
const forceUseOldCanvas = this.data.forceUseOldCanvas;
const isUseNewCanvas = canUseNewCanvas && !forceUseOldCanvas;
this.setData({ isUseNewCanvas });
if (forceUseOldCanvas && canUseNewCanvas) {
console.warn('开发者强制使用旧canvas,建议关闭');
}
if (isUseNewCanvas) {
// console.log('微信基础库版本大于2.9.0,开始使用<canvas type="2d"/>');
// 2.9.0 可以使用 <canvas type="2d"></canvas>
this.initByNewWay(callback);
} else {
const isValid = compareVersion(version, '1.9.91') >= 0
if (!isValid) {
console.error('微信基础库版本过低,需大于等于 1.9.91。'
+ '参见:https://github.com/ecomfe/echarts-for-weixin'
+ '#%E5%BE%AE%E4%BF%A1%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82');
return;
} else {
console.warn('建议将微信基础库调整大于等于2.9.0版本。升级后绘图将有更好性能');
this.initByOldWay(callback);
}
}
},
initByOldWay(callback) {
// 1.9.91 <= version < 2.9.0:原来的方式初始化
ctx = wx.createCanvasContext(this.data.canvasId, this);
const canvas = new WxCanvas(ctx, this.data.canvasId, false);
echarts.setCanvasCreator(() => {
return canvas;
});
// const canvasDpr = wx.getSystemInfoSync().pixelRatio // 微信旧的canvas不能传入dpr
const canvasDpr = 1
var query = wx.createSelectorQuery().in(this);
query.select('.ec-canvas').boundingClientRect(res => {
if (typeof callback === 'function') {
this.chart = callback(canvas, res.width, res.height, canvasDpr);
}
else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
this.chart = this.data.ec.onInit(canvas, res.width, res.height, canvasDpr);
}
else {
this.triggerEvent('init', {
canvas: canvas,
width: res.width,
height: res.height,
canvasDpr: canvasDpr // 增加了dpr,可方便外面echarts.init
});
}
}).exec();
},
initByNewWay(callback) {
// version >= 2.9.0:使用新的方式初始化
const query = wx.createSelectorQuery().in(this)
query
.select('.ec-canvas')
.fields({ node: true, size: true })
.exec(res => {
const canvasNode = res[0].node
this.canvasNode = canvasNode
const canvasDpr = wx.getSystemInfoSync().pixelRatio
const canvasWidth = res[0].width
const canvasHeight = res[0].height
const ctx = canvasNode.getContext('2d')
const canvas = new WxCanvas(ctx, this.data.canvasId, true, canvasNode)
echarts.setCanvasCreator(() => {
return canvas
})
if (typeof callback === 'function') {
this.chart = callback(canvas, canvasWidth, canvasHeight, canvasDpr)
} else if (this.data.ec && typeof this.data.ec.onInit === 'function') {
this.chart = this.data.ec.onInit(canvas, canvasWidth, canvasHeight, canvasDpr)
} else {
this.triggerEvent('init', {
canvas: canvas,
width: canvasWidth,
height: canvasHeight,
dpr: canvasDpr
})
}
})
},
canvasToTempFilePath(opt) {
if (this.data.isUseNewCanvas) {
// 新版
const query = wx.createSelectorQuery().in(this)
query
.select('.ec-canvas')
.fields({ node: true, size: true })
.exec(res => {
const canvasNode = res[0].node
opt.canvas = canvasNode
wx.canvasToTempFilePath(opt)
})
} else {
// 旧的
if (!opt.canvasId) {
opt.canvasId = this.data.canvasId;
}
ctx.draw(true, () => {
wx.canvasToTempFilePath(opt, this);
});
}
},
touchStart(e) {
if (this.chart && e.touches.length > 0) {
var touch = e.touches[0];
var handler = this.chart.getZr().handler;
handler.dispatch('mousedown', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {}
});
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {}
});
handler.processGesture(wrapTouch(e), 'start');
}
},
touchMove(e) {
if (this.chart && e.touches.length > 0) {
var touch = e.touches[0];
var handler = this.chart.getZr().handler;
handler.dispatch('mousemove', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {}
});
handler.processGesture(wrapTouch(e), 'change');
}
},
touchEnd(e) {
if (this.chart) {
const touch = e.changedTouches ? e.changedTouches[0] : {};
var handler = this.chart.getZr().handler;
handler.dispatch('mouseup', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {}
});
handler.dispatch('click', {
zrX: touch.x,
zrY: touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {}
});
handler.processGesture(wrapTouch(e), 'end');
}
}
}
});
function wrapTouch(event) {
for (let i = 0; i < event.touches.length; ++i) {
const touch = event.touches[i];
touch.offsetX = touch.x;
touch.offsetY = touch.y;
}
return event;
}

4
src/components/ec-canvas/ec-canvas.json

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

4
src/components/ec-canvas/ec-canvas.wxml

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
<!-- 新的:接口对其了H5 -->
<canvas wx:if="{{isUseNewCanvas}}" type="2d" class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>
<!-- 旧的 -->
<canvas wx:else class="ec-canvas" canvas-id="{{ canvasId }}" bindinit="init" bindtouchstart="{{ ec.disableTouch ? '' : 'touchStart' }}" bindtouchmove="{{ ec.disableTouch ? '' : 'touchMove' }}" bindtouchend="{{ ec.disableTouch ? '' : 'touchEnd' }}"></canvas>

5
src/components/ec-canvas/ec-canvas.wxss

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
.ec-canvas {
width: 100%;
height: 100%;
z-index: 1;
}

111
src/components/ec-canvas/wx-canvas.js

@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
export default class WxCanvas {
constructor(ctx, canvasId, isNew, canvasNode) {
this.ctx = ctx;
this.canvasId = canvasId;
this.chart = null;
this.isNew = isNew
if (isNew) {
this.canvasNode = canvasNode;
}
else {
this._initStyle(ctx);
}
// this._initCanvas(zrender, ctx);
this._initEvent();
}
getContext(contextType) {
if (contextType === '2d') {
return this.ctx;
}
}
// canvasToTempFilePath(opt) {
// if (!opt.canvasId) {
// opt.canvasId = this.canvasId;
// }
// return wx.canvasToTempFilePath(opt, this);
// }
setChart(chart) {
this.chart = chart;
}
addEventListener() {
// noop
}
attachEvent() {
// noop
}
detachEvent() {
// noop
}
_initCanvas(zrender, ctx) {
zrender.util.getContext = function () {
return ctx;
};
zrender.util.$override('measureText', function (text, font) {
ctx.font = font || '12px sans-serif';
return ctx.measureText(text);
});
}
_initStyle(ctx) {
ctx.createRadialGradient = () => {
return ctx.createCircularGradient(arguments);
};
}
_initEvent() {
this.event = {};
const eventNames = [{
wxName: 'touchStart',
ecName: 'mousedown'
}, {
wxName: 'touchMove',
ecName: 'mousemove'
}, {
wxName: 'touchEnd',
ecName: 'mouseup'
}, {
wxName: 'touchEnd',
ecName: 'click'
}];
eventNames.forEach(name => {
this.event[name.wxName] = e => {
const touch = e.touches[0];
this.chart.getZr().handler.dispatch(name.ecName, {
zrX: name.wxName === 'tap' ? touch.clientX : touch.x,
zrY: name.wxName === 'tap' ? touch.clientY : touch.y,
preventDefault: () => {},
stopImmediatePropagation: () => {},
stopPropagation: () => {}
});
};
});
}
set width(w) {
if (this.canvasNode) this.canvasNode.width = w
}
set height(h) {
if (this.canvasNode) this.canvasNode.height = h
}
get width() {
if (this.canvasNode)
return this.canvasNode.width
return 0
}
get height() {
if (this.canvasNode)
return this.canvasNode.height
return 0
}
}

158
src/components/freeAudio/free-audio.vue

@ -0,0 +1,158 @@ @@ -0,0 +1,158 @@
<template>
<!-- 音频播放器组件 -->
<view v-if="url" class="flex justify-between align-center audio">
<view class="mr-3" @click="start(audioId)">
<image :src="startPic" class="icon" v-show="!status"></image>
<image :src="endPic" class="icon" v-show="status"></image>
</view>
<view class="flex-1">
<slider
@change="changeAudio"
:activeColor="activeColor"
:min="0"
:max="duration.toFixed(0)"
:value="currentTime.toFixed(0)"
:step="0.1"
></slider>
</view>
<view class="ml-3">{{ getTime(Math.round(currentTime)) }}</view>
</view>
</template>
<script>
export default {
data() {
return {
context: null,
currentTime: 0,
duration: 100,
status: false,
};
},
props: {
url: String,
activeColor: {
type: String,
default: "#0E7EFC",
},
startPic: String,
endPic: String,
audioId: [String, Number],
},
created() {
this.context = uni.createInnerAudioContext();
this.context.src = this.url;
this.onTimeUpdate();
this.onCanplay();
this.onEnded();
uni.$on("stop", (id) => {
if (id && id != this.audioId) {
this.context.stop();
this.status = false;
} else if (!id) {
this.context.stop();
this.status = false;
}
});
},
methods: {
start(id) {
//
let audioId = id;
if (this.status) {
this.context.pause();
this.status = !this.status;
} else {
uni.$emit("stop", id);
this.context.play();
this.status = !this.status;
}
},
onCanplay() {
//
this.context.onCanplay(() => {
this.context.duration;
setTimeout(() => {
this.duration = this.context.duration;
}, 1000);
});
},
onTimeUpdate() {
//
this.context.onTimeUpdate(() => {
if (!Number.isFinite(this.context.duration)) {
this.duration = this.context.currentTime + 10;
this.currentTime = this.context.currentTime;
} else {
this.duration = this.context.duration;
this.currentTime = this.context.currentTime;
}
});
},
onEnded() {
//
this.context.onEnded(() => {
this.status = false;
this.currentTime = 0;
});
},
changeAudio(e) {
let paused = this.context.paused;
this.context.pause();
this.context.seek(e.detail.value);
if (!paused) {
this.context.play();
}
},
getTime(time) {
let m = parseInt(time / 60);
let s = time % 60;
return this.towNum(m) + ":" + this.towNum(s);
},
towNum(num) {
if (num >= 10) {
return num;
} else {
return "0" + num;
}
},
},
};
</script>
<style>
.audio {
background: #f4f4f4;
padding: 20rpx;
}
.icon {
width: 80rpx;
height: 80rpx;
}
.flex {
display: flex;
flex-direction: row;
}
.justify-between {
justify-content: between;
}
.align-center {
align-items: center;
}
.flex-1 {
flex: 1;
}
.ml-3 {
margin-left: 30rpx;
}
.mr-3 {
margin-right: 30rpx;
}
</style>

176
src/components/freeAudio/index.js

@ -0,0 +1,176 @@ @@ -0,0 +1,176 @@
const app = getApp();
import dayjs from "dayjs";
Component({
behaviors: [],
properties: {
audio: {
type: Object,
observer(val) {
if (this.audioContext) {
const { play } = this.data;
if (play) {
this.audioContext.pause();
this.setData({
play: false,
progress: 0,
time: "00",
});
}
this.audioContext.destroy();
}
if (val) {
this.audioAddEventListener(val);
}
},
},
},
data: {
Timestamp: app.globalData.Timestamp,
progress: 0,
time: "00",
duration: "00",
play: false,
loading: true,
imageUrl: app.globalData.imageUrl,
progressimg: true,
},
lifetimes: {
created() {},
async attached() {},
moved() {},
detached() {
if (this.audioContext) {
const { play } = this.data;
if (play) {
this.audioContext.pause();
}
this.audioContext.destroy();
}
this.audioAddEventListener = null;
this.setData({
play: false,
progress: 0,
time: "00",
});
},
},
pageLifetimes: {
// 组件所在页面的生命周期函数
show: function () {},
hide: function () {
const { play } = this.data;
if (play) {
this.audioContext.pause();
}
},
resize: function () {},
},
methods: {
togglePlay() {
const { play, loading } = this.data;
if (loading) {
wx.showToast({
title: "音频加载中",
icon: "none",
});
return;
}
if (play) {
this.audioContext.pause();
} else {
this.audioContext.play();
}
},
formatTime(time) {
let m = parseInt(time / 60);
let s = parseInt(time % 60);
return this.towNum(m) + ":" + this.towNum(s);
},
towNum(num) {
if (num >= 10) {
return num;
} else {
return "0" + num;
}
},
audioAddEventListener(val) {
const that = this;
this.setData({
duration: this.formatTime(val.size),
});
that.audioContext = wx.createInnerAudioContext();
that.audioContext.src = val.url;
that.setData({
loading: false,
});
that.audioContext.onError(({ errCode, ...reset }) => {
console.log("reset: ", reset);
console.log("errCode: ", errCode);
if (errCode === 10004 || errCode == 10001 || errCode == -1) {
that.audioContext.destroy();
that.setData({
loading: true,
});
setTimeout(() => {
that.audioAddEventListener(val);
}, 300);
}
});
that.audioContext.onPlay(() => {
that.setData({
play: true,
});
});
that.audioContext.onPause(() => {
console.log(1111111);
that.setData({
play: false,
});
});
that.audioContext.onEnded(() => {
that.audioContext.seek(0);
that.setData({
play: false,
progress: 0,
time: "00",
});
});
that.audioContext.onTimeUpdate(() => {
const duration = that.audioContext.duration || 0;
const currentTime = that.audioContext.currentTime || 0;
const progress = (currentTime / duration) * 100;
if (duration == Infinity) {
return;
}
that.setData({
play: true,
duration: that.formatTime(duration),
time: that.formatTime(currentTime),
});
if (that.data.progressimg) {
this.setData({
progress: progress,
});
}
});
},
handleAuthChangeimg() {
console.log(11111);
this.setData({
progressimg: false,
});
},
handleAuthChange(e) {
console.log(22222222222);
let { duration } = this.data;
const secods = this.audioContext.duration || duration.split(":")[0] * 60 + duration.split(":")[1] * 1;
const progress = e.detail.value;
let seek = ((secods / 100) * progress).toFixed(3) * 1;
this.audioContext.seek(seek);
this.setData({
progressimg: true,
});
},
},
});

5
src/components/freeAudio/index.json

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
{
"navigationBarTitleText": "",
"component": true,
"usingComponents": {}
}

54
src/components/freeAudio/index.scss

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
.audio {
width: 100%;
height: 86rpx;
position: relative;
.audio-bg {
margin: 0 120rpx 0 80rpx;
height: 100%;
width: calc(100% - 200rpx);
}
.card-auth {
position: absolute;
left: 0;
top: 0;
width: 100%;
padding: 14rpx 26rpx 12rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
background: rgba(174, 182, 205, 0.1);
border-radius: 50rpx 50rpx 50rpx 50rpx;
border: 1rpx solid rgba(181, 205, 255, 0.1);
.icon {
width: 60rpx;
height: 60rpx;
flex-shrink: 0;
}
.center {
position: relative;
width: 100%;
margin: 0 30rpx;
display: flex;
align-items: center;
.progress-bg {
width: 100%;
height: 60rpx;
}
.progress {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 100%;
margin: 0;
}
}
.time {
flex-shrink: 0;
font-size: 24rpx;
color: #e04775;
}
}
}

26
src/components/freeAudio/index.wxml

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
<view class="audio">
<view class="card-auth">
<image
class="icon extend-via-pseudo-elem"
src="{{imageUrl}}1/audio-{{play?'pause':'play'}}.png?t={{Timestamp}}"
catchtap="togglePlay"
></image>
<view
class="center"
>
<image class="progress-bg" mode="aspectFill" src="https://m.zd.hbraas.com/zd/1/firee-audio-bg.png?t=1689998782161"></image>
<slider
active
activeColor="#E04775"
backgroundColor="#E04775"
bind:changing="handleAuthChangeimg"
bindchange="handleAuthChange"
block-color="#E04775"
block-size="12"
class="progress"
value="{{progress}}"
/>
</view>
<text class="time" wx:if="{{duration}}">{{duration}}</text>
</view>
</view>

7
src/components/loginNavbar/index.json

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"van-nav-bar": "@vant/weapp/nav-bar/index",
"van-icon": "@vant/weapp/icon/index"
}
}

24
src/components/loginNavbar/index.scss

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
.navbar {
padding: 0 20rpx 0 0;
display: flex;
align-items: center;
.back-icon {
margin-right: 40rpx;
}
.num {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
border: 3rpx solid #e04876;
text-align: center;
line-height: 64rpx;
margin-right: 24rpx;
font-size: 40rpx;
color: #e04876;
font-weight: bold;
&.active {
background-color: #e04876;
color: #fff;
}
}
}

23
src/components/loginNavbar/index.ts

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
const app = getApp<IAppOption>();
Component({
properties: {
num: {
type: Number,
value: 0,
},
back: {
type: Boolean,
value: false,
},
},
data: {
imageUrl: app.globalData.imageUrl,
Timestamp: app.globalData.Timestamp,
},
methods: {
handleBack() {
this.triggerEvent("back");
},
},
});

7
src/components/loginNavbar/index.wxml

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
<van-nav-bar title="" custom-style="background:transparent;">
<view class="navbar" slot="left">
<van-icon wx:if="{{back}}" class="back-icon" name="arrow-left" color="#333333" size="22" bind:tap="handleBack" />
<view class="num {{num>=1 && 'active'}}">1</view>
<view class="num {{num>=2 && 'active'}}">2</view>
</view>
</van-nav-bar>

250
src/components/navBar/navBar.js

@ -0,0 +1,250 @@ @@ -0,0 +1,250 @@
const app = getApp();
Component({
options: {
multipleSlots: true,
addGlobalClass: true,
},
properties: {
slotLeft:{
type:Boolean,
value:false,
},
extClass: {
type: String,
value: "",
},
background: {
type: String,
value: "transparent",
observer: "_showChange",
},
backgroundColorTop: {
type: String,
value: "transparent",
observer: "_showChangeBackgroundColorTop",
},
color: {
type: String,
value: "#000000",
},
title: {
type: String,
value: "",
},
back: {
type: Boolean,
value: false,
},
home: {
type: Boolean,
value: true,
},
iconTheme: {
type: String,
value: "nuohe",
},
/* animated: {
type: Boolean,
value: true
},
show: {
type: Boolean,
value: true,
observer: '_showChange'
}, */
delta: {
type: Number,
value: 1,
},
fixed: {
type: Boolean,
value: false,
},
},
created: function () {
this.getSystemInfo();
},
attached: function () {
this.setStyle(); //设置样式
},
data: {
imageUrl: app.globalData.imageUrl,
Timestamp: app.globalData.Timestamp,
isHome: false,
},
pageLifetimes: {
show: function () {
if (getApp().globalSystemInfo.ios) {
this.getSystemInfo();
this.setStyle(); //设置样式1
}
let pages = getCurrentPages();
this.setData({
isHome: !pages[pages.length - 2],
});
},
hide: function () {},
},
methods: {
setStyle: function (life) {
const { statusBarHeight, navBarHeight, capsulePosition, navBarExtendHeight, ios, windowWidth } =
getApp().globalSystemInfo;
const { back, home, title } = this.data;
let rightDistance = windowWidth - capsulePosition.right; //胶囊按钮右侧到屏幕右侧的边距
let leftWidth = windowWidth - capsulePosition.left; //胶囊按钮左侧到屏幕右侧的边距
this.setData({
leftWidth: leftWidth,
});
let navigationbarinnerStyle = [
`color: ${this.data.color}`,
`background: ${this.data.background}`,
`height:${navBarHeight + navBarExtendHeight}px`,
`padding-top:${statusBarHeight}px`,
`padding-right:${leftWidth}px`,
`padding-bottom:${navBarExtendHeight}px`,
].join(";");
let navBarLeft = [];
if ((back && !home) || (!back && home)) {
navBarLeft = [`width:${capsulePosition.width}px`, `height:${capsulePosition.height}px`].join(";");
} else if ((back && home) || title) {
navBarLeft = [
`width:${capsulePosition.width}px`,
`height:${capsulePosition.height}px`,
`margin-left:${rightDistance}px`,
].join(";");
} else {
navBarLeft = [`width:auto`, `margin-left:0px`].join(";");
}
if (life === "created") {
this.data = {
navigationbarinnerStyle,
navBarLeft,
navBarHeight,
capsulePosition,
navBarExtendHeight,
ios,
};
} else {
this.setData({
navigationbarinnerStyle,
navBarLeft,
navBarHeight,
capsulePosition,
navBarExtendHeight,
ios,
});
}
},
_showChange: function (value) {
this.setStyle();
},
// 返回事件
back: function () {
let pages = getCurrentPages();
if (app.globalData.backPage) {
wx.reLaunch({
url: app.globalData.backPage,
});
app.globalData.backPage = null;
return;
}
if (!pages[pages.length - 2] && !app.globalData.anyWhere) {
wx.reLaunch({
url: "/pages/index/index",
});
return;
}
this.triggerEvent("back", { delta: this.data.delta });
},
home: function () {
wx.reLaunch({
url: "/pages/start/index",
});
},
search: function () {
this.triggerEvent("search", {});
},
getSystemInfo() {
var app = getApp();
if (app.globalSystemInfo && !app.globalSystemInfo.ios) {
return app.globalSystemInfo;
} else {
let systemInfo = wx.getSystemInfoSync();
let ios = !!(systemInfo.system.toLowerCase().search("ios") + 1);
let rect;
try {
rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null;
if (rect === null) {
throw "getMenuButtonBoundingClientRect error";
}
//取值为0的情况 有可能width不为0 top为0的情况
if (!rect.width || !rect.top || !rect.left || !rect.height) {
throw "getMenuButtonBoundingClientRect error";
}
} catch (error) {
let gap = ""; //胶囊按钮上下间距 使导航内容居中
let width = 96; //胶囊的宽度
if (systemInfo.platform === "android") {
gap = 8;
width = 96;
} else if (systemInfo.platform === "devtools") {
if (ios) {
gap = 5.5; //开发工具中ios手机
} else {
gap = 7.5; //开发工具中android和其它手机
}
} else {
gap = 4;
width = 88;
}
if (!systemInfo.statusBarHeight) {
//开启wifi的情况下修复statusBarHeight值获取不到
systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
}
rect = {
//获取不到胶囊信息就自定义重置一个
bottom: systemInfo.statusBarHeight + gap + 32,
height: 32,
left: systemInfo.windowWidth - width - 10,
right: systemInfo.windowWidth - 10,
top: systemInfo.statusBarHeight + gap,
width: width,
};
console.log("error", error);
console.log("rect", rect);
}
let navBarHeight = "";
if (!systemInfo.statusBarHeight) {
systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20;
navBarHeight = (function () {
let gap = rect.top - systemInfo.statusBarHeight;
return 2 * gap + rect.height;
})();
systemInfo.statusBarHeight = 0;
systemInfo.navBarExtendHeight = 0; //下方扩展4像素高度 防止下方边距太小
} else {
navBarHeight = (function () {
let gap = rect.top - systemInfo.statusBarHeight;
return systemInfo.statusBarHeight + 2 * gap + rect.height;
})();
if (ios) {
systemInfo.navBarExtendHeight = 4; //下方扩展4像素高度 防止下方边距太小
} else {
systemInfo.navBarExtendHeight = 0;
}
}
systemInfo.navBarHeight = navBarHeight; //导航栏高度不包括statusBarHeight
systemInfo.capsulePosition = rect; //右上角胶囊按钮信息bottom: 58 height: 32 left: 317 right: 404 top: 26 width: 87 目前发现在大多机型都是固定值 为防止不一样所以会使用动态值来计算nav元素大小
systemInfo.ios = ios; //是否ios
app.globalSystemInfo = systemInfo; //将信息保存到全局变量中,后边再用就不用重新异步获取了
//console.log('systemInfo', systemInfo);
return systemInfo;
}
},
},
});

4
src/components/navBar/navBar.json

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

185
src/components/navBar/navBar.scss

@ -0,0 +1,185 @@ @@ -0,0 +1,185 @@
page {
--height: 44px; /* 4*2+32 */
--right: 40px; /* 10+87 */
--navBarExtendHeight: 4px;
box-sizing: border-box;
}
.lxy-nav-bar {
view,
text,
scroll-view,
input,
button,
image,
cover-view {
box-sizing: border-box;
}
}
.lxy-nav-bar_fixed {
position: fixed;
top: 0;
z-index: 1000;
}
.lxy-nav-bar .ios {
--height: 44px; /* 4*2+32 */
--right: 40px; /* 10+87 */
--navBarExtendHeight: 4px;
box-sizing: border-box;
}
.lxy-nav-bar .android {
--height: 48px; /* 8*2+32 */
--right: 40px; /* 10+87 */
--navBarExtendHeight: 4px;
box-sizing: border-box;
}
.lxy-nav-bar .devtools {
--height: 42px; /* 5*2+32 */
--right: 40px; /* 10+87 */
--navBarExtendHeight: 4px;
box-sizing: border-box;
}
.lxy-nav-bar__inner {
position: fixed;
top: 0;
left: 0;
z-index: 5001;
height: var(--height);
display: flex;
align-items: center;
padding-right: var(--right);
width: 100%;
padding-bottom: var(--navBarExtendHeight);
}
.lxy-nav-bar__inner .lxy-nav-bar__left {
position: relative;
width: var(--right);
height: 32px;
/* padding-left: 10px; */
display: flex;
align-items: center;
}
.lxy-nav-bar__buttons {
height: 100%;
width: 100%;
display: flex;
align-items: center;
border-radius: 16px;
border: 1rpx solid rgba(204, 204, 204, 0.6);
position: relative;
}
.lxy-nav-bar__buttons.android {
border: 1rpx solid rgba(234, 234, 234, 0.6);
}
.lxy-nav-bar__buttons::after {
position: absolute;
content: "";
width: 1rpx;
height: 18.4px;
background: rgba(204, 204, 204, 0.6);
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.lxy-nav-bar__buttons.android::after {
background: rgba(234, 234, 234, 0.6);
}
.lxy-nav-bar__button {
width: 100%;
height: 100%;
display: flex;
font-size: 12px;
background-repeat: no-repeat;
background-position: center center;
background-size: 1em 2em;
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback:active,
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome:active {
opacity: 0.5;
}
.lxy-nav-bar__inner .lxy-nav-bar__center {
font-size: 17px;
line-height: 17px;
text-align: center;
position: relative;
flex: 1;
display: -webkit-box;
display: -webkit-flex;
display: flex;
align-items: center;
justify-content: center;
padding-left: 10px;
}
.lxy-nav-bar__inner .lxy-nav-bar__center .lxy-nav-bar__center-title {
margin-top: -2px;
font-weight: bold;
white-space: nowrap;
}
.lxy-nav-bar__inner .lxy-nav-bar__loading {
font-size: 0;
}
.lxy-nav-bar__inner .lxy-nav-bar__loading .lxy-loading {
margin-left: 0;
}
.lxy-nav-bar__inner .lxy-nav-bar__right {
margin-right: 10px;
}
.lxy-nav-bar__placeholder {
height: var(--height);
background: #f8f8f8;
position: relative;
z-index: 50;
}
.lxy-nav-bar-search {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 32px;
border-radius: 16px;
position: relative;
background: #f6f6f6;
}
.lxy-nav-bar-search__input {
height: 100%;
display: flex;
align-items: center;
color: #999;
font-size: 15px;
line-height: 15px;
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E");
background-position: 30rpx center;
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback.nuohe {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E");
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback.white {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z' fill='%23ffffff'/%3E%3C/svg%3E");
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome {
background-image: url("data:image/svg+xml,%3Csvg t='1565752242401' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4326' width='48' height='48'%3E%3Cpath d='M931.148 451.25L591.505 97.654c-21.106-21.953-49.313-34.034-79.551-34.034-30.235 0-58.448 12.081-79.554 34.034L92.76 451.25c-35.049 36.498-30.536 68.044-24.742 81.222 4.13 9.35 18.07 35.05 58.231 35.05h49.78v272.016c0 61.756 44.342 119.906 107.357 119.906h144.587v-287.87c0-30.866-4.675-48.062 26.848-48.062h114.268c31.52 0 26.845 17.196 26.845 48.061v287.872h144.588c63.013 0 107.358-58.15 107.358-119.906V567.523h49.782c40.16 0 54.1-25.7 58.229-35.05 5.793-13.18 10.306-44.726-24.743-81.224z m-33.486 60.28h-105.77v328.007c0 30.865-19.877 63.917-51.37 63.917h-88.6V671.572c0-61.761-19.79-104.05-82.832-104.05H454.821c-63.045 0-82.836 42.289-82.836 104.05v231.883h-88.599c-31.495 0-51.37-33.052-51.37-63.917V511.529H126.25c-0.984 0-1.888-3.852-2.708-3.907 1.94-3.388 5.276-11.975 10.825-17.743l339.671-353.35c10.142-10.578 24.467-17.057 38.353-16.924 13.888-0.133 27.342 6.346 37.483 16.923L889.54 489.88c5.549 5.768 8.885 14.355 10.825 17.743-0.818 0.055-1.72 3.907-2.704 3.907z' fill='%23000000' p-id='4327'%3E%3C/path%3E%3C/svg%3E");
background-size: 22px 22px;
}
.lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome.white {
background-image: url("data:image/svg+xml,%3Csvg t='1565752242401' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4326' width='48' height='48'%3E%3Cpath d='M931.148 451.25L591.505 97.654c-21.106-21.953-49.313-34.034-79.551-34.034-30.235 0-58.448 12.081-79.554 34.034L92.76 451.25c-35.049 36.498-30.536 68.044-24.742 81.222 4.13 9.35 18.07 35.05 58.231 35.05h49.78v272.016c0 61.756 44.342 119.906 107.357 119.906h144.587v-287.87c0-30.866-4.675-48.062 26.848-48.062h114.268c31.52 0 26.845 17.196 26.845 48.061v287.872h144.588c63.013 0 107.358-58.15 107.358-119.906V567.523h49.782c40.16 0 54.1-25.7 58.229-35.05 5.793-13.18 10.306-44.726-24.743-81.224z m-33.486 60.28h-105.77v328.007c0 30.865-19.877 63.917-51.37 63.917h-88.6V671.572c0-61.761-19.79-104.05-82.832-104.05H454.821c-63.045 0-82.836 42.289-82.836 104.05v231.883h-88.599c-31.495 0-51.37-33.052-51.37-63.917V511.529H126.25c-0.984 0-1.888-3.852-2.708-3.907 1.94-3.388 5.276-11.975 10.825-17.743l339.671-353.35c10.142-10.578 24.467-17.057 38.353-16.924 13.888-0.133 27.342 6.346 37.483 16.923L889.54 489.88c5.549 5.768 8.885 14.355 10.825 17.743-0.818 0.055-1.72 3.907-2.704 3.907z' fill='%23ffffff' p-id='4327'%3E%3C/path%3E%3C/svg%3E");
background-size: 22px 22px;
}
.lxy-nav-bar-search__icon {
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
background-image: url("data:image/svg+xml,%3Csvg t='1565691512239' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='1240' width='48' height='48'%3E%3Cpath d='M819.2 798.254545L674.909091 653.963636c46.545455-48.872727 74.472727-114.036364 74.472727-186.181818 0-151.272727-123.345455-274.618182-274.618182-274.618182-151.272727 0-274.618182 123.345455-274.618181 274.618182 0 151.272727 123.345455 274.618182 274.618181 274.618182 65.163636 0 128-23.272727 174.545455-62.836364l144.290909 144.290909c2.327273 2.327273 6.981818 4.654545 11.636364 4.654546s9.309091-2.327273 11.636363-4.654546c6.981818-6.981818 6.981818-18.618182 2.327273-25.6zM235.054545 467.781818c0-132.654545 107.054545-239.709091 239.709091-239.709091 132.654545 0 239.709091 107.054545 239.709091 239.709091 0 132.654545-107.054545 239.709091-239.709091 239.709091-132.654545 0-239.709091-107.054545-239.709091-239.709091z' fill='%23999999' p-id='1241'%3E%3C/path%3E%3C/svg%3E");
background-repeat: no-repeat;
background-size: cover;
}

33
src/components/navBar/navBar.wxml

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
<view
class="lxy-nav-bar {{extClass}} {{fixed && 'lxy-nav-bar_fixed'}}"
style="background: {{background}};height: {{navBarHeight+ navBarExtendHeight}}px;"
>
<view
class="lxy-nav-bar__placeholder {{ios ? 'ios' : 'android'}}"
style="padding-top: {{navBarHeight+ navBarExtendHeight}}px;visibility: hidden;"
></view>
<view class="lxy-nav-bar__inner {{ios ? 'ios' : 'android'}}" style="{{navigationbarinnerStyle}}{{displayStyle}}">
<view class="lxy-nav-bar__left" style="width: {{leftWidth}}px">
<block wx:if="{{slotLeft}}">
<slot name="left"></slot>
</block>
<block wx:elif="{{(back && !isHome)&&(!home||!isHome)}}">
<view bindtap="back" class="lxy-nav-bar__button lxy-nav-bar__btn_goback {{iconTheme}}"></view>
</block>
<block wx:elif="{{(!back||isHome)&&home}}">
<view bindtap="home" class="lxy-nav-bar__button lxy-nav-bar__btn_gohome {{iconTheme}}"></view>
</block>
</view>
<view class="lxy-nav-bar__center">
<block wx:if="{{title}}">
<text class="lxy-nav-bar__center-title">{{title}}</text>
</block>
<block wx:else>
<slot name="center"></slot>
</block>
</view>
<view class="lxy-nav-bar__right">
<slot name="right"></slot>
</view>
</view>
</view>

7
src/components/pageNavbar/index.json

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"van-nav-bar": "@vant/weapp/nav-bar/index",
"van-icon": "@vant/weapp/icon/index"
}
}

16
src/components/pageNavbar/index.scss

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
.navbar {
padding: 0 20rpx 0 0;
display: flex;
align-items: center;
.back-icon {
margin-right: 40rpx;
}
.nav-img {
width: 356rpx;
height: 46rpx;
}
.drug-nav-img {
width: 199rpx;
height: 36rpx;
}
}

23
src/components/pageNavbar/index.ts

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
const app = getApp<IAppOption>();
Component({
properties: {
back: {
type: Boolean,
value: false,
},
drug: {
type: Boolean,
value: false,
},
},
data: {
imageUrl: app.globalData.imageUrl,
Timestamp: app.globalData.Timestamp,
},
methods: {
handleBack() {
wx.navigateBack();
},
},
});

7
src/components/pageNavbar/index.wxml

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
<van-nav-bar title="" custom-style="background:transparent;">
<view class="navbar" slot="left">
<van-icon wx:if="{{back}}" class="back-icon" name="arrow-left" color="#333333" size="22" bind:tap="handleBack" />
<image wx:if="{{drug}}" class="drug-nav-img" src="{{imageUrl}}1.5/page-title.png?t={{Timestamp}}"></image>
<image wx:else class="nav-img" src="{{imageUrl}}1/page-title.png?t={{Timestamp}}"></image>
</view>
</van-nav-bar>

28
src/components/pagination/index.js

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
const app = getApp()
Component({
properties: {
pagination: {
type: Object,
value() {
return {}
},
},
customEmpty: {
type: Boolean,
value: false,
},
emptyTitle: {
type: String,
value: '',
},
},
data: {
imageUrl: app.globalData.imageUrl,
},
methods: {
handleTouchmove() {
return false
},
},
})

6
src/components/pagination/index.json

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"van-divider": "@vant/weapp/divider/index"
}
}

11
src/components/pagination/index.scss

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
/* components/pagination/index.wxss */
.none {
display: block;
margin: 30rpx auto;
width: 80%;
}
.empty-title {
text-align: center;
font-size: 32rpx;
color: #b0b0b0;
}

12
src/components/pagination/index.wxml

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
<block wx:if="{{pagination.count==0}}">
<slot wx:if="{{customEmpty}}"></slot>
<block wx:else>
<image class="none external-class" src="{{imageUrl}}none.png?t={{Timestamp}}"></image>
<view class="empty-title" wx:if="{{emptyTitle}}">{{emptyTitle}}</view>
</block>
</block>
<van-divider contentPosition="center" wx:elif="{{pagination.page<pagination.pages}}">
<van-loading />
加载中...
</van-divider>
<van-divider contentPosition="center" wx:elif="{{pagination.page>=pagination.pages}}">没有更多了</van-divider>

7
src/components/pickerArea/index.json

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"van-popup": "@vant/weapp/popup/index",
"van-icon": "@vant/weapp/icon/index"
}
}

144
src/components/pickerArea/index.scss

@ -0,0 +1,144 @@ @@ -0,0 +1,144 @@
.aside-share {
position: fixed;
right: 22rpx;
bottom: 120rpx;
width: 136rpx;
height: 136rpx;
}
.popup {
position: relative;
background-color: #fff;
.close {
position: absolute;
top: 52rpx;
right: 32rpx;
color: rgba(72, 72, 72, 1);
font-size: 34rpx;
}
.p-header {
padding: 46rpx 30rpx 0;
.title {
text-align: center;
font-size: 36rpx;
color: rgba(72, 72, 72, 1);
font-weight: bold;
}
.tip {
text-align: center;
margin-top: 16rpx;
font-size: 32rpx;
color: rgba(72, 72, 72, 1);
}
.area {
margin-top: 48rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14rpx;
.item {
flex: 1;
padding: 18rpx 30rpx;
box-shadow: 0rpx 4rpx 12rpx 0rpx rgba(0, 0, 0, 0.11);
border-radius: 12rpx 12rpx 12rpx 12rpx;
border: 2rpx solid var(--color);
display: flex;
align-items: center;
justify-content: space-between;
.content {
font-size: 32rpx;
color: rgba(72, 72, 72, 1);
max-width: 5em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.icon {
width: 32rpx;
height: 32rpx;
}
.tril {
margin-top: 5rpx;
width: 0;
height: 0;
border-style: solid;
border-width: 12rpx 12rpx 0 12rpx;
border-color: var(--color) transparent transparent transparent;
}
}
.picker {
flex: 1;
}
.btn {
width: 126rpx;
height: 76rpx;
font-size: 32rpx;
color: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
justify-content: center;
background: var(--color);
border-radius: 12rpx 12rpx 12rpx 12rpx;
}
}
}
.select {
margin-top: 44rpx;
padding: 0 30rpx 0;
display: grid;
grid-template-columns: repeat(auto-fit, 68rpx);
gap: 26rpx 20rpx;
.s-item {
width: 68rpx;
height: 68rpx;
text-align: center;
line-height: 68rpx;
font-size: 28rpx;
color: rgba(158, 158, 158, 1);
border-radius: 50%;
background: #f7f8f9;
&.active {
color: #fff;
background-color: var(--color);
}
}
}
.scroll {
padding: 0 30rpx;
height: 45vh;
box-sizing: border-box;
.item {
padding: 32rpx 0;
font-size: 28rpx;
color: rgba(72, 72, 72, 1);
border-bottom: 2px solid #fbfbfb;
display: flex;
align-items: center;
justify-content: space-between;
.word {
margin-right: 24rpx;
display: inline-block;
width: 1em;
color: rgba(158, 158, 158, 1);
}
.icon {
width: 36rpx;
height: 36rpx;
}
&.active {
color: var(--color);
.word {
color: var(--color);
}
}
}
}
}
.theme1 {
--color: rgba(224, 71, 117, 1);
}
.theme2 {
--color: #67baca;
}

325
src/components/pickerArea/index.ts

@ -0,0 +1,325 @@ @@ -0,0 +1,325 @@
const app = getApp<IAppOption>()
Component({
properties: {
// 省份名称
pname: {
type: String,
value: '',
},
// 省份ID
pid: {
type: String,
value: '',
},
// 城市名称
cname: {
type: String,
value: '',
},
// 城市ID
cid: {
type: String,
value: '',
},
theme: {
type: String,
value: 'theme1',
},
},
observers: {
show(newVal: boolean) {
this.triggerEvent('show', newVal)
},
},
data: {
show: false,
word: '',
imageUrl: app.globalData.imageUrl,
Timestamp: app.globalData.Timestamp,
active: 0,
ProvinceName: '',
ProvinceId: '',
CityName: '',
CityId: '',
proList: {
A: [
{
name: '安徽省',
code: '340000',
},
{
name: '澳门特别行政区',
code: '820000',
},
],
B: [
{
name: '北京市',
code: '110000',
},
],
C: [
{
name: '重庆市',
code: '500000',
},
],
F: [
{
name: '福建省',
code: '350000',
},
],
G: [
{
name: '甘肃省',
code: '620000',
},
{
name: '广东省',
code: '440000',
},
{
name: '广西壮族自治区',
code: '450000',
},
{
name: '贵州省',
code: '520000',
},
],
H: [
{
name: '海南省',
code: '460000',
},
{
name: '河北省',
code: '130000',
},
{
name: '河南省',
code: '410000',
},
{
name: '黑龙江省',
code: '230000',
},
{
name: '湖北省',
code: '420000',
},
{
name: '湖南省',
code: '430000',
},
],
J: [
{
name: '吉林省',
code: '220000',
},
{
name: '江苏省',
code: '320000',
},
{
name: '江西省',
code: '360000',
},
],
L: [
{
name: '辽宁省',
code: '210000',
},
],
N: [
{
name: '宁夏回族自治区',
code: '640000',
},
{
name: '内蒙古自治区',
code: '150000',
},
],
Q: [
{
name: '青海省',
code: '630000',
},
],
S: [
{
name: '山东省',
code: '370000',
},
{
name: '山西省',
code: '140000',
},
{
name: '陕西省',
code: '610000',
},
{
name: '上海市',
code: '310000',
},
{
name: '四川省',
code: '510000',
},
],
T: [
{
name: '天津市',
code: '120000',
},
{
name: '台湾省',
code: '710000',
},
],
X: [
{
name: '西藏自治区',
code: '540000',
},
{
name: '新疆维吾尔自治区',
code: '650000',
},
{
name: '香港特别行政区',
code: '810000',
},
],
Y: [
{
name: '云南省',
code: '530000',
},
],
Z: [
{
name: '浙江省',
code: '330000',
},
],
},
area: [] as any,
range: [],
scrollIntoView0: '',
scrollIntoView1: '',
},
methods: {
handleShow() {
let { active, pid } = this.data
if (!pid) {
active = 0
}
this.setData({
show: true,
ProvinceName: this.data.pname || '',
ProvinceId: this.data.pid || '',
CityName: this.data.cname || '',
CityId: this.data.cid || '',
scrollIntoView0: this.data.pid || '',
scrollIntoView1: this.data.cid || '',
active,
})
this.getArea()
},
getArea() {
if (this.data.area.length) {
this.getRangeList()
return
}
wx.ajax({
method: 'GET',
url: '/js/area.json',
isJSON: true,
}).then((res) => {
this.setData({
area: res,
})
this.getRangeList()
})
},
handleItem(e: any) {
const { code, name } = e.currentTarget.dataset
this.setData({
ProvinceId: code,
ProvinceName: name,
CityId: '',
CityName: '',
})
this.getRangeList()
},
handleChangeCity(e: any) {
const { code, name } = e.currentTarget.dataset
this.setData({
CityId: code,
CityName: name,
})
},
handleShare() {
this.setData({
show: false,
})
this.triggerEvent('ok', {
ProvinceName: this.data.ProvinceName,
ProvinceId: this.data.ProvinceId,
CityName: this.data.CityName,
CityId: this.data.CityId,
})
},
handleSelect(e) {
const { id } = e.currentTarget.dataset
this.setData({
word: id,
scrollIntoView0: id,
})
},
getRangeList() {
const { area, ProvinceId } = this.data
if (!ProvinceId) return
const range = area.filter((item: any) => item.value == ProvinceId)[0].children
this.setData({
range,
active: 1,
scrollIntoView0: '',
scrollIntoView1: `id${this.data.CityId}`,
})
},
handleProvince() {
this.setData({
active: 0,
scrollIntoView0: this.data.word || `id${this.data.ProvinceId}`,
scrollIntoView1: '',
})
},
handleCity() {
const { ProvinceId } = this.data
if (!ProvinceId) {
wx.showToast({
title: '请先选择省份',
icon: 'none',
})
return
}
this.getRangeList()
},
handleClose() {
this.setData({
show: false,
})
},
},
})

87
src/components/pickerArea/index.wxml

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
<slot bind:tap="handleShow"></slot>
<van-popup show="{{ show }}" position="bottom" round close-on-click-overlay bind:click-overlay="handleClose">
<view class="popup {{theme}}">
<van-icon catch:tap="handleClose" class="close" name="cross" />
<view class="p-header">
<view class="title">选您地区</view>
<!-- <view class="tip">对方打开后自动定位此地区</view> -->
<view class="area">
<view class="item" bind:tap="handleProvince">
<view class="content">{{ProvinceName || '请选择省份'}}</view>
<view class="tril"></view>
</view>
<view class="item" bind:tap="handleCity">
<view class="content">{{CityName || '请选择城市'}}</view>
<view class="tril"></view>
</view>
<view class="btn" bind:tap="handleShare">确定</view>
</view>
</view>
<block wx:if="{{active===1}}">
<scroll-view
class="scroll"
scroll-y
scroll-with-animation
scroll-into-view="{{scrollIntoView1}}"
show-scrollbar="{{false}}"
>
<view
class="item {{item.value === CityId && 'active'}}"
id="{{'id'+item.value}}"
wx:for="{{range}}"
wx:key="index"
bind:tap="handleChangeCity"
data-code="{{item.value}}"
data-name="{{item.label}}"
>
<view class="left">{{item.label}}</view>
<image wx:if="{{item.value === CityId}}" class="icon" src="{{imageUrl}}icon7.png?t={{Timestamp}}"></image>
</view>
</scroll-view>
</block>
<block wx:else>
<view class="select">
<view
class="s-item {{word===index && 'active'}}"
wx:for="{{proList}}"
wx:key="index"
bind:tap="handleSelect"
data-id="{{index}}"
>
{{index}}
</view>
</view>
<scroll-view
class="scroll"
scroll-y
scroll-into-view="{{scrollIntoView0}}"
scroll-with-animation
show-scrollbar="{{false}}"
>
<block wx:for="{{proList}}" wx:for-item="list" wx:for-index="key" wx:key="key">
<block wx:for="{{list}}" wx:key="index">
<view wx:if="{{index==0}}" id="{{key}}"></view>
<view
class="item {{item.code=== ProvinceId && 'active'}}"
id="{{'id'+item.code}}"
bind:tap="handleItem"
data-code="{{item.code}}"
data-name="{{item.name}}"
>
<view class="left">
<text class="word">{{index===0 ? key : ''}}</text>
{{item.name}}
</view>
<image
wx:if="{{item.code=== ProvinceId}}"
class="icon"
src="{{imageUrl}}icon7.png?t={{Timestamp}}"
></image>
</view>
</block>
</block>
</scroll-view>
</block>
</view>
</van-popup>

6
src/components/referralFrom/index.json

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"van-popup": "@vant/weapp/popup/index"
}
}

95
src/components/referralFrom/index.scss

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
.from {
padding: 48rpx 40rpx;
width: 650rpx;
box-sizing: border-box;
background: linear-gradient(349deg, #ffffff 0%, #e2f1f4 100%);
border-radius: 24rpx 24rpx 24rpx 24rpx;
border: 2rpx solid #ffffff;
.title {
font-size: 32rpx;
color: #283031;
font-weight: bold;
}
.date {
margin-top: 24rpx;
padding: 14rpx 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
background-color: #f2f4f5;
border-radius: 16rpx;
.conetent {
font-size: 32rpx;
color: #283031;
}
.tril {
width: 0;
height: 0;
border-style: solid;
border-width: 10rpx 10rpx 0 10rpx;
border-color: #aeb3b4 transparent transparent transparent;
}
}
.select-title {
margin-top: 48rpx;
font-size: 32rpx;
color: #283031;
font-weight: bold;
.sub {
font-weight: normal;
}
}
.list {
margin-top: 26rpx;
max-height: 55vh;
overflow-y: auto;
&::-webkit-scrollbar{
display: none;
}
.item {
margin-bottom: 16rpx;
padding: 14rpx 32rpx;
font-size: 32rpx;
color: #283031;
line-height: 48rpx;
background-color: #f2f4f5;
border: 1px solid #f2f4f5;
border-radius: 16rpx;
&.active {
border-color: #67baca;
background-color: #e7f5f8;
color: #67baca;
}
}
}
.footer {
margin-top: 32rpx;
display: flex;
justify-content: space-between;
align-items: center;
gap: 26rpx;
text-align: center;
.cancel {
flex: 1;
height: 80rpx;
font-size: 36rpx;
color: #67BACA;
line-height: 80rpx;
background: #ffffff;
border-radius: 98rpx 98rpx 98rpx 98rpx;
border: 2rpx solid #67baca;
}
.submit {
flex: 1;
height: 80rpx;
font-size: 36rpx;
color: #FFFFFF;
line-height: 80rpx;
background: #67baca;
border-radius: 98rpx 98rpx 98rpx 98rpx;
border: 2rpx solid #67baca;
}
}
}

139
src/components/referralFrom/index.ts

@ -0,0 +1,139 @@ @@ -0,0 +1,139 @@
import dayjs from 'dayjs'
const _app = getApp<IAppOption>()
// pages/story/a.ts
Component({
/**
*
*/
properties: {
show: {
type: Boolean,
value: false,
},
params: {
type: Object,
value: undefined,
},
},
observers: {
show() {
if (this.data.params) {
this.setData({
...this.data.params,
})
this.handleDateChange()
} else {
this.setData({
visitDateName: '',
visitDate: '',
hormone: 2,
traditionalInhibitor: 2,
gammaGlobulin: 2,
plasmaExchange: 2,
bCellInhibitor: 2,
fcRnAntagonists: 2,
c5ComplementInhibitor: 2,
chineseMedicine: 2,
other: 2,
recordId: '',
})
}
},
},
data: {
currentDate: dayjs().format('YYYY-MM-DD'),
visitDateName: '',
visitDate: '',
hormone: 2,
traditionalInhibitor: 2,
gammaGlobulin: 2,
plasmaExchange: 2,
bCellInhibitor: 2,
fcRnAntagonists: 2,
c5ComplementInhibitor: 2,
chineseMedicine: 2,
other: 2,
recordId: '',
},
methods: {
handleDateChange() {
this.setData({
visitDateName: dayjs(this.data.visitDate).format('YYYY年MM月DD日'),
})
},
handleSelect(e) {
const { name } = e.currentTarget.dataset
const value = this.data[name]
this.setData({
[name]: value === 2 ? 1 : 2,
})
},
submit() {
const { visitDate, recordId } = this.data
const params = {
visitDate,
recordId,
}
if(!visitDate){
wx.showToast({
title: '请选择复诊日期',
icon: 'none',
})
return
}
const selectKeys = [
'hormone',
'traditionalInhibitor',
'gammaGlobulin',
'plasmaExchange',
'bCellInhibitor',
'fcRnAntagonists',
'c5ComplementInhibitor',
'chineseMedicine',
'other',
]
selectKeys.forEach((item) => {
params[item] = this.data[item]
})
const onlySelect = selectKeys.some((item) => {
return this.data[item] === 1
})
if (!onlySelect) {
wx.showToast({
title: '请至少选择一种复诊后的方案',
icon: 'none',
})
return
}
wx.ajax({
method: 'POST',
url: '?r=igg4/re-visit/save-record',
data: params,
}).then(() => {
if (recordId) {
wx.showToast({
icon: 'none',
title: '编辑成功',
})
} else {
wx.showToast({
icon: 'none',
title: '添加成功',
})
}
this.handleCancel()
this.triggerEvent('refresh', params)
})
},
handleCancel() {
this.setData({
show: false,
})
},
},
})

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

Loading…
Cancel
Save