Browse Source

暴风眼

master
aBin 4 years ago
commit
cb74b8b073
  1. 8
      .editorconfig
  2. 0
      .env
  3. 8
      .env.development
  4. 2
      .env.mock
  5. 8
      .env.production
  6. 13
      .eslintignore
  7. 48
      .eslintrc.js
  8. 27
      .gitignore
  9. 11
      .hbuilderx/launch.json
  10. 1
      .npmrc
  11. 9
      .prettierignore
  12. 13
      .prettierrc
  13. 243
      CHANGELOG.md
  14. 101
      README.md
  15. 57
      babel.config.js
  16. 1
      commitlint.config.js
  17. 52
      jest.config.js
  18. 114
      package.json
  19. 21
      postcss.config.js
  20. BIN
      public/img/icons/android-chrome-192x192.png
  21. BIN
      public/img/icons/android-chrome-512x512.png
  22. BIN
      public/img/icons/android-chrome-maskable-192x192.png
  23. BIN
      public/img/icons/android-chrome-maskable-512x512.png
  24. BIN
      public/img/icons/apple-touch-icon-120x120.png
  25. BIN
      public/img/icons/apple-touch-icon-152x152.png
  26. BIN
      public/img/icons/apple-touch-icon-180x180.png
  27. BIN
      public/img/icons/apple-touch-icon-60x60.png
  28. BIN
      public/img/icons/apple-touch-icon-76x76.png
  29. BIN
      public/img/icons/apple-touch-icon.png
  30. BIN
      public/img/icons/favicon-16x16.png
  31. BIN
      public/img/icons/favicon-32x32.png
  32. BIN
      public/img/icons/msapplication-icon-144x144.png
  33. BIN
      public/img/icons/mstile-150x150.png
  34. 3
      public/img/icons/safari-pinned-tab.svg
  35. 28
      public/index.html
  36. 2
      public/robots.txt
  37. 159
      src/App.vue
  38. 67
      src/apis/carbasics.js
  39. 19
      src/apis/project.js
  40. 49
      src/apis/role.js
  41. 109
      src/apis/tall.js
  42. 17
      src/apis/task.js
  43. 7
      src/apis/wbs.js
  44. 7
      src/common/styles/app.scss
  45. 257
      src/common/styles/iconfont.css
  46. 257
      src/common/styles/iconfont.scss
  47. 4509
      src/common/styles/tailwind.scss
  48. 473
      src/components/Calendar/Calendar.vue
  49. 136
      src/components/Calendar/generateDates.js
  50. 86
      src/components/Globals/Globals.vue
  51. 37
      src/components/ImageCode/ImageCode.vue
  52. 467
      src/components/PatientList/PatientList.vue
  53. BIN
      src/components/PatientList/icon/yanshi.png
  54. 520
      src/components/PrettyExchange/PrettyExchange.vue
  55. 161
      src/components/Projects/ProjectItem.vue
  56. 65
      src/components/Projects/Projects.vue
  57. 264
      src/components/Roles/Roles.vue
  58. 33
      src/components/Service/Service.vue
  59. BIN
      src/components/Service/icon/tel-we.png
  60. 126
      src/components/TimeLine/TimeLine.vue
  61. 42
      src/components/TimeLine/component/Barrier.vue
  62. 185
      src/components/TimeLine/component/TaskTools.vue
  63. 142
      src/components/TimeLine/component/TimeBox.vue
  64. 231
      src/components/TimeLine/component/TimeStatus.vue
  65. 7
      src/components/TimeLine/component/Title.vue
  66. 235
      src/components/Title/Title.vue
  67. 460
      src/components/Title/components/CreateTask.vue
  68. 210
      src/components/Title/components/ShareProject.vue
  69. 201
      src/components/Upload/Upload.vue
  70. 418
      src/components/select-lay/select-lay.vue
  71. 6
      src/config/app.js
  72. 3
      src/config/db.js
  73. 97
      src/config/plugin.js
  74. 2
      src/config/task.js
  75. 17
      src/config/time.js
  76. 43
      src/config/user copy.js
  77. 44
      src/config/user.js
  78. 5
      src/config/zIndex.js
  79. 52
      src/main.js
  80. 81
      src/manifest.json
  81. 48
      src/mixins/userAuth.js
  82. 78
      src/pages.json
  83. 71
      src/pages/camera/camera.vue
  84. 444
      src/pages/establish/establish.vue
  85. BIN
      src/pages/establish/icon/bottom.png
  86. BIN
      src/pages/establish/icon/idcard.png
  87. 156
      src/pages/get-phone-power/get-phone-power.vue
  88. 170
      src/pages/index/index.vue
  89. 36
      src/pages/inner/inner.vue
  90. 181
      src/pages/patientLine/patientLine.vue
  91. BIN
      src/pages/patientLine/png/播放.png
  92. 192
      src/pages/phone-bind/phone-bind.vue
  93. 41
      src/pages/project-webview/project-webview.vue
  94. 31
      src/pages/questionnaire-webview/questionnaire-webview.vue
  95. 319
      src/pages/task-page/task-page.vue
  96. 29
      src/registerServiceWorker.js
  97. 3
      src/store/carbasics/actions.js
  98. 3
      src/store/carbasics/getters.js
  99. 12
      src/store/carbasics/index.js
  100. 35
      src/store/carbasics/mutations.js

8
.editorconfig

@ -0,0 +1,8 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 140
root = true

0
.env

8
.env.development

@ -0,0 +1,8 @@
VUE_APP_NODE_ENV=development
VUE_APP_BASE_URL=https://test.tall.wiki
VUE_APP_API_URL=https://test.tall.wiki/gateway
VUE_APP_MSG_URL=ws://101.201.226.163/websocket/message/v4.0/ws
VUE_APP_PROJECT_PATH=https://test.tall.wiki/carBasicTall
VUE_APP_QUESTION_PATH=https://test.tall.wiki/carbasics
VUE_APP_VERSION=v3.1.0
VUE_APP_PUBLIC_PATH=/carBasicCalendar/

2
.env.mock

@ -0,0 +1,2 @@
VUE_APP_BASE_URL=http://127.0.0.1:8080
VUE_APP_API_URL=http://127.0.0.1:8080

8
.env.production

@ -0,0 +1,8 @@
VUE_APP_NODE_ENV=production
VUE_APP_BASE_URL=https://test.tall.wiki
VUE_APP_API_URL=https://test.tall.wiki/gateway
VUE_APP_MSG_URL=ws://101.201.226.163/websocket/message/v4.0/ws
VUE_APP_PROJECT_PATH=https://test.tall.wiki/carBasicTall
VUE_APP_QUESTION_PATH=https://test.tall.wiki/carbasics
VUE_APP_VERSION=v3.1.0
VUE_APP_PUBLIC_PATH=/carBasicCalendar/

13
.eslintignore

@ -0,0 +1,13 @@
node_modules
dist/
test
build/
unpackage/
babel.config.js
package.json
postcss.config.js
.eslint.js
vue.config.js
src/common/styles/index.css
src/pages.json
manifest.json

48
.eslintrc.js

@ -0,0 +1,48 @@
module.exports = {
root: true,
env: {
node: true,
browser: true,
},
extends: ['plugin:vue/essential', 'eslint:recommended', '@vue/prettier'],
rules: {
'vue/html-self-closing': 'off',
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-param-reassign': 'off',
'max-len': ['error', { code: 140, tabWidth: 2 }],
'object-curly-newline': ['error', { multiline: true }],
'arrow-parens': ['error', 'as-needed'],
'linebreak-style': 'off',
'vue/attributes-order': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/html-indent': 'off',
'vue/html-closing-bracket-newline': [
'error',
{
singleline: 'never',
multiline: 'always',
},
],
},
parserOptions: { parser: 'babel-eslint' },
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)'],
env: { jest: true },
},
],
globals: {
Vue: true,
VueRouter: true,
Vuex: true,
_: true,
uni: true,
wx: true,
},
};

27
.gitignore

@ -0,0 +1,27 @@
.DS_Store
node_modules/
unpackage/
dist/
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
package-lock.json
# Editor directories and files
.project
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
.eslintcache

11
.hbuilderx/launch.json

@ -0,0 +1,11 @@
{ // launch.json configurations app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtypelocalremote, localremote
"version": "0.0",
"configurations": [{
"type": "uniCloud",
"default": {
"launchtype": "local"
}
}
]
}

1
.npmrc

@ -0,0 +1 @@
registry=https://registry.npm.taobao.org

9
.prettierignore

@ -0,0 +1,9 @@
node_modules
dist/
test
build/
unpackage/
babel.config.js
package.json
postcss.config.js
.eslint.js

13
.prettierrc

@ -0,0 +1,13 @@
{
"printWidth": 140,
"singleQuote": true,
"semi": true,
"trailingComma": "all",
"arrowParens": "avoid",
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"proseWrap": "always",
"endOfLine": "lf"
}

243
CHANGELOG.md

@ -0,0 +1,243 @@
# 0.1.0 (2021-11-22)
### 🌟 新功能
范围|描述|commitId
--|--|--
- | 绑定手机号 | [52e0352](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/52e0352)
- | 暴风眼相关小程序修改 | [8947ea8](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8947ea8)
- | 标题栏变化 | [3898cfe](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/3898cfe)
- | 标题栏变化 | [c0fcd9d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c0fcd9d)
- | 标题栏角色栏全局任务组件新建 | [0500cb4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0500cb4)
- | 插件参数处理调整 | [a3e68d3](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a3e68d3)
- | 插件数据获取 | [5b91bdc](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5b91bdc)
- | 存token | [b8a178d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b8a178d)
- | 导入项目,更新项目 | [5e06adf](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5e06adf)
- | 导入项目后提示并打开项目详情页 | [410f527](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/410f527)
- | 导入wbs | [1224fcb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/1224fcb)
- | 点击日历日期查询项目列表 | [c458385](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c458385)
- | 定期任务面板骨架屏添加 | [b2698c0](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b2698c0)
富文本插件 | 富文本插件demo测试 | [ed3d644](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ed3d644)
- | 缓存修改 | [63e1f0d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/63e1f0d)
- | 获取用户收取那,提交用户信息 | [a3c54f1](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a3c54f1)
- | 角色栏实现 | [94cd671](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/94cd671)
- | 进入项目默认打开项目列表第一个项目 | [f666730](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f666730)
- | 距调整pc端 | [5069aa1](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5069aa1)
- | 客服加分享 | [ccae107](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ccae107)
- | 客服位置更改及首页分享功能 | [239a6ba](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/239a6ba)
- | 面变化首页变化 | [5e860f1](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5e860f1)
- | 模拟接口测试 | [69e7931](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/69e7931)
- | 配置默认插件接口 | [f0c177d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f0c177d)
- | 全局插件及默认插件位置修改 | [6c80d08](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/6c80d08)
- | 任务进行中状态数字 | [27b7326](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/27b7326)
- | 任务状态时间显示 | [56f5183](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/56f5183)
- | 日常任务插件调整 | [c1881f9](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c1881f9)
- | 日历定位;合并 | [ea3f937](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ea3f937)
- | 删除项目 | [00b886c](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/00b886c)
- | 上传逻辑变化 | [3ff1dc2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/3ff1dc2)
- | 设置小红点 | [9316bcb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/9316bcb)
- | 升级版本v3.1.0;tailwindcss添加class | [9ef05e1](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/9ef05e1)
- | 时间基准线,默认插件 | [a33ba1e](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a33ba1e)
- | 时间轴界面 | [33927e9](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/33927e9)
- | 时间轴修改状态时提示框增加 | [e841392](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e841392)
- | 适配小程序;小程序登录 | [cefc0eb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/cefc0eb)
- | 首页项目样式改变 | [8514c85](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8514c85)
- | 提交到本地 | [9cbe411](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/9cbe411)
- | 添加 环境变量,动态控制webview project的path | [8a40481](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8a40481)
- | 添加时间轴上下滚动 | [2b81bbc](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/2b81bbc)
- | 添加项目排序 | [a0b491b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a0b491b)
- | 添加子任务插件 子项目插件 | [7bda7e2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7bda7e2)
- | 问卷功能取消,排名查看功能新增 | [f405a38](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f405a38)
- | 细节调整,添加project-webview | [4d9050b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4d9050b)
- | 向右箭头图标变化 | [8e9ca55](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8e9ca55)
- | 项目列表, 项目url | [32e005b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/32e005b)
- | 项目列表排序 | [224c58b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/224c58b)
- | 项目api url设置 | [6cd5245](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/6cd5245)
- | 修改小程序id | [4206bf2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4206bf2)
- | 引入dayjs | [29b8b93](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/29b8b93)
- | 字体大小更改 | [82cfdd4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/82cfdd4)
- | api封装 | [7d4edfc](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7d4edfc)
bind phone | 图形验证码;短信验证码;绑定手机号 | [93ffea2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/93ffea2)
- | cache indexedDB处理 | [3388967](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/3388967)
calendar, tall.js | 上下滑动切换日历的模式,tall.js中domain根据环境变量切换 | [364e25d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/364e25d)
- | db store | [6414c4f](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/6414c4f)
default plugin | 添加默认插件;项目列表;全局项目最大高度设置 | [ed1d87b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ed1d87b)
- | indexedDB | [687394e](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/687394e)
modules update;network | npm包升级;网络判断,网络不好才开启本地存储 | [ebf7bdc](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ebf7bdc)
mp | 兼容小程序,去除window,document等 | [9178255](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/9178255)
phone-bind | 验证码validate | [a427250](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a427250)
pinch | alloy finger实现图片的pinch放大缩小 | [de01343](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/de01343)
plugin | 插件添加了token及param参数 | [aeb0292](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/aeb0292)
- | post 封装 | [da52e94](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/da52e94)
- | tailwindcss添加部分属性 | [5b46b6d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5b46b6d)
- | tall插件封装 | [1bcb920](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/1bcb920)
task status | 任务状态切换未完 | [7ffd135](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7ffd135)
- | ws storage | [21b3a06](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/21b3a06)
### 🎨 代码样式
范围|描述|commitId
--|--|--
- | 代码格式细节调整 | [cb2532b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/cb2532b)
- | 代码审查 | [d75134c](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/d75134c)
- | 格式细节调整 | [b907a03](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b907a03)
- | 更新代码 | [8c27e68](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8c27e68)
- | 更新代码 | [1f40a76](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/1f40a76)
- | 任务快捷方式图标增加 | [4aba872](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4aba872)
- | 日常任务修改 | [dfa7ee2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/dfa7ee2)
- | 删除插件携带的多余文件 | [0f392bb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0f392bb)
- | 删除多余字段 | [5ae3973](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5ae3973)
- | 删除没用代码 | [34b20e1](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/34b20e1)
- | 删除calendar中多余的console | [e339eec](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e339eec)
- | 删除console.log | [5064a38](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5064a38)
- | 删除index中没用的alert代码 | [9c9eec7](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/9c9eec7)
- | 删除mock,console;upload添加loading | [99d42e2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/99d42e2)
- | 添加插件数据 | [2f11b42](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/2f11b42)
- | 图标修改 | [54bca09](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/54bca09)
- | 无基本变化 | [21ac4bb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/21ac4bb)
- | 细节调整 | [2cfc09a](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/2cfc09a)
- | 修改角色样式 | [73e268e](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/73e268e)
- | 组件新建 | [89c0035](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/89c0035)
- | calendar注释 | [a2ec112](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a2ec112)
- | indexedDB.js格式整理 | [b0d3a36](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b0d3a36)
### 🐛 Bug 修复
范围|描述|commitId
--|--|--
- | 1.时间轴数据渲染 2.时间基准线 | [d643af2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/d643af2)
- | 插件bug解决 | [41257eb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/41257eb)
- | 初始展示角色修改 | [2ac4053](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/2ac4053)
- | 定期任务本地缓存和api赋值,未完成 | [5a10856](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5a10856)
定期任务本地缓存和api赋值,未完成 | 定期任务本地缓存和api赋值,未完成 | [b22a366](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b22a366)
- | 定期任务插件 | [92b3254](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/92b3254)
- | 定期任务骨架屏修改 | [8ff72dd](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8ff72dd)
- | 定期任务接口 | [aa4981c](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/aa4981c)
- | 定期任务未加载时,显示空的时间轴并能上下滑动 | [ce38093](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ce38093)
- | 定期任务key值修改 | [c6688db](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c6688db)
- | 骨架屏替换 | [e9fdd71](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e9fdd71)
- | 监听时间基本点 | [033fca0](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/033fca0)
- | 角色栏修改 | [19228d6](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/19228d6)
- | 角色显示状态修改 | [7d3b906](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7d3b906)
- | 解决时间轴报错 | [da1eece](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/da1eece)
- | 平车演示临时去掉项目快捷方式的toast提示 | [e0b2c23](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e0b2c23)
- | 切换到默认项目角色没有激活状态的bug | [438d448](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/438d448)
- | 切换日历时查询小红点 | [7091789](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7091789)
- | 任务开始时间延迟插件 | [992a313](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/992a313)
- | 日常任务插件遍历时的key值修改 | [cd26285](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/cd26285)
- | 日常任务插件面板高度修改 | [249f9e4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/249f9e4)
- | 日常任务html数据查验 | [880ce5c](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/880ce5c)
- | 日历无任务时添加小绿点,时间轴刻度无任务不显示时分 | [0f90868](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0f90868)
- | 上下滚动时间轴 | [d533a01](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/d533a01)
- | 上下滑动加载定期任务 | [4090d89](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4090d89)
- | 设置时间轴自动滚动到当前位置 | [a3474f8](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a3474f8)
- | 时间轴插件 | [225d3cc](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/225d3cc)
- | 时间轴骨架屏修改 | [ca78d02](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ca78d02)
- | 时间轴滚动位置修改 | [551da63](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/551da63)
- | 时间轴上下滚动数据加载bug修改 | [e82ede4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e82ede4)
- | 时间轴上下滑动 | [4d0ae46](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4d0ae46)
- | 时间轴无任务时时间刻度加载修改 | [4921672](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4921672)
- | 收到消息修改任务状态 | [c378063](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c378063)
- | 手动展开日常任务 | [0a4a622](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0a4a622)
- | 提示信息显示bug及日常任务收缩问题 | [f2f06c5](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f2f06c5)
- | 跳转详情页返回路径修改 | [c5e17c0](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c5e17c0)
- | 下拉加载定期任务传参,时间格式化修改 | [0b95a0e](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0b95a0e)
项目列表排序 | 项目列表排序 | [402c563](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/402c563)
- | 项目列表排序修改 | [fd3c3ac](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/fd3c3ac)
- | 项目列表排序修改 | [59f4c21](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/59f4c21)
- | 修改报错 | [531c14d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/531c14d)
- | 修改定期任务状态0和4时不加载圆圈 | [30e352f](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/30e352f)
- | 修改角色栏组件 | [a54c601](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a54c601)
- | 修改接口路径 | [df6acf2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/df6acf2)
- | 修改小红点传参 | [87b20fd](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/87b20fd)
- | 修改样式 | [f0ddc90](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f0ddc90)
- | 修改main | [749ae9a](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/749ae9a)
- | api 存storage | [81032ba](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/81032ba)
ID1000343 | 解决向下预加载查询参数时间没+1颗粒度;以及滚动加载颗粒度写死的问题 | [940603a](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/940603a), closes [#ID1000343](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/issues/ID1000343)
plugin | 插件解析机制完善 | [0f5a27d](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0f5a27d)
project title | 项目标题修改; 切换角色移除script | [5c20017](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5c20017)
role | 切换角色的逻辑修正完善 | [4ae534f](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4ae534f)
roles | 修复默认显示不是我的角色的问题 | [b69f94f](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b69f94f)
task任务逻辑完善 | 减少初始global及regular的不必要请求 | [bd4bd38](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/bd4bd38)
- | title.vue根据页面栈显示返回按钮;标题文本超出显示... | [0cbacf4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0cbacf4)
### 📝 文档
范围|描述|commitId
--|--|--
- | README.md | [ab0eb05](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/ab0eb05)
### 🔧 测试
范围|描述|commitId
--|--|--
- | 禁用任务开始操作 | [b5425db](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b5425db)
- | 添加测试,测试utils/time.js的computeDurationText | [e758010](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e758010)
- | 暂时移除了jest浏览器配置 | [5088d01](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5088d01)
### 🔨 代码重构
范围|描述|commitId
--|--|--
- | 界面样式调整 | [4367249](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4367249)
- | 去掉tailwindcss | [4bed47e](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4bed47e)
- | 任务状态重构 | [4693655](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4693655)
- | 删除多余的技术验证界面 | [542ae5b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/542ae5b)
- | 删除多余的weekmode store里的东西 | [0841fe0](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0841fe0)
- | 下滑时间轴添加备注 | [4fd20e3](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4fd20e3)
- | 修改utils/upload 兼容小程序选择客户端文件上传WBS | [8f49129](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8f49129)
- | 重构store分层 | [5f6fff8](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5f6fff8)
calendar | 日历细节调整 | [1a8d6bf](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/1a8d6bf)
- | project 代码健壮性完善 | [a3202c5](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a3202c5)
store/home | 删除store/home | [db8a3b4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/db8a3b4)
task beginTime | 格式化任务开始时间 | [fbc0301](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/fbc0301)
template | eslint prettier sass uview tailwindcss | [9c966a1](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/9c966a1)
tip | 任务状态显示及tip组件数据的重构 | [78a5750](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/78a5750)
tips | 修改任务状态方法重构 | [b57d3ac](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b57d3ac)
title.vue | 移除测试的repeat; 样式细节调整 | [c32d2bd](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c32d2bd)
### 🚀 性能优化
范围|描述|commitId
--|--|--
- | 1.时间轴筛选相同的时间替换数据 2.整理代码 | [e082ccb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e082ccb)
- | 测试接口 | [215e074](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/215e074)
- | 插件查询及展示 | [4dba770](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/4dba770)
- | 角色栏文字颜色修改 | [215c6b3](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/215c6b3)
- | 解决警告 | [c932b09](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c932b09)
- | 日历的更改 | [7353ac8](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7353ac8)
- | 数据存储,避免重复调用接口 | [d22308a](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/d22308a)
- | 提交本地代码 | [e0cf2ed](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e0cf2ed)
- | 小红点api缓存修改 | [e992343](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/e992343)
- | 修改代码格式 | [14123d7](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/14123d7)
- | 修改定期任务骨架屏高度 | [909a734](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/909a734)
- | 整理代码 | [7a55315](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7a55315)
- | 组件文件夹新建 | [22bfe7b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/22bfe7b)
- | 组件文件夹新建 | [17bb8c9](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/17bb8c9)
- | 组件文件夹新建 | [1421504](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/1421504)
### chore
范围|描述|commitId
--|--|--
- | 删除多余的构建的命令 | [3f4eb2f](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/3f4eb2f)
- | 添加mp-weixin的构建命令 | [3776a67](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/3776a67)
信息配置 | 配置eslint等配置 | [7421443](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/7421443)
- | 修复不能build的问题 | [0b7b91e](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/0b7b91e)
- | api 封装 | [8dcb8a2](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/8dcb8a2)
- | dart-sass替换node-sass;删除多余的uni平台包 | [519f28b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/519f28b)
- | env host修改 | [a79a4a5](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a79a4a5)
- | merge globals | [b0957cc](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/b0957cc)
- | merge wrr | [5ccc7a5](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/5ccc7a5)
- | mock | [51c24a5](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/51c24a5)
package manifest | 去掉了摇树 | [f7c1dd4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f7c1dd4)
pwa 小程序 | 移除了pwa,alloyFinger添加平台判断 | [875fab4](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/875fab4)
- | uview-ui | [a9ea34b](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/a9ea34b)
v3.0.1 | tall api 地址从1.0改成了3.0 | [db5afd5](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/db5afd5)
范围|描述|commitId
--|--|--
- | style:index | [978f272](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/978f272)
- | !2 基础模板v1.1.0 | [f5e61dd](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/f5e61dd)
- | init | [c0f1deb](https://dd.tall.wiki/gitea/jiawei/tall-qcp2.0/commits/c0f1deb)

101
README.md

@ -0,0 +1,101 @@
# tall-mui-cli
## 发布注意事项
环境变量里的`VUE_APP_VERSION=v3.0.1` 如果project路径变了 或者版本升级了, 发行之前一定要跟着改
本项目, TALL程序主体框架升级, 或者服务端路径变化, 要修改`manifest.json` 下的 `h5.router.base`
---
## 项目运行
### 安装依赖
```
yarn
```
### 本地环境运行
+ h5
```
yarn dev:h5
```
浏览器输入网址:
http://localhost:8080/tall/v3.0.1/#/?u=1217647686598135808&p=1420652719055839232
- u: userId
- p: projectId
- r: roleId
- pn: projectName
- t: taskId
+ 微信小程序
```
yarn dev:mp-weixin
```
+ app
```
yarn dev:app-plus
```
### 生产环境构建
+ h5
```
yarn build:h5
```
+ app
```
yarn build:app-plus
```
+ 微信小程序
```
yarn build:mp-weixin
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
## 代码提交
+ 项目设置了commit lint, commit信息验证;运行`yarn cz` 命令依次填写commit信息即可
+ 以及pre-commit钩子执行eslint代码格式检测, 代码格式不符合规则无法提交
```
yarn cz
```
## 技术栈
### UI及工具库
+ uni-app的cli构建版本
+ vuex vue官方状态管理库
+ tailwindcss 公共样式库(注意这个版本不是最新版本, 最新版本安装后报错)
+ uview-ui uni-app组件库
+ alloyfinger 移动端手势库
+ dayjs 时间处理库
+ pwa 处理缓存, 构建离线应用
### 构建相关
+ sass node-sass
+ prettier 自动格式化代码
+ eslint 代码可是校验
+ commitlint git commit信息校验
+ husky lint-staged git钩子处理commit校验及eslint代码检测
+ vue-cli-plugin-mock mock数据
## H5 indexedDB
### 设计概要
db挂载到全局的store上,store存放的数据:
```js
{
db: null, // object
name: 'TALL_indexedDB', // string
version: 1, // number
}
```

57
babel.config.js

@ -0,0 +1,57 @@
const plugins = [];
if (process.env.UNI_OPT_TREESHAKINGNG) {
plugins.push(require('@dcloudio/vue-cli-plugin-uni-optimize/packages/babel-plugin-uni-api/index.js'));
}
if (
(process.env.UNI_PLATFORM === 'app-plus' && process.env.UNI_USING_V8) ||
(process.env.UNI_PLATFORM === 'h5' && process.env.UNI_H5_BROWSER === 'builtin')
) {
const path = require('path');
const isWin = /^win/.test(process.platform);
const normalizePath = path => (isWin ? path.replace(/\\/g, '/') : path);
const input = normalizePath(process.env.UNI_INPUT_DIR);
try {
plugins.push([
require('@dcloudio/vue-cli-plugin-hbuilderx/packages/babel-plugin-console'),
{
file(file) {
file = normalizePath(file);
if (file.indexOf(input) === 0) {
return path.relative(input, file);
}
return false;
},
},
]);
} catch (e) {}
}
process.UNI_LIBRARIES = process.UNI_LIBRARIES || ['@dcloudio/uni-ui'];
process.UNI_LIBRARIES.forEach(libraryName => {
plugins.push([
'import',
{
libraryName: libraryName,
customName: name => {
return `${libraryName}/lib/${name}/${name}`;
},
},
]);
});
module.exports = {
presets: [
[
'@vue/app',
{
modules: 'commonjs',
useBuiltIns: process.env.UNI_PLATFORM === 'h5' ? 'usage' : 'entry',
},
],
],
plugins,
};

1
commitlint.config.js

@ -0,0 +1 @@
module.exports = { extends: ['./node_modules/vue-cli-plugin-commitlint/lib/lint'] };

52
jest.config.js

@ -0,0 +1,52 @@
module.exports = {
globalTeardown: '@dcloudio/uni-automator/dist/teardown.js',
// testEnvironment: '@dcloudio/uni-automator/dist/environment.js',
testEnvironmentOptions: {
compile: true,
h5: {
// 为了节省测试时间,可以指定一个 H5 的 url 地址,若不指定,每次运行测试,会先 npm run dev:h5
url: 'http://localhost:8080/tall/v3.0.1/#/h5',
options: {
headless: true, // 配置是否显示 puppeteer 测试窗口
args: ['--no-sandbox'],
},
},
'app-plus': {
// 需要安装 HBuilderX
android: {
executablePath: 'HBuilderX/plugins/launcher/base/android_base.apk', // apk 目录
},
ios: {
// uuid 必须配置,目前仅支持模拟器,可以(xcrun simctl list)查看要使用的模拟器 uuid
id: '',
executablePath: 'HBuilderX/plugins/launcher/base/Pandora_simulator.app', // ipa 目录
},
},
'mp-weixin': {
port: 9420, // 默认 9420
account: '', // 测试账号
args: '', // 指定开发者工具参数
cwd: '', // 指定开发者工具工作目录
launch: true, // 是否主动拉起开发者工具
teardown: 'disconnect', // 可选值 "disconnect"|"close" 运行测试结束后,断开开发者工具或关闭开发者工具
remote: false, // 是否真机自动化测试
executablePath: '', // 开发者工具cli路径,默认会自动查找, windows: C:/Program Files (x86)/Tencent/微信web开发者工具/cli.bat", mac: /Applications/wechatwebdevtools.app/Contents/MacOS/cli
},
'mp-baidu': {
port: 9430, // 默认 9430
args: '', // 指定开发者工具参数
cwd: '', // 指定开发者工具工作目录
launch: true, // 是否主动拉起开发者工具
teardown: 'disconnect', // 可选值 "disconnect"|"close" 运行测试结束后,断开开发者工具或关闭开发者工具
remote: false, // 是否真机自动化测试
executablePath: '', // 开发者工具cli路径,默认会自动查找
},
},
testTimeout: 15000,
reporters: ['default'],
watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
moduleFileExtensions: ['js', 'json'],
rootDir: __dirname,
testMatch: ['<rootDir>/src/test/**/*test.[jt]s?(x)'], // 测试文件目录
testPathIgnorePatterns: ['/node_modules/'],
};

114
package.json

@ -0,0 +1,114 @@
{
"name": "TALL",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "npm run dev:h5",
"build": "npm run build:h5",
"lint": "vue-cli-service lint",
"fix": "vue-cli-service lint --fix",
"test": "npm run test:h5",
"dev:h5": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode development",
"dev:h5-pro": "cross-env NODE_ENV=development UNI_PLATFORM=h5 vue-cli-service uni-serve --mode production",
"build:h5-test": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode development",
"build:h5": "cross-env NODE_ENV=production UNI_PLATFORM=h5 vue-cli-service uni-build --mode production",
"build:app-plus": "cross-env NODE_ENV=production UNI_PLATFORM=app-plus vue-cli-service uni-build --mode production",
"build:mp-weixin": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode production",
"cz": "npm run log && git add . && git cz",
"dev:app-plus": "cross-env NODE_ENV=development UNI_PLATFORM=app-plus vue-cli-service uni-build --watch",
"dev:custom": "cross-env NODE_ENV=development uniapp-cli custom",
"dev:mp-weixin": "cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch",
"info": "node node_modules/@dcloudio/vue-cli-plugin-uni/commands/info.js",
"log": "conventional-changelog --config ./node_modules/vue-cli-plugin-commitlint/lib/log -i CHANGELOG.md -s -r 0",
"test:android": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=android jest -i",
"test:h5": "cross-env UNI_PLATFORM=h5 jest -i",
"test:ios": "cross-env UNI_PLATFORM=app-plus UNI_OS_NAME=ios jest -i",
"test:mp-weixin": "cross-env UNI_PLATFORM=mp-weixin jest -i"
},
"dependencies": {
"@dcloudio/uni-h5": "^2.0.0-32220210818002",
"@dcloudio/uni-helper-json": "*",
"@dcloudio/uni-i18n": "^2.0.0-32220210818002",
"@dcloudio/uni-mp-vue": "^2.0.0-32220210818002",
"@dcloudio/uni-mp-weixin": "^2.0.0-32220210818002",
"@dcloudio/uni-stat": "^2.0.0-32220210818002",
"@vue/shared": "^3.2.6",
"dayjs": "^1.10.6",
"flyio": "^0.6.2",
"lodash": "^4.17.21",
"regenerator-runtime": "^0.12.1",
"uview-ui": "^1.8.5",
"vue": "^2.6.11",
"vuex": "^3.2.0"
},
"devDependencies": {
"@babel/runtime": "~7.12.0",
"@dcloudio/types": "^2.5.1",
"@dcloudio/uni-automator": "^2.0.0-32220210818002",
"@dcloudio/uni-cli-shared": "^2.0.0-32220210818002",
"@dcloudio/uni-migration": "^2.0.0-32220210818002",
"@dcloudio/uni-template-compiler": "^2.0.0-32220210818002",
"@dcloudio/vue-cli-plugin-hbuilderx": "^2.0.0-32220210818002",
"@dcloudio/vue-cli-plugin-uni": "^2.0.0-32220210818002",
"@dcloudio/vue-cli-plugin-uni-optimize": "^2.0.0-32220210818002",
"@dcloudio/webpack-uni-mp-loader": "^2.0.0-32220210818002",
"@dcloudio/webpack-uni-pages-loader": "^2.0.0-32220210818002",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/eslint-config-prettier": "^6.0.0",
"autoprefixer": "^9.8.6",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.11.0",
"commitizen": "^4.0.3",
"commitlint": "^8.2.0",
"compression-webpack-plugin": "^5.0.2",
"conventional-changelog-cli": "^2.0.28",
"core-js": "^3.16.3",
"cross-env": "^7.0.3",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-vue": "^6.2.2",
"husky": "^3.0.9",
"jest": "^25.4.0",
"lint-staged": "^11.1.2",
"mini-types": "*",
"miniprogram-api-typings": "^3.4.3",
"postcss": "^7.0.36",
"postcss-class-rename": "^1.0.1",
"postcss-comment": "^2.0.0",
"prettier": "^2.2.1",
"puppeteer": "^10.2.0",
"right-pad": "^1.0.1",
"sass": "^1.38.2",
"sass-loader": "^8.0.2",
"vue-cli-plugin-commitlint": "~1.0.12",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [
"Android >= 4",
"ios >= 8"
],
"config": {
"commitizen": {
"path": "./node_modules/vue-cli-plugin-commitlint/lib/cz"
}
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,json,css,vue}": [
"eslint --fix",
"git add"
],
"*.js": "eslint --cache --fix"
},
"uni-app": {
"scripts": {}
}
}

21
postcss.config.js

@ -0,0 +1,21 @@
const path = require('path');
module.exports = {
parser: require('postcss-comment'),
plugins: [
require('postcss-import')({
resolve(id, basedir, importOptions) {
if (id.startsWith('~@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(3));
} else if (id.startsWith('@/')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(2));
} else if (id.startsWith('/') && !id.startsWith('//')) {
return path.resolve(process.env.UNI_INPUT_DIR, id.substr(1));
}
return id;
},
}),
require('autoprefixer')({ remove: process.env.UNI_PLATFORM !== 'h5' }),
require('@dcloudio/vue-cli-plugin-uni/packages/postcss'),
],
};

BIN
public/img/icons/android-chrome-192x192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
public/img/icons/android-chrome-512x512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
public/img/icons/android-chrome-maskable-192x192.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
public/img/icons/android-chrome-maskable-512x512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
public/img/icons/apple-touch-icon-120x120.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
public/img/icons/apple-touch-icon-152x152.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
public/img/icons/apple-touch-icon-180x180.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
public/img/icons/apple-touch-icon-60x60.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/img/icons/apple-touch-icon-76x76.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
public/img/icons/apple-touch-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
public/img/icons/favicon-16x16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

BIN
public/img/icons/favicon-32x32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/img/icons/msapplication-icon-144x144.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
public/img/icons/mstile-150x150.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

3
public/img/icons/safari-pinned-tab.svg

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.00251 14.9297L0 1.07422H6.14651L8.00251 4.27503L9.84583 1.07422H16L8.00251 14.9297Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 215 B

28
public/index.html

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<script>
// document.addEventListener('DOMContentLoaded', function() {
// document.documentElement.style.fontSize = document.documentElement.clientWidth / 20 + 'px'
// })
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<link rel="stylesheet" href="<%= BASE_URL %>static/index.<%= VUE_APP_INDEX_CSS_HASH %>.css" />
</head>
<body>
<noscript>
<strong>Please enable JavaScript to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

2
public/robots.txt

@ -0,0 +1,2 @@
User-agent: *
Disallow:

159
src/App.vue

@ -0,0 +1,159 @@
<script>
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
export default {
async onLaunch(options) {
// const obj = { title: 'Typhoneye' };
// uni.showShareMenu(obj);
console.log('options: ', options);
this.checkNetwork(); //
this.getSystemInfo(); //
this.autoUpdate();
/* #ifdef MP-WEIXIN */
await this.signin();
/* #endif */
/* #ifdef H5 */
options.query.url && (this.$t.domain = options.query.url);
if (!this.token) {
// tokenuserIdtoken
// token userId
if (!options.query || !options.query.u) {
// u (userId)
this.$t.ui.showToast('缺少用户信息参数');
} else {
const data = await this.getToken(options.query.u);
this.noPhone(data.phone);
}
}
/* #endif */
this.initSocket();
},
computed: {
...mapGetters(['useStorage']),
...mapState('user', ['token']),
},
methods: {
...mapActions('user', ['getToken']),
...mapActions('socket', ['initSocket']),
...mapMutations(['setNetworkConnected', 'setSystemInfo']),
...mapMutations('user', ['setToken', 'setUser']),
// 使
autoUpdate() {
var self = this;
//
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);
}
},
});
}
},
});
}
});
},
downLoadAndUpdate(updateManager) {
wx.showLoading();
//
updateManager.onUpdateReady(function () {
wx.hideLoading();
// applyUpdate
updateManager.applyUpdate();
});
updateManager.onUpdateFailed(function () {
//
wx.showModal({
title: '已经有新版本了哟',
content: '新版本已经上线啦,请您删除当前小程序,重新搜索打开哟',
});
});
},
// store
// 2g 3g ;
checkNetwork() {
uni.getNetworkType({
success: ({ networkType }) => {
this.setNetworkConnected(!(networkType === 'none' || networkType === '2g' || networkType === '3g'));
},
});
//
uni.onNetworkStatusChange(({ isConnected, networkType }) => {
this.setNetworkConnected(isConnected && !(networkType === '2g' || networkType === '3g'));
});
},
//
async signin() {
try {
const data = await this.$u.api.signin();
if (data && data.token) {
this.setUser(data);
this.setToken(data.token);
// this.noPhone(data.phone);
} else {
this.$t.ui.showToast('返回数据异常');
}
} catch (error) {
console.error('error: ', error);
this.$t.ui.showToast(error || '登录失败');
}
},
/**
* 没有手机号 跳转绑定手机号的界面
* @param {string} phone
*/
async noPhone(phone) {
if (!phone) {
// this.$u.route('/pages/get-phone-power/get-phone-power');
this.$u.route('/pages/phone-bind/phone-bind');
}
},
//
getSystemInfo() {
uni.getSystemInfo({
success: result => {
this.setSystemInfo(result);
},
fail: error => {
console.error('getSystemInfo fail:', error);
},
});
},
},
};
</script>
<style lang="scss">
@import './common/styles/tailwind.scss';
@import './common/styles/iconfont.scss';
@import 'uview-ui/index.scss';
@import './common/styles/app.scss';
</style>

67
src/apis/carbasics.js

@ -0,0 +1,67 @@
const apiUrl = process.env.VUE_APP_API_URL;
export const carbasics = `${apiUrl}/carbasics/v4.0`;
const patient = `${carbasics}/patient`; // 患者相关接口
const firstAid = `${carbasics}/firstAid`; // 急救数据相关接口
// 获取急救/出院数据列表
export const querySelf = {
async index(params) {
let timer = null;
timer = setTimeout(() => {
uni.$t.ui.showLoading('正在努力查询...');
timer = null;
}, 10);
try {
const data = await uni.$u.http.post(`${patient}/querySelf`, params);
clearTimeout(timer);
return data;
} catch (error) {
clearTimeout(timer);
throw new Error(error);
}
},
};
/**
* 批量提交急救信息
* @param {object} param 提交的参数
* 提交信息
* @param { Array } codeAndAnswerList code和答案
* @param { String } firstAidId 项目id
* @param { Number } userType 提交人类型0平车 1
*/
export const setRecord = {
async index(params) {
try {
const param = {
...params,
userType: 1,
};
const data = await uni.$u.http.post(`${patient}/aidRecord`, { param });
return data;
} catch (error) {
uni.$t.ui.showToast(error);
throw new Error(error);
}
},
};
const install = (Vue, vm) => {
vm.$u.api = { ...vm.$u.api } || {};
// 存储患者病况信息
vm.$u.api.setRecord = params => setRecord.index(params);
// 获取急救/出院数据列表
vm.$u.api.querySelf = params => querySelf.index(params);
// 患者加入急救
vm.$u.api.joinAid = params => vm.$u.post(`${firstAid}/join`, params);
// 患者退出急救
vm.$u.api.quitAid = params => vm.$u.post(`${firstAid}/quit`, params);
// 删除演示的急救患者数据
vm.$u.api.delDemo = params => vm.$u.post(`${firstAid}/delDemo`, params);
// 删除演示的急救患者数据
vm.$u.api.savePatient = params => vm.$u.post(`${patient}/savePatient`, params);
// 上传身份证图像识别
vm.$u.api.identifyWords = `${carbasics}/ocr/identifyWords`;
};
export default { install };

19
src/apis/project.js

@ -0,0 +1,19 @@
const install = (Vue, vm) => {
vm.$u.api = { ...vm.$u.api } || {};
//根据id获取项目信息
vm.$u.api.findProjectById = param => vm.$u.post(`${uni.$t.domain}/project/findProjectById`, param);
//创建分享连接
vm.$u.api.createShare = param => vm.$u.post(`${uni.$t.domain}/share/create`, param);
//点击分享连接
vm.$u.api.clickShare = param => vm.$u.post(`${uni.$t.domain}/share/click`, param);
//查询医院是否填写了调查问卷
vm.$u.api.queryNotWrite = param => vm.$u.post(`${uni.$t.domain}/questionnaire/queryNotWrite`, param);
// 根据项目id查询当前项目是不是 医院体验项目
vm.$u.api.getByProject = param => vm.$u.post(`${uni.$t.domain}/organization/getByProject`, param);
};
export default { install };

49
src/apis/role.js

@ -0,0 +1,49 @@
const url = process.env.VUE_APP_API_URL;
// 查询首页按钮的名称及跳转
export const queryMustShow = {
async index(params) {
try {
const data = await uni.$u.http.post(`${url}/carbasics/v4.0/role/queryMustShow`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
export const queryLastRoleChoose = {
async index(params) {
try {
const data = await uni.$u.http.post(`${url}/carbasics/v4.0/role/queryLastRoleChoose`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
export const saveRoleRecord = {
async index(params) {
try {
await uni.$u.http.post(`${url}/carbasics/v4.0/role/saveRoleRecord`, params);
} catch (error) {
throw new Error(error);
}
},
};
const install = (Vue, vm) => {
vm.$u.api = { ...vm.$u.api } || {};
// 根据项目id查找所有体验角色
vm.$u.api.queryMustShow = params => queryMustShow.index(params);
// 根据项目id查找上次退出项目时所选择的角色
vm.$u.api.queryLastRoleChoose = params => queryLastRoleChoose.index(params);
// 保存当前用户在当前项目最后一次选择的角色
vm.$u.api.saveRoleRecord = params => saveRoleRecord.index(params);
// 根据项目id查找角色
vm.$u.api.findShowRole = param => vm.$u.post(`${uni.$t.domain}/role/show`, param);
// 根据项目id查找所有成员
vm.$u.api.queryChecker = param => vm.$u.post(`${uni.$t.domain}/deliver/queryChecker`, param);
};
export default { install };

109
src/apis/tall.js

@ -0,0 +1,109 @@
const apiUrl = process.env.VUE_APP_API_URL;
export const tall = `${apiUrl}/tall3/v3.0`;
export const carbasics = `${apiUrl}/carbasics/v4.0`;
import { mp } from '@/config/user.js';
// 登录
export const login = {
async index(params) {
try {
/* #ifdef MP-WEIXIN */
params = await mp();
/* #endif */
const data = await uni.$u.http.post(`${tall}/users/signin`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
// 获取手机号
export const bindPhone = {
async index(params) {
try {
/* #endif */
const data = await uni.$u.http.post(`${tall}/users/bindingNoCode`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
// 绑定手机号(合并或者替换)
export const bindNowPhone = {
async index(params) {
try {
/* #endif */
const data = await uni.$u.http.post(`${tall}/users/merge`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
// 查询首页按钮的名称及跳转
export const getQueryButton = {
async index(params) {
try {
/* #endif */
const data = await uni.$u.http.post(`${carbasics}/questionnaire/queryButton`, params);
return data;
} catch (error) {
return null;
}
},
};
// 查询当前用户是否展示宣传页
export const queryIsShow = {
async index(params) {
try {
/* #endif */
const data = await uni.$u.http.post(`${carbasics}/questionnaire/queryIsShow`, params);
return data;
} catch (error) {
throw new Error(error);
}
},
};
const install = (Vue, vm) => {
vm.$u.api = { ...vm.$u.api } || {};
// 登录
vm.$u.api.signin = params => login.index(params);
// 绑定手机号无需验证码
vm.$u.api.bindPhone = params => bindPhone.index(params);
// 手机号已被注册,是否合并
vm.$u.api.bindNowPhone = params => bindNowPhone.index(params);
// 获取图片验证码
vm.$u.api.getImageCode = () => vm.$u.get(`${tall}/users/code`);
// 获取短信验证码
vm.$u.api.getSmsCode = params => vm.$u.get(`${tall}/users/smscode`, params);
// 根据userId获取token
vm.$u.api.getToken = userId => vm.$u.get(`${tall}/users/userId`, { userId });
// 绑定手机号
vm.$u.api.phoneBind = (phone, smsCode) => vm.$u.http.post(`${tall}/users/binding`, { phone, smsCode });
// 修改用户信息
vm.$u.api.updateUserInfo = params => vm.$u.http.post(`${tall}/users/userInfo`, params);
// 获取项目列表
vm.$u.api.getProjects = (startTime, endTime) => vm.$u.post(`${tall}/project/query`, { startTime, endTime });
// 查询首页的按钮展示及跳转
vm.$u.api.getQueryButton = params => getQueryButton.index(params);
// 查询当前用户是否展示宣传页按钮
vm.$u.api.queryIsShow = params => queryIsShow.index(params);
// 查询日历是否有小红点
vm.$u.api.findRedPoint = (startTime, endTime) => vm.$u.post(`${tall}/project/day`, { startTime, endTime });
// 设置项目顺序
vm.$u.api.setProjectSort = params => vm.$u.post(`${tall}/project/setProjectSort`, params);
// 设置项目父子结构
vm.$u.api.setProjectRelation = params => vm.$u.post(`${tall}/project/setProjectRelation`, params);
// 删除某个项目
vm.$u.api.delProject = projectId => vm.$u.post(`${tall}/project/deleteProject`, { projectId });
};
export default { install };

17
src/apis/task.js

@ -0,0 +1,17 @@
const install = (Vue, vm) => {
vm.$u.api = { ...vm.$u.api } || {};
vm.$u.api.getGlobal = param => vm.$u.post(`${uni.$t.domain}/task/global`, param);
vm.$u.api.getPermanent = param => vm.$u.post(`${uni.$t.domain}/task/permanent`, param);
//根据时间基准点和角色查找定期任务
vm.$u.api.getRegularTask = param => vm.$u.post(`${uni.$t.domain}/task/regular`, param);
//修改任务状态
vm.$u.api.updateTaskType = param => vm.$u.post(`${uni.$t.domain}/task/type`, param);
//新建任务
vm.$u.api.saveTask = param => vm.$u.post(`${uni.$t.domain}/task/save`, param);
//克隆任务
vm.$u.api.cloneTask = param => vm.$u.post(`${uni.$t.domain}/task/clone`, param);
//模糊查询 查找项目下的任务
vm.$u.api.queryTaskOfProject = param => vm.$u.post(`${uni.$t.domain}/task/queryTaskOfProject`, param);
};
export default { install };

7
src/apis/wbs.js

@ -0,0 +1,7 @@
const install = (Vue, vm) => {
vm.$u.api = { ...vm.$u.api } || {};
// 导入wbs
vm.$u.api.import = formData => vm.$t.chooseAndUpload(`${uni.$t.domain}/wbs`, formData);
};
export default { install };

7
src/common/styles/app.scss

@ -0,0 +1,7 @@
.min-0 {
min-width: 0;
}
.flex-shrink-0 {
flex-shrink: 0;
}

257
src/common/styles/iconfont.css

File diff suppressed because one or more lines are too long

257
src/common/styles/iconfont.scss

File diff suppressed because one or more lines are too long

4509
src/common/styles/tailwind.scss

File diff suppressed because it is too large

473
src/components/Calendar/Calendar.vue

@ -0,0 +1,473 @@
<template>
<view class="zzx-calendar">
<view class="calendar-heander">
{{ timeStr }}
</view>
<!-- 星期几标题 -->
<view class="calendar-weeks">
<view class="calendar-week" :class="{ 'text-red-500': week === '六' || week === '日' }" v-for="(week, index) in weeks" :key="index">
{{ week }}
</view>
</view>
<view class="calendar-content">
<swiper
class="calendar-swiper"
:style="{
width: '100%',
height: sheight,
}"
:indicator-dots="false"
:autoplay="false"
:duration="duration"
:current="current"
@change="changeSwp"
:circular="true"
>
<swiper-item class="calendar-item" v-for="sitem in swiper" :key="sitem">
<view class="calendar-days">
<!-- 当前的 -->
<template v-if="sitem === current">
<view
class="calendar-day"
v-for="(item, index) in days"
:key="index"
:class="{ 'day-hidden': !item.show }"
@click="clickItem(item)"
>
<view class="date" :class="[item.isToday ? todayClass : '', item.fullDate === selectedDate ? checkedClass : '']">
{{ item.time.getDate() }}
</view>
<view class="dot-show" v-if="item.info === '0'" :style="dotStyle"> </view>
</view>
</template>
<template v-else>
<!-- 下一个月/ -->
<template v-if="current - sitem === 1 || current - sitem === -2">
<view
class="calendar-day"
v-for="(item, index) in predays"
:key="index"
:class="{
'day-hidden': !item.show,
}"
>
<view class="date" :class="[item.isToday ? todayClass : '']">
{{ item.time.getDate() }}
</view>
</view>
</template>
<!-- 上一个月/ -->
<template v-else>
<view
class="calendar-day"
v-for="(item, index) in nextdays"
:key="index"
:class="{
'day-hidden': !item.show,
}"
>
<view class="date" :class="[item.isToday ? todayClass : '']">
{{ item.time.getDate() }}
</view>
</view>
</template>
</template>
</view>
</swiper-item>
</swiper>
<!-- <view class="mode-change" @click="changeMode">
<view :class="weekMode ? 'mode-arrow-bottom' : 'mode-arrow-top'"> </view>
</view> -->
</view>
<view class="flex justify-center u-font-18" style="color: #3b82f6" @click="goToday"> 今日 </view>
</view>
</template>
<script>
import { mapState } from 'vuex';
import { gegerateDates, formatDate } from './generateDates.js';
export default {
props: {
duration: { type: Number, default: 500 },
//
showBack: { type: Boolean, default: false },
// class
todayClass: { type: String, default: 'is-today' },
// class
checkedClass: { type: String, default: 'is-checked' },
//
dotStyle: {
type: Object,
default: () => {
return { background: '#4ade80' };
},
},
},
watch: {
dotList: function (newvalue) {
const days = this.days.slice(0);
const index = days.findIndex(day => day.show);
days.forEach((day, i) => {
newvalue.forEach((item, j) => {
if (i - index === j) {
day.info = item;
}
});
});
this.days = days;
},
},
data() {
return {
weeks: ['日', '一', '二', '三', '四', '五', '六'], //
current: 1,
currentYear: '',
currentMonth: '',
currentDate: '',
days: [],
weekMode: false, // false -> true ->
swiper: [0, 1, 2],
selectedDate: formatDate(new Date(), 'yyyy-MM-dd'), //
start: '',
end: '',
};
},
computed: {
...mapState('project', ['dotList']),
sheight() {
//
//
let h = '35px';
if (!this.weekMode) {
const d = new Date(this.currentYear, this.currentMonth, 0);
const days = d.getDate(); //
let day = new Date(d.setDate(1)).getDay();
// if (day === 0) {
// day = 7;
// }
const pre = 8 - day;
const rows = Math.ceil((days - pre) / 7) + 1;
h = 35 * rows + 'px';
}
return h;
},
//
timeStr() {
let str = '';
const d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
const y = d.getFullYear();
const m = d.getMonth() + 1 <= 9 ? `0${d.getMonth() + 1}` : d.getMonth() + 1;
str = `${y}${m}`;
return str;
},
// days
predays() {
let pres = [];
if (this.weekMode) {
//
const d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
d.setDate(d.getDate() - 7);
pres = gegerateDates(d, 'week');
} else {
//
const d = new Date(this.currentYear, this.currentMonth - 2, 1);
pres = gegerateDates(d, 'month');
}
return pres;
},
// days
nextdays() {
let nexts = [];
if (this.weekMode) {
//
const d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
d.setDate(d.getDate() + 7);
nexts = gegerateDates(d, 'week');
} else {
//
const d = new Date(this.currentYear, this.currentMonth, 1);
nexts = gegerateDates(d, 'month');
}
return nexts;
},
},
created() {
this.initDate();
this.start = this.$moment().startOf('month').valueOf();
this.end = this.$moment().endOf('month').valueOf();
},
methods: {
//
/**
* 滑动切换上下周期
* 根据前一个减去目前的值我们可以判断是下一个月/周还是上一个月/
* current - pre === 1, -2 下一个月/
* current - pre === -1, 2 上一个月或者上一周
*/
changeSwp(e) {
const pre = this.current;
const current = e.target.current;
this.current = current;
if (current - pre === 1 || current - pre === -2) {
//
this.daysNext();
const arr = this.days.filter(s => s.show);
const end = `${arr[arr.length - 1].fullDate} 23:59:59`;
this.start = this.$moment(arr[0].fullDate).valueOf();
this.end = this.$moment(end).valueOf();
this.$emit('handleFindPoint', this.start, this.end);
} else {
//
this.daysPre();
const arr = this.days.filter(s => s.show);
const end = `${arr[arr.length - 1].fullDate} 23:59:59`;
this.start = this.$moment(arr[0].fullDate).valueOf();
this.end = this.$moment(end).valueOf();
this.$emit('handleFindPoint', this.start, this.end);
}
},
//
initDate(cur) {
let date = '';
if (cur) {
date = new Date(cur);
} else {
date = new Date();
}
this.currentDate = date.getDate(); //
this.currentYear = date.getFullYear(); //
this.currentMonth = date.getMonth() + 1; //
this.currentWeek = date.getDay() === 0 ? 7 : date.getDay(); // 1...6,0
// const nowY = new Date().getFullYear(); //
// const nowM = new Date().getMonth() + 1;
// const nowD = new Date().getDate(); //
// const nowW = new Date().getDay();
// this.selectedDate = formatDate(new Date(), 'yyyy-MM-dd')
this.days = [];
let days = [];
if (this.weekMode) {
days = gegerateDates(date, 'week');
// this.selectedDate = days[0].fullDate;
} else {
days = gegerateDates(date, 'month');
// const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/'));
// const isMonth = sel.getFullYear() === this.currentYear && (sel.getMonth() + 1) === this.currentMonth;
// if(!isMonth) {
// this.selectedDate = formatDate(new Date(this.currentYear, this.currentMonth-1,1), 'yyyy-MM-dd')
// }
}
//
days.forEach((day, i) => {
this.dotList.forEach((item, j) => {
if (i === j) {
day.info = item;
}
});
});
this.days = days;
// ,
let obj = {
start: '',
end: '',
};
if (this.weekMode) {
obj.start = this.days[0].time;
obj.end = this.days[6].time;
} else {
const start = new Date(this.currentYear, this.currentMonth - 1, 1);
const end = new Date(this.currentYear, this.currentMonth, 0);
obj.start = start;
obj.end = end;
}
this.$emit('days-change', obj);
},
//
daysPre() {
if (this.weekMode) {
const d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
d.setDate(d.getDate() - 7);
this.initDate(d);
} else {
const d = new Date(this.currentYear, this.currentMonth - 2, 1);
this.initDate(d);
}
},
//
daysNext() {
if (this.weekMode) {
const d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
d.setDate(d.getDate() + 7);
this.initDate(d);
} else {
const d = new Date(this.currentYear, this.currentMonth, 1);
this.initDate(d);
}
},
//
changeMode() {
const premode = this.weekMode;
let isweek = false;
if (premode) {
isweek = !!this.days.find(item => item.fullDate === this.selectedDate);
}
this.weekMode = !this.weekMode;
let d = new Date(this.currentYear, this.currentMonth - 1, this.currentDate);
const sel = new Date(this.selectedDate.replace('-', '/').replace('-', '/'));
const isMonth = sel.getFullYear() === this.currentYear && sel.getMonth() + 1 === this.currentMonth;
if ((this.selectedDate && isMonth) || isweek) {
d = new Date(this.selectedDate.replace('-', '/').replace('-', '/'));
}
this.initDate(d);
},
//
clickItem(e) {
this.selectedDate = e.fullDate;
this.$emit('selected-change', e);
},
//
goToday() {
const d = new Date();
this.initDate(d);
},
},
};
</script>
<style lang="scss" scoped>
.zzx-calendar {
width: 100%;
height: auto;
background-color: #fff;
padding-bottom: 10px;
.calendar-heander {
text-align: center;
padding: 16px 0;
position: relative;
font-size: 15px;
}
.calendar-weeks {
width: 100%;
display: flex;
flex-flow: row nowrap;
margin-bottom: 10px;
justify-content: center;
align-items: center;
font-size: 12px;
color: #9ca3af;
font-weight: bold;
.calendar-week {
width: calc(100% / 7);
height: 100%;
text-align: center;
}
}
swiper {
width: 100%;
height: 60upx;
}
.calendar-content {
min-height: 30px;
}
.calendar-swiper {
min-height: 35px;
transition: height ease-out 0.3s;
}
.calendar-item {
margin: 0;
padding: 0;
height: 100%;
}
.calendar-days {
display: flex;
flex-flow: row wrap;
width: 100%;
height: 100%;
overflow: hidden;
font-size: 14px;
.calendar-day {
width: calc(100% / 7);
height: 35px;
text-align: center;
display: flex;
flex-flow: column nowrap;
justify-content: flex-start;
align-items: center;
position: relative;
}
}
.day-hidden {
visibility: hidden;
}
.mode-change {
display: flex;
justify-content: center;
margin-top: 5px;
.mode-arrow-top {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 5px solid #ff6633;
}
.mode-arrow-bottom {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 5px solid #ff6633;
}
}
.is-today {
background: #ffffff;
border: 1upx solid #ff6633;
border-radius: 50%;
color: #ff6633;
}
.is-checked {
background: #ff6633;
color: #ffffff;
}
.date {
width: 25px;
height: 25px;
line-height: 25px;
margin: 0 auto;
border-radius: 25px;
}
.dot-show {
width: 6px;
height: 6px;
// background: red;
border-radius: 5px;
position: absolute;
top: 2px;
right: 10px;
}
}
</style>

136
src/components/Calendar/generateDates.js

@ -0,0 +1,136 @@
/*
*此函数的作用是根据传入的一个日期返回这一周的日期或者这一个月的日期
* 如果是月的话注意还包含上个月和下个月的日期月的话总共数据有 6 * 7 = 42
*
*/
/*
* 时间格式化函数
* 重要提示微信小程序new Date('2020-04-16')在ios中无法获取时间对象
* 解决方式: 建议将时间都格式化成'2020/04/16 00:00:00'的格式
* 函数示例: formatDate(new Date(), 'YYYY/MM/dd hh:mm:ss')
*/
export const formatDate = (date, fmt) => {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : padLeftZero(str));
}
}
return fmt;
};
const padLeftZero = str => {
return ('00' + str).substr(str.length);
};
// 判断是不是date对象
export const judgeType = s => {
// 函数返回数据的具体类型
return Object.prototype.toString.call(s).slice(8, -1);
};
export const equalDate = (d1, d2) => {
let result = false;
if (d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate()) {
result = true;
}
return result;
};
/* ,2020-04-04
*/
export const dateEqual = (before, after) => {
before = new Date(before.replace('-', '/').replace('-', '/'));
after = new Date(after.replace('-', '/').replace('-', '/'));
if (before.getTime() - after.getTime() === 0) {
return true;
} else {
return false;
}
};
export const gegerateDates = (date = new Date(), type = 'week') => {
const result = [];
if (judgeType(date) === 'Date') {
// 年,月,日
const y = date.getFullYear();
const m = date.getMonth();
const d = date.getDate();
const days = new Date(y, m + 1, 0).getDate();
// 获取日期是星期几
// let weekIndex = date.getDay() === 0 ? 7 : date.getDay();
let weekIndex = date.getDay();
if (type === 'month') {
const dobj = new Date(y, m, 1);
// weekIndex = dobj.getDay() === 0 ? 7 : dobj.getDay();
weekIndex = dobj.getDay();
}
if (type === 'week') {
for (let i = weekIndex; i > 0; i--) {
const dtemp = new Date(y, m, d);
dtemp.setDate(dtemp.getDate() - i);
result.push({
time: dtemp,
show: true,
fullDate: formatDate(dtemp, 'yyyy-MM-dd'),
isToday: equalDate(new Date(), dtemp),
});
}
for (let i = 0; i <= 7 - weekIndex; i++) {
const dtemp = new Date(y, m, d);
dtemp.setDate(dtemp.getDate() + i);
result.push({
time: dtemp,
show: true,
fullDate: formatDate(dtemp, 'yyyy-MM-dd'),
isToday: equalDate(new Date(), dtemp),
});
}
} else if (type === 'month') {
// 上个月
for (let i = weekIndex; i > 0; i--) {
const dtemp = new Date(y, m, 1);
dtemp.setDate(dtemp.getDate() - i);
result.push({
time: dtemp,
show: false,
fullDate: formatDate(dtemp, 'yyyy-MM-dd'),
isToday: equalDate(new Date(), dtemp),
});
}
// 这个月的日期
for (let i = 0; i < days; i++) {
const dtemp = new Date(y, m, 1);
dtemp.setDate(dtemp.getDate() + i);
result.push({
time: dtemp,
show: true,
fullDate: formatDate(dtemp, 'yyyy-MM-dd'),
isToday: equalDate(new Date(), dtemp),
});
}
const len = 42 - result.length;
// 下个月的日期
for (let i = 1; i <= len; i++) {
const dtemp = new Date(y, m + 1, 0);
dtemp.setDate(dtemp.getDate() + i);
result.push({
time: dtemp,
show: false,
fullDate: formatDate(dtemp, 'yyyy-MM-dd'),
isToday: equalDate(new Date(), dtemp),
});
}
}
}
return result;
};

86
src/components/Globals/Globals.vue

@ -0,0 +1,86 @@
<template>
<view :class="organData && organData.organizationType === 0 ? 'organ-box' : ''" v-if="globals && globals.length">
<u-subsection :list="globalTabs" :current="current" @change="change"></u-subsection>
</view>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
name: 'Global',
data() {
return {
// loading: true,
task: null,
current: 0,
global: [],
globalTabs: [],
showLine: false,
};
},
watch: {},
computed: {
...mapState('project', ['organData']),
...mapGetters('task', ['globals']),
},
created() {
var obj = JSON.parse(this.globals[0].plugins[0][0].param);
this.setGlobalData(obj);
this.global[0] = this.globals[0];
this.globalTabs = [...this.globals];
},
methods: {
...mapMutations('carbasics', ['setGlobalData']),
change(e) {
console.log('e: ', e);
this.current = e;
if (e !== this.globals.length) {
this.global[0] = this.globals[e];
var obj = JSON.parse(this.globals[e].plugins[0][0].param);
this.setGlobalData(obj);
this.$emit('changeShowLine', false);
this.showLine = false;
} else {
this.$emit('changeShowLine', true);
this.showLine = true;
}
},
},
};
</script>
<style scoped lang="scss">
.organ-box {
position: fixed;
height: 100vh;
width: 750rpx;
left: 0;
top: 0;
z-index: 999;
}
.u-card__body {
padding: 16px 0;
}
.u-card-wrap {
background-color: $u-bg-color;
padding: 1px;
}
.u-body-item {
font-size: 32rpx;
color: #333;
padding: 20rpx 10rpx;
}
.u-body-item image {
width: 120rpx;
flex: 0 0 120rpx;
height: 120rpx;
border-radius: 8rpx;
margin-left: 12rpx;
}
</style>

37
src/components/ImageCode/ImageCode.vue

@ -0,0 +1,37 @@
<template>
<!-- TODO: 设置默认图片 -->
<image :src="src" mode="aspectFit" class="image" @click="getCode"></image>
</template>
<script>
export default {
data() {
return { src: '' };
},
created() {
this.getCode();
},
methods: {
//
async getCode() {
try {
const data = await this.$u.api.getImageCode();
const { imageBase64, verificationCodeId } = data;
this.src = imageBase64;
this.$emit('on-code', verificationCodeId);
} catch (error) {
console.error('error: ', error);
}
},
},
};
</script>
<style lang="scss" scoped>
.image {
width: 100px !important;
height: 35px !important;
}
</style>

467
src/components/PatientList/PatientList.vue

@ -0,0 +1,467 @@
<template>
<div class="flex flex-1 flex-col patient-list">
<div class="title flex justify-between items-center">
<u-search
style="width: 500rpx"
v-model="name"
shape="round"
action-text=" "
placeholder="请输入患者"
@search="getData(true)"
@blur="searchBlur"
></u-search>
<select-lay
:zindex="99"
:showplaceholder="false"
:value="tval"
name="name"
placeholder="请选择类型"
:options="provinces"
@selectitem="changeSelect"
>
</select-lay>
</div>
<u-modal
v-model="show"
:closeOnClickOverlay="true"
title="提示"
content="是否确定删除当前患者信息?"
show-cancel-button
confirm-text="删除"
@confirm="confirmDel"
></u-modal>
<u-modal
v-model="show1"
:closeOnClickOverlay="true"
title="提示"
content="退出当前病例后,其他医生可接诊。确定要退出吗?"
show-cancel-button
confirm-text="退出"
@confirm="confirmBack"
></u-modal>
<u-toast ref="uToast" />
<scroll-view scroll-y="true" :style="{ height: height }" :lower-threshold="50" scroll-with-animation @scrolltolower="scrolltolower">
<div v-if="patientList.length" class="white list-box w-full flex-1 flex-col">
<div class="patients flex flex-row">
<div>{{ tval === 0 ? '急救患者' : '出院患者' }}</div>
<div class="ml-2 card-total flex items-center justify-center">{{ total }}</div>
</div>
<div
v-for="(card, cardIndex) in patientList"
:key="cardIndex"
class="mb-4 w-full card-box flex flex-row flex-nowrap"
style="position: relative"
@mousedown="start"
@touchstart="start"
@mousemove="move($event, cardIndex)"
@touchmove="move($event, cardIndex)"
@mouseup="end"
@touchend="end"
>
<div
@longpress="longpress(card.firstAidId)"
@click="changeTimeLine(card.recordUserId, card.firstAidId, card.caseType)"
style="height: 100%; width: 100%; position: absolute; top: 0; left: 0; z-index: 10"
></div>
<div :style="{ 'margin-left': outIndex === cardIndex && card.recordUserId - 0 ? '-68px' : '' }" class="w-full anima">
<div class="crad-content">
<div class="status-wait" v-if="!(card.recordUserId - 0)"></div>
<div class="flex mb-2">
<div class="pr-2 fw-bold">
<span v-if="card.name">
{{ card.name }}
</span>
<span v-else> 无名氏 </span>
</div>
<div class="pr-2 fw-bold">
<span v-if="card.gender === 0"></span>
<span v-else-if="card.gender === 1"></span>
<span v-else>-</span>
</div>
<div class="pr-2 fw-bold">
<span v-if="card.age || card.age === 0">{{ card.age }}</span>
<span v-else>-</span>
</div>
<u-tag style="z-index: 99; margin-right: 0" plain shape="circle" v-if="card.isJmrs - 0 === 1" text="静脉溶栓"> </u-tag>
<u-tag
class="ml-2 flex items-center justify-center"
type="success"
plain
shape="circle"
v-if="card.isXgzl - 0 === 1"
text="血管内治疗"
>
</u-tag>
</div>
<div class="flex">
<div>{{ tval === 0 ? '到院时间:' : '出院时间:' }}</div>
<div v-if="card.record2" style="color: rgba(0, 0, 0, 0.85)">
{{ $moment(+card.record2).format('YYYY-MM-DD HH:mm') }}
</div>
</div>
</div>
<img src="./icon/yanshi.png" class="img-box" v-if="card.demonstrate === 1" />
</div>
<div
:style="{
width: outIndex === cardIndex && card.recordUserId - 0 ? '68px' : 0,
color: outIndex === cardIndex && card.recordUserId - 0 ? 'rgba(255,255,255,1)' : 'rgba(255,0,0,0)',
}"
class="sign-out flex items-center justify-center anima"
@click="quitTips(card.firstAidId)"
>
退出
</div>
</div>
</div>
</scroll-view>
<div class="add-patient" @click="jump">
<u-icon color="#fff" size="40" name="plus"></u-icon>
</div>
</div>
</template>
<script>
import { mapState, mapMutations, mapGetters } from 'vuex';
export default {
props: {},
data: () => ({
height: '',
name: '', //
provinces: [
{
label: '急救患者',
value: 0,
},
{
label: '出院患者',
value: 1,
},
],
tval: 0, // :
pageNum: 1, //
patientList: [], //
total: '', //
role: '',
show: false, // modal
show1: false, // 退modal
delFirstAidId: '', // id
firstAidId: '',
quitId: '', // 退id
startX: 0, //
outIndex: -1, // card退
isMove: false, //
}),
computed: {
...mapState('carbasics', ['detailsData', 'globalData', 'refreshList']),
...mapGetters('project', ['projectId']),
},
mounted() {
const system = uni.getSystemInfoSync();
this.height = system.windowHeight - 33 - 38 - 52 + 'px';
console.log('this.height: ', this.height);
},
created() {
this.getData(true);
},
methods: {
...mapMutations('carbasics', ['setFirstAidId']),
jump() {
uni.navigateTo({ url: `/pages/establish/establish?role=${this.role}` });
},
//
start(event) {
if (event.touches) {
this.startX = event.touches[0].clientX;
// console.log(event.touches[0].clientX);
}
this.isMove = true;
},
//
end() {
this.isMove = false;
},
// box
move(event, index) {
if (this.isMove) {
if (this.outIndex === index) {
this.outIndex = -1;
} else {
if (event.touches && (event.touches[0].clientX - this.startX > 10 || event.touches[0].clientX - this.startX < -10)) {
this.outIndex = index;
}
}
}
this.isMove = false;
},
// 退
quitTips(id) {
console.log('id: ', id);
this.quitId = id;
this.show1 = true;
},
// 退
async confirmBack() {
try {
const param = { firstAidId: this.quitId, projectId: this.projectId };
const res = await uni.$u.api.quitAid(param);
console.log('res: ', res);
this.getData(true);
this.visible1 = false;
} catch (error) {
this.$refs.uToast.show({
title: '退出失败',
type: 'error',
});
}
this.outIndex = -1;
},
// card,
// ,
// ,
changeTimeLine(recordUserId, firstAidId, caseType) {
try {
if (!(recordUserId - 0)) {
this.changeShow(firstAidId);
} else {
this.setFirstAidId(firstAidId);
// this.$router.push(`/patient-line`);
uni.navigateTo({ url: `/pages/patientLine/patientLine?caseType=${caseType}` });
}
} catch (error) {
this.$refs.uToast.show({
title: '切换失败',
type: 'error',
});
0;
}
},
// ,,
changeShow(id) {
if (this.role === 'YiSheng') {
// id,
// ,线,线,/,
this.firstAidId = id;
this.handleOk(this.detailsData.type);
// this.visible = true; // /
}
},
//
async handleOk(type) {
try {
const { projectId, firstAidId } = this;
const param = {
projectId,
firstAidId,
type,
};
const res = await uni.$u.api.joinAid(param);
console.log('res: ', res);
if (res) {
this.$refs.uToast.show({
title: '加入成功',
type: 'success',
});
const codeAndAnswerList = [
{
questionCode: 'JKJY-SFXJ',
answer: ['是'],
},
{
questionCode: 'JKJY-XJFS',
answer: ['集体病区教育'],
},
{
questionCode: 'YJJL-CTendTime-outSide',
answer: ['否'],
},
];
const setParam = {
codeAndAnswerList,
firstAidId,
};
uni.$u.api.setRecord(setParam);
this.getData(true);
} else {
this.$refs.uToast.show({
title: '加入失败',
type: 'error',
});
}
} catch (error) {
this.$refs.uToast.show({
title: error,
type: 'error',
});
}
},
//
longpress(delFirstAidId) {
this.delFirstAidId = delFirstAidId;
this.show = true;
},
async confirmDel() {
try {
const param = { firstAidId: this.delFirstAidId };
const res = await this.$u.api.delDemo(param);
console.log('res: ', res);
this.getData(true);
} catch (error) {
console.log('error: ', error);
}
},
//
async getData(status) {
try {
// status ,,
if (status) {
this.pageNum = 1;
this.patientList = [];
}
const params = {
param: {
firstAidType: this.tval,
name: this.name,
type: this.globalData.powerType,
projectId: this.projectId,
caseType: this.globalData.createAuth - 0 === 0 ? 1 : 0,
pageNum: this.pageNum,
pageSize: 10,
},
};
const data = await this.$u.api.querySelf(params);
if (data.page.list.length) {
if (status) {
this.patientList = data.page.list;
} else {
this.patientList = this.patientList.concat(data.page.list);
}
this.total = data.page.total;
this.role = data.role;
} else {
this.$t.ui.showToast('没有更多数据');
}
} catch (error) {
console.log('error: ', error);
}
},
// ,
changeSelect(e) {
this.tval = this.provinces[e].value;
this.getData(true);
},
searchBlur() {
setTimeout(() => this.getData(true), 10);
},
// scroll-view,
scrolltolower() {
this.pageNum++;
this.getData(false);
},
},
watch: {
refreshList() {
setTimeout(() => {
this.getData(true);
}, 300);
},
},
// --
beforeUpdate() {},
// --
updated() {},
// --()
activated() {},
// --()
deactivated() {},
// --
beforeDestroy() {},
};
</script>
<style lang="scss" scoped>
.add-patient {
z-index: 10;
border-radius: 50%;
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
background-color: #0284c7;
position: fixed;
bottom: 100px;
right: 16px;
}
.patient-list {
background-color: #fff;
}
.title {
height: 52px;
width: 750rpx;
padding: 8px 16px;
// position: fixed;
// top: 32px;
background: #fff;
box-shadow: 0 0 4px 4px #ccc;
}
.card-box {
/* box-shadow: 0 0 5px #ccc; */
border: 2px solid #ccc;
margin: 16px 0;
border-radius: 8px;
overflow: hidden;
}
.anima {
transition: all 0.5s;
position: relative;
}
.crad-content {
font-size: 14px;
color: #414141;
padding: 12px;
}
.status-wait {
position: absolute;
right: 6px;
top: 6px;
height: 14px;
width: 14px;
border-radius: 50%;
background: #ff8f00;
box-shadow: 0 0 5px #ff8f00;
}
.fw-bold {
font-size: 16px;
font-weight: bold;
}
.sign-out {
background-color: red;
font-size: 14px;
color: #fff;
z-index: 10;
}
.list-box {
padding: 16px;
}
.patients {
font-size: 16px;
font-weight: bold;
padding: 10px 0;
}
.card-total {
padding: 0 8px;
border-radius: 10px;
background-color: #009dff;
color: white;
}
.img-box {
position: absolute;
top: 14px;
width: 44px;
height: 44px;
right: 16px;
}
</style>

BIN
src/components/PatientList/icon/yanshi.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

520
src/components/PrettyExchange/PrettyExchange.vue

@ -0,0 +1,520 @@
<template>
<view>
<scroll-view scroll-y="true">
<view v-if="!changeEvent">
<view :id="'cu-' + index" :key="item.id" class="cu-item flex-col" v-for="(item, index) in itemList">
<ProjectItem
class="w-full"
:index="index"
:item="item"
:menuList="menuList"
@chooseAction="chooseAction"
@openSubProject="openSubProject"
/>
</view>
</view>
<view v-else>
<view
:id="'cu-' + index"
:key="index"
:style="{ 'background-color': item.color }"
@touchend="stops($event, index)"
@touchmove.stop.prevent="move"
@touchstart="start($event, index)"
class="cu-item flex-col"
v-for="(item, index) in itemList"
>
<view class="border-100 bg-blue-500" v-if="item.showTopBorder"></view>
<!-- 内容区 -->
<!-- 父项目 -->
<view class="w-full">
<view class="flex items-center justify-between p-3">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<view class="flex-1 px-3">
<view class="flex items-center mb-1">
<view class="mr-2">{{ item.name }}</view>
<!-- 状态 TODO:-->
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view>
</view>
<view class="flex items-center text-xs text-gray-400">
<view class="pr-2">{{ $moment(+item.startTime).format('MM-DD HH:mm') }}</view>
<view class="pl-2">{{ $moment(+item.endTime).format('MM-DD HH:mm') }}</view>
</view>
</view>
<!-- 箭头 -->
<view v-if="item.sonProjectList && item.sonProjectList.length">
<u-icon
@click="openSubProject(item.sonProjectList.length, index)"
class="text-gray-400"
name="arrow-up"
size="14px"
v-if="item.show"
></u-icon>
<u-icon
@click="openSubProject(item.sonProjectList.length, index)"
class="text-gray-400"
name="arrow-down"
size="14px"
v-else
></u-icon>
</view>
<u-icon class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon>
</view>
<!-- 父项目 end -->
<!-- 子项目 -->
<view class="ml-8" v-if="item.show">
<view
:id="'cu-' + index + '-' + subIndex"
:key="subIndex"
@touchend.stop.prevent="stops($event, index + '-' + subIndex, item.sonProjectList.length)"
@touchmove.stop.prevent="move($event, item.sonProjectList.length)"
@touchstart.stop.prevent="start($event, index + '-' + subIndex)"
class="cu-item flex-col"
v-for="(subItem, subIndex) in item.sonProjectList"
>
<view class="flex items-center justify-between p-3 w-full">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<view class="flex-1 px-3">
<view class="flex items-center">
<view class="mr-2">{{ subItem.name }}</view>
<!-- 状态 -->
<view
:class="
subItem.status === 0
? 'text-blue-400 bg-blue-100'
: subItem.status === 1
? 'text-green-400 bg-green-100'
: subItem.status === 2
? 'text-red-400 bg-red-100'
: 'text-gray-400 bg-gray-100'
"
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0"
>
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }}
</view>
</view>
</view>
<!-- 箭头 -->
<u-icon class="text-gray-400" name="arrow-right" size="14px"></u-icon>
</view>
</view>
</view>
</view>
<!-- 内容区 end -->
<view class="border-100 bg-blue-500" v-if="item.showBorder"></view>
<view class="border-80 bg-blue-500" v-if="item.showSubBorder"></view>
</view>
</view>
<view class="edition">v1.7.1</view>
</scroll-view>
<!-- 移动悬浮 begin -->
<view v-if="showMoveImage">
<view :style="{ left: moveLeft + 'px', top: moveTop + 'px' }" class="cu-item absolute">
<ProjectItem class="w-full" :item="moveItem" />
</view>
</view>
<!-- 移动悬浮 end -->
</view>
</template>
<script>
import ProjectItem from '../Projects/ProjectItem.vue';
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
components: { ProjectItem },
name: 'exchange',
model: { prop: 'showPop', event: 'change' },
data() {
return {
itemTop: 0,
itemLeft: 0,
itemHeight: 0, //
subItemHeight: 0, //
itemWidth: 0, //
showMoveImage: false,
moveItem: '',
moveLeft: 0,
moveTop: 0,
deltaLeft: 0,
deltaTop: 0,
beginleft: 0,
begintop: 0,
itemList: [],
setSubItem: false,
changeEvent: false,
showMenu: false,
tips: {
text: '',
color: '#909399',
fontSize: 28,
},
menuList: [{ text: '复制' }, { text: '编辑' }, { text: '删除' }, { text: '置顶' }, { text: '排序' }],
// show: false,
border: 'border border-blue-500 shadow rounded-md',
showBorder: false,
showItemIndex: undefined,
};
},
computed: {
...mapGetters('user', ['userId']),
...mapState('project', ['projects']),
eventName() {
if (this.changeEvent) {
return 'move.stop.prevent';
} else {
return 'move';
}
},
},
mounted() {
this.$nextTick(function () {
this.itemList = this.projects;
this.itemList.forEach(item => {
item.showBorder = false;
item.showSubBorder = false;
item.showTopBorder = false;
});
});
},
watch: {
projects(val) {
this.itemList = val;
this.itemList.forEach(item => {
item.showBorder = false;
item.showSubBorder = false;
item.showTopBorder = false;
});
},
},
methods: {
...mapMutations('project', ['setProjectItemShow', 'setProjects']),
//
openSubProject(length, index) {
this.setProjectItemShow({ index, show: this.itemList[index].show ? false : true });
if (length && index) {
this.$emit('changeHeight', length, index);
}
this.showItemIndex = index;
},
//
getDate() {
const query = uni.createSelectorQuery().in(this);
query
.select(`#cu-0`)
.boundingClientRect(data => {
console.log('data: ', data);
this.begintop = data.top;
this.beginleft = data.left;
})
.exec();
},
//
chooseAction(obj) {
let action = this.menuList[obj.index].text;
if (action === '排序') {
this.changeEvent = true;
this.$t.ui.showToast('请移动进行排序');
}
if (action === '删除') {
this.changeEvent = false;
this.delProject(obj.projectId);
}
if (this.showItemIndex !== undefined) {
this.setProjectItemShow({ index: this.showItemIndex, show: true });
}
},
isNumber(val) {
return val === +val;
},
start(e, index) {
console.log('开始', e);
setTimeout(() => {
this.getDate();
}, 300);
if (this.isNumber(index)) {
this.setSubItem = false;
const query = uni.createSelectorQuery().in(this);
query
.select(`#cu-${index}`)
.boundingClientRect(data => {
this.moveTop = data.top;
this.moveLeft = data.left;
this.moveItem = this.itemList[index];
this.itemWidth = data.width;
this.itemHeight = data.height;
})
.exec();
} else {
let arr = index.split('-');
this.setSubItem = true;
const query = uni.createSelectorQuery().in(this);
query
.select(`#cu-${arr[0] - 0}`)
.boundingClientRect(data => {
this.itemHeight = data.height;
})
.exec();
query
.select(`#cu-${index}`)
.boundingClientRect(data => {
this.moveTop = data.top;
this.moveLeft = data.left;
this.moveItem = this.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
this.itemWidth = data.width;
this.subItemHeight = data.height;
})
.exec();
}
},
move(e, length) {
console.log('移动');
this.showMoveImage = true; //
const touch = e.touches[0];
if (this.deltaLeft == 0) {
//
this.deltaLeft = touch.pageX - this.moveLeft;
this.deltaTop = touch.pageY - this.moveTop;
}
this.moveLeft = touch.pageX - this.deltaLeft;
this.moveTop = touch.pageY - this.deltaTop;
let lastIndex = (lastIndex = this.findOverIndex(touch.pageY, length));
// 线
for (let i = 0; i < this.itemList.length; i++) {
if (this.moveLeft > 35) {
this.itemList[i].showBorder = false;
this.itemList[i].showTopBorder = false;
if (i === lastIndex) {
this.itemList[i].showSubBorder = true;
} else {
this.itemList[i].showSubBorder = false;
}
} else {
if (lastIndex === -1) {
this.itemList[0].showTopBorder = true;
this.itemList[i].showSubBorder = false;
this.itemList[i].showBorder = false;
} else {
this.itemList[i].showSubBorder = false;
this.itemList[i].showTopBorder = false;
if (i === lastIndex) {
this.itemList[i].showBorder = true;
} else {
this.itemList[i].showBorder = false;
}
}
}
}
},
stops(e, index, length) {
console.log('结束');
const touch = e.mp.changedTouches[0];
let lastIndex = (lastIndex = this.findOverIndex(touch.pageY, length));
//
for (let i = 0; i < this.itemList.length; i++) {
//
if (this.itemList[i].showTopBorder) {
if (this.isNumber(index)) {
let Value = this.itemList[index];
this.itemList.unshift(Value);
this.itemList.splice(index + 1, 1);
} else {
let arr = index.split('-');
let Value = this.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
this.itemList.unshift(Value);
this.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1);
const options = {
id: Value.id,
parentId: 0,
};
this.$emit('change', options);
}
//
this.clearSet(i);
this.$emit('change', this.itemList);
return;
}
//
if (this.itemList[i].showBorder) {
if (this.isNumber(index)) {
let Value = this.itemList[index];
this.itemList.splice(i + 1, 0, Value);
if (i < index) {
this.itemList.splice(index + 1, 1);
} else {
this.itemList.splice(index, 1);
}
} else {
let arr = index.split('-');
let Value = this.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
this.itemList.splice(i + 1, 0, Value);
this.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1);
const options = {
id: Value.id,
parentId: 0,
};
this.$emit('change', options);
}
//
this.clearSet(i);
this.$emit('change', this.itemList);
return;
}
//
if (this.itemList[i].showSubBorder) {
if (this.isNumber(index)) {
let Value = this.itemList[index];
if (this.itemList[lastIndex - 1].sonProjectList && this.itemList[lastIndex - 1].sonProjectList.length) {
this.itemList[lastIndex - 1].sonProjectList.push(Value);
} else {
this.itemList[lastIndex].sonProjectList = [Value];
}
this.itemList.splice(index, 1);
//
this.clearSet(i);
const options = {
id: Value.id,
parentId: this.itemList[lastIndex - 1].id,
};
this.$emit('change', options);
} else {
let arr = index.split('-');
let Value = this.itemList[arr[0] - 0].sonProjectList[arr[1] - 0];
if (this.itemList[lastIndex].sonProjectList && this.itemList[lastIndex].sonProjectList.length) {
this.itemList[lastIndex].sonProjectList.push(Value);
} else {
this.itemList[lastIndex].sonProjectList = [Value];
}
this.itemList[arr[0] - 0].sonProjectList.splice([arr[1] - 0], 1);
//
this.clearSet(i);
const options = {
id: Value.id,
parentId: this.itemList[lastIndex].id,
};
this.$emit('change', options);
const options1 = {
id: Value.id,
parentId: 0,
};
this.$emit('change', options1);
}
return;
}
}
},
//
clearSet(i) {
this.itemList[i].showBorder = false;
this.itemList[i].showSubBorder = false;
this.itemList[i].showTopBorder = false;
this.deltaLeft == 0;
this.showMoveImage = false;
this.setSubItem = false;
this.changeEvent = false;
this.showItemIndex = undefined;
},
//
findOverIndex(posY) {
//
let leng = this.itemList.length * this.itemHeight; //
if (posY < this.begintop) {
return -1;
}
for (var i = 0; i < this.itemList.length; i++) {
let begin = this.itemHeight * i + this.begintop;
let end = this.itemHeight * i + this.begintop + this.itemHeight;
if (begin <= posY && end >= posY) {
return i;
}
}
if (posY > leng) {
//
return this.itemList.length - 1;
} else if (posY < this.begintop) {
return 0;
}
},
//
delProject(id) {
uni.showModal({
title: '',
content: '是否删除项目?',
showCancel: true,
success: async ({ confirm }) => {
if (confirm) {
await this.$u.api.delProject(id);
let flag_index = 0;
this.itemList.forEach((item, index) => {
if (item.id == id) {
flag_index = index;
}
});
this.itemList.splice(flag_index, 1);
this.setProjects(this.itemList);
}
},
});
},
},
};
</script>
<style lang="scss" scoped>
.edition {
position: fixed;
width: 200px;
text-align: center;
left: 50%;
margin-left: -100px;
bottom: 12px;
font-size: 12px;
color: #ccc;
}
.cu-item {
width: 100%;
display: flex;
align-items: center;
font-size: 14px;
}
.border-100 {
width: 92%;
height: 4rpx;
}
.border-80 {
width: 84%;
height: 2px;
margin-left: 30px;
}
</style>

161
src/components/Projects/ProjectItem.vue

@ -0,0 +1,161 @@
<template>
<view class="w-full">
<!-- 有子项目 -->
<view class="flex items-center justify-between p-3">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<!-- <u-icon @click="openMenu(item)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> -->
<view @click="openProject(item)" class="flex-1 px-3">
<view class="flex items-center mb-1">
<view class="mr-2">{{ item.name }}</view>
<!-- 状态 TODO:-->
<view class="px-2 text-xs text-green-400 bg-green-100 rounded-full flex-shrink-0">进行中</view>
</view>
<view class="flex items-center text-xs text-gray-400">
<view class="pr-2">{{ $moment(+item.startTime).format('MM-DD HH:mm') }}</view>
<view class="pl-2">{{ $moment(+item.endTime).format('MM-DD HH:mm') }}</view>
</view>
</view>
<!-- 箭头 -->
<view v-if="item.sonProjectList && item.sonProjectList.length">
<u-icon
@click="$emit('openSubProject', item.sonProjectList.length, index)"
class="text-gray-400"
name="arrow-up"
size="14px"
v-if="item.show"
></u-icon>
<u-icon
@click="$emit('openSubProject', item.sonProjectList.length, index)"
class="text-gray-400"
name="arrow-down"
size="14px"
v-else
></u-icon>
</view>
<u-icon @click="openProject(item)" class="text-gray-400" name="arrow-right" size="14px" v-else></u-icon>
</view>
<!-- 有子项目 -->
<view class="ml-8" v-if="item.show">
<view
:id="'cu-' + index + '-' + subIndex"
:key="subIndex"
class="cu-item flex-col"
v-for="(subItem, subIndex) in item.sonProjectList"
>
<!-- <view :key="subItem.id" v-for="subItem in item.sonProjectList"> -->
<view class="flex items-center justify-between p-3">
<u-icon class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon>
<!-- <u-icon @click="openMenu(subItem)" class="mover" name="https://www.tall.wiki/staticrec/drag.svg" size="48"></u-icon> -->
<view @click="openProject(subItem)" class="flex-1 px-3">
<view class="flex items-center">
<view class="mr-2">{{ subItem.name }}</view>
<!-- 状态 -->
<view
:class="
subItem.status === 0
? 'text-blue-400 bg-blue-100'
: subItem.status === 1
? 'text-green-400 bg-green-100'
: subItem.status === 2
? 'text-red-400 bg-red-100'
: 'text-gray-400 bg-gray-100'
"
class="px-2 text-xs text-gray-400 bg-gray-100 rounded-full flex-shrink-0"
>
{{ subItem.status === 0 ? '未开始' : subItem.status === 1 ? '进行中' : subItem.status === 2 ? '暂停' : '已完成' }}
</view>
</view>
</view>
<!-- 箭头 -->
<u-icon @click="openProject(subItem)" class="text-gray-400" name="arrow-right" size="14px"></u-icon>
</view>
</view>
</view>
<!-- 项目操作面板 -->
<!-- <u-action-sheet :list="menuList" :tips="tips" @click="$emit('chooseAction', $event)" v-model="showMenu"></u-action-sheet> -->
<u-action-sheet :list="menuList" :tips="tips" @click="chooseAction" v-model="showMenu"></u-action-sheet>
</view>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
props: {
item: {
type: Object,
default: () => {},
},
index: {
type: Number,
default: 0,
},
menuList: {
type: Array,
default: () => [],
},
},
data() {
return {
showMenu: false,
tips: {
text: '',
color: '#909399',
fontSize: 28,
},
show: false,
border: 'border border-blue-500 shadow rounded-md',
showBorder: false,
projectId: 0,
};
},
computed: mapGetters('user', ['userId']),
methods: {
//
openProject(project) {
const { name, id, url } = project;
url && (uni.$t.domain = url);
this.$u.route('pages/project-webview/project-webview', {
u: this.userId,
p: id,
pname: name,
url: encodeURIComponent(url),
});
// uni.navigateTo({ url: `/pages/task-page/task-page?u=${this.userId}&p=${id}&pname=${name}&url=${encodeURIComponent(url)}` });
},
/**
* 弹出项目操作面板
*/
openMenu(project) {
this.showMenu = true;
this.projectId = project.id;
this.tips.text = project.name;
},
chooseAction(e) {
let data = { index: e, projectId: this.projectId };
this.$emit('chooseAction', data);
},
},
};
</script>
<style lang="scss" scoped>
.border-100 {
height: 4rpx;
margin: 0 20rpx;
}
.border-80 {
height: 4rpx;
margin: 0 20rpx 0 90rpx;
}
</style>

65
src/components/Projects/Projects.vue

@ -0,0 +1,65 @@
<template>
<view class="py-3 mt-4 bg-white u-font-15">
<PrettyExchange @change="change" />
</view>
</template>
<script>
import PrettyExchange from '../PrettyExchange/PrettyExchange.vue';
export default {
components: { PrettyExchange },
data() {
return {};
},
methods: {
change(options) {
if (options instanceof Array) {
let projectIdList = [];
let arr = [];
options.forEach(item => {
projectIdList.push(item.id);
arr.push(item.name);
});
this.setProjectSort(projectIdList);
} else {
this.setProjectRelation(options);
}
},
/**
* 设置项目顺序
* @param { Array } projectIdList 项目id
*/
async setProjectSort(projectIdList) {
try {
const params = { projectIdList };
await this.$u.api.setProjectSort(params);
this.$t.ui.showToast('排序修改成功');
} catch (error) {
console.log('error: ', error);
this.$t.ui.showToast(error.msg || '排序修改失败');
}
this.$emit('getProjects');
},
/**
* 设置项目父子结构
* @param { string } id 当前移动的项目的id
* @param { string } parentId 父项目的id
*/
async setProjectRelation(options) {
try {
const params = options;
await this.$u.api.setProjectRelation(params);
this.$t.ui.showToast('排序修改成功');
} catch (error) {
console.error('error: ', error);
this.$t.ui.showToast(error.msg || '排序修改失败');
}
this.$emit('getProjects');
},
},
};
</script>

264
src/components/Roles/Roles.vue

@ -0,0 +1,264 @@
<template>
<view class="px-2 bg-white wrap">
<view class="home-box u-skeleton">
<scroll-view :enable-flex="true" :scroll-left="scrollLeft" :throttle="false" scroll-with-animation scroll-x @scroll="scroll">
<view class="tab-box">
<!-- 角色项
default-tab-choice 我的角色 && 当前展示
default-tab-item 我的角色 && 当前不展示
tab-choice 不是我的 && 当前展示
-->
<div class="flex flex-1">
<template v-for="(item, index) in roles">
<view
:class="{
'default-tab-choice': item.mine == 1 && roleId === item.id,
'default-tab-item': item.mine == 1 && roleId !== item.id,
'tab-choice': item.mine == 0 && roleId === item.id,
}"
:key="index"
@click="changeRole(item.id)"
class="tab-item"
>
<view class="tab-children u-skeleton-fillet u-font-14">
{{ item.name }}
</view>
</view>
</template>
</div>
<u-icon
name="arrow-down"
color="#2979ff"
size="28"
type="down"
v-if="invisibleRoles.length"
:style="{ transform: `rotate(${isActive ? 0 : 180}deg)` }"
@click="isActive = !isActive"
style="transition: transform 0.24s; margin-right: 0px"
/>
</view>
</scroll-view>
</view>
<view :style="{ height: isActive ? '100px' : 0 }" style="transition: all 0.24s" class="role-more-box">
<template v-for="roleItem in allRoles">
<u-tag
:type="roleId === roleItem.id ? 'primary' : 'info'"
v-if="roleItem.name !== '平车'"
class="m-3"
:key="roleItem.id"
:text="roleItem.name"
mode="light"
@click="changeRole(roleItem.id)"
/>
</template>
</view>
<!-- 骨架屏 -->
<u-skeleton :animation="true" :loading="loading" bg-color="#fff"></u-skeleton>
</view>
</template>
<script>
import { mapState, mapMutations, mapGetters } from 'vuex';
export default {
name: 'Roles',
data() {
return {
tabIndex: 0, // 访 index 0
tabList: [], // tab dom
scrollLeft: 0, // scrollview
loading: false, //
roles: [
{ id: 1, name: '项目经理', mine: 0, pm: 1, sequence: 1 },
{ id: 2, name: '运维', mine: 0, pm: 0, sequence: 2 },
],
allRoles: [],
roleLeft: 0,
isActive: false,
};
},
computed: {
...mapState('role', ['visibleRoles', 'invisibleRoles', 'roleId']),
...mapGetters('project', ['projectId']),
},
watch: {
visibleRoles(val) {
if (val && val.length) {
// this.roles = [...this.visibleRoles, ...this.invisibleRoles];
this.roles = [...this.visibleRoles];
this.allRoles = [...this.visibleRoles, ...this.invisibleRoles];
this.loading = false;
}
},
roleId(val) {
let list = [...this.allRoles];
let newRoleList = [...this.roles];
let newList = [];
for (var i = 0; i < list.length; i++) {
if (val === list[i].id && list[i].name !== '平车') {
newList.push(list[i]);
newRoleList[0] = list[i];
list.splice(i, 1);
break;
}
}
for (var j = 0; j < list.length; j++) {
newList.push(list[j]);
}
this.roles = [...newRoleList];
this.allRoles = [...newList];
},
},
mounted() {
if (!this.visibleRoles || !this.visibleRoles.length) {
this.loading = true;
} else {
this.roles = [...this.visibleRoles];
this.allRoles = [...this.visibleRoles, ...this.invisibleRoles];
}
},
methods: {
...mapMutations('role', ['setRoleId']),
scroll(e) {
this.scrollLeft = e.detail.scrollLeft;
},
//
// projectroleId
// projectroleId
changeRole(id) {
try {
this.isActive = false;
const { projectId } = this;
// script
this.$nextTick(async () => {
this.setRoleId(id);
const saveParams = { param: { roleId: id, projectId } };
await uni.$u.api.saveRoleRecord(saveParams);
});
} catch (error) {
console.error('role.vue changeRole error: ', error);
}
},
},
};
</script>
<style lang="scss" scoped>
.role-more-box {
box-shadow: 0 6px 6px rgba(0, 0, 0, 0.15);
// border-top: 1px solid rgba(0, 0, 0, 0.15);
overflow: hidden;
width: 100%;
background: #fff;
z-index: 10;
left: 0;
// top: 0;
position: fixed;
border-radius: 0 0 6px 6px;
}
.home-box {
// sticky
position: sticky;
top: 0;
background: #fff; //
/* #ifdef H5 */
// h5 44px
top: 88rpx;
/* #endif */
.tab-box {
position: relative;
white-space: nowrap;
height: 60rpx;
display: flex;
align-items: center;
// height: 84rpx;
.tab-item {
// width: 20%;
// height: 84rpx;
// padding: 20rpx 24rpx;
padding: 0 24rpx;
position: relative;
// display: inline-block;
// text-align: center;
font-size: 30rpx;
height: 60rpx;
line-height: 60rpx;
transition-property: background-color, width;
}
.default-tab-item {
color: $roleChoiceColor;
}
.default-tab-choice {
//
position: relative;
color: $roleChoiceColor;
font-weight: 600;
}
.default-tab-choice:before {
content: '';
position: absolute;
left: 0;
bottom: -20rpx;
width: 100%;
height: 6rpx;
border-radius: 2rpx;
background: $roleChoiceColor;
}
.tab-choice {
//
position: relative;
color: $uni-color-primary;
font-weight: 600;
}
.tab-choice:before {
content: '';
position: absolute;
left: 0;
bottom: -20rpx;
width: 100%;
height: 6rpx;
border-radius: 2rpx;
background: $uni-color-primary;
}
}
}
// //
/* #ifndef APP-NVUE */
::-webkit-scrollbar,
::-webkit-scrollbar,
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
/* #endif */
/* #ifdef H5 */
// 穿H5scroll-view
scroll-view ::v-deep ::-webkit-scrollbar {
display: none;
}
/* #endif */
.skeleton {
height: 44rpx;
}
</style>

33
src/components/Service/Service.vue

@ -0,0 +1,33 @@
<template>
<div class="upload">
<!-- <div class="flex justify-center w-12 h-12 bg-blue-100 rounded-full shadow-md" style="line-height: 42px">客服</div> -->
<button
class="flex justify-center w-12 h-12 rounded-full shadow-md"
style="padding: 0; align-items: center; background-color: #04be02"
open-type="contact"
>
<!-- 客服 -->
<img style="height: 28px; width: 28px" src="./icon/tel-we.png" alt="" />
</button>
</div>
</template>
<script>
export default {
name: 'Service',
data() {
return {};
},
};
</script>
<style lang="scss" scoped>
.upload {
z-index: 99;
position: fixed;
left: 50%;
margin-left: -1.5rem;
font-size: 14px;
bottom: 5rem;
transform: translate3d(0, 99%, 0);
color: $uni-color-primary !important;
}
</style>

BIN
src/components/Service/icon/tel-we.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

126
src/components/TimeLine/TimeLine.vue

@ -0,0 +1,126 @@
<template>
<!-- 时间间隔栏 -->
<!-- <Barrier /> -->
<scroll-view
:lower-threshold="300"
scroll-y="true"
:upper-threshold="300"
:scroll-into-view="scrollToTaskId"
@scroll="scroll"
@scrolltolower="handleScrollBottom"
@scrolltoupper="handleScrollTop"
id="scroll"
>
<!-- 时间轴 -->
<!-- <u-divider bg-color="#f3f4f6" class="pt-5" fontSize="14px" v-if="topEnd">到顶啦</u-divider> -->
<TimeBox />
<!-- <u-divider bg-color="#f3f4f6" class="pb-5" fontSize="14px" v-if="bottomEnd">到底啦</u-divider> -->
</scroll-view>
</template>
<script>
// import Barrier from './component/Barrier.vue';
import { mapState, mapMutations, mapGetters } from 'vuex';
import { setPlaceholderTasks } from '@/utils/task';
import TimeBox from './component/TimeBox.vue';
export default {
name: 'TimeLine',
components: { TimeBox },
data() {
return { top: 0 };
},
computed: {
...mapState('role', ['visibleRoles']),
...mapState('task', ['scrollTop', 'tasks', 'topEnd', 'bottomEnd', 'showSkeleton', 'timeNode', 'scrollToTaskId']),
...mapGetters('task', ['timeGranularity']),
},
methods: {
...mapMutations('task', ['setScrollTop', 'setShrink', 'setUpTasks', 'setDownTasks', 'setScrollToTaskId']),
//
scroll(e) {
this.top = e.detail.scrollTop;
this.setShrink(this.top > this.scrollTop);
this.setScrollTop(this.top);
},
//
async handleScrollTop() {
if (!this.tasks || !this.tasks.length || this.showSkeleton) return;
const startTime = this.tasks[0].planStart - 0;
if (this.topEnd) {
//
console.warn('滚动到顶部没有数据时: ');
const addTasks = setPlaceholderTasks(startTime, true, this.timeGranularity);
this.setUpTasks(addTasks);
} else {
//
console.warn('滚动到顶部有数据时: ');
const detailId = this.tasks.findIndex(task => task.detailId);
const timeNode = this.tasks[detailId].planStart - 0;
const upQuery = {
timeNode,
queryType: 0,
queryNum: 6,
};
await this.$emit('getTasks', upQuery);
}
},
//
async handleScrollBottom() {
if (!this.tasks || !this.tasks.length || this.showSkeleton) return;
const { tasks, timeGranularity } = this;
const startTime = tasks[tasks.length - 1].planStart - 0;
if (this.bottomEnd) {
//
console.warn('滚动到底部没有数据时: ');
const addTasks = setPlaceholderTasks(startTime, false, this.timeGranularity);
this.setDownTasks(addTasks);
} else {
// =+
console.warn('滚动到底部有数据时: ');
const arr = [];
this.tasks.forEach(task => {
if (task.detailId) {
arr.push(task);
}
});
const nextQueryTime = +this.$t.time.add(+arr[arr.length - 1].planStart, 1, timeGranularity);
const downQuery = {
timeNode: nextQueryTime,
queryType: 1,
queryNum: 6,
};
await this.$emit('getTasks', downQuery);
}
},
//
setScrollPosition() {
// storagetaskId id
const taskId = this.$t.storage.getStorageSync('taskId');
if (taskId) {
this.setScrollToTaskId(`a${taskId}`);
this.$t.storage.setStorageSync('taskId', ''); //
} else {
const item = this.tasks.find(task => task.detailId);
console.log(this.tasks);
if (item) {
this.setScrollToTaskId(`a${item.id}`);
} else {
// taskId
// 线id 线
const task = this.tasks.find(item => this.$moment(+item.planStart).isSame(this.timeNode, this.timeGranularity));
task && this.setScrollToTaskId(`a${task.id}`); // task id
}
}
},
},
};
</script>

42
src/components/TimeLine/component/Barrier.vue

@ -0,0 +1,42 @@
<!--
* @Author: aBin
* @email: binbin0314@126.com
* @Date: 2021-07-19 14:22:54
* @LastEditors: aBin
* @LastEditTime: 2021-07-20 11:46:04
-->
<template>
<view class>
<!-- :class="{ active: cycleTasks.time.start === filter.startTime }" -->
<view class="cycle-time active">
<!-- {{ $util.formatStartTimeToCycleTime(filter.time, cycleTasks.time.start) }} -->
2021年30周
</view>
</view>
</template>
<script>
export default {
name: 'Barrier',
data() {
return {};
},
};
</script>
<style scoped lang="scss">
.cycle-time {
padding: 8rpx 16rpx;
margin-bottom: 16rpx;
background: #fafafc;
color: $uni-text-color;
font-size: 28rpx;
position: sticky;
top: -1px;
left: 0;
z-index: 99;
&.active {
background: $uni-color-primary;
color: $uni-text-color-inverse;
}
}
</style>

185
src/components/TimeLine/component/TaskTools.vue

@ -0,0 +1,185 @@
<template>
<view>
<view class="flex justify-between" style="min-width: 90px; position: relative">
<u-icon custom-prefix="custom-icon" name="C-bxl-redux" size="17px"></u-icon>
<u-icon custom-prefix="custom-icon" name="attachment" size="21px"></u-icon>
<!-- <u-icon custom-prefix="custom-icon" name="moneycollect" size="20px"></u-icon> -->
<u-icon name="xuanxiang" custom-prefix="custom-icon" size="21px" @click="operation"></u-icon>
<!-- 右上角 ... 弹窗 -->
<view class="popup border shadow-md" v-if="show">
<view class="flex justify-center pb-3 border-b-1">
<span @click="createTask">新建任务</span>
</view>
<view class="flex pt-3 justify-center">
<span>克隆任务</span>
</view>
</view>
</view>
<!-- 遮罩 -->
<view class="mask" v-if="maskShow" @click="closeMask"></view>
<!-- 新建任务弹窗 -->
<CreateTask
:startTime="startTime"
:endTime="endTime"
:task="task"
@showTime="showTime"
@closeMask="closeMask"
class="thirdPopup flex transition-transform"
v-if="createTaskShow"
/>
<u-picker title="开始时间" mode="time" v-model="showStart" :params="params" @confirm="confirmStartTime"></u-picker>
<u-picker title="结束时间" mode="time" v-model="showEnd" :params="params" @confirm="confirmEndTime"></u-picker>
</view>
</template>
<script>
import CreateTask from '../../Title/components/CreateTask.vue';
export default {
components: { CreateTask },
props: {
task: {
type: Object,
default: () => {},
},
},
data() {
return {
show: false, // ...
createTaskShow: false, //
secondShow: false, //
maskShow: false, //
showStart: false,
showEnd: false,
startTime: '', //
endTime: '', //
params: {
year: true,
month: true,
day: true,
hour: true,
minute: true,
second: true,
},
};
},
methods: {
//
operation() {
// this.$t.ui.showToast('');
this.show = !this.show;
},
//
createTask() {
// ...
this.show = false;
//
this.maskShow = true;
//
this.createTaskShow = true;
},
//
closeMask() {
//
this.maskShow = false;
//
this.secondShow = false;
//
this.createTaskShow = false;
},
showTime() {
this.showStart = !this.showStart;
},
//
confirmStartTime(e) {
this.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`;
this.showEnd = true;
},
//
confirmEndTime(e) {
this.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`;
},
},
};
</script>
<style lang="scss" scoped>
.mask {
width: 100%;
height: 100vh;
z-index: 21;
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.3);
}
.thirdPopup {
background: #ffffff;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 33;
border-radius: 5px;
width: 90%;
}
.popup {
width: 110px;
background: #fff;
position: absolute;
right: 0;
top: 35px;
z-index: 99;
padding: 15px 0;
color: black;
animation: opacity 1s ease-in;
}
@keyframes opacity {
0% {
opacity: 0;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
::v-deep .u-slot-content {
min-width: 0;
}
::v-deep .u-dropdown__content {
min-height: 120px !important;
height: auto !important;
overflow-y: auto;
background: #fff !important;
transition: none !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
::v-deep .u-dropdown__menu__item .u-flex {
justify-content: space-between;
width: 100%;
height: 100%;
flex-wrap: nowrap;
border: 1px solid #afbed1;
padding: 0 8px;
}
::v-deep .u-dropdown__content__mask {
display: none;
}
</style>

142
src/components/TimeLine/component/TimeBox.vue

@ -0,0 +1,142 @@
<template>
<view class="column">
<!-- v-if="tasks && tasks.length" -->
<view>
<view :key="task.id" v-for="task in tasks" :id="`a${task.id}`">
<view class="flex">
<TimeStatus :task="task" />
<view class="flex items-center justify-between flex-1 ml-2 task-column">
<view v-if="task.process !== 4">{{ $moment(+task.planStart).format(startTimeFormat) }}</view>
<view v-else>{{ $moment(+task.planStart).format('D日') }}</view>
<!-- 任务功能菜单 -->
<TaskTools v-if="task.process !== 4" :task="task" />
</view>
</view>
<view class="border-l-2 border-gray-300 plugin">
<view class="h-3" v-if="task.process === 4"></view>
<view class="ml-3 overflow-hidden shadow-lg task-box">
<u-card
:show-foot="false"
:show-head="false"
:style="{ height: setHeight(task.panel) }"
class="h-16"
margin="0"
v-if="showSkeleton"
>
<view slot="body">
<view>
<skeleton :banner="false" :loading="true" :row="4" animate class="mt-2 u-line-2 skeleton"></skeleton>
</view>
</view>
</u-card>
<u-card
@click="onClickTask(task.planStart - 0, task.id)"
:show-foot="false"
:show-head="false"
:style="{ height: setHeight(task.panel) }"
class="h-16"
margin="0"
v-if="tasks && tasks.length && task.process !== 4 && !showSkeleton"
>
<!-- 任务面板插件 -->
<view slot="body">
<view class="p-0 u-col-between">
<view :key="pIndex" v-for="(row, pIndex) in task.plugins">
<view class="grid gap-2" v-if="row.length">
<Plugin
:class="[`row-span-${plugin.row}`, `col-span-${plugin.col}`]"
:task="task"
:key="plugin.pluginTaskId"
:plugin-task-id="plugin.pluginTaskId"
:plugin-id="plugin.pluginId"
:param="plugin.param"
:style-type="styleType || 0"
v-for="plugin in row"
/>
</view>
</view>
</view>
</view>
</u-card>
</view>
</view>
</view>
</view>
<!-- 局部弹框操作栏 -->
<Tips />
</view>
</template>
<script>
import { mapState, mapMutations, mapGetters, mapActions } from 'vuex';
import Skeleton from '@/components/Skeleton/Skeleton';
import TimeStatus from './TimeStatus.vue';
import TaskTools from './TaskTools.vue';
export default {
name: 'TimeBox',
components: { TimeStatus, Skeleton, TaskTools },
data() {
return { currentComponent: '', styleType: 0 };
},
computed: {
...mapState('role', ['roleId']),
...mapState('task', ['timeUnit', 'tasks', 'taskLoading', 'showSkeleton']),
...mapGetters('task', ['startTimeFormat']),
},
methods: {
...mapActions('task', ['getGlobal']),
...mapMutations('task', ['setTipsContent', 'setTipsContent']),
//
setHeight(panel) {
if (panel && panel.height) {
return panel.height + 'px';
} else {
return 'auto';
}
},
/**
* 点击了定期任务的面板 更新可变的日常任务
* @param {number} planStart 任务计划开始时间
* @param {string} taskId 任务id
*/
onClickTask(planStart, taskId) {
const param = { roleId: this.roleId, timeNode: planStart, timeUnit: this.timeUnit };
this.getGlobal(param);
this.$t.storage.setStorageSync('taskId', taskId);
this.$t.storage.setStorageSync('roleId', this.roleId);
},
},
};
</script>
<style scoped lang="scss">
.task-box {
border-radius: 24rpx;
}
.column {
padding: 24px 14px;
}
.task-column {
height: 33px;
}
.plugin {
margin-top: 8px;
margin-bottom: 8px;
margin-left: 15px;
}
::v-deep .ml-2 {
margin-left: 16px;
}
::v-deep .ml-3 {
margin-left: 20px;
}
</style>

231
src/components/TimeLine/component/TimeStatus.vue

@ -0,0 +1,231 @@
<template>
<view class="u-font-14">
<view
class="flex items-center justify-center rounded-full icon-column"
:style="{ color: orderStyle.color }"
@click="changeStatus(task.process, $event)"
>
<!-- 1进行中 2暂停中 3已完成 -->
<u-circle-progress
:percent="orderStyle.persent - 0"
:active-color="orderStyle.color"
bg-color="rgba(255,255,255,0)"
border-width="4"
:width="task.process !== 4 ? 66 : 50"
v-if="task.process === 1 || task.process === 2 || task.process === 3"
>
<view class="u-progress-content">
<view class="u-progress-dot"></view>
<view class="u-progress-info">
<u-icon :name="orderStyle.icon" v-if="orderStyle.icon" size="15px"></u-icon>
<template v-else>{{ durationText }}</template>
</view>
</view>
</u-circle-progress>
<!-- 0未开始 4添加任务 -->
<view class="flex items-center justify-center rounded-full progress-box" v-else :class="task.process === 4 ? 'progress-box-4' : ''">
<view class="u-progress-content">
<view class="u-progress-dot"></view>
<view class="u-progress-info">
<span v-if="orderStyle.icon">
<u-icon :name="orderStyle.icon" v-if="task.process !== 4" size="15px"></u-icon>
<u-icon :name="orderStyle.icon" v-else size="15px"></u-icon>
</span>
<template v-else>{{ durationText }}</template>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { mapState, mapMutations } from 'vuex';
export default {
name: 'TimeStatus',
props: { task: { type: Object, default: () => {} } },
data() {
return {
time: '',
start: [{ text: '确认开始任务', color: 'blue' }],
pause: [{ text: '继续' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }],
proceed: [{ text: '暂停' }, { text: '重新开始任务', color: 'blue' }, { text: '结束' }],
again: [{ text: '重新开始任务', color: 'blue' }],
timer: null,
durationText: 0,
};
},
computed: {
...mapState('task', ['tip']),
status() {
return this.task ? this.task.process : 0;
},
taskName() {
return this.task ? this.task.name : '';
},
taskId() {
return this.task ? this.task.id : '';
},
//
// 0 1 2 3
orderStyle() {
let color = '#9CA3AF';
let icon = 'play-right-fill';
let persent = 100;
switch (this.status) {
case 1: //
color = '#60A5FA';
icon = '';
if (+this.computeCyclePersent() > 100) {
persent = 96;
} else {
persent = this.computeCyclePersent();
}
break;
case 2: //
color = '#F87171';
icon = 'pause';
persent = 50; // TODO:
break;
case 3: //
color = '#34D399';
icon = 'checkmark';
persent = 100;
break;
case 4: //
color = '#60A5FA';
icon = 'plus';
persent = 100;
break;
default:
//
color = '#9CA3AF';
icon = 'play-right';
persent = 100;
break;
}
return { color, icon, persent };
},
},
mounted() {
// TODO:
const time = this.computeDurationText();
this.updateDurationText(time);
},
destroyed() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
methods: {
...mapMutations('task', ['setTip']),
/**
* 点击了图标 修改任务状态
* @param {object} event
*/
changeStatus(process, event) {
if (process === 4) {
this.addTask();
return;
}
// return false;
const { status, taskId, taskName, tip } = this;
tip.status = status;
tip.taskId = taskId;
tip.left = event.target.x;
tip.top = event.target.y;
tip.show = true;
tip.text = this.genetateTips(status, taskName);
this.setTip(tip);
},
//
addTask() {
this.$t.ui.showToast('新建任务');
},
//
computeCyclePersent() {
if (!this.task || !this.task.realStart || !this.task.planDuration) return 100;
const { realStart, planDuration } = this.task;
return (((Date.now() - +realStart) * 100) / +planDuration).toFixed(2);
},
/**
* 计算tip的标题内容
*/
genetateTips(status, content) {
switch (status) {
case 0:
return `确认开始任务"${content}"吗?`;
case 1:
return `请选择要执行的操作`;
case 2:
return `请选择要执行的操作`;
case 3:
return `是否要重新开始此任务`;
}
},
//
// = realStart() + planDuration()
// = -
// = realStart + planDuration - Date.now()
computeDurationText() {
const { realStart, planDuration } = this.task;
const leftTime = +realStart + +planDuration - Date.now(); //
const { num, time } = this.$t.time.computeDurationText(leftTime);
if (num <= 0) {
clearInterval(this.timer);
this.timer = null;
}
this.durationText = num;
return time;
},
updateDurationText(time) {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
if (!time) return;
setInterval(() => {
this.computeDurationText();
}, time);
},
},
};
</script>
<style scoped lang="scss">
.icon-column {
height: 33px;
width: 33px;
}
.one {
height: 33px;
width: 33px;
}
.progress-box {
background: rgba(255, 255, 255, 0);
width: 33px;
height: 33px;
border: 2px solid #9ca3af;
}
.progress-box-4 {
width: 25px;
height: 25px;
border: 2px solid #60a5fa;
}
</style>

7
src/components/TimeLine/component/Title.vue

@ -0,0 +1,7 @@
<!--
* @Author: aBin
* @email: binbin0314@126.com
* @Date: 2021-07-19 15:40:02
* @LastEditors: aBin
* @LastEditTime: 2021-07-19 15:40:03
-->

235
src/components/Title/Title.vue

@ -0,0 +1,235 @@
<template>
<view>
<!-- :is-back="false" -->
<u-navbar :custom-back="onBack" class="overflow-hidden">
<view class="flex justify-start flex-1 px-3 font-bold min-0">
<view class="truncate">{{ project.name }}</view>
</view>
<view class="mr-2" slot="right">
<u-icon class="m-1" name="xuanzhong2" custom-prefix="custom-icon" size="20px" @click="lwbs"></u-icon>
<u-icon class="m-1" name="shuaxin1" custom-prefix="custom-icon" size="20px" @click="projectOverview"></u-icon>
<u-icon class="m-1" name="home" custom-prefix="custom-icon" size="20px" @click="openIndex"></u-icon>
<u-icon class="m-1" name="xuanxiang" custom-prefix="custom-icon" size="20px" @click="operation"></u-icon>
</view>
</u-navbar>
<view
class="mask"
v-if="maskShow"
@click="closeMask"
style="width: 100%; height: 100vh; z-index: 21; position: fixed; background: rgba(0, 0, 0, 0.3)"
></view>
<!-- 右上角 ... 弹窗 -->
<view class="popup border shadow-md" v-if="show">
<view class="flex pb-3 border-b-1">
<u-icon name="plus-circle" size="36" style="margin: 0 15px 3px 0"></u-icon>
<!-- <view @click="createTask">新建任务</view> -->
<view>新建任务</view>
</view>
<view class="flex pt-3">
<u-icon name="share" size="32" style="margin: 0 15px 3px 0"></u-icon>
<view @click="share">分享项目</view>
</view>
</view>
<!-- 分享项目弹窗 -->
<ShareProject v-if="secondShow" class="second-popup" />
<!-- 新建任务弹窗 -->
<CreateTask
:startTime="startTime"
:endTime="endTime"
@showTime="showTime"
@closeMask="closeMask"
class="third-popup flex transition-transform"
v-if="createTaskShow"
/>
<u-picker title="开始时间" mode="time" v-model="showStart" :params="params" @confirm="confirmStartTime"></u-picker>
<u-picker title="结束时间" mode="time" v-model="showEnd" :params="params" @confirm="confirmEndTime"></u-picker>
</view>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
import CreateTask from './components/CreateTask.vue';
import ShareProject from './components/ShareProject.vue';
export default {
name: 'ProjectTitle',
components: { CreateTask, ShareProject },
data() {
return {
show: false, // ...
createTaskShow: false, //
secondShow: false, //
maskShow: false, //
showStart: false,
showEnd: false,
startTime: '', //
endTime: '', //
params: {
year: true,
month: true,
day: true,
hour: true,
minute: true,
second: true,
},
};
},
computed: {
...mapState('project', ['project']),
...mapGetters('user', ['userId']),
},
methods: {
showTime() {
this.showStart = !this.showStart;
},
//
confirmStartTime(e) {
this.startTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`;
this.showEnd = true;
},
//
confirmEndTime(e) {
this.endTime = `${e.year}-${e.month}-${e.day} ${e.hour}:${e.minute}:${e.second}`;
},
//
onBack() {
// eslint-disable-next-line no-undef
const pages = getCurrentPages(); //
console.log('历史pages: ', pages.length);
// if (pages.length > 1) {
// uni.webView.navigateBack();
uni.webView.reLaunch({ url: `/pages/index/index?u=${this.userId}` });
// } else {
// // this.$u.route('/', { u: this.userId });
// uni.webView.reLaunch({ url: `/pages/index/index?u=${this.userId}` });
// }
},
// LWBS
lwbs() {
// this.$t.ui.showToast('LWBS');
},
//
projectOverview() {
// this.$t.ui.showToast('');
},
//
openIndex() {
console.log(111);
uni.webView.reLaunch({ url: `/pages/index/index?u=${this.userId}` });
},
//
operation() {
// this.$t.ui.showToast('');
this.show = !this.show;
},
//
createTask() {
// ...
this.show = false;
//
this.maskShow = true;
//
this.createTaskShow = true;
},
//
share() {
// ...
this.show = false;
//
this.maskShow = true;
//
this.secondShow = true;
},
//
closeMask() {
//
this.maskShow = false;
//
this.secondShow = false;
//
this.createTaskShow = false;
},
},
};
</script>
<style lang="scss" scoped>
.second-popup {
background: #ffffff;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 33;
border-radius: 5px;
width: 90%;
}
.third-popup {
background: #ffffff;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 33;
border-radius: 5px;
width: 90%;
}
.popup {
width: 40%;
background: #fff;
position: absolute;
right: 0;
z-index: 99;
padding: 15px;
color: black;
animation: opacity 0.5s ease-in;
}
@keyframes opacity {
0% {
opacity: 0;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
::v-deep .u-slot-content {
min-width: 0;
}
::v-deep .u-dropdown__content {
min-height: 120px !important;
height: auto !important;
overflow-y: auto;
background: #fff !important;
transition: none !important;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
::v-deep .u-dropdown__menu__item .u-flex {
justify-content: space-between;
width: 100%;
height: 100%;
flex-wrap: nowrap;
border: 1px solid #afbed1;
padding: 0 8px;
}
::v-deep .u-dropdown__content__mask {
display: none;
}
</style>

460
src/components/Title/components/CreateTask.vue

@ -0,0 +1,460 @@
<template>
<div class="new-projects-box">
<div class="form">
<!-- 项目名称 -->
<view class="mb-3 font-bold text-base flex justify-center">新建任务</view>
<div class="flex items-center mb-2">
<div>名称<span class="text-red-500">*</span></div>
<u-input max-length="5" v-model="name" :type="type" :border="border" />
</div>
<!-- 起止时间 -->
<div class="mb-2">
<div>起止时间</div>
<u-input placeholder="请选择起止时间" v-model="timeValue" :type="type" :border="border" @click="$emit('showTime')" />
</div>
<!-- 多选框 -->
<div class="flex justify-between items-center">
<div>负责人<span class="text-red-500">*</span></div>
<div class="flex-1" v-if="hasRole">{{ roleName }}</div>
<div label="负责人" class="flex-1" v-else>
<u-dropdown disabled ref="uDropdown" placeholder="请选择负责人">
<u-dropdown-item :title="roleList">
<view class="slot-content bg-white">
<div
class="flex flex-row justify-between mb-1 drop-item"
v-for="(role, roleIndex) in roleOptions"
:key="roleIndex"
@click="change(roleIndex)"
>
<view v-model="role.id">{{ role.name }}</view>
<u-icon v-if="role.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon>
</div>
</view>
</u-dropdown-item>
</u-dropdown>
</div>
</div>
<!-- 下拉图标 -->
<div class="flex justify-center my-6">
<u-icon v-if="arrow" name="arrow-down" size="28" @click="openDropdown"></u-icon>
<u-icon v-else name="arrow-up" size="28" @click="closeSecondDropdown"></u-icon>
</div>
<!-- 下拉框的内容 -->
<div v-if="show" class="mb-6">
<!-- 描述 -->
<div class="flex items-center mb-2">
<div>描述</div>
<u-input v-model="description" max-length="48" type="textarea" height="36" auto-height :border="border" />
</div>
<!-- 所属项目 -->
<div class="w flex items-center mb-2">
<div>所属项目<span class="text-red-500">*</span></div>
<div>{{ project.name }}</div>
</div>
<!-- 所属任务 -->
<div class="w flex items-center mb-2" v-if="task && task.id">
<div>所属任务</div>
<div>{{ task.name }}</div>
</div>
<!-- 上道工序 -->
<div class="flex items-center mb-2">
<div>上道工序</div>
<InputSearch
@searchPrevTask="searchPrevTask"
:dataSource="allTasks"
@select="handleChange"
@clearAllTasks="clearAllTasks"
placeholder="请输入上道工序"
/>
</div>
<!-- 检查人多选框 -->
<div class="flex justify-between items-center">
<div>检查人<span class="text-red-500">*</span></div>
<div label="检查人" class="flex-1">
<u-dropdown ref="dropdown">
<u-dropdown-item :title="checkerList">
<view class="slot-content bg-white">
<div
class="flex flex-row justify-between mb-1 drop-item"
v-for="(checkoutOption, Index) in checkoutOptions"
:key="Index"
@click="choose(Index)"
>
<view v-model="checkoutOption.value">{{ checkoutOption.name }}</view>
<u-icon v-if="checkoutOption.dropdownShow" name="checkbox-mark" color="#2979ff" size="28"></u-icon>
</div>
</view>
</u-dropdown-item>
</u-dropdown>
</div>
</div>
<!-- 是否是日常任务 -->
<div class="flex justify-between items-center mt-6">
是否是日常任务
<u-switch v-model="isGlobal" size="28"></u-switch>
</div>
<div class="mt-6">
<div>交付物</div>
<div v-for="(sort, sortIndex) in deliverSort" :key="sortIndex">
<u-input
@blur="addDeliverInput"
v-model="sort.name"
:placeholder="`交付物名称${sortIndex + 1}`"
:type="type"
:border="border"
/>
</div>
</div>
</div>
<div class="flex items-center mb-6">
<u-button type="primary" size="medium" @click="setParameters">提交</u-button>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
props: {
startTime: {
type: String,
default: '',
},
endTime: {
type: String,
default: '',
},
task: {
type: Object,
default: null,
},
},
data() {
return {
arrow: true,
show: false,
isGlobal: false, //
name: '', //
showChooseTime: false,
timeValue: '', //
description: '', //
projectShow: false, //
processTaskId: '', //
type: 'text',
border: true,
roleList: undefined, //
checkerList: undefined, //
roleOptions: [], //
checkoutOptions: [], //
roleIdList: [], // id
checkerIdList: [], // id
deliverables: [], //
deliverSort: [{ name: '' }], //
allTasks: [],
roleName: '', //
hasRole: false, //
};
},
computed: {
...mapState('role', ['visibleRoles', 'roleId']),
...mapState('project', ['project']),
...mapState('task', ['tasks']),
...mapGetters('project', ['projectId']),
},
watch: {
endTime(val) {
if (val) {
this.timeValue = this.startTime + ' 至 ' + val;
}
},
},
mounted() {
//
if (this.visibleRoles.length) {
this.visibleRoles.forEach(role => {
role.dropdownShow = false;
role.status = false;
});
}
this.roleOptions = this.$u.deepClone(this.visibleRoles);
this.checkoutOptions = this.$u.deepClone(this.visibleRoles);
//
if (this.roleId) {
const item = this.visibleRoles.find(r => r.id === this.roleId);
if (item) {
this.roleName = item.name;
this.hasRole = true;
}
}
},
methods: {
...mapMutations('task', ['updateTasks']),
//
change(index) {
let arr = [...this.roleOptions];
//
arr[index].dropdownShow = !arr[index].dropdownShow;
//
this.roleList = arr[index].name;
let shows = '';
// arr
arr.map(val => {
if (val.dropdownShow === true) {
shows += val.name + ',';
this.roleIdList.push(val.id);
}
});
this.roleOptions = [...arr];
// ','
this.roleList = shows.slice(0, shows.length - 1);
},
//
choose(index) {
let arr = [...this.checkoutOptions];
//
arr[index].dropdownShow = !arr[index].dropdownShow;
//
this.checkerList = arr[index].name;
let shows = '';
// arr
arr.map(val => {
if (val.dropdownShow === true) {
shows += val.name + ',';
this.checkerIdList.push(val.id);
}
});
this.checkoutOptions = [...arr];
// ','
this.checkerList = shows.slice(0, shows.length - 1);
// this.roleList = arr[value - 1].name;
},
//
openDropdown() {
this.arrow = !this.arrow;
this.show = true;
},
//
closeSecondDropdown() {
this.arrow = !this.arrow;
this.show = false;
},
/**
* 模糊查询 查找项目下的任务
* @param name 任务名
* @param projectId 项目id
*/
async searchPrevTask(val) {
try {
const params = { name: val, projectId: this.projectId };
const data = await this.$u.api.queryTaskOfProject(params);
this.allTasks = data;
return data;
} catch (error) {
console.error('error: ', error);
}
},
//
handleChange(data) {
console.log('data', data);
this.processTaskId = data.detailId;
},
//
clearAllTasks() {
this.allTasks = [];
},
//
addDeliverInput() {
if (this.deliverSort[this.deliverSort.length - 1].name) {
this.deliverSort.push({ name: '' });
}
},
//
async setParameters() {
const {
projectId,
task,
name,
startTime,
endTime,
hasRole,
roleIdList,
roleId,
description,
processTaskId,
checkerIdList,
isGlobal,
} = this;
if (!name) {
this.$t.ui.showToast('请输入名称');
return;
}
if ((!roleIdList || !roleIdList.length) && !hasRole) {
this.$t.ui.showToast('请选择负责人');
return;
}
if (!checkerIdList || !checkerIdList.length) {
this.$t.ui.showToast('请选择检查人');
return;
}
const deliverList = [];
this.deliverSort.forEach(item => {
if (item.name) {
deliverList.push(item.name);
}
});
const params = {
name,
startTime: startTime ? this.$moment(startTime).format('x') - 0 : '',
endTime: endTime ? this.$moment(endTime).format('x') - 0 : '',
roleIdList: hasRole ? [roleId] : roleIdList,
description,
projectId,
parentTaskId: task && task.id ? task.id : '', //
processTaskId, // TODO
checkerIdList,
global: isGlobal ? 1 : 0,
deliverList,
};
await this.handleSubmit(params);
},
/**
* 新建任务
* @param name 任务名
* @param startTime 开始时间
* @param endTime 结束时间
* @param roleIdList 负责人id数组
* @param description 描述
* @param projectId 所属项目id
* @param parentTaskId 所属任务id
* @param processTaskId 上道工序任务id
* @param checkerIdList 检查人id数组
* @param global 是否日常任务 0 1
* @param deliverList 交付物名字数组
*/
async handleSubmit(params) {
try {
const data = await this.$u.api.saveTask(params);
// TODO or
this.$emit('closeMask');
const newTasks = {
data: data[0],
processTaskId: params.processTaskId,
};
// store
//
if (!this.task || !this.task.id) {
this.addNewTasks(newTasks);
}
} catch (error) {
this.$emit('closeMask');
console.error('error: ', error);
}
},
// tasks
addNewTasks(data) {
const oldTasks = this.$u.deepClone(this.tasks);
let res = data.data;
//
if (data.processTaskId) {
const index = oldTasks.find(item => item.detailId === data.processTaskId);
if (index) {
oldTasks.splice(index + 1, 0, res);
}
} else {
this.setAddPosition(res, oldTasks);
}
},
//
setAddPosition(res, oldTasks) {
if (res.planStart - 0 < oldTasks[0].planStart - 0) {
//
oldTasks.splice(0, 0, res);
} else if (res.planStart - 0 === oldTasks[0].planStart - 0) {
//
oldTasks.splice(1, 0, res);
} else if (res.planStart - 0 >= oldTasks[oldTasks.length - 1].planStart - 0) {
//
oldTasks.splice(-1, 0, res);
} else {
//
for (let i = 0; i < oldTasks.length; i++) {
const item = oldTasks[i];
if (res.planStart - 0 > item.planStart - 0) {
if (res.planStart - 0 <= oldTasks[i + 1].planStart - 0) {
oldTasks.splice(i + 1, 0, res);
console.log('res: ', res);
return;
}
}
}
}
// TODO:
console.log('oldTasks: ', oldTasks);
this.updateTasks([...oldTasks]);
},
},
};
</script>
<style lang="scss" scoped>
.form {
display: flex;
flex-direction: column;
width: 100%;
max-height: 400px;
overflow-y: scroll;
}
.drop-item {
border-bottom: 1px solid #f1f1f1;
padding: 16rpx;
}
::v-deep.u-input--border {
border: none;
border-radius: 0;
}
::v-deep.u-dropdown__menu__item > uni-view {
border: none !important;
padding: 5px;
}
.u-input {
border-bottom: 1px solid #dcdfe6;
}
.new-projects-box {
margin-top: 20px;
padding: 15px;
width: 100%;
overflow: hidden;
}
.w {
width: 300px;
height: 39px;
}
::v-deep .u-dropdown__menu__item .u-flex {
border: 0 !important;
border-bottom: 1px solid #dcdfe6 !important;
padding: 0 20rpx;
}
</style>

210
src/components/Title/components/ShareProject.vue

@ -0,0 +1,210 @@
<template>
<view class="flex justify-center">
<view class="content p-3 pb-8">
<view class="mb-3 font-bold text-base flex justify-center">创建分享链接</view>
<view class="flex flex-col">
<view class="mb-1">用户以什么角色加入项目</view>
<!-- 下拉多选 -->
<view class="uni-list">
<view class="uni-list-cell">
<view class="uni-list-cell-db ml-2" v-if="rolesArray.length">
<picker @change="changeRole" :value="index" :range="rolesArray">
<view class="uni-input">{{ allRolesName[index].name }}</view>
</picker>
</view>
</view>
</view>
<!-- 复制链接 -->
<view class="link flex items-center mt-4">
<view class="link-url">{{ links }}</view>
<u-button
style="border-radius: 0; height: 100%"
type="primary"
v-clipboard:copy="copyText"
v-clipboard:success="copySuccess"
v-clipboard:error="copyError"
>
复制链接
</u-button>
</view>
<view @click="select">
<!-- 全选按钮 -->
<!-- <view class="flex mt-4">
<view>
<u-checkbox-group>
<u-checkbox v-model="checked" @change="checkedAll"></u-checkbox>
</u-checkbox-group>
</view>
<view>已选择({{ this.quantity }})</view>
<view style="color: #f37378; margin-left: 20px">批量删除</view>
</view> -->
<!-- 多选框 -->
<!-- <view>
<u-checkbox-group class="checkboxs flex flex-1 items-center mt-4" v-for="(item, index) in list" :key="index">
<div class="flex-1 flex items-center">
<u-checkbox v-model="item.checked"></u-checkbox>
<u-avatar :src="item.src" size="55" style="background: #d8dce0; margin-right: 10px"></u-avatar>
<div style="width: 60%; font-size: 12px">
<div style="color: gray">{{ item.name }}</div>
<div style="color: #c4d0e1">{{ item.joinMethod }}</div>
</div>
</div>
</u-checkbox-group>
</view> -->
</view>
</view>
</view>
</view>
</template>
<script>
import { mapGetters, mapState } from 'vuex';
export default {
data() {
return {
rolesArray: [],
allRolesName: [],
index: 0,
links: '', //
copyText: '',
checked: false, //
roleName: '观众',
//
list: [
{
name: '冯老师',
src: '',
joinMethod: '文件创建者',
role: '观众',
checked: false,
disabled: false,
},
{
name: '马壮',
src: '',
joinMethod: '通过链接加入',
role: '干系人',
checked: false,
disabled: false,
},
{
name: '张野',
src: '',
joinMethod: '通过链接加入',
role: '观众',
checked: false,
disabled: false,
},
],
quantity: 0, //
path: '',
};
},
computed: {
...mapState('role', ['visibleRoles', 'invisibleRoles']),
...mapState('project', ['project']),
...mapGetters('project', ['projectId']),
},
mounted() {
this.$nextTick(() => {
this.path = window.location.href.split('?')[0];
const { path, projectId } = this;
const params = { path: `${path}`, projectId, roleId: '0' };
this.creatShare(params);
});
if (this.visibleRoles.length || this.invisibleRoles.length) {
const arr = this.visibleRoles.concat(this.invisibleRoles);
arr.forEach(role => {
let item = { id: '', name: '' };
item.id = role.id;
item.name = role.name;
this.allRolesName.push(item);
this.rolesArray.push(role.name);
});
const firstItem = { id: '0', name: '观众' };
this.allRolesName.unshift(firstItem);
this.rolesArray.unshift('观众');
}
},
methods: {
//
async changeRole(e) {
this.index = e.target.value;
this.roleName = this.allRolesName[this.index].name;
const { path, projectId } = this;
const params = { path, projectId, roleId: this.allRolesName[this.index].id };
await this.creatShare(params);
},
//
copySuccess() {
this.$t.ui.showToast('复制成功');
},
//
copyError() {
this.$t.ui.showToast('复制失败,请稍后重试');
},
/**
* 创建分享链接
* @param path 路径前缀
* @param projectId 项目id
* @param roleId 角色id
*/
async creatShare(params) {
try {
const data = await this.$u.api.createShare(params);
this.links = data.path;
this.copyText = `邀请您加入${this.project.name}的项目,角色为${this.roleName},链接为${data.path}&url=${this.$t.domain}`;
} catch (error) {
console.error('error: ', error);
}
},
//
select() {
this.quantity = 0;
this.list.forEach(val => {
if (val.checked == true) {
this.quantity++;
}
});
},
//
checkedAll() {
this.list.map(val => {
val.checked = !this.checked;
});
},
},
};
</script>
<style lang="scss" scoped>
.content {
width: 100%;
max-height: 400px;
}
.link {
height: 40px;
border: 1px solid #afbed1;
}
.link-url {
color: #afbed1;
width: 80%;
line-height: 40px;
margin-left: 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>

201
src/components/Upload/Upload.vue

@ -0,0 +1,201 @@
<template>
<view class="upload">
<!-- <u-icon name="plus" size="24px" class="flex justify-center w-12 h-12 bg-blue-100 rounded-full shadow-md" @click="handleUpload">
</u-icon> -->
<template v-if="user.phone">
<div
v-if="btnList.length === 1"
class="flex justify-center w-12 h-12 bg-blue-100 rounded-full shadow-md"
style="line-height: 48px"
@click="jumpV(btnList[0].url)"
>
{{ btnList[0].name }}
</div>
<div v-else-if="btnList.length === 2" class="flex h-12 bg-blue-100 rounded-full shadow-md items-center" style="line-height: 48px">
<div class="border-gray-300 min-w-16 border-r-2 border-solid flex justify-center h-4 items-center" @click="jumpV(btnList[0].url)">
{{ btnList[0].name }}
</div>
<div class="flex min-w-16 justify-center" @click="jumpV(btnList[1].url)">{{ btnList[1].name }}</div>
</div>
<div v-else-if="btnList.length > 2" class="flex h-12 bg-blue-100 rounded-full shadow-md items-center" style="line-height: 48px">
<div class="border-gray-300 min-w-16 border-r-2 border-solid flex justify-center h-4 items-center" @click="jumpV(btnList[0].url)">
{{ btnList[0].name }}
</div>
<div class="flex min-w-16 justify-center" @click="isActive = !isActive">
<div class="mr-1">其他</div>
<u-icon
name="arrow-down"
color="#0284c7"
size="28"
type="down"
:style="{ transform: `rotate(${isActive ? -180 : 0}deg)` }"
@click="isActive = !isActive"
style="transition: transform 0.24s; margin-right: 0px"
/>
</div>
</div>
</template>
<u-button
v-else
open-type="getPhoneNumber"
@getphonenumber="getphonenumber"
:custom-style="customStyle"
shape="square"
size="default"
type="primary"
>
授权
</u-button>
<view :style="{ height: isActive ? '200px' : 0 }" style="transition: all 0.24s" class="role-more-box">
<template v-for="(item, index) in btnList">
<div :class="index === btnList.length - 1 ? '' : 'border-bottom'" class="jump-list-box" v-if="index !== 0" :key="item.url">
{{ item.name }}
</div>
</template>
</view>
<!-- <u-button @click="jump" size="default" type="primary"> 拍照 </u-button> -->
</view>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
data() {
return {
name: '',
url: '',
customStyle: {
borderRadius: '50%',
width: '3rem;',
height: '3rem;',
'--tw-bg-opacity': 1,
backgroundColor: 'rgba(219, 234, 254, var(--tw-bg-opacity))',
color: '#0284c7',
},
btnList: [],
isActive: false,
};
},
computed: {
...mapState('user', ['user']),
...mapGetters('user', ['userId']),
},
created() {
this.getInfo();
},
methods: {
...mapMutations('user', ['setToken', 'setUser']),
jump() {
uni.navigateTo({ url: '/pages/camera/camera' });
},
async getphonenumber(e) {
try {
if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
console.log('e: ', e);
} else if (e.detail.errMsg === 'getPhoneNumber:ok') {
const params = {
encryptedData: e.detail.encryptedData,
iv: e.detail.iv,
miniType: 'basicCar',
};
const data = await this.$u.api.bindPhone(params);
if (data && data.id) {
this.setUser(data);
this.setToken(data.token);
this.$refs.uTips.show({
title: '授权成功',
type: 'success',
duration: '3000',
});
}
}
} catch (error) {
this.$refs.uTips.show({
title: '授权失败',
type: 'success',
duration: '3000',
});
}
},
// wbs
async handleUpload() {
try {
const data = await this.$u.api.import();
// WBS
//
this.$emit('success');
data.url && (uni.$t.domain = data.url);
setTimeout(() => {
this.$u.route('/pages/project-webview/project-webview', {
u: this.userId,
p: data.id,
pname: data.pname,
url: data.url,
});
}, 2000);
} catch (error) {
console.log('error: ', error);
this.$emit('error', error);
}
},
//
jumpV(url) {
if (this.user.phone) {
this.$u.route('pages/questionnaire-webview/questionnaire-webview', { u: this.userId, url: url });
} else {
// this.$u.route('/pages/get-phone-power/get-phone-power');
this.$u.route('/pages/phone-bind/phone-bind');
}
},
//
async getInfo() {
const that = this;
const data = await that.$u.api.getQueryButton({});
this.btnList = data;
console.log('data: ', data);
if (data && data.length) {
that.name = data[0].name;
that.url = data[0].url;
} else if (data && data.name) {
that.name = data.name;
that.url = data.url;
} else if (!data) {
setTimeout(() => {
that.getInfo();
}, 500);
}
},
},
};
</script>
<style lang="scss" scoped>
.upload {
position: absolute;
z-index: 99;
right: 10px;
bottom: 0;
transform: translate3d(0, 50%, 0);
color: $uni-color-primary !important;
}
.role-more-box {
box-shadow: 0 6px 6px rgba(0, 0, 0, 0.15);
overflow: hidden;
width: 750rpx;
background: #fff;
z-index: 10;
right: -10px;
position: fixed;
border-radius: 0 0 6px 6px;
}
.jump-list-box {
height: 48px;
line-height: 48px;
text-align: center;
}
.border-bottom {
border-bottom: 1px solid rgba(155, 155, 155, 0.35);
}
</style>

418
src/components/select-lay/select-lay.vue

@ -0,0 +1,418 @@
<template>
<view class="uni-select-lay" :style="{ 'z-index': zindex }">
<input type="text" :name="name" v-model="value" class="uni-select-input" />
<view class="uni-select-lay-select" :class="{ active: active }">
<!-- 禁用mask -->
<view class="uni-disabled" v-if="disabled"></view>
<!-- 禁用mask -->
<!-- 清空 -->
<view class="uni-select-lay-input-close" v-if="changevalue != '' && this.active">
<text @click.stop="removevalue"></text>
</view>
<!-- 清空 -->
<input
type="text"
class="uni-select-lay-input"
:class="{ active: changevalue != '' && changevalue != placeholder }"
v-model="changevalue"
:disabled="disabled"
:placeholder="placeholder"
@focus="unifocus"
@input="intchange"
@blur="uniblur"
/>
<view class="uni-select-lay-icon" :class="{ disabled: disabled }" @click.stop="select"><text></text></view>
</view>
<scroll-view class="uni-select-lay-options" :scroll-y="true" v-show="active">
<template v-if="!changes">
<view class="uni-select-lay-item" v-if="showplaceholder" :class="{ active: value == '' }" @click.stop="selectitem(-1, null)">
{{ placeholder }}
</view>
<view
class="uni-select-lay-item"
:class="{ active: value == item[svalue] }"
v-for="(item, index) in options"
:key="index"
@click.stop="selectitem(index, item)"
>
{{ item[slabel] }}
</view>
</template>
<!-- 搜索 -->
<template v-else>
<template v-if="vlist.length > 0">
<view
class="uni-select-lay-item"
:class="{ active: value == item[svalue] }"
v-for="(item, index) in vlist"
:key="index"
@click.stop="selectitem(index, item)"
>{{ item[slabel] }}
</view>
</template>
<template v-else>
<view class="nosearch">{{ changesValue }}</view>
</template>
</template>
</scroll-view>
</view>
</template>
<script>
export default {
name: 'select-lay',
props: {
disabled: {
type: Boolean,
default: false,
},
zindex: {
type: Number,
default: 999,
},
options: {
type: Array,
default() {
return [];
},
},
name: {
type: String,
default: '',
},
value: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '请选择',
},
showplaceholder: {
type: Boolean,
default: true,
},
slabel: {
type: String,
default: 'label',
},
svalue: {
type: String,
default: 'value',
},
},
data() {
return {
active: false, //
isremove: false, //
changevalue: '', //
oldvalue: '', //
changes: false, //
changesValue: '',
vlist: [], //
settimer: null, //value
};
},
mounted() {
this.itemcheck();
},
watch: {
//value
value() {
this.itemcheck();
},
//
options() {
// value,
this.itemcheck();
},
},
methods: {
//active
itemcheck() {
// value,
if (this.value != '') {
// plachhoder
//
if (this.options.length > 0) {
this.options.forEach(item => {
if (this.value == item[this.svalue]) {
this.oldvalue = this.changevalue = item[this.slabel];
return;
}
});
}
} else {
this.oldvalue = this.changevalue = '';
}
},
//
select() {
if (this.disabled) return;
this.active = !this.active;
if (this.active) {
this.changes = false;
} else {
this.changevalue = this.oldvalue;
}
},
//
unifocus() {
if (this.disabled) return;
this.active = true;
this.changes = false;
},
//
uniblur() {
// bug
setTimeout(() => {
if (this.isremove) {
this.isremove = false;
} else {
this.changevalue = this.oldvalue;
this.isremove = false;
this.active = false;
}
}, 153);
},
//
removevalue() {
this.isremove = true;
this.changes = false;
this.changevalue = '';
},
//value
intchange() {
if (this.changevalue == '') {
this.changes = false;
return;
}
this.vlist = [];
this.changes = true;
this.changesValue = '正在搜索...';
if (this.settimer) {
clearTimeout(this.settimer);
}
this.settimer = setTimeout(() => {
this.vlist = this.options.filter(item => {
return item[this.slabel].includes(this.changevalue);
});
if (this.vlist.length === 0) {
this.changesValue = '暂无匹配内容!';
}
}, 600);
},
//
selectitem(index, item) {
this.changevalue = this.oldvalue;
this.active = false;
this.$emit('selectitem', index, item);
},
},
};
</script>
<style lang="scss" scoped>
.uni-select-lay {
position: relative;
z-index: 999;
.uni-select-input {
opacity: 0;
position: absolute;
z-index: -111;
}
// select
.uni-select-lay-select {
user-select: none;
position: relative;
z-index: 3;
height: 36px;
padding: 0 30px 0 10px;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid rgb(229, 229, 229);
display: flex;
align-items: center;
font-size: 14px;
color: #999;
.uni-disabled {
position: absolute;
left: 0;
width: 100%;
height: 100%;
z-index: 19;
cursor: no-drop;
background: rgba(255, 255, 255, 0.5);
}
// input
.uni-select-lay-input-close {
position: absolute;
right: 35px;
top: 0;
height: 100%;
width: 15px;
display: flex;
align-items: center;
justify-content: center;
z-index: 3;
cursor: pointer;
text {
position: relative;
background: #fff;
width: 13px;
height: 13px;
border-radius: 50%;
border: 1px solid #bbb;
&::before,
&::after {
content: '';
position: absolute;
left: 20%;
top: 50%;
height: 1px;
width: 60%;
transform: rotate(45deg);
background-color: #bbb;
}
&::after {
transform: rotate(-45deg);
}
}
}
.uni-select-lay-input {
font-size: 14px;
color: #999;
display: block;
width: 98%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 30px;
box-sizing: border-box;
&.active {
color: #333;
}
}
.uni-select-lay-icon {
cursor: pointer;
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 30px;
display: flex;
align-items: center;
justify-content: center;
&::before {
content: '';
width: 1px;
height: 100%;
position: absolute;
left: 0;
top: 0;
background-color: #e5e5e5;
}
text {
display: block;
width: 0;
height: 0;
border-width: 12rpx 12rpx 0;
border-style: solid;
border-color: #bbb transparent transparent;
transition: 0.3s;
}
&.disabled {
cursor: no-drop;
text {
width: 20rpx;
height: 20rpx;
border: 2px solid #ff0000;
border-radius: 50%;
transition: 0.3s;
position: relative;
z-index: 999;
&::after {
content: '';
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 2px;
margin-top: -1px;
background-color: #ff0000;
transform: rotate(45deg);
}
}
}
}
&.active .uni-select-lay-icon {
text {
transform: rotate(180deg);
}
}
}
// options
.uni-select-lay-options {
user-select: none;
position: absolute;
top: calc(100% + 5px);
left: 0;
width: 100%;
max-height: 500rpx;
// overflow-y: auto;
border-radius: 4px;
border: 1px solid rgb(229, 229, 229);
background: #fff;
padding: 5px 0;
box-sizing: border-box;
z-index: 9;
.uni-select-lay-item {
padding: 0 10px;
box-sizing: border-box;
cursor: pointer;
line-height: 2.5;
transition: 0.3s;
font-size: 14px;
&.active {
background: #007aff;
color: #fff;
&:hover {
background: #007aff;
color: #fff;
}
}
&:hover {
background-color: #f5f5f5;
}
}
.nosearch {
font-size: 16px;
line-height: 3;
text-align: center;
color: #666;
}
}
}
</style>

6
src/config/app.js

@ -0,0 +1,6 @@
export default {
V: process.env.VUE_APP_VERSION,
version: process.env.VUE_APP_VERSION,
theme: [],
tokenKey: 'anyringToken', // storage token key
};

3
src/config/db.js

@ -0,0 +1,3 @@
export const db = null; // indexedDB 对象
export const name = 'TALL_indexedDB'; // indexDB name
export const version = 1; // indexDB version

97
src/config/plugin.js

@ -0,0 +1,97 @@
// 定义插件相关信息
/* eslint-disable */
export default {
defaults: [
{
id: 1,
name: 'TASK_NAME',
description: '任务名插件',
component: 'p-task-title',
},
{
id: 2,
name: 'TASK_DESCRIPTION',
description: '任务描述插件',
component: 'p-task-description',
},
{
id: 3,
name: 'TASK_DURATION_DELAY',
description: '任务时长延迟插件(+-1min)时间格式可设置',
component: 'p-task-duration-delay',
},
{
id: 4,
name: 'TASK_START_TIME_DELAY',
description: '任务开始时间延迟插件(+-1hour)',
component: 'p-task-start-time-delay',
},
{
id: 5,
name: 'DELIVERABLE',
description: '交付物插件(人 + 交付物)可配置【仅人】 or 【仅交付物】 or 【人+交付物】',
component: 'p-deliverable',
},
{
id: 6,
name: 'SUBTASKS',
description: '子任务插件:显示子任务',
component: 'p-subtasks',
},
{
id: 7,
name: 'SUB_PROJECT',
description: '子项目插件:显示子项目',
component: 'p-sub-project',
},
{
id: 8,
name: 'TASK_COUNTDOWN',
description: '任务倒计时插件',
component: 'p-task-countdown',
},
{
id: 9,
name: 'MANAGE_PROJECT',
description: '项目信息管理插件',
component: 'p-manage-project',
},
{
id: 10,
name: 'MANAGE_ROLE',
description: '角色信息管理插件',
component: 'p-manage-role',
},
{
id: 11,
name: 'MANAGE_MEMBER',
description: '成员信息管理插件',
component: 'p-manage-member',
},
{
id: 12,
name: 'MANAGE_TASK',
description: '任务信息管理插件',
component: 'p-manage-task',
},
{
id: 13,
name: 'WBS_IMPORT',
description: '导入WBS新建项目',
component: 'p-wbs-import',
},
{
id: 14,
name: 'WBS_IMPORT_UPDATE',
description: '导入WBS更新项目',
component: 'p-wbs-update',
},
{
id: 15,
name: 'DELIVER_CHECK',
description: '交付物检查',
component: 'p-deliver-check',
},
], // 默认插件id列表
};

2
src/config/task.js

@ -0,0 +1,2 @@
// 每页加载颗粒度的个数
export default { pageCount: 10 };

17
src/config/time.js

@ -0,0 +1,17 @@
export default {
timeUnits: [
// 时间颗粒度
{ id: 0, value: '毫秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'millisecond' },
{ id: 1, value: '秒', format: 'x', cycle: 'YY-M-D HH:mm:ss', granularity: 'second' },
{ id: 2, value: '分', format: 'ss', cycle: 'YY-M-D HH:mm', granularity: 'minute' },
{ id: 3, value: '时', format: 'mm', cycle: 'YY-M-D HH时', granularity: 'hour' },
{ id: 4, value: '天', format: 'D日 HH:mm', cycle: 'YY-M-D', granularity: 'day' },
{ id: 5, value: '周', format: 'D日 HH:mm', cycle: '', granularity: 'week' },
{ id: 6, value: '月', format: 'D日 H:m', cycle: 'YYYY年', granularity: 'month' },
{ id: 7, value: '季度', format: '', cycle: 'YYYY年', granularity: 'quarter' },
{ id: 8, value: '年', format: 'YYYY', cycle: '', granularity: 'year' },
{ id: 9, value: '年代', format: '', cycle: '', granularity: '' },
{ id: 10, value: '世纪', format: '', cycle: '', granularity: '' },
{ id: 11, value: '千年', format: '', cycle: '', granularity: '' },
],
};

43
src/config/user copy.js

@ -0,0 +1,43 @@
// 用户登录client
export const clients = { mp: 0, h5: 1, android: 2, ios: 3 };
// 用户登录类型
export const types = {
mp: 0,
phone: 1,
email: 2,
username: 3,
wx: 4,
wx_web: 5,
wb: 6,
};
// 小程序获取参数
export const mp = () => {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success(res) {
if (res.code) {
const params = {
client: uni.$t.user.clients['mp'],
type: uni.$t.user.types['mp'],
data: { identifier: res.code },
};
resolve(params);
} else {
reject(res.errMsg);
}
},
fail() {
console.log('fail');
reject('微信登录失败');
},
});
});
};
export default {
clients,
types,
};

44
src/config/user.js

@ -0,0 +1,44 @@
// 用户登录client
export const clients = { mp: 0, h5: 1, android: 2, ios: 3 };
// 用户登录类型
export const types = {
mp: 0,
phone: 1,
email: 2,
username: 3,
wx: 4,
wx_web: 5,
wb: 6,
};
// 小程序获取参数
export const mp = () => {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success(res) {
if (res.code) {
const params = {
client: uni.$t.user.clients['mp'],
type: uni.$t.user.types['mp'],
data: { identifier: res.code, credential: 'basicCar' },
// data: { identifier: res.code, credential: 'tall' },
};
resolve(params);
} else {
reject(res.errMsg);
}
},
fail() {
console.log('fail');
reject('微信登录失败');
},
});
});
};
export default {
clients,
types,
};

5
src/config/zIndex.js

@ -0,0 +1,5 @@
// 定义项目中定位的元素的层级
/* eslint-disable */
export default {
roleBar: 999, // 角色栏成绩
};

52
src/main.js

@ -0,0 +1,52 @@
import App from './App';
import Tall from '@/utils/tall';
import Vue from 'vue';
import request from '@/utils/request.js';
import dayjs from 'dayjs';
import project from '@/apis/project.js';
import role from '@/apis/role.js';
import store from './store';
import tall from '@/apis/tall.js';
import task from '@/apis/task.js';
import carbasics from '@/apis/carbasics.js';
import uView from 'uview-ui';
import wbs from '@/apis/wbs.js';
//#ifdef H5
// import './registerServiceWorker';
// import AlloyFinger from 'alloyfinger';
// import AlloyFingerPlugin from 'alloyfinger/vue/alloy_finger_vue';
// Vue.use(AlloyFingerPlugin, { AlloyFinger });
// indexedDB
// import indexedDB from '@/utils/indexedDB';
// Vue.use(indexedDB);
//#endif
Vue.config.productionTip = false;
Vue.prototype.$moment = dayjs;
Vue.use(uView);
Vue.use(Tall);
uni.$moment = dayjs;
dayjs.locale('zh-cn');
App.mpType = 'app';
const app = new Vue({ ...App, store });
const obj = { title: '暴风眼Typhoneye' };
uni.showShareMenu(obj);
uni.onShareAppMessage = function () {
return { title: '暴风眼Typhoneye', path: '/pages/index/index' };
};
Vue.use(request, app);
Vue.use(tall, app);
Vue.use(project, app);
Vue.use(role, app);
Vue.use(carbasics, app);
Vue.use(task, app);
Vue.use(wbs, app);
app.$mount();

81
src/manifest.json

@ -0,0 +1,81 @@
{
"name": "暴风眼Typhoneye",
"appid": "wx3190e3f68dd4d068",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
/* 5+App */ "usingComponents": true,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
"modules": {
/* */
},
"distribute": {
/* */
"android": {
/* android */
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios": {
/* ios */
},
"sdkConfigs": {
/* SDK */
}
}
},
"quickapp": {
/* */
},
"mp-weixin": {
"appid": "wx3190e3f68dd4d068",
"setting": {
"urlCheck": false
},
"usingComponents": true
},
"h5": {
"router": {
"base": "/carBasicCalendar"
},
"title": "暴风眼Typhoneye",
"sdkConfigs": {
"maps": {}
},
"optimization": {
"treeShaking": {
"enable": false
}
}
}
}

48
src/mixins/userAuth.js

@ -0,0 +1,48 @@
import { mapMutations, mapState } from 'vuex';
import { waitTokenRequest } from '@/utils/cacheAndRequest';
export default {
computed: mapState('user', ['token', 'user']),
methods: {
...mapMutations('user', ['setUser']),
// 获取授权
openAuth() {
if (this.user && this.user.wxInfo && this.user.wxInfo.nickname) return; // 用户信息里有微信信息就不用再获取了
if (this.token) {
this.updateUserInfo();
} else {
waitTokenRequest(this.updateUserInfo);
}
},
// 弹出授权框
// 用户允许后 提交微信用户信息
updateUserInfo() {
/* #ifdef MP-WEIXIN */
uni.getUserProfile({
desc: '仅需要获取您的基本用户信息',
success: async res => {
const { avatarUrl, city, country, gender, language, nickName, province } = res.userInfo;
const data = await this.$u.api.updateUserInfo({
city,
country,
headImgUrl: avatarUrl,
language,
nickname: nickName,
province,
sex: gender,
});
console.log('data: ', data);
const { user } = this;
user.wxInfo = data;
this.setUser(user);
},
fail: error => {
console.log('error: ', error);
},
});
/* #endif */
},
},
};

78
src/pages.json

@ -0,0 +1,78 @@
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarText": "暴风眼Typhoneye"
}
},
{
"path": "pages/get-phone-power/get-phone-power",
"style": {
"navigationBarTitleText": "宣传页"
}
},
{
"path": "pages/phone-bind/phone-bind",
"style": {
"navigationBarTitleText": "绑定手机号"
}
},
{
"path": "pages/project-webview/project-webview",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "暴风眼Typhoneye"
}
},
{
"path": "pages/questionnaire-webview/questionnaire-webview",
"style": {
"navigationBarTitleText": "详情"
}
},
{
"path": "pages/camera/camera",
"style": {
"navigationBarTitleText": "拍照"
}
},
{
"path": "pages/task-page/task-page",
"style": {
"navigationBarTitleText": "项目详情页"
}
},
{
"path": "pages/patientLine/patientLine",
"style": {
"navigationBarTitleText": "病例填写目录"
}
},
{
"path": "pages/establish/establish",
"style": {
"navigationBarTitleText": "创建病例"
}
},
{
"path": "pages/inner/inner",
"style": {
"navigationBarTitleText": "信息录入"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "暴风眼Typhoneye",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"easycom": {
"autoscan": true,
"custom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue",
"^p-(.*)": "@/plugins/p-$1/p-$1.vue"
}
}
}

71
src/pages/camera/camera.vue

@ -0,0 +1,71 @@
<template>
<div class="camera">
<camera device-position="back" flash="auto" @error="error" style="width: 100%; height: 500upx">
<!-- <cover-image src="../../static/scan-frame/scan-img.png" class="scan-img"></cover-image> -->
</camera>
<view class="scan-text">支持将住院病案首页医嘱单拍照进行文字识别</view>
<button type="primary" @click="takePhoto">拍照</button>
<image v-for="item in srcList" :key="item" mode="widthFix" class="photos-box" :src="item"></image>
</div>
</template>
<script>
export default {
components: {},
data: () => ({ srcList: [] }),
computed: {},
methods: {
takePhoto() {
const ctx = uni.createCameraContext();
ctx.takePhoto({
quality: 'high',
success: res => {
this.srcList.push(res.tempImagePath);
console.log('this.srcList: ', this.srcList);
/* 返回调用页面并把图片URL传递过去 */
/* let pages = getCurrentPages();
let prevPage = pages[pages.length - 2];
prevPage.setData({
"image": res.tempImagePath,
})
uni.navigateBack(); */
/* 调用页面获取图片URL方法 */
/* let pages = getCurrentPages();
let currPage = pages[pages.length-1];
if(typeof(currPage.data.image) != undefined && currPage.data.image != null){
console.log('获取图片:', currPage.data.image)
} */
},
});
},
error(e) {
console.log(e.detail);
},
},
watch: {},
// --
onLoad() {},
// --
onReady() {},
// --(not-nvue)
onShow() {},
// --
onHide() {},
// --
onUnload() {},
// --
onPullDownRefresh() {
uni.stopPullDownRefresh();
},
// --
onReachBottom() {},
// --(not-nvue)
/* onPageScroll(event) {}, */
// --
/* onShareAppMessage(options) {}, */
};
</script>
<style></style>

444
src/pages/establish/establish.vue

@ -0,0 +1,444 @@
<template>
<div class="establish bg-gray-100">
<div bordered class="list-box bg-white" v-for="(item, index) in list" :key="index">
<div
class="list-item"
:style="{ 'border-bottom': listIndex === item.length - 1 ? '' : '1px solid #ccc;' }"
v-for="(listItem, listIndex) in item"
:key="listIndex"
>
<div class="w-full flex flex-nowrap justify-between items-center">
<div class="flex items-center" v-if="listItem.title">
{{ listItem.title }}
<span v-if="listItem.isTrue" style="color: red; font-size: 20px">*</span>
</div>
<div>
<u-switch v-if="listItem.type === 1" v-model="codeValue['CJBL-YLMS']" />
<u-upload
v-else-if="listItem.type === 2"
name="part"
accept="image/*"
:action="action"
:show-upload-list="false"
:custom-btn="true"
@on-success="successUpload"
>
<img slot="addBtn" src="./icon/idcard.png" class="img-icon" />
</u-upload>
<div v-else-if="listItem.type === 3" style="width: 174px">
<!-- {{ codeValue[listItem.code] }} -->
<u-input
input-align="right"
:value="codeValue[listItem.code]"
placeholder="请输入"
@blur="iptBlur(listItem.code)"
style="text-align: right"
/>
<!-- <input type="text" :value="codeValue[listItem.code]" /> -->
<div class="text-red-400 font-12" style="text-align: right" v-if="!isFit && listItem.code === 'CJBL-idCard'">
身份证号输入有误!
</div>
</div>
<u-radio-group :value="codeValue[listItem.code]" v-else-if="listItem.type === 4" name="radioGroup">
<div @click="clickRadio(listItem.code, radioItem.name)" v-for="radioItem in listItem.listItem" :key="radioItem.id">
<u-radio
@change="changeRadio($event, listItem.code)"
:disabled="isDisabled(listItem.code, radioItem.name)"
:key="radioItem.id"
:name="radioItem.id"
>
{{ radioItem.name }}
</u-radio>
</div>
<!-- <u-radio :value="1"> </u-radio> -->
</u-radio-group>
<u-radio-group
:disabled="sexIsDisabled('CJBL-idCard')"
:value="codeValue[listItem.code]"
v-else-if="listItem.type === 41"
name="radioGroup"
>
<u-radio
@change="changeRadio($event, listItem.code)"
v-for="radioItem in listItem.listItem"
:key="radioItem.id"
:name="radioItem.id"
>
{{ radioItem.name }}
</u-radio>
</u-radio-group>
<!-- <div
v-else-if="listItem.type === 5"
style="min-width: 100px; width: auto; justify-content: flex-end"
class="d-flex align-center"
@click="showDrop = true"
>
<span style="margin-right: 10px">{{ codeValue['CJBL-YSBL'] }}</span>
<img src="@/static/icon/right.png" style="height: 20px" />
</div> -->
</div>
</div>
</div>
</div>
<div class="d-flex btn-box" v-if="showBtn">
<u-button type="primary" :disabled="isSubmit" id="complete-btn" class="flex-1 btn mr-2" @click="submit"> 完成创建病例 </u-button>
<!-- <a-button
:disabled="isStartDisabled()"
:class="isStartDisabled() ? '' : 'green-btn'"
class="flex-2 btn ml-2"
@click="startGreen">
一键启动绿道
</a-button> -->
</div>
<u-toast ref="uToast" />
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from 'vuex';
export default {
components: {},
data: () => ({
action: '',
codeValue: {
'CJBL-YLMS': false,
'CJBL-NAME': null,
'CJBL-SEX': null,
'CJBL-nation': null,
'CJBL-idCard': null,
'CJBL-YSBL': 'AS急性脑卒中',
},
list: [
[{ title: '演练模式', type: 1, isTrue: false }],
[{ title: '扫描身份证', type: 2, isTrue: false }],
[
{ title: '姓名', type: 3, code: 'CJBL-NAME', isTrue: true },
{
title: '性别',
type: 41,
code: 'CJBL-SEX',
isTrue: true,
listItem: [
{ name: '女', id: 0 },
{ name: '男', id: 1 },
],
},
{ title: '民族', type: 3, code: 'CJBL-nation', isTrue: false },
{ title: '身份证号', type: 3, code: 'CJBL-idCard', isTrue: false },
{
title: '疑似诊断',
code: 'CJBL-YSBL',
type: 4,
isTrue: false,
},
{
title: '',
code: 'CJBL-YSBL',
type: 4,
isTrue: false,
listItem: [
{ name: 'AS急性脑卒中', id: 'AS急性脑卒中' },
{ name: 'AMI急性心肌梗死', id: 'AMI急性心肌梗死' },
{ name: 'ATI急性创伤', id: 'ATI急性创伤' },
{ name: 'AGB急性消化道出血', id: 'AGB急性消化道出血' },
],
},
],
],
dropList: [],
bodyStyle: { textAlign: 'center', height: '240px', padding: '24px 24px 0 24px', position: 'relative', overFlow: 'hidden' },
visible: false,
isActive: true,
active: 0,
showBtn: true,
role: '',
isFit: true, //
firstAidId: null,
showTips: false,
topOrBottom: true,
timer: null,
isSubmit: false,
isStart: false,
}),
computed: {
...mapGetters('project', ['projectId']),
...mapState('carbasics', ['globalData']),
},
onLoad(options) {
console.log('options: ', options);
this.role = options.role;
this.action = uni.$u.api.identifyWords;
},
methods: {
...mapMutations('carbasics', ['setRefreshList']),
//
successUpload(res) {
const { code, data } = res;
let obj = { ...this.codeValue };
if (code === 200) {
obj['CJBL-NAME'] = data.name;
obj['CJBL-SEX'] = data.sex;
obj['CJBL-idCard'] = data.idCardNo;
obj['CJBL-nation'] = data.nation;
this.codeValue = { ...obj };
}
},
//
isDisabled(code, name) {
if (code === 'CJBL-YSBL' && name !== 'AS急性脑卒中') {
return true;
} else {
return false;
}
},
sexIsDisabled(code) {
if (this.codeValue[code] && this.codeValue[code].length > 0) {
return true;
}
},
//
clickRadio(code, name) {
if (code === 'CJBL-YSBL' && name !== 'AS急性脑卒中') {
// this.$message.warning('');
this.$refs.uToast.show({
title: '请联系系统代理商开通该功能',
type: 'warning',
});
}
},
//
changeRadio(e, code) {
this.codeValue[code] = e;
},
//
iptBlur(code) {
if (code === 'CJBL-idCard') {
if (this.codeValue['CJBL-idCard']) {
this.isFit = this.isIdentityId(this.codeValue['CJBL-idCard']);
const idcard = this.codeValue['CJBL-idCard'];
if (this.codeValue['CJBL-idCard'].length === 15) {
this.codeValue['CJBL-SEX'] = (idcard[idcard.length - 1] - 0) % 2;
} else if (this.codeValue['CJBL-idCard'].length === 18) {
this.codeValue['CJBL-SEX'] = (idcard[idcard.length - 2] - 0) % 2;
}
} else {
this.isFit = true;
}
}
},
//
isIdentityId(identityId) {
var patrn = /(^\d{15}$)|(^\d{17}(\d|X|x)$)/; //
//
var aCity = {
11: '北京',
12: '天津',
13: '河北',
14: '山西',
15: '内蒙古',
21: '辽宁',
22: '吉林',
23: '黑龙江',
31: '上海',
32: '江苏',
33: '浙江',
34: '安徽',
35: '福建',
36: '江西',
37: '山东',
41: '河南',
42: '湖北',
43: '湖南',
44: '广东',
45: '广西',
46: '海南',
50: '重庆',
51: '四川',
52: '贵州',
53: '云南',
54: '西藏',
61: '陕西',
62: '甘肃',
63: '青海',
64: '宁夏',
65: '新疆',
71: '台湾',
81: '香港',
82: '澳门',
91: '国外',
};
//
var sBirthday = (identityId.substr(6, 4) + '-' + Number(identityId.substr(10, 2)) + '-' + Number(identityId.substr(12, 2))).replace(
/-/g,
'/',
),
d = new Date(sBirthday);
// 4 /X
var sum = 0,
weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2],
codes = '10X98765432',
codes1 = '10x98765432';
for (var i = 0; i < identityId.length - 1; i++) {
sum += identityId[i] * weights[i];
}
var last = codes[sum % 11]; //
var last1 = codes1[sum % 11]; //
var errorMsg = true;
if (identityId === '') {
errorMsg = false;
} else if (!patrn.exec(identityId)) {
errorMsg = false;
} else if (!aCity[parseInt(identityId.substr(0, 2))]) {
errorMsg = false;
} else if (sBirthday != d.getFullYear() + '/' + (d.getMonth() + 1) + '/' + d.getDate()) {
errorMsg = false;
} else if (identityId[identityId.length - 1] != last && identityId[identityId.length - 1] != last1) {
errorMsg = false;
}
return errorMsg;
},
/**
* 创建病例
*/
async submit() {
try {
this.isSubmit = true;
const params = {
gender: this.codeValue['CJBL-SEX'],
idcard: this.codeValue['CJBL-idCard'],
name: this.codeValue['CJBL-NAME'],
nation: this.codeValue['CJBL-nation'],
projectId: this.projectId,
suspected: this.codeValue['CJBL-YSBL'],
valueType: this.codeValue['CJBL-YLMS'] ? 1 : 0,
};
const data = await this.$u.api.savePatient(params);
if (data && data.firstAidId) {
// this.$message.success(',');
this.$refs.uToast.show({
title: '创建成功',
type: 'success',
});
this.firstAidId = data.firstAidId;
this.isSubmit = false;
if (this.role === 'YiSheng') {
// this.visible = true;
this.handleOk(this.globalData.createAuth - 0 === 0 ? 1 : 0);
uni.navigateBack();
uni.navigateTo({ url: `/pages/patientLine/patientLine?caseType=${this.globalData.createAuth - 0 === 0 ? 1 : 0}` });
} else if (this.role === 'HuShi') {
// this.$router.push(`/function`);
uni.navigateBack();
}
this.setRefreshList();
} else {
this.$refs.uToast.show({
title: '创建失败',
type: 'error',
});
this.isSubmit = false;
}
} catch (error) {
this.$refs.uToast.show({
title: '创建失败',
type: 'error',
});
this.isSubmit = false;
}
},
/**
* 点击了modal框的确认按钮
* 加入急救,并且切换tall的时间轴界面
*/
async handleOk(type) {
try {
const params = {
projectId: this.projectId,
firstAidId: this.firstAidId,
type,
};
const data = await uni.$u.api.joinAid(params);
if (data) {
const codeAndAnswerList = [
{
questionCode: 'JKJY-SFXJ',
answer: ['是'],
},
{
questionCode: 'JKJY-XJFS',
answer: ['集体病区教育'],
},
{
questionCode: 'YJJL-CTendTime-outSide',
answer: ['否'],
},
];
const setParams = {
codeAndAnswerList,
firstAidId: this.firstAidId,
};
uni.$u.api.setRecord(setParams);
}
} catch (error) {
console.log(error);
}
},
},
watch: {},
// --
onReady() {},
// --(not-nvue)
onShow() {},
// --
onHide() {},
// --
onUnload() {},
// --
onPullDownRefresh() {
uni.stopPullDownRefresh();
},
// --
onReachBottom() {},
// --(not-nvue)
/* onPageScroll(event) {}, */
// --
/* onShareAppMessage(options) {}, */
};
</script>
<style>
.img-icon {
height: 24px;
width: 24px;
color: #717171;
}
.btn-box {
padding: 16px;
}
.list-box {
border-radius: 4px;
overflow: hidden;
box-sizing: border-box;
margin: 0 0 0.75rem 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
font-feature-settings: 'tnum';
position: relative;
}
.list-item {
min-height: 54px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
}
</style>

BIN
src/pages/establish/icon/bottom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
src/pages/establish/icon/idcard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

156
src/pages/get-phone-power/get-phone-power.vue

@ -0,0 +1,156 @@
<template>
<view>
<!-- <img style="margin-left: 75rpx; width: 600rpx; height: 800rpx" src="https://www.tall.wiki/staticrec/img/data.png" /> -->
<div style="margin-top: 100rpx; width: 750rpx; height: 850rpx">
<swiper class="swiper" :circular="true" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="3000">
<swiper-item v-for="item in imgList" :key="item">
<view class="swiper-item uni-bg-red">
<img style="width: 750rpx; height: 800rpx" :src="item" />
</view>
</swiper-item>
</swiper>
</div>
<!-- <u-button
v-if="user && user.wxInfo && !user.wxInfo.headImgUrl"
type="primary"
:custom-style="customStyle1"
size="default"
open-type="openAuth"
@click="openAuth"
>
授权信息
</u-button>
<u-button v-else-if="projects.length" type="primary" :custom-style="customStyle1" size="default" @click="openProject">
脑防委数据上报及质控分析体验
</u-button> -->
<u-button :custom-style="customStyle" disabled shape="square" size="default" type="primary">
<!-- open-type="getPhoneNumber" -->
<!-- @getphonenumber="getphonenumber" -->
<!-- 脑防委数据上报及质控分析体验 -->
问卷提交截止
</u-button>
<u-modal
v-model="show"
:content="content"
show-cancel-button
cancel-text="不合并"
confirm-text="合并"
@confirm="bindNowPhone(0)"
@cancel="bindNowPhone(1)"
></u-modal>
</view>
</template>
<script>
import { mapState, mapMutations, mapGetters } from 'vuex';
import UserAuthMixin from '@/mixins/userAuth';
export default {
mixins: [UserAuthMixin],
data() {
return {
imgList: [
'https://www.tall.wiki/staticrec/img/1.png',
'https://www.tall.wiki/staticrec/img/2.png',
'https://www.tall.wiki/staticrec/img/3.png',
'https://www.tall.wiki/staticrec/img/4.png',
'https://www.tall.wiki/staticrec/img/5.png',
],
customStyle: {
margin: '0 88rpx',
width: '574rpx',
},
customStyle1: {
backgroundColor: 'rgba(41, 121, 255, 0.6)',
margin: '0 88rpx',
width: '574rpx',
},
show: false,
content: '当前手机号已被注册,是否合并信息',
phone: '',
};
},
computed: {
...mapState('user', ['user']),
...mapState('project', ['projects']),
...mapGetters('user', ['userId']),
},
created() {
this.getProjects();
},
methods: {
...mapMutations('user', ['setUser']),
...mapMutations('project', ['setProjects']),
//
getProjects(start = this.$moment().startOf('day').valueOf(), end = this.$moment().endOf('day').valueOf()) {
// const data = await this.$u.api.getProjects(start, end);
this.$t.$q.getProjects(start, end, (err, data) => {
if (err) {
console.error('err: ', err);
} else {
data.forEach(item => {
item.show = false;
});
this.setProjects(data);
console.log('data: ', data);
console.log('project:', this.projects);
}
});
},
openProject() {
const { name, id, url } = this.projects[0];
url && (uni.$t.domain = url);
this.$u.route('pages/project-webview/project-webview', {
u: this.userId,
p: id,
pname: name,
url: encodeURIComponent(url),
});
},
// ,
// ,
// ,,id,,
// id,,
async getphonenumber(e) {
console.log('e: ', e);
// // if (e.detail.errMsg === 'getPhoneNumber:fail user deny') {
// // this.$u.route('/pages/phone-bind/phone-bind');
// // } else {
// if (e.detail.errMsg === 'getPhoneNumber:ok') {
// const params = {
// encryptedData: e.detail.encryptedData,
// iv: e.detail.iv,
// miniType: 'basicCar',
// };
// const data = await this.$u.api.bindPhone(params);
// if (data && data.id) {
// // const newUser = { ...this.user, phone: data.phone };
// // this.setUser(newUser);
// // setTimeout(() => uni.navigateBack({ delta: 2 }), 50);
// this.$u.route('pages/questionnaire-webview/questionnaire-webview', { u: this.userId });
// } else {
// this.phone = data.phone;
// this.show = true;
// }
// }
},
// isMerge:0 , 1
async bindNowPhone(num) {
const params = {
phone: this.phone,
isMerge: num,
};
const data = await this.$u.api.bindNowPhone(params);
this.setUser(data);
this.show = false;
setTimeout(() => uni.navigateBack({ delta: 2 }), 500);
},
},
};
</script>
<style scoped lang="scss">
.swiper {
height: 800rpx;
}
</style>

170
src/pages/index/index.vue

@ -0,0 +1,170 @@
<template>
<!-- <view class="flex flex-col h-full bg-gray-50" @click="openAuth"> -->
<view class="flex flex-col h-screen bg-gray-50">
<view class="relative" @touchmove="onMove">
<!-- 日历 -->
<Calendar @selected-change="onDateChange" :show-back="true" ref="calendar" @handleFindPoint="handleFindPoint" />
<!-- 上传 导入wbs -->
<Upload @success="onUploadSuccess" @error="onUploadError" />
<!-- #ifdef H5 -->
<!-- #endif -->
</view>
<!-- 联系客服 -->
<Service />
<!-- 项目列表 -->
<Projects @getProjects="getProjects" class="flex-1 overflow-y-auto" />
<!-- 全局提示框 -->
<u-top-tips ref="uTips"></u-top-tips>
</view>
</template>
<script>
import { mapState, mapMutations, mapGetters } from 'vuex';
import UserAuthMixin from '@/mixins/userAuth';
let prevY = 0;
export default {
mixins: [UserAuthMixin],
data() {
return {
calendar: null,
days: [],
};
},
computed: {
...mapState('user', ['token', 'user']),
...mapGetters('user', ['userId']),
},
watch: {
// token(value) {
// if (!value) return;
// this.getProjects();
// this.handleFindPoint();
// },
user(value) {
if (!value) return;
const that = this;
setTimeout(function () {
that.getProjects();
that.handleFindPoint();
}, 100);
},
},
async created() {
if (!this.token) return;
this.getProjects();
this.handleFindPoint();
},
onReady() {
this.calendar = this.$refs.calendar;
},
methods: {
...mapMutations('project', ['setProjects', 'setDotList']),
//
getProjects(start = this.$moment().startOf('day').valueOf(), end = this.$moment().endOf('day').valueOf()) {
// const res = await this.$u.api.getQueryButton({});
// const data = await this.$u.api.getProjects(start, end);
this.$t.$q.getProjects(start, end, (err, data) => {
if (err) {
console.error('err: ', err);
} else {
data.forEach(item => {
item.show = false;
});
this.setProjects(data);
if (data && data[0]) {
// this.openFirstProject(data[0]);
}
// else if (this.user.phone) {
// this.$u.route('pages/questionnaire-webview/questionnaire-webview', { u: this.userId, url: res.url });
// }
}
});
},
/**
* 查询小红点
* @param { string } endTime 结束时间
* @param { string } startTime 开始时间
*/
async handleFindPoint(start, end) {
try {
const startTime = start || this.$moment().startOf('month').valueOf();
const endTime = end || this.$moment().endOf('month').valueOf();
const data = await this.$u.api.findRedPoint(startTime, endTime);
this.setDotList(data);
} catch (error) {
console.log('error: ', error);
}
},
//
onDateChange(event) {
const day = this.$moment(event.fullDate);
const start = day.startOf('date').valueOf();
const end = day.endOf('date').valueOf();
this.getProjects(start, end);
},
// /
onMove(event) {
const y = event.changedTouches[0].pageY;
if (y - prevY > 0) {
// weekMode=true weekMode=false
this.calendar.weekMode && (this.calendar.weekMode = false);
} else if (y - prevY < 0) {
// weekMode=false weekMode=true
!this.calendar.weekMode && (this.calendar.weekMode = true);
}
prevY = y;
this.calendar.initDate();
},
//
onUploadSuccess() {
this.$refs.uTips.show({
title: '导入成功,即将打开新项目',
type: 'success',
duration: '3000',
});
},
//
onUploadError(error) {
this.$refs.uTips.show({
title: error || '导入失败',
type: 'error',
duration: '6000',
});
},
//
openFirstProject(project) {
const { name, id, url } = project;
url && (uni.$t.domain = url);
this.$u.route('pages/project-webview/project-webview', {
u: this.userId,
p: id,
pname: name,
url: encodeURIComponent(url),
});
},
},
};
</script>
<style lang="scss" scoped>
page {
height: 100%;
overflow: hidden;
}
</style>

36
src/pages/inner/inner.vue

@ -0,0 +1,36 @@
<template>
<div class="inner">inner</div>
</template>
<script>
export default {
components: {},
data: () => ({}),
computed: {},
methods: {},
watch: {},
// --
onLoad() {},
// --
onReady() {},
// --(not-nvue)
onShow() {},
// --
onHide() {},
// --
onUnload() {},
// --
onPullDownRefresh() {
uni.stopPullDownRefresh();
},
// --
onReachBottom() {},
// --(not-nvue)
/* onPageScroll(event) {}, */
// --
/* onShareAppMessage(options) {}, */
};
</script>
<style></style>

181
src/pages/patientLine/patientLine.vue

@ -0,0 +1,181 @@
<template>
<div class="patient-line">
<template v-if="showLine">
<div v-for="(item, index) in dataList" :key="index" class="flex flex-row item-box mb-2">
<div class="flex flex-col items-center mr-1" style="width: 36px">
<img src="./png/播放.png" alt="" style="width: 36px; height: 36px" />
<div class="mt-1 flex-1" style="border-right: 2px solid rgb(209, 213, 219)"></div>
<div v-if="index === innerList.length - 1" class="end-ric mt-2"></div>
</div>
<div class="flex-1 flex-col">
<div class="w-full time-box flex items-center" style="height: 34px; padding-left: 12px">
<!-- {{ $moment(time).format('MM月DD日') }} -->
{{ item.time }}
</div>
<div class="flex mt-2" style="height: 50px" @click="jump(item.code, item.path)">
<div class="name-box flex items-center time-box" style="padding-left: 16px; position: relative">
{{ item.name }}
<u-icon name="arrow-right" size="28" color="#303133" style="position: absolute; right: 16px" />
</div>
</div>
</div>
</div>
</template>
</div>
</template>
<script>
import { mapMutations } from 'vuex';
export default {
components: {},
data: () => ({
showLine: false,
innerList: [
//
{
name: '患者信息',
time: '11月24日 16:56',
code: '',
path: '/pages/inner/inner',
},
{
name: '基本信息',
time: '11月24日 16:56',
code: '',
path: '/pages/inner/inner',
},
{
name: '入院评估',
time: '11月24日 16:56',
code: 'RYPG',
path: '/pages/inner/inner',
},
{
name: '主要治疗操作',
time: '11月24日 16:56',
code: 'SSXG',
path: '/pages/inner/inner',
},
{
name: '出院记录',
time: '11月24日 16:56',
code: 'CYJL',
path: '/pages/inner/inner',
},
],
outSideList: [
//
{
name: '患者信息',
time: '11月24日 16:56',
code: '',
path: '/pages/inner/inner',
},
{
name: '基本信息',
time: '11月24日 16:56',
code: '',
path: '/pages/inner/inner',
},
{
name: '入院评估',
time: '11月24日 16:56',
code: 'RYPG',
path: '/pages/inner/inner',
},
{
name: '主要检查',
time: '11月24日 16:56',
code: 'ZYJC',
path: '/pages/inner/inner',
},
{
name: '手术相关操作',
time: '11月24日 16:56',
code: 'SSXG',
path: '/pages/inner/inner',
},
{
name: '出院记录',
time: '11月24日 16:56',
code: 'CYJL',
path: '/pages/inner/inner',
},
],
time: 0,
caseType: 0,
}),
computed: {
dataList() {
return this.caseType === 0 ? this.innerList : this.outSideList;
},
},
onLoad(options) {
console.log('options: ', options);
this.caseType = options.caseType - 0;
this.showLine = true;
},
methods: {
...mapMutations('carbasics', ['setInputCode']),
jump(code, path) {
// console.log('code, path: ', code, path);
this.setInputCode(code ? code : 'RYPG');
uni.navigateTo({ url: path });
},
},
watch: {},
// --
onReady() {},
// --(not-nvue)
onShow() {},
// --
onHide() {},
// --
onUnload() {},
// --
onPullDownRefresh() {
uni.stopPullDownRefresh();
},
// --
onReachBottom() {},
// --(not-nvue)
/* onPageScroll(event) {}, */
// --
/* onShareAppMessage(options) {}, */
};
</script>
<style>
.patient-line {
margin-top: 16px;
}
.end-ric {
height: 6px;
width: 6px;
border: 2px solid rgb(209, 213, 219);
border-radius: 50%;
}
.name-box {
background-color: #fff;
height: 50px;
width: 100%;
border-radius: 10px;
box-shadow: 0px 5px 15px 5px rgba(0, 0, 0, 0.1);
}
.time-box {
font-size: 14px;
font-weight: 500;
color: #303133;
}
.red {
border: 1px solid red;
}
.green {
border: 1px solid green;
}
.item-box {
/* height: 110px; */
width: 100%;
padding: 0 16px;
}
</style>

BIN
src/pages/patientLine/png/播放.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

192
src/pages/phone-bind/phone-bind.vue

@ -0,0 +1,192 @@
<template>
<view class="wrap" @click="openAuth">
<u-form :model="model" :rules="rules" ref="uForm" :errorType="errorType">
<!-- 手机号 -->
<u-form-item :rightIconStyle="{ color: '#888', fontSize: '32rpx' }" label="手机号码" prop="phone" label-width="160">
<u-input placeholder="请输入手机号" v-model="model.phone" type="number"></u-input>
</u-form-item>
<u-form-item label="图形验证码" prop="imageValue" label-width="160">
<u-input placeholder="输入计算结果" v-model="model.imageValue" type="number"></u-input>
<ImageCode slot="right" @on-code="codeId = $event" />
</u-form-item>
<u-form-item label="验证码" prop="code" label-width="160">
<u-input placeholder="请输入短信验证码" v-model="model.code" type="number"></u-input>
<u-button slot="right" type="primary" size="mini" @click="getCode">{{ codeTips }}</u-button>
</u-form-item>
</u-form>
<view class="mt-8">
<u-button @click="submit" type="primary">提交</u-button>
</view>
<u-verification-code :seconds="seconds" ref="uCode" @change="codeChange"></u-verification-code>
<u-top-tips ref="uTips"></u-top-tips>
</view>
</template>
<script>
import UserAuthMixin from '@/mixins/userAuth';
export default {
mixins: [UserAuthMixin],
data() {
return {
model: {
phone: '', //
imageValue: '', //
code: '', //
},
codeId: '', // id
rules: {
phone: [
{
required: true,
message: '请输入手机号',
trigger: ['change', 'blur'],
},
{
validator: (rule, value) => {
return this.$u.test.mobile(value);
},
message: '手机号码不正确',
trigger: ['change', 'blur'],
},
],
code: [
{
required: true,
message: '请输入验证码',
trigger: ['change', 'blur'],
},
{
validator: (rule, value) => {
return this.$u.test.code(value, 4);
},
message: '验证码格式不正确',
trigger: ['change', 'blur'],
},
],
},
seconds: 120, //
codeTips: '',
errorType: ['message'],
};
},
onReady() {
this.$refs.uForm.setRules(this.rules);
},
methods: {
submit() {
this.$refs.uForm.validate(async valid => {
if (valid) {
try {
const { phone, code } = this.model;
const data = await this.$u.api.phoneBind(phone, code);
console.log('data: ', data);
this.$refs.uTips.show({
title: '手机号绑定成功, 即将跳转上一页',
type: 'success',
duration: '3000',
});
setTimeout(() => uni.navigateBack(-2), 2000);
} catch (error) {
this.$refs.uTips.show({
title: error.msg || '手机号绑定失败',
type: 'error',
duration: '3000',
});
}
} else {
console.log('验证失败');
}
});
},
codeChange(text) {
this.codeTips = text;
},
//
async getCode() {
if (this.$refs.uCode.canGetCode) {
try {
if (!this.validateCodeParams()) return;
const params = {
phone: this.model.phone,
verificationCodeId: this.codeId,
verificationCodeValue: this.model.imageValue,
};
const data = await this.$u.api.getSmsCode(params);
this.seconds = data.expiredInSeconds;
this.$refs.uTips.show({
title: '短信发送成功, 请查收',
type: 'success',
duration: '3000',
});
//
this.$refs.uCode.start();
} catch (error) {
this.$refs.uTips.show({
title: error.msg || '发送失败',
type: 'error',
duration: '3000',
});
}
} else {
this.$u.toast('倒计时结束后再发送');
}
},
//
validateCodeParams() {
if (!this.$u.test.mobile(this.model.phone)) {
this.$refs.uTips.show({
title: '手机号输入不正确',
type: 'error',
duration: '3000',
});
return false;
}
if (!this.codeId) {
this.$refs.uTips.show({
title: '点击刷新图形验证码重试',
type: 'error',
duration: '3000',
});
return false;
}
if (!this.model.imageValue && this.model.imageValue !== 0) {
this.$refs.uTips.show({
title: '请输入图形验证码',
type: 'error',
duration: '3000',
});
return false;
}
return true;
},
},
};
</script>
<style scoped lang="scss">
.wrap {
padding: 30rpx;
}
.agreement {
display: flex;
align-items: center;
margin: 40rpx 0;
.agreement-text {
padding-left: 8rpx;
color: $u-tips-color;
}
}
</style>

41
src/pages/project-webview/project-webview.vue

@ -0,0 +1,41 @@
<template>
<web-view :src="src" />
</template>
<script>
export default {
data() {
return { src: '' };
},
onLoad(options) {
const obj = { title: '暴风眼Typhoneye', path: '/pages/index/index' };
uni.showShareMenu(obj);
if (!options) {
this.$t.ui.showModal('缺少参数, 请返回重试');
} else {
const { p, u, pname, url } = options;
// if (pname) {
// uni.setNavigationBarTitle({ title: pname });
// }
if (!u) {
this.$t.ui.showToast('缺少用户信息, 请登录');
return;
}
if (!p || !url) {
this.$t.ui.showToast('缺少项目信息, 请重新打开');
return;
}
const baseUrl = process.env.VUE_APP_PROJECT_PATH;
this.src = `${baseUrl}/#/?u=${u}&p=${p}&url=${url}&pname=${pname}`;
}
},
methods: {
onShareAppMessage() {
return { title: '暴风眼Typhoneye', path: '/pages/index/index' };
},
},
};
</script>

31
src/pages/questionnaire-webview/questionnaire-webview.vue

@ -0,0 +1,31 @@
<template>
<web-view :src="src" />
</template>
<script>
export default {
data() {
return { src: '' };
},
onLoad(options) {
console.log('options: ', options);
if (!options) {
this.$t.ui.showModal('缺少参数, 请返回重试');
} else {
const { u, url } = options;
if (!u) {
this.$t.ui.showToast('缺少用户信息, 请登录');
return;
}
if (!url) {
this.$t.ui.showToast('缺少跳转信息, 请返回重试');
return;
}
// const baseUrl = process.env.VUE_APP_QUESTION_PATH;
// this.src = `${baseUrl}/hospital?userId=${u}`;
this.src = `${url}?userId=${u}`;
}
},
};
</script>

319
src/pages/task-page/task-page.vue

@ -0,0 +1,319 @@
<template>
<view :style="{ height: height }" class="flex flex-col overflow-hidden u-font-14">
<view class="container flex flex-col flex-1 mx-auto overflow-hidden bg-gray-100">
<!-- 角色栏 -->
<Roles />
<!-- 日常任务面板 -->
<Globals v-if="globals.length" />
<!-- 任务详情 -->
<div v-if="globalData && permanents.length" class="flex flex-1">
<PatientList v-if="showPage === 'function'" class="flex flex-1" />
</div>
</view>
</view>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
export default {
data() {
return {
height: '',
show: false,
count: 0,
chooseItem: false,
name: '',
url: '',
showStatus: 0,
showPage: '',
};
},
computed: {
...mapState('task', ['permanents']),
...mapState('user', ['user', 'token']),
...mapState('role', ['visibleRoles', 'roleId']),
...mapState('project', ['project']),
...mapGetters('task', ['globals']),
...mapGetters('project', ['projectId']),
...mapState('carbasics', ['globalData']),
},
async onLoad(options) {
this.init(options);
},
watch: {
/**
* 当角色发生变化时
* 重新查询永久日常任务和普通日常任务
* 注意: 切换角色后 重新设置了时间基准点 时间基准点一定会变
* 所以监听时间基准点获取 可变日常任务即可 这里不用获取 避免重复获取
*/
roleId(val) {
if (val) {
const params = { roleId: val, projectId: this.projectId };
this.getPermanent(params);
}
},
//
newProjectInfo(val) {
if (val && val.projectId && val.url) {
this.$u.route('/', { u: this.userId, p: val.projectId, url: val.url });
this.clearTasksData();
this.setRoleId('');
const options = this.$route.query;
this.init(options);
this.getByProject(val.projectId);
}
},
globalData(val) {
var strList = val.url.split('/');
this.showPage = strList[strList.length - 1];
},
},
mounted() {
const system = uni.getSystemInfoSync();
this.height = system.windowHeight + 'px';
},
onUnload() {
// this.clearTasksData();
this.setRoleId('');
},
methods: {
...mapActions('user', ['getToken']),
...mapActions('task', ['getPermanent']),
...mapMutations('user', ['setToken']),
...mapMutations('project', ['setProject', 'setProjectName', 'setOrganData']),
...mapMutations('role', ['setInvisibleRoles', 'setVisibleRoles', 'setRoleId']),
//
async initPlanTasks() {
this.setPrevPlaceholderTasks(); //
this.setNextPlaceholderTasks(); //
// // this.$nextTick(() => this.$refs.timeLine.setScrollPosition()); //
await this.getInitTasks(); //
//
// let timer = null;
// timer = setInterval(() => {
// if (this.showScrollTo) {
// clearInterval(timer);
// this.$nextTick(() => this.$refs.timeLine.setScrollPosition());
// }
// }, 1000);
},
// ||
getInitTasks() {
//
function preloadFn(that) {
const detailId = that.tasks.findIndex(task => task.detailId);
const arr = [];
// setTimeout(() => {
// that.tasks.forEach(task => {
// if (task.detailId) {
// arr.push(task);
// }
// });
// that.$nextTick(() => that.$refs.timeLine.setScrollPosition());
// }, 100);
if (detailId !== -1) {
// 1
const { pageCount } = that.$t.task;
that.$nextTick(() => {
//
const { tasks, timeGranularity } = that;
that.getTasks({ timeNode: +tasks[detailId].planStart, queryType: 0, queryNum: pageCount });
//
const nextQueryTime = +that.$t.time.add(+arr[arr.length - 1].planStart, 1, timeGranularity);
that.getTasks({ timeNode: nextQueryTime, queryType: 1, queryNum: pageCount });
});
} else {
//
//
// that.setPrevPlaceholderTasks();
// //
// that.setNextPlaceholderTasks();
}
}
//
this.getTasks({ queryType: 0 }); //
// id
this.getTasks({ queryType: 1 }, preloadFn); //
},
/**
* 根据时间基准点和角色查找定期任务
* @param {object} query
* @param {string} query.roleId 角色id
* @param {string} query.timeNode 时间基准点 默认当前
* @param {string} query.timeUnit 时间颗粒度 默认天
* @param {string} query.queryNum 查找颗粒度数量 默认3个
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己上查不包含
*/
getTasks(query, fn) {
this.setShowSkeleton(true);
const params = this.generateGetTaskParam(query);
this.$t.$q.getRegularTask(params, (err, data) => {
this.setShowSkeleton(false);
if (err) {
// TODO:
console.error('err: ', err);
} else {
this.setShowScrollTo(true);
//
//
if (data && data.length) {
this.replacePrevData(data, params.queryType);
params.queryType === 0 ? this.setTopEnd(false) : this.setBottomEnd(false);
} else {
// TODO: 0 -> 1 ->
params.queryType === 0 ? this.setPrevPlaceholderTasks() : this.setNextPlaceholderTasks();
}
if (this.tasks.length && fn) {
fn(this);
}
}
});
},
/**
* 生成getTasks所用的参数
* @param {object} query getTasks传递的参数
*/
generateGetTaskParam(query) {
const { roleId, timeNode, timeUnit, projectId } = this;
return {
roleId,
timeNode: query.timeNode || timeNode,
timeUnit: query.timeUnit || timeUnit,
queryNum: query.queryNum || 3,
queryType: query.queryType,
projectId,
};
},
/**
* 初始化
* @param {object | null} options
*/
init(options) {
//
options && options.pname && this.setProjectName(options.pname);
if (!options || !options.p) {
this.$t.ui.showToast('缺少项目信息参数'); // id
} else {
uni.setNavigationBarTitle({ title: options.pname });
if (options.p !== this.$t.storage.getStorageSync('projectId')) {
this.$t.storage.setStorageSync('roleId', '');
}
// TODO
this.getProjectById({ projectId: options.p, num: 0 }); // id
this.getByProject(options.p);
}
},
/**
* 通过项目id获取项目信息
* @param {object} params 提交的参数
*/
async getProjectById(params) {
try {
const data = await this.$u.api.findProjectById(params);
this.setProject(data);
// id
this.getRoles(params);
} catch (error) {
console.log('error: ', error || '获取项目信息失败');
}
},
/**
* 通过项目id获取角色信息
* @param {string} projectId
* @param {object} params 提交的参数
*/
getRoles(params) {
this.$t.$q.findShowRole(params, (err, data) => {
if (err) {
console.error('err: ', err || '获取角色信息失败');
} else {
this.setInvisibleRoles(data ? data.invisibleList : []);
this.setVisibleRoles(data ? data.visibleList : []);
this.setInitialRoleId(data ? data.visibleList : []);
}
});
},
//
async setInitialRoleId(visibleList) {
if (!visibleList || !visibleList.length) return;
const index = visibleList.findIndex(item => +item.mine === 1);
const currentRole = index > 0 ? visibleList[index] : visibleList[0];
const storageRoleId = this.$t.storage.getStorageSync('roleId');
const params = { param: { projectId: this.projectId } };
const data = await uni.$u.api.queryLastRoleChoose(params);
if (data && data.roleId) {
this.setRoleId(data.roleId);
} else {
const currentRoleId = storageRoleId ? storageRoleId : currentRole ? currentRole.id : '';
const saveParams = { param: { roleId: currentRoleId, projectId: this.projectId } };
await uni.$u.api.saveRoleRecord(saveParams);
this.setRoleId(currentRoleId);
}
// storage
this.$t.storage.setStorageSync('roleId', '');
},
//
clearTasksData() {
//
this.setPermanents([]);
this.setDailyTasks([]);
//
this.clearTasks();
//
//
this.clearEndFlag();
},
// id
async getByProject(projectId) {
try {
const param = { projectId };
const data = await this.$u.api.getByProject(param);
if (data && data.organizationType === 3) {
this.showEXP = true;
this.showGuide = true;
this.setOrganData(data);
} else if (data && data.organizationType === 0) {
this.setOrganData(data);
} else {
this.showEXP = false;
this.showGuide = false;
}
} catch (error) {
console.log('error: ', error);
}
},
},
};
</script>
<style lang="scss" scoped>
.container {
width: 100%;
}
.border-b {
border-bottom: 1px solid #e4e7ed;
}
</style>

29
src/registerServiceWorker.js

@ -0,0 +1,29 @@
/* eslint-disable no-console */
import { register } from 'register-service-worker';
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log('App is being served from cache by a service worker.\n' + 'For more details, visit https://goo.gl/AFskqB');
},
registered() {
console.log('Service worker has been registered.');
},
cached() {
console.log('Content has been cached for offline use.');
},
updatefound() {
console.log('New content is downloading.');
},
updated() {
console.log('New content is available; please refresh.');
},
offline() {
console.log('No internet connection found. App is running in offline mode.');
},
error(error) {
console.error('Error during service worker registration:', error);
},
});
}

3
src/store/carbasics/actions.js

@ -0,0 +1,3 @@
const actions = {};
export default actions;

3
src/store/carbasics/getters.js

@ -0,0 +1,3 @@
const getters = {};
export default getters;

12
src/store/carbasics/index.js

@ -0,0 +1,12 @@
import state from './state';
import getters from './getters';
import mutations from './mutations';
import actions from './actions';
export default {
namespaced: true,
state,
getters,
mutations,
actions,
};

35
src/store/carbasics/mutations.js

@ -0,0 +1,35 @@
const mutations = {
/**
* 存储当前正在进行的患者的急救id
* @param { object } state
* @param { string } id
*/
setFirstAidId(state, id) {
state.firstAidId = id;
},
/**
* 存储当前插件的param参数,包括url和其他具体参数
* @param { object } state
* @param { object } data
*/
setGlobalData(state, data) {
state.globalData = { ...data };
},
/**
* 监听refreshList变化,重新请求病例列表数据
* @param { object } state
*/
setRefreshList(state) {
state.refreshList = state.refreshList + 1;
},
/**
* 信息录入界面默认为 RYPG(入院评估),当进入患者目录选择时,切换此code
* @param { object } state
* @param { string } data
*/
setInputCode(state, data) {
state.InputCode = data;
},
};
export default mutations;

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

Loading…
Cancel
Save