Browse Source

跳绳比赛远程报名系统

master
aBin 5 years ago
commit
1fe04ebefa
  1. 23
      .eslintrc.js
  2. 17
      .gitignore
  3. 12
      .prettierrc
  4. 31
      App.vue
  5. 111
      README.md
  6. 6
      api/base.js
  7. 5
      api/coachInfo.js
  8. 5
      api/competeTime.js
  9. 5
      api/deletecoach.js
  10. 5
      api/delplayer.js
  11. 5
      api/firstproject.js
  12. 5
      api/getbase.js
  13. 5
      api/getcode.js
  14. 5
      api/getcompany.js
  15. 5
      api/group.js
  16. 6
      api/groupplayer.js
  17. 5
      api/join.js
  18. 5
      api/login.js
  19. 5
      api/overview.js
  20. 5
      api/password.js
  21. 5
      api/phone.js
  22. 4
      api/photoBase64.js
  23. 5
      api/playerInfo.js
  24. 5
      api/querycoach.js
  25. 5
      api/register.js
  26. 5
      api/savecoach.js
  27. 5
      api/savecompany.js
  28. 5
      api/saveplayer.js
  29. 6
      api/secondproject.js
  30. 4
      api/upload.js
  31. 5
      api/user.js
  32. 0
      api/xxxx.js
  33. 184
      colorui/animation.scss
  34. 1226
      colorui/icon.scss
  35. 3990
      colorui/main.scss
  36. 34
      common/style/global.scss
  37. 0
      common/style/iconfont.scss
  38. 17
      components/ms-dropdown/README.md
  39. 213
      components/ms-dropdown/dropdown-item.vue
  40. 35
      components/ms-dropdown/dropdown-menu.vue
  41. 19
      components/test/test.vue
  42. 1
      config/config.default.js
  43. 20
      config/config.user.js
  44. 64
      main.js
  45. 116
      manifest.json
  46. 36
      package-lock.json
  47. 34
      package.json
  48. 129
      pages.json
  49. 632
      pages/Athletes/AthAdd.vue
  50. 200
      pages/Athletes/Athletes.vue
  51. 225
      pages/Basics/Basics.vue
  52. 270
      pages/Choice/Choice.vue
  53. 346
      pages/Coach/Coach.vue
  54. 214
      pages/First/First.vue
  55. 227
      pages/Leader/Leader.vue
  56. 220
      pages/Login/Login.vue
  57. 806
      pages/Login/Register.vue
  58. 282
      pages/Login/Retrieve.vue
  59. 209
      pages/Project/MatchDetail/MatchDetail.vue
  60. 225
      pages/Project/MatchDetail/TeamDetail.vue
  61. 181
      pages/Project/MatchDetail/TeamMatch.vue
  62. 86
      pages/Project/NumMatch.vue
  63. 89
      pages/Project/PatternMatch.vue
  64. 97
      pages/Project/Project.vue
  65. 17
      pages/index/index.vue
  66. 29
      pages/read/Privacy.vue
  67. 67
      pages/read/read.vue
  68. 112
      plugins/request/index.js
  69. 273
      plugins/request/readme.md
  70. 316
      plugins/request/request.js
  71. 11
      static/html/01.html
  72. BIN
      static/item.png
  73. BIN
      static/item01.png
  74. BIN
      static/item02.png
  75. BIN
      static/location.png
  76. BIN
      static/title.png
  77. 13
      store/index.js
  78. 9
      store/modules/project/actions.js
  79. 5
      store/modules/project/index.js
  80. 5
      store/modules/project/mutations.js
  81. 7
      store/modules/project/state.js
  82. 9
      store/modules/user/actions.js
  83. 5
      store/modules/user/index.js
  84. 26
      store/modules/user/mutations.js
  85. 7
      store/modules/user/state.js
  86. 133
      uni.scss
  87. 31
      utils/ui.js
  88. 132
      utils/user.js
  89. 20
      utils/util.js
  90. 21
      vue.config.js
  91. 8
      yarn.lock

23
.eslintrc.js

@ -0,0 +1,23 @@
/*
* Copyright (c) 2019.
* author: wally
* email: 18603454788@163.com
*/
module.exports = {
root: true,
env: { browser: true, node: true },
extends: ['plugin:vue/recommended', 'plugin:vue/essential', '@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': ['error', { props: true, ignorePropertyModificationsFor: ['state'] }],
'max-len': ['error', { code: 120, tabWidth: 2 }],
'object-curly-newline': ['error', { multiline: true }],
'arrow-parens': ['error', 'as-needed'],
},
parserOptions: { parser: 'babel-eslint' },
};

17
.gitignore

@ -0,0 +1,17 @@
.DS_Store
node_modules/
dist/
unpackage/
unpackage/*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

12
.prettierrc

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

31
App.vue

@ -0,0 +1,31 @@
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
export default {
async onLaunch(options) {
},
created () {
//sessionStorage
if (sessionStorage.getItem("store") ) {
this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store"))))
}
//vuexsessionStorage
window.addEventListener("beforeunload",()=>{
sessionStorage.setItem("store",JSON.stringify(this.$store.state))
})
}
};
</script>
<style lang="scss">
@import 'colorui/main.scss';
@import 'colorui/icon.scss';
@import 'colorui/animation.scss';
@import 'common/style/global';
@import 'common/style/iconfont';
body{
background: $white;
padding-bottom: 100px;
}
</style>

111
README.md

@ -0,0 +1,111 @@
# uni-templete使用说明
## 环境准备
+ 使用HbuilderX作为构建开发环境, 主要使用HX的编译环境, 也可以使用vscode编码 + HX编译的搭配
+ HX需要安装插件
+ es6编译
+ Eslint-js
+ Eslint-plugin-vue
+ git
+ prettier
+ **Scss/sass编译**
+ 微信小程序开发工具
+ appId申请
## 目录说明
+ **api**
+ api接口统一管理文件夹,不允许在组件中直接定义api地址,必须在api文件夹下进行统一管理
+ 不同的模块,分成不同的js文件进行管理
+ 采用`export const signin = params => http.post('/api/xxx', params);`格式,封装请求方法及请求地址,方便统一管理
+ **colorui** colorui样式库引入,如果不需要直接删除,注意删除app.vue里的引用
+ **common** 存放公用的css js font等文件,工具类js建议封装到utils文件里
+ **components**
+ 组件存放文件夹
+ 统一格式 `组件/组件.vue ` 文件夹与组件同名,在页面或组件中引入就不用再`import`和在`components`注册了
+ 如果是某个组件特用的子组件,确定不公用的情况下,建议封装到 `组件/components`文件夹里
+ **config** 配置信息文件,一些复杂对象或复杂数组的配置信息,不要在组件内直接定义,公用的建议提到config文件夹下(分模块管理),确定非公用的直接写到组件的文件夹内部即可
+ **pages** 页面存放目录
+ **plugins** 插件,request插件是封装的类似axios请求处理插件,跟axios用法一致,**注意返回值**:成功的返回对象是res.data.data,失败的返回值是res.data.msg,可根据后台接口对应修改。请求做了header里的token处理
+ **static** 存放静态文件
+ **store** vuex文件,注意分模块处理,参考模板中的user,组件中使用store文件时,优先使用mapState等解构方法
+ **utils** 公用工具类,注意分模块,如:`ui.js` `time.js` ` query.js`
+ **.eslintrc.js** eslint代码格式检测配置文件
+ **.gitignore** 上传git仓库忽略的文件
+ **.prettierrc** prettier自动格式化代码风格的插件配置文件
+ App.vue 入口文件
+ Main.js 入口文件
+ **Manifest.json** 项目配置文件
+ **package.json** 项目中有使用到npm包,初始时先运行`npm i`
+ **uni.scss** scss样式遍历定义文件,在组件中可直接使用其变量而不需要导入
+ **Vue.config.js** vue配置文件,定义了常用的alias,使用时尽量使用alias的绝对路径代替相对路径,如:`api/user.js`代替`./api/user.js`
```js
'~': __dirname,
config: resolve('config'),
api: resolve('api'),
store: resolve('store'),
components: resolve('components'),
pages: resolve('pages'),
common: resolve('common'),
plugins: resolve('plugins'),
utils: resolve('utils'),
```
## scss
+ 使用scss代替css样式
+ HX必须要安装`scss插件`
+ 项目开发前应该先定义好uni.scss里的变量,统一引用这里的变量,有利用界面风格统一及后期维护
[官方文档uni.scss](https://uniapp.dcloud.io/collocation/uni-scss)
## 约定
+ Package.json里内置了时间处理插件`moment.js`,统一使用`moment.js`进行时间处理
+ 使用scss进行样式开发
+ 样式变量(颜色,字体大小,间距等)统一定义到`uni.scss`文件里
+ 尽量使用alias定义的绝对路径代替相对路径,如:`api/user.js`代替`./api/user.js`
+ 保持代码风格统一,建议使用vscode + prettier插件,自动格式化代码
+ 代码提交前,进行lint检测,不允许有eslint未通过提交的情况
+ `components`里的组件统一格式 `组件/组件.vue ` 文件夹与组件同名,在页面或组件中引入就不用再`import`和在`components`注册了
+ 页面及组件中分割出来的子组件,确定不公用的情况下,建议封装到 `组件/components`文件夹里
+ 一些复杂对象或复杂数组的配置信息,不要在组件内直接定义,公用的建议提到config文件夹下(分模块管理),确定非公用的直接写到组件的文件夹内部即可
+ api 参考上文目录说明中的api项
+ git提交规范参考前端规范里的代码提交规范
https://kdocs.cn/l/saAjmwvzT?f=130
[文档] 1-前端技术规范-v1.0-20200618.docx

6
api/base.js

@ -0,0 +1,6 @@
// api基础地质
// export const BASE_URL = 'https://www.sxwikionline.com/gateway';
export const BASE_URL = '/gateway';
// 错误码
export const ERR_CODE = 200;

5
api/coachInfo.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 查看教练/领队 信息详情
export const coachInfo = params => http.post(`${proxyUrl}/province/coachInfo`, params);

5
api/competeTime.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 查看比赛届数
export const competeTime = params => http.post(`${proxyUrl}/compete/competeTime`, params);

5
api/deletecoach.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 删除教练/领队信息
export const deletecoach = params => http.post(`${proxyUrl}/province/delete/coach`, params);

5
api/delplayer.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 删除参赛人员
export const delplayer = params => http.post(`${proxyUrl}/province/del/player`, params);

5
api/firstproject.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 根据比赛届数查询一级目录
export const firstproject = params => http.post(`${proxyUrl}/province/first/project`, params);

5
api/getbase.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 验证码图片及id获取
export const getbase = params => http.get(`${proxyUrl}/users/code`, params);

5
api/getcode.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 获取验证码
export const getcode = params => http.get(`${proxyUrl}/users/smscode`, params);

5
api/getcompany.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 根据比赛届数查询用户参赛单户基本信息
export const getcompany = params => http.post(`${proxyUrl}/province/get/company`, params);

5
api/group.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 查询比赛的所有组别
export const group = params => http.post(`${proxyUrl}/compete/group`, params);

6
api/groupplayer.js

@ -0,0 +1,6 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 查询本单位所有注册的选手
export const groupplayer = params => http.post(`${proxyUrl}/province/group/player`, params);

5
api/join.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 选手报名/取消报名
export const join = params => http.post(`${proxyUrl}/province/join`, params);

5
api/login.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 登录接口
export const login = params => http.post(`${proxyUrl}/users/signin`, params);

5
api/overview.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 根据比赛id查询当前用户填写的信息概览
export const overview = params => http.post(`${proxyUrl}/province/overview`, params);

5
api/password.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 修改密码接口
export const password = params => http.post(`${proxyUrl}/users/password`, params);

5
api/phone.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 检查是否被注册过
export const phone = params => http.get(`${proxyUrl}/users/phone`, params);

4
api/photoBase64.js

@ -0,0 +1,4 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
export const photoBase64 = params => http.post(`${proxyUrl}/file/upload/photoBase64`, params);

5
api/playerInfo.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 参赛选手详细信息
export const playerInfo = params => http.post(`${proxyUrl}/province/playerInfo`, params);

5
api/querycoach.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 查看教练/领队 信息列表
export const querycoach = params => http.post(`${proxyUrl}/province/query/coach`, params);

5
api/register.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 注册接口
export const register = params => http.post(`${proxyUrl}/users/signup`, params);

5
api/savecoach.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 添加教练/领队信息
export const savecoach = params => http.post(`${proxyUrl}/province/save/coach`, params);

5
api/savecompany.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 添加/修改单位基本信息
export const savecompany = params => http.post(`${proxyUrl}/province/save/company`, params);

5
api/saveplayer.js

@ -0,0 +1,5 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 添加/修改参赛人员
export const saveplayer = params => http.post(`${proxyUrl}/province/save/player`, params);

6
api/secondproject.js

@ -0,0 +1,6 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
// 根据一级项目id查询二级项目
export const secondproject = params => http.post(`${proxyUrl}/province/second/project`, params);

4
api/upload.js

@ -0,0 +1,4 @@
const proxyUrl = '/mt';
import { http } from 'plugins/request/index';
export const upload = params => http.post(`${proxyUrl}/file/upload/photo`, params, {header:{'Content-Type' : 'multipart/form-data'}});

5
api/user.js

@ -0,0 +1,5 @@
const proxyUrl = '/tall/v1.0';
import { http } from 'plugins/request/index';
// 请求统一使用如下格式
// export const signin = params => http.post('/api/xxx', params);

0
api/xxxx.js

184
colorui/animation.scss

@ -0,0 +1,184 @@
/*
Animation 微动画
基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28
*/
/* css 滤镜 控制黑白底色gif的 */
.gif-black{
mix-blend-mode: screen;
}
.gif-white{
mix-blend-mode: multiply;
}
/* Animation css */
[class*=animation-] {
animation-duration: .5s;
animation-timing-function: ease-out;
animation-fill-mode: both
}
.animation-fade {
animation-name: fade;
animation-duration: .8s;
animation-timing-function: linear
}
.animation-scale-up {
animation-name: scale-up
}
.animation-scale-down {
animation-name: scale-down
}
.animation-slide-top {
animation-name: slide-top
}
.animation-slide-bottom {
animation-name: slide-bottom
}
.animation-slide-left {
animation-name: slide-left
}
.animation-slide-right {
animation-name: slide-right
}
.animation-shake {
animation-name: shake
}
.animation-reverse {
animation-direction: reverse
}
@keyframes fade {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@keyframes scale-up {
0% {
opacity: 0;
transform: scale(.2)
}
100% {
opacity: 1;
transform: scale(1)
}
}
@keyframes scale-down {
0% {
opacity: 0;
transform: scale(1.8)
}
100% {
opacity: 1;
transform: scale(1)
}
}
@keyframes slide-top {
0% {
opacity: 0;
transform: translateY(-100%)
}
100% {
opacity: 1;
transform: translateY(0)
}
}
@keyframes slide-bottom {
0% {
opacity: 0;
transform: translateY(100%)
}
100% {
opacity: 1;
transform: translateY(0)
}
}
@keyframes shake {
0%,
100% {
transform: translateX(0)
}
10% {
transform: translateX(-9px)
}
20% {
transform: translateX(8px)
}
30% {
transform: translateX(-7px)
}
40% {
transform: translateX(6px)
}
50% {
transform: translateX(-5px)
}
60% {
transform: translateX(4px)
}
70% {
transform: translateX(-3px)
}
80% {
transform: translateX(2px)
}
90% {
transform: translateX(-1px)
}
}
@keyframes slide-left {
0% {
opacity: 0;
transform: translateX(-100%)
}
100% {
opacity: 1;
transform: translateX(0)
}
}
@keyframes slide-right {
0% {
opacity: 0;
transform: translateX(100%)
}
100% {
opacity: 1;
transform: translateX(0)
}
}

1226
colorui/icon.scss

File diff suppressed because one or more lines are too long

3990
colorui/main.scss

File diff suppressed because it is too large

34
common/style/global.scss

@ -0,0 +1,34 @@
.cc-active {
transform: translate3d(1rpx, 1rpx, 0);
}
.card {
margin-bottom: 60rpx;
.card-head {
display: flex;
align-items: center;
height: 120rpx;
padding: 0 32rpx;
.card-head-avatar {
display: flex;
justify-content: center;
align-items: center;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
}
.card-head-title {
flex: 1;
margin: 0 20rpx;
font-size: 16px;
color: $black;
overflow: hidden;
}
.card-head-action {
font-size: 14px;
}
}
}

0
common/style/iconfont.scss

17
components/ms-dropdown/README.md

@ -0,0 +1,17 @@
## 简介
## 更新日志
**2019.12.30**
优化:
定位下拉内容
待实现:只同时展示一个下拉内容
**2019.12.27**
bug修复
支持H5、微信小程序
**2019.12.20**
第一次发布
支持H5下拉,暂不支持小程序。
原因:一些适配H5的方法是用vue来写的,小程序不支持。
`this.$slots.`在小程序中不能用

213
components/ms-dropdown/dropdown-item.vue

@ -0,0 +1,213 @@
<template>
<div class="dropdown-item">
<!-- selected -->
<view class="dropdown-item__selected"
@click="changePopup">
<slot name="title" v-if="$slots.title"></slot>
<block v-else>
<view class="selected__name">{{title ? title : selectItem.text}}</view>
<view class="selected__icon"
:class="showClass === 'show'? 'up' : 'down'"
>
<span class="iconfont">&#xe851;</span>
</view>
</block>
</view>
<view class="dropdown-item__content" :style="{top: contentTop + 'px'}" v-if="showList">
<!-- dropdown -->
<view :class="['list', showClass]">
<slot v-if="$slots.default"></slot>
<block v-else>
<view class="list__option"
v-for="(item, index) in list"
:key="index"
@click="choose(item)">
<view>{{item.text}}</view>
<icon v-if="item.value === value" type="success_no_circle" size="26"/>
</view>
</block>
</view>
<!-- dropdown-mask -->
<view :class="['dropdown-mask', showClass]" v-if="showList" @click="closePopup"></view>
</view>
</div>
</template>
<script>
export default {
components: {
},
props: {
value: [Number, String, Object],
list: {
type: Array,
default: ()=> {
return []
}
},
title: [Number, String]
},
data() {
return {
showList: "",
showClass: '',
selectItem: {},
contentTop: 0
}
},
watch: {
},
mounted() {
this.showList = this.active;
this.selectItem = this.list[this.value];
// document.addEventListener('click', e => {
// //this.$el
// if (!this.$el.contains(e.target)) {
// console.log('change');
// this.close()
// }
// });
},
methods: {
choose(item) {
this.selectItem = item
this.$emit('input', item.value)
this.closePopup()
},
changePopup() {
if(this.showList) {
this.closePopup()
} else {
this.openPopup()
}
},
openPopup() {
// this.$parent -> dropdown-menu
this.$parent.$emit('close')
this.showList = true
this.$nextTick(() => {
this.getElementData('.dropdown-item__selected', (data)=>{
this.contentTop = data[0].bottom
this.showClass = 'show'
})
})
},
closePopup() {
this.showClass = ''
setTimeout(() => {
this.showList = false
}, 300)
},
close() {
this.showClass = ''
this.showList = false
},
getElementData(el, callback){
uni.createSelectorQuery().in(this).selectAll(el).boundingClientRect().exec((data) => {
callback(data[0]);
});
}
}
}
</script>
<style lang="scss">
@font-face {
font-family: 'iconfont'; /* project id 1564327 */
src: url('https://at.alicdn.com/t/font_1564327_fcszez4n5i.eot');
src: url('https://at.alicdn.com/t/font_1564327_fcszez4n5i.eot?#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_1564327_fcszez4n5i.woff2') format('woff2'),
url('https://at.alicdn.com/t/font_1564327_fcszez4n5i.woff') format('woff'),
url('https://at.alicdn.com/t/font_1564327_fcszez4n5i.ttf') format('truetype'),
url('https://at.alicdn.com/t/font_1564327_fcszez4n5i.svg#iconfont') format('svg');
}
.iconfont{
font-family:"iconfont" !important;
font-size:28rpx;
font-style:normal;
-webkit-font-smoothing: antialiased;
-webkit-text-stroke-width: 0.2px;
-moz-osx-font-smoothing: grayscale;
}
.dropdown-item {
width: 100%;
flex:1;
position: relative;
&__selected {
position: relative;
display: flex;
align-items: center;
background: #fff;
padding: 30rpx;
box-sizing: border-box;
justify-content: center;
.selected__name {
font-size: 32rpx;
}
.selected__icon {
margin-left: 20rpx;
&.down {
transition: transform .3s;
transform: rotateZ(0);
}
&.up {
transition: transform .3s;
transform: rotateZ(-180deg);
}
}
}
&__content {
position: fixed;
left: 0;
right: 0;
overflow: hidden;
top: 0;
bottom: 0;
z-index: 1;
.list {
max-height: 400px;
overflow-y: auto;
position: absolute;
left: 0;
right: 0;
z-index: 3;
background: #fff;
transform: translateY(-100%);
transition: all .3s;
&.show {
transform: translateY(0);
}
&__option {
font-size:32rpx;
padding: 26rpx 28rpx;
display: flex;
justify-content: space-between;
&:not(:last-child) {
border-bottom: 1rpx solid #DDDDDD;
}
}
}
.dropdown-mask {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
transition: all .3s;
z-index: 2;
&.show {
background:rgba(0,0,0,0.5);
}
}
}
&:not(:last-child):after {
content: ' ';
position: absolute;
width: 2rpx;
top: 36rpx;
bottom: 36rpx;
right: 0;
background: $uni-border-color;
}
}
</style>

35
components/ms-dropdown/dropdown-menu.vue

@ -0,0 +1,35 @@
<template>
<div class="dropdown-menu">
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
}
},
mounted() {
this.$on('close', this.closeDropdown)
},
methods: {
closeDropdown() {
this.$children.forEach(item =>{
item.close();
})
}
}
}
</script>
<style lang="scss">
.dropdown-menu {
display: flex;
overflow: auto;
white-space: nowrap;
}
dropdown-item {
flex: 1;
}
</style>

19
components/test/test.vue

@ -0,0 +1,19 @@
<template>
<view>
test组件
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style lang="scss">
</style>

1
config/config.default.js

@ -0,0 +1 @@
export const ERR_CODE = 200;

20
config/config.user.js

@ -0,0 +1,20 @@
/*
* Copyright (c) 2020.
* author: wally
* email: 18603454788@163.com
*/
// 用户登录client
export const SIGN_IN_CLIENTS = { mp: 0, h5: 1, android: 2, ios: 3, wx_work: 4 };
// 用户登录类型
export const SIGN_IN_TYPES = {
mp: 0,
phone: 1,
email: 2,
username: 3,
wx: 4,
wx_web: 5,
wb: 6,
wx_work: 7,
};

64
main.js

@ -0,0 +1,64 @@
import Vue from 'vue';
import moment from 'moment';
import { http } from 'plugins/request/index';
import App from './App';
import store from './store';
import axios from 'axios'
Vue.prototype.$axios = axios
import Fingerprint from 'fingerprintjs'
// 白名单页面
const whitePathList = [
'basic-info',
'statistics',
'user-code',
'sign',
'my-signs',
'my-code',
'my-trips',
];
/**
* 检查url是否在是否在白名单内
* @param {string} url path+query
*/
const checkWhitePath = url => {
let str = url.slice(7).split('/')[0];
return whitePathList.includes(str);
};
// var fingerprint = new Fingerprint().get();
// store.state.user.fingerprint = fingerprint
// console.log(store.state.user.fingerprint)
Vue.config.productionTip = false;
Vue.prototype.$http = http;
Vue.prototype.$moment = moment;
moment.locale('zh-cn');
Vue.prototype.goHome = () => {
uni.reLaunch({
url: '/pages/index/index',
});
};
Vue.prototype.openPage = function(path, query = '') {
let url = query ? `${path}?${query}` : path;
store.commit('user/setPagePath', url);
const isWhite = checkWhitePath(url);
if ((!store.state.user.userInfo || !store.state.user.userInfo.id) && !isWhite) {
url = '/pages/basic-info/basic-info';
}
uni.navigateTo({ url });
};
App.mpType = 'app';
const app = new Vue({
store,
...App,
});
app.$mount();

116
manifest.json

@ -0,0 +1,116 @@
{
"name" : "Loading...",
"appid" : "__UNI__4CABB72",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
"networkTimeout" : {
"uploadFile" : 20000,
"request" : 20000
},
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.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" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx2f9ef33e08053bbf",
"setting" : {
"urlCheck" : false,
"es6" : true,
"postcss" : true,
"minified" : true
},
"permission" : {
"scope.userLocation" : {
"desc" : "你的位置信息将用于获取地理位置及地图展示"
}
},
"usingComponents" : true,
"navigateToMiniProgramAppIdList" : [ "wx5b97b0686831c076" ]
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"h5" : {
"devServer" : {
"proxy" : {
"/gateway" : {
"target" : "https://test.tall.wiki/gateway",
"changeOrigin" : true,
"secure" : true,
"pathRewrite" : {
"^/gateway" : ""
}
}
},
"https" : false,
"port" : ""
},
"router" : {
"base" : ""
},
"domain" : "https//:www.tall.wiki",
"async" : {
//js
"loading" : "AsyncLoading", //js使
"error" : "AsyncError", //js使
"delay" : 500, // loading js delay loading
"timeout" : 1000 //js error
},
"title" : "2020年山西省学生跳绳比赛报名系统"
}
}

36
package-lock.json

@ -0,0 +1,36 @@
{
"name": "uniTemplete",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"axios": {
"version": "0.21.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz",
"integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==",
"requires": {
"follow-redirects": "^1.10.0"
}
},
"fingerprintjs": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/fingerprintjs/-/fingerprintjs-0.5.3.tgz",
"integrity": "sha1-ACZCL64asQA2keE2wUPhm3UnslU="
},
"fingerprintjs2": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-2.1.2.tgz",
"integrity": "sha512-ZPsLgjziFRbUb5tXWpEMtWp4XFnzSah8SiNfl3aoURDZ+2zi2tuIOYUULqDBV+Cb6paN+raWT+Q2qpOaCbX/Yw=="
},
"follow-redirects": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
}
}
}

34
package.json

@ -0,0 +1,34 @@
{
"name": "uniTemplete",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"uni-app": {
"scripts": {
"mp-dingtalk": {
"title": "钉钉小程序",
"env": {
"UNI_PLATFORM": "mp-alipay"
},
"define": {
"MP-DINGTALK": true
}
}
}
},
"repository": {
"type": "git",
"url": ""
},
"author": "wally",
"license": "ISC",
"dependencies": {
"axios": "^0.21.0",
"fingerprintjs": "^0.5.3",
"fingerprintjs2": "^2.1.2",
"moment": "^2.29.1"
}
}

129
pages.json

@ -0,0 +1,129 @@
{
"pages": [
{
"path" : "pages/Login/Login",
"style" : {}
},
{
"path" : "pages/First/First",
"style" : {}
}
,{
"path" : "pages/Leader/Leader",
"style" : {
"navigationBarTitleText": "添加领队",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Coach/Coach",
"style" : {
"navigationBarTitleText": "添加教练",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Choice/Choice",
"style" : {
"navigationBarTitleText": "教练及领队信息",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Basics/Basics",
"style" : {
"navigationBarTitleText": "基础信息填写",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Athletes/Athletes",
"style" : {
"navigationBarTitleText": "添加运动员",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Athletes/AthAdd",
"style" : {
"navigationBarTitleText": "添加运动员",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Project/Project",
"style" : {
"navigationBarTitleText": "参赛项目",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Project/NumMatch",
"style" : {
"navigationBarTitleText": "计数赛项目",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Project/MatchDetail/MatchDetail",
"style" : {
"navigationBarTitleText": "队员添加",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Login/Register",
"style" : {}
}
,{
"path" : "pages/read/read",
"style" : {
"navigationBarTitleText": "安全责任书",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/read/Privacy",
"style" : {
"navigationBarTitleText": "隐私协议",
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#f8f8f8"
}
}
,{
"path" : "pages/Project/PatternMatch",
"style" : {}
}
,{
"path" : "pages/Login/Retrieve",
"style" : {}
}
,{
"path" : "pages/Project/MatchDetail/TeamMatch",
"style" : {}
}
,{
"path" : "pages/Project/MatchDetail/TeamDetail",
"style" : {}
}
],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "2020年山西省学生跳绳比赛报名系统",
"navigationBarBackgroundColor": "#6D99F2",
"backgroundColor": "#0a97c6"
},
"easycom": {
"autoscan": true
}
}

632
pages/Athletes/AthAdd.vue

@ -0,0 +1,632 @@
<template>
<view class="body-box">
<view class="infor-box" v-for="(item,index) in list" :key="index">
<view class="infor-title">{{ item.title }}<text class="fbt">*</text></view>
<input v-if="item.type === 0" placeholder="请输入" name="input" v-model.trim="item.content"></input>
<view class="drop-box">
<view v-show="showview === 1" class="mask" @click="showBox">
</view>
<input v-if="item.type === 3" placeholder="请选择" name="input" disabled @click="showBox" v-model.trim="item.content"/>
<view v-if="item.type === 3" class="group-box" v-show="showview === 1">
<view v-for="(item,index) in groupList" :key="index" @click="choice(index)">
{{ item }}
</view>
</view>
</view>
<radio-group v-if="item.type === 1" name="" @change="radioChange">
<label class="w100">
<radio value="0" :checked="che===0?true:false" /></radio><text></text>
<radio value="1" :checked="che===1?true:false" /></radio><text></text>
</label>
</radio-group>
<!-- 身份证明 -->
<view v-if="item.type === 4" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids">
<view class="mask-box" @click="jump(item.content1)">
</view>
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="idCardFront" mode="aspectFill"></image>
<icon v-show="idCardFront !== ''" type="icon" class="cuIcon-roundclosefill close-icon" @click="delimg(0)"></icon>
</view>
<view class="solids">
<view class="mask-box" @click="jump(item.content2)">
</view>
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="idCardBack"></image>
<icon v-show="idCardBack !== ''" type="icon" class="cuIcon-roundclosefill close-icon" @click="delimg(1)"></icon>
</view>
</view>
</view>
<!-- 一寸证件照 -->
<view v-if="item.type === 5" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids" @click="jump(item.content)">
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="idPhone"></image>
</view>
</view>
</view>
<!-- 学籍证明/俱乐部证明 -->
<view v-if="item.type === 6" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids" @click="jump(item.content)">
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="studentRecord"></image>
</view>
</view>
</view>
<!-- 体检证明 -->
<view v-if="item.type === 7" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids" @click="jump(item.content)">
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="healthRecord"></image>
</view>
</view>
</view>
<!-- 人身意外保险 -->
<view v-if="item.type === 8" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids" @click="jump(item.content)">
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="insuranceRecord"></image>
</view>
</view>
</view>
<text v-if="item.tips !== ''" class="tips">{{ item.tips }}</text>
</view>
<button v-show="isId-0===0" class="btn cu-btn bg-green margin-tb-sm lg" @tap="submit">提交</button>
<button v-show="isId-0===1" class="btn cu-btn bg-blue margin-tb-sm lg" @click="change">修改</button>
</view>
</template>
<script>
import { upload } from 'api/upload'
import { saveplayer } from 'api/saveplayer'
import { playerInfo } from 'api/playerInfo'
import { group } from 'api/group'
// import { photoBase64 } from 'api/photoBase64'
export default {
async onLoad(option) {
const that = this
const params1 = {
param: {
type: 0
}
}
const data1 = await group(params1)
// that.groupList = data1
if(option.id) {
// console.log(option)
try{
const params = {
param: {
playerId: option.id
}
}
const data = await playerInfo(params)
// console.log(data)
that.list[0].content = data.playerName
that.che = data.gender
that.groupRemark = data.groupRemark
that.list[3].content = data.idCard
that.isId = 1
that.playerId = data.playerId
that.idCardFront = data.idCardFront
that.idCardFrontId = data.idCardFrontId
that.idCardBack = data.idCardBack
that.idCardBackId = data.idCardBackId
that.idPhone = data.idPhone
that.idPhoneId = data.idPhoneId
that.studentRecord = data.studentRecord
that.studentRecordId = data.studentRecordId
that.healthRecord = data.healthRecord
that.healthRecordId = data.healthRecordId
that.insuranceRecord = data.insuranceRecord
that.insuranceRecordId = data.insuranceRecordId
// for( var i=0; i<data1.length; i++){
// if(data1[i].groupId - 0 === data.groupId - 0) {
var num = data.groupRemark - 0 - 1
that.list[2].content = that.groupList[`${num}`]
// console.log(data1[i].groupName)
// }
// }
}catch(e){
//TODO handle the exception
}
}
},
data() {
return {
list: [{
title:'姓名',
type: 0, // 0123
tips: '',
content: ''
}, {
title:'性别',
type: 1,
tips: '',
content: ''
}, {
title:'组别',
type: 3,
tips: '',
content: ''
}, {
title:'身份证号',
type: 0,
tips: '',
content: ''
}, {
title:'身份证明(图片大小不得大于2M)',
type: 4,
tips: '身份证、户口本、出生证等,身份证需上传正反面,户口本需上传运动员本人页',
content1: 'idCardFront',
content2: 'idCardBack'
}, {
title:'一寸证件照(图片大小不得大于2M)',
type: 5,
tips: '用于生成参赛证及比赛证书',
content: 'idPhone'
}, {
title:'学籍证明/俱乐部证明(图片大小不得大于2M)',
type: 6,
tips: '',
content: 'studentRecord'
}, {
title:'体检证明(图片大小不得大于2M)',
type: 7,
tips: '',
content: 'healthRecord'
}, {
title:'人身意外保险(图片大小不得大于2M)',
type: 8,
tips: '',
content: 'insuranceRecord'
}],
idCardFront: '', //
idCardFrontId: '', //
idCardBack: '', //
idCardBackId: '', //
idPhone: '', //
idPhoneId: '', //
studentRecord: '', //
studentRecordId: '', //
healthRecord: '', //
healthRecordId: '', //
insuranceRecord: '', //
insuranceRecordId: '', //
che: 0,
isId: 0,
playerId: 0,
groupList: ['小学','中学','高职院校','本科院校','俱乐部'],
groupRemark: 0,
showview: 0
}
},
methods: {
delimg(index) {
const that = this
if (index - 0 === 0) {
uni.showModal({
title: '删除',
content: '确定要删除这张图片么?',
success: (res) => {
if (res.confirm) {
that.idCardFront = ''
that.idCardFrontId = ''
}
}
})
} else if (index - 0 === 1) {
uni.showModal({
title: '删除',
content: '确定要删除这张图片么?',
success: (res) => {
if (res.confirm) {
that.idCardBack = ''
that.idCardBackId = ''
}
},
})
}
},
radioChange(e) {
const that = this
that.che = e.detail.value - 0
// console.log(that.che)
},
jump(type) {
const that = this
uni.chooseImage({
count:1,
sizeType: ['original', 'compressed'], //
sourceType: ['album'], //
success: function (res) {
// console.log(res)
if (res.tempFiles[0].size > 2*1024*1024) {
uni.showToast({
title: '图片超过2M,请重新选择',
icon: 'none',
duration: 1500
})
} else {
const tempFilePaths = res.tempFilePaths[0]
if(type === 'idCardBack' && that.idCardFront === ''){
that.idCardFront = tempFilePaths
} else {
that[`${type}`] = tempFilePaths
}
// that[`${type}Id`] = jsondata.data.id
// that.urlTobase64(res.tempFilePaths[0],type);
uni.uploadFile({
url: '//www.tall.wiki/gateway/mt/file/upload/photo',
filePath: tempFilePaths,
header:{
"Authorization" : "Bearer " + that.$store.state.user.user.token
},
name: 'file',
success: (res) => {
// console.log(JSON.stringify());
const jsondata = JSON.parse(res.data)
if (jsondata.code === 200) {
console.log(jsondata)
if(type === 'idCardBack' && that.idCardFrontId === ''){
that.idCardFront = jsondata.data.visitUrl
that.idCardFrontId = jsondata.data.id
} else {
that[`${type}`] = jsondata.data.visitUrl
that[`${type}Id`] = jsondata.data.id
}
} else {
uni.showToast({
title: `图片上传失败,请重新上传`,
icon: 'none',
duration: 1500
})
}
}
});
}
}
})
},
// urlTobase64(url,type){
// const that = this
// uni.request({
// url: url,
// method: 'GET',
// responseType: 'arraybuffer',
// success: async res => {
// let base64 = wx.arrayBufferToBase64(res.data); //arraybufferbase64
// base64 = 'data:image/jpeg;base64,' + base64; //
// // console.log(base64);
// try{
// const params = {
// fileBase64: base64
// }
// const data = await photoBase64(params)
// // console.log(data)
// // console.log(that[`${type}Id`])
// // console.log(that.idCardFrontId)
// // console.log(`${type}Id`)
// }catch(e){
// //TODO handle the exception
// // console.log(e)
// }
// }
// });
// },
showBox() {
const that = this
if (that.showview === 0) {
that.showview = 1
} else {
that.showview = 0
}
},
choice(index) {
const that = this
that.list[2].content = that.groupList[`${index}`]
that.groupRemark = index - 0 + 1
that.showview = 0
},
async submit() {
const that = this
if (that.list[0].content === '') {
uni.showToast({
title: '姓名不能为空',
icon: 'none',
duration: 1500
})
} else if (that.groupRemark - 0 === 0) {
uni.showToast({
title: '请选择组别',
icon: 'none',
duration: 1500
})
}else if (that.list[2].content === '') {
uni.showToast({
title: '组别不能为空',
icon: 'none',
duration: 1500
})
} else if (that.list[3].content === '') {
uni.showToast({
title: '身份证号不能为空',
icon: 'none',
duration: 1500
})
} else if (that.idCardFrontId === '') {
uni.showToast({
title: '请上传身份证明',
icon: 'none',
duration: 1500
})
} else if (that.idPhoneId === '') {
uni.showToast({
title: '请上传一寸证件照',
icon: 'none',
duration: 1500
})
} else if (that.studentRecordId === '') {
uni.showToast({
title: '请上传学籍证明',
icon: 'none',
duration: 1500
})
} else if (that.healthRecordId === '') {
uni.showToast({
title: '请上传体检证明',
icon: 'none',
duration: 1500
})
} else if (that.insuranceRecordId === '') {
uni.showToast({
title: '请上传保险证明',
icon: 'none',
duration: 1500
})
} else {
try{
const params = {
param: {
competeId: that.$store.state.project.companyId,
groupRemark: that.groupRemark,//id
playerName: that.list[0].content,//
gender: that.che,//
idCard: that.list[3].content,//
idCardFront: that.idCardFrontId,//
idCardBack: that.idCardBackId,//
idPhone: that.idPhoneId,//
studentRecord: that.studentRecordId,//
healthRecord: that.healthRecordId,//
insuranceRecord: that.insuranceRecordId//
}
}
const data = await saveplayer(params)
uni.showToast({
title: '添加成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
setTimeout(function(){
uni.navigateBack()
},1000)
// console.log(that.$store.state.project.num)
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
},
async change() {
const that = this
if (that.list[0].content === '') {
uni.showToast({
title: '姓名不能为空',
icon: 'none',
duration: 1500
})
} else if (that.groupRemark - 0 === 0) {
uni.showToast({
title: '请选择组别',
icon: 'none',
duration: 1500
})
} else if (that.list[2].content === '') {
uni.showToast({
title: '组别不能为空',
icon: 'none',
duration: 1500
})
} else if (that.list[3].content === '') {
uni.showToast({
title: '身份证号不能为空',
icon: 'none',
duration: 1500
})
} else if (that.idCardFrontId === '') {
uni.showToast({
title: '请上传身份证正面照',
icon: 'none',
duration: 1500
})
} else if (that.idCardBackId === '') {
uni.showToast({
title: '请上传身份证反面照',
icon: 'none',
duration: 1500
})
} else if (that.idPhoneId === '') {
uni.showToast({
title: '请上传一寸证件照',
icon: 'none',
duration: 1500
})
} else if (that.studentRecordId === '') {
uni.showToast({
title: '请上传学籍证明',
icon: 'none',
duration: 1500
})
} else if (that.healthRecordId === '') {
uni.showToast({
title: '请上传体检证明',
icon: 'none',
duration: 1500
})
} else if (that.insuranceRecordId === '') {
uni.showToast({
title: '请上传保险证明',
icon: 'none',
duration: 1500
})
}else {
try{
const params = {
param: {
playerId: that.playerId,
competeId: that.$store.state.project.companyId,
groupRemark: that.groupRemark,//id
playerName: that.list[0].content,//
gender: that.che,//
idCard: that.list[3].content,//
idCardFront: that.idCardFrontId,//
idCardBack: that.idCardBackId,//
idPhone: that.idPhoneId,//
studentRecord: that.studentRecordId,//
healthRecord: that.healthRecordId,//
insuranceRecord: that.insuranceRecordId//
}
}
const data = await saveplayer(params)
uni.showToast({
title: '修改成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
setTimeout(function(){
uni.navigateBack()
},1000)
// console.log(that.$store.state.project.num)
}catch(e){
//TODO handle the exception
}
}
}
}
}
</script>
<style lang="scss" scoped>
.body-box {
padding-bottom: 100px;
}
.infor-box {
width: 680rpx;
margin-left: 35rpx;
margin-top: 25px;
border-bottom: 1px solid #eee;
}
.infor-title {
font-size: 16px;
color: #505050;
margin-bottom: 10px;
}
.w100 {
width: 680rpx;
display: flex;
radio {
margin-right: 50rpx;
}
text {
flex: 1;
}
}
.btn {
width: 600rpx;
margin: 50px 0 0 75rpx;
}
.tips {
color: $gray;
font-size: 14px;
}
.drop-box {
position: relative;
}
.fbt {
color: $red;
}
.group-box {
position: absolute;
background: white;
z-index: 10;
height: 200px;
overflow-y: auto;
border-radius: 0 0 10px 10px;
box-sizing: border-box !important;
// border: 2rpx solid $grey;
box-shadow: 0 0 5px $grey;
width: 680rpx;
z-index: 1000;
view {
box-sizing: border-box !important;
left: 0;
height: 40px;
padding: 0 0 10px 20px;
line-height: 40px;
font-size: 14px;
width: 100%;
border-bottom: 1rpx solid #eee;
}
}
.mask {
position: fixed;
width: 750rpx;
height: 100%;
background: rgba(0,0,0,0);
left: 0;
top: 0;
z-index: 10;
}
.img-box {
width: 150rpx;
height: 150rpx;
}
.close-icon {
position: absolute;
right: 0;
top: 0;
font-size: 22px;
z-index: 100;
color: $red;
}
.mask-box {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 100;
background: rgba(0,0,0,0);
}
</style>

200
pages/Athletes/Athletes.vue

@ -0,0 +1,200 @@
<template>
<view>
<button v-show="cha !== 2" class="change" @click="change" :class="cha - 0 === 1 ? 'delsure' : ''">
<text v-show="cha === 0">编辑</text>
<text v-show="cha === 1">确定</text>
</button>
<view class="img-B" v-show="cha - 0 === 2" >
<img src="static/item.png" class="img-box" />
<view>
暂无运动员信息
</view>
</view>
<view class="member-box" v-for="(item,index) in list" :key="index">
<view class="member-title">{{ item.groupName }}</view>
<view class="member-con" v-for="(a,b) in item.playerList" :key="b" >
<view class="click-box" @click="details(a.playerId)">
{{ a.playerName }}
</view>
<icon v-show="cha === 1" type="icon" class="cuIcon-roundclosefill del" @click="Del(a.playerId)"></icon>
</view>
</view>
<view class="choice" @tap="jump">
添加
</view>
</view>
</template>
<script>
import { groupplayer } from 'api/groupplayer'
import { delplayer } from 'api/delplayer'
export default {
async onLoad () {
this.add()
},
data() {
return {
list: [],
cha: 2
}
},
methods: {
async add() {
const that = this
try{
const params = {
param: {
companyId: that.$store.state.project.companyId
}
}
const data = await groupplayer(params)
// console.log(data)
that.list = data
if (that.list[0]) {
that.cha = 0
} else {
that.cha = 2
uni.navigateTo({
url: './AthAdd'
})
}
}catch(e){
//TODO handle the exception
that.cha = 2
}
},
jump() {
uni.navigateTo({
url:`./AthAdd`
})
},
async Del(num) {
const that = this
uni.showModal({
title: '提示',
content: '确认要删除吗?',
success:async function (res) {
if (res.confirm) {
try{
const params = {
param: {
playerId: num
}
}
const data = await delplayer(params)
that.add()
uni.showToast({
title: '删除成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
}catch(e){
//TODO handle the exception
}
}
}
})
},
change() {
const that = this
if (that.cha - 0 === 0) {
that.cha = 1
} else {
that.cha = 0
}
},
details(id) {
uni.navigateTo({
url: `./AthAdd?id=${id}`
})
}
},
watch:{
'$store.state.project.num'(val) {
const that = this
that.add()
}
}
}
</script>
<style lang="scss" scoped>
.member-box {
margin-left: 35rpx;
width: 680rpx;
}
.member-title {
height: 40px;
line-height: 40px;
font-size: 16px;
margin-top: 10px;
border-bottom: 1px solid $grey;
}
.member-con {
position: relative;
height: 60px;
margin-top: 10px;
line-height: 60px;
border-radius: 10px;
text-align: center;
box-shadow: 0 0 5px $gray;
}
.active {
box-shadow: 0 0 4px $green !important;
}
.choice {
position: fixed;
width: 150rpx;
height: 150rpx;
background: #709AFC;
box-shadow: 0 0 20px #709AFC;
border-radius: 50%;
color: $white;
right: 10px;
bottom: 100px;
text-align: center;
line-height: 150rpx;
font-size: 18px;
}
.man-num {
margin-left: 20px;
font-size: 14px;
color: $gray;
}
.del {
font-size: 28px;
position: absolute;
right: 10px;
top: 0;
font-weight: 700;
color: $red;
}
.change {
position: absolute;
top: 5px;
height: 30px;
line-height: 30px;
background: #eee;
font-size: 14px;
right: 40rpx;
}
.delsure {
background: $green;
color: $white;
}
.click-box {
margin: 0 50px;
}
.img-box {
width: 70%;
height: 70%;
}
.img-B {
width: 100%;
margin-top: 100px;
height: 450rpx;
text-align: center;
color: #aaa;
}
</style>

225
pages/Basics/Basics.vue

@ -0,0 +1,225 @@
<template>
<view>
<button v-show="!sub" type="default" class="change" @click="changeData">编辑</button>
<view class="infor-box" v-for="(item,index) in list" :key="index">
<view v-if="item.type === 0" class="infor-title">{{ item.title }}<text class="fbt">*</text></view>
<input :class="sub? 'iptactive' : ''" :disabled="!sub" v-if="item.type === 0" placeholder="请输入" name="input" v-model.trim="item.content"></input>
<view v-show="sub" class="safe-box">
<radio-group v-if="item.type === 1" name="sex">
<label class="w100">
<radio value="0" :checked="che" @click="radioChange"/></radio>
</label>
</radio-group>
<text v-if="item.type === 1" class="asfe-title" @click="jump">{{ item.title }}</text>
</view>
</view>
<button v-show="sub" class="btn cu-btn bg-green margin-tb-sm lg" @click="submit">
<text v-if="!chan">确定提交</text>
<text v-else>确定修改</text>
</button>
</view>
</template>
<script>
import { getcompany } from 'api/getcompany'
import { savecompany } from 'api/savecompany'
export default {
async onLoad (options) {
const that = this
console.log(options)
that.projectId = options.id - 0
that.companyId = options.companyId - 0
try{
const params = {
param : {
competeTimeId: options.id
}
}
const data = await getcompany(params)
if (options.companyId - 0 === 0) {
that.list[2].content = that.$store.state.user.user.phone
} else {
that.list[0].content = data.companyName
that.list[1].content = data.contactsName
that.list[2].content = data.contactsPhone
that.che = true
that.sub = false
that.chan = true
that.companyId = options.companyId
}
}catch(e){
//TODO handle the exception
}
},
data() {
return {
list: [{
title:'参赛队伍名称',
type: 0, // 012
content: ''
}, {
title:'主要联系人',
type: 0,
content: ''
}, {
title:'联系方式',
type: 0,
content: ''
}, {
title:'阅读并同意《安全责任书》',
type: 1,
content: ''
}],
projectId: 0,
che: false,
companyId: '',
sub: true,
chan: false
// imgList: []
}
},
methods: {
radioChange() {
const that = this
if (that.che) {
that.che = false
} else {
that.che = true
}
},
async submit() {
const that = this
if (that.list[0].content === '') {
uni.showToast({
title: '参赛队伍名称不能为空',
icon: 'none',
duration: 1500
})
}else if (that.list[1].content === '') {
uni.showToast({
title: '主要联系人不能为空',
icon: 'none',
duration: 1500
})
}else if (that.list[2].content === '') {
uni.showToast({
title: '联系方式不能为空',
icon: 'none',
duration: 1500
})
} else if (!/^1([3-9])[0-9]{9}$/.test(that.list[2].content)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none',
duration: 2000
})
}else if (that.che === false) {
uni.showToast({
title: '请同意《安全责任书》',
icon: 'none',
duration: 1500
})
}else {
try{
if (that.$store.state.project.companyId - 0 === 0) {
that.companyId = ''
} else {
that.companyId = that.$store.state.project.companyId
}
const params = {
param: {
companyId: that.companyId,
companyName: that.list[0].content,
competeTimeId: that.projectId - 0,
contactsName: that.list[1].content,
contactsPhone: that.list[2].content,
authorization: 1,
}
}
const data = await savecompany(params)
that.$store.state.project.companyId = data.companyId
uni.showToast({
title: '提交成功',
icon: 'success',
duration: 1500
})
that.sub = false
that.chan = true
that.$store.state.project.num++
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
},
jump() {
//
uni.navigateTo({
url: './../read/read'
})
},
changeData() {
const that = this
that.sub = true
that.chan = true
}
}
}
</script>
<style lang="scss" scoped>
.infor-box {
width: 680rpx;
margin-left: 35rpx;
margin-top: 25px;
border-bottom: 1px solid #eee;
}
.infor-box:last-of-type{
border: none;
}
.infor-title {
font-size: 16px;
color: #505050;
margin-bottom: 10px;
}
.w100 {
radio {
margin-right: 20rpx;
}
}
.btn {
width: 680rpx;
margin: 50px 0 0 35rpx;
}
.asfe-title {
font-size: 14px;
}
.safe-box {
display: flex;
}
input {
height: 30px;
font-size: 14px;
border-radius: 5px;
}
.iptactive {
border: 1px solid $gray;
padding-left: 3%;
}
.change {
position: absolute;
top: -20px;
height: 30px;
line-height: 30px;
background: #eee;
font-size: 14px;
right: 40rpx;
}
.fbt {
color: $red;
}
</style>

270
pages/Choice/Choice.vue

@ -0,0 +1,270 @@
<template>
<view>
<view class="fixed-box">
<scroll-view scroll-x class="bg-white nav text-center">
<view class="cu-item" :class="index==TabCur?'bor':''" v-for="(item,index) in list" :key="index" @tap="tabSelect" :data-id="index">
{{ item }}
</view>
</scroll-view>
<view class="num-box">
<text v-if="TabCur === 0">已填 {{ competeCompanyCoachList.length }} / 2 </text>
<text v-if="TabCur === 1">已填 {{ competeCompanyLeadersList.length }} / 3 </text>
</view>
<button v-show="TabCur === 0 && cha1 !== 2" class="change" :class="cha === 1 ? 'delsure' : ''" @click="change1">
<text v-show="cha1 === 0">编辑</text>
<text v-show="cha1 === 1">确定</text>
</button>
<button v-show="TabCur === 1 && cha !== 2" class="change" :class="cha === 1 ? 'delsure' : ''" @click="change">
<text v-show="cha === 0">编辑</text>
<text v-show="cha === 1">确定</text>
</button>
</view>
<view class="content-box">
<view v-show="TabCur === 0">
<view class="infor-box" v-for="(item,index) in competeCompanyCoachList" :key="index">
<view class="click-box" @click="details(item.coachId)">
<view class="infor-content"><text>姓名</text><text class="con">{{ item.coachName }}</text></view>
<view class="infor-content" v-if="item.gender === 0"><text>性别:</text><text class="con"></text></view>
<view class="infor-content" v-else-if="item.gender === 1"><text>性别</text><text class="con"></text></view>
<view class="infor-content"><text>联系电话</text><text class="con">{{ item.phone }}</text></view>
</view>
<icon v-show="cha1 === 1" type="icon" class="cuIcon-roundclosefill del" @click="del(item.coachId)"></icon>
</view>
</view>
<view v-show="TabCur === 1">
<view class="infor-box" v-for="(item,index) in competeCompanyLeadersList" :key="index">
<view class="click-box" @click="details1(item.guideId)">
<view class="infor-content"><text>姓名</text><text class="con">{{ item.guideName }}</text></view>
<view class="infor-content" v-if="item.gender === 0"><text>性别:</text><text class="con"></text></view>
<view class="infor-content" v-else-if="item.gender === 1"><text>性别</text><text class="con"></text></view>
<view class="infor-content"><text>联系电话</text><text class="con">{{ item.phone }}</text></view>
</view>
<icon v-show="cha === 1" type="icon" class="cuIcon-roundclosefill del" @click="del(item.guideId)"></icon>
</view>
</view>
</view>
<view v-show="TabCur === 0 && competeCompanyCoachList.length < 2" class="choice" @tap="jump">
+添加领队
</view>
<view v-show="TabCur === 1 && competeCompanyLeadersList.length < 3" class="choice" @tap="jump">
+添加教练
</view>
</view>
</template>
<script>
import { querycoach } from 'api/querycoach'
import { deletecoach } from 'api/deletecoach'
export default {
onLoad () {
this.query()
},
data() {
return {
list: ['领队', '教练'],
TabCur: 0,
competeCompanyCoachList: [],
competeCompanyLeadersList: [],
cha: 2,
cha1: 2
}
},
methods: {
tabSelect(e) {
this.TabCur = e.currentTarget.dataset.id - 0;
this.scrollLeft = (e.currentTarget.dataset.id - 1) * 60
console.log(this.TabCur,this.cha,this.cha1)
},
async del(num) {
console.log(num)
const that = this
uni.showModal({
title: '提示',
content: '确认要删除吗?',
success: async function (res) {
if (res.confirm) {
try{
const params = {
param: {
coachId: num
}
}
const data = await deletecoach(params)
uni.showToast({
title: '删除成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
that.query()
}catch(e){
//TODO handle the exception
}
}
}
})
},
change() {
const that = this
if(that.cha - 0 === 0) {
that.cha = 1
} else {
that.cha = 0
}
},
change1() {
const that = this
if(that.cha1 - 0 === 0) {
that.cha1 = 1
} else {
that.cha1 = 0
}
},
jump() {
const that = this
if (that.TabCur === 1) {
uni.navigateTo({
url:`../Coach/Coach`
})
} else if (that.TabCur === 0) {
uni.navigateTo({
url:`../Leader/Leader`
})
}
},
async query() {
const that = this
try{
const params = {
param: {
companyId: that.$store.state.project.companyId
}
}
const data = await querycoach(params)
if (data.competeCompanyCoachList[0]) {
that.competeCompanyCoachList = data.competeCompanyCoachList
that.cha1 = 0
} else {
that.cha1 = 2
that.competeCompanyCoachList = []
}
if (data.competeCompanyLeadersList[0]) {
that.competeCompanyLeadersList = data.competeCompanyLeadersList
that.cha = 0
} else {
that.cha = 2
that.competeCompanyLeadersList = []
}
}catch(e){
//TODO handle the exception
that.cha = 2
}
},
details(id) {
uni.navigateTo({
url:`../Leader/Leader?id=${id}`
})
},
details1(id) {
uni.navigateTo({
url:`../Coach/Coach?id=${id}`
})
}
},
watch:{
'$store.state.project.num'(val) {
const that = this
that.query()()
}
}
}
</script>
<style lang="scss" scoped>
.nav{
display: flex !important;
}
.cu-item {
width: 325rpx;
}
.bor {
border-bottom: 4px solid #709AFC;
color: #709AFC;
}
.infor-box {
position: relative;
box-shadow: 0px 0px 5px $grey;
background: #fff;
margin-top: 10px;
width: 670rpx;
margin-left: 40rpx;
border-radius: 10px;
padding-left: 75rpx;
}
.infor-content {
// margin-top: 10px;
padding: 10px 0 10px 0;
// margin-bottom: 20px;
}
.con {
position: absolute;
left: 350rpx;
}
.choice {
width: 300rpx;
margin: 20px auto;
background: $white;
box-shadow: 0 0 5px $grey;
border-radius: 5px;
color: $gray;
text-align: center;
height: 40px;
line-height: 40px;
font-size: 14px;
}
.num-box {
position: relative;
width: auto;
text-align: left;
height: 30px;
line-height: 40px;
font-size: 14px;
color: $gray;
margin-left: 35rpx;
}
.del {
font-size: 22px;
position: absolute;
right: 10px;
z-index: 10;
top: 10px;
font-weight: 700;
color: $red;
}
.change {
position: absolute;
margin-top: -30px;
height: 30px;
line-height: 30px;
background: #eee;
font-size: 14px;
right: 40rpx;
}
.delsure {
background: $green;
color: $white;
}
.click-box {
margin-right: 50px;
}
.fixed-box {
position: fixed;
z-index: 100;
background-color: $white;
top: 44px;
width: 100%;
}
.content-box {
margin-top: 90px;
}
</style>

346
pages/Coach/Coach.vue

@ -0,0 +1,346 @@
<template>
<view>
<view class="infor-box" v-for="(item,index) in list" :key="index">
<view class="infor-title">{{ item.title }}<text v-if="item.type !== 21" class="fbt">*</text></view>
<input v-if="item.type === 0" placeholder="请输入" name="input" v-model.trim="item.content"></input>
<radio-group v-if="item.type === 1" name="" @change="radioChange">
<label class="w100">
<radio value="0" :checked="che===0?true:false" /></radio><text></text>
<radio value="1" :checked="che===1?true:false" /></radio><text></text>
</label>
</radio-group>
<view v-if="item.type === 20" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids" @click="jump(item.content)">
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="idPhotoUrl"></image>
</view>
</view>
</view>
<view v-if="item.type === 21" class="cu-form-group">
<view class="grid col-4 grid-square flex-sub">
<view class="solids" @click="jump(item.content)">
<text class='cuIcon-cameraadd'></text>
<image class="img-box" :src="coachCertificateUrl"></image>
</view>
</view>
</view>
<text v-if="item.type === 20" class="tips">用于生成参赛证及比赛证书</text>
</view>
<button v-show="isId-0===0" class="btn cu-btn bg-green margin-tb-sm lg" @tap="submit">提交</button>
<button v-show="isId-0===1" class="btn cu-btn bg-blue margin-tb-sm lg" @click="change">修改</button>
</view>
</template>
<script>
import { savecoach } from 'api/savecoach'
import { coachInfo } from 'api/coachInfo'
import { photoBase64 } from 'api/photoBase64'
export default {
async onLoad(option) {
const that = this
if(option.id) {
console.log(option)
try{
const params = {
param: {
coachId: option.id
}
}
const data = await coachInfo(params)
console.log(data)
that.list[0].content = data.name
that.che = data.gender
that.list[2].content = data.phone
that.list[3].content = data.idCard
that.isId = 1
that.guideId = data.coachId
that.idPhoto = data.idPhotoId
that.idPhotoUrl = data.idPhoto
that.coachCertificate = data.coachCertificateId
that.coachCertificateUrl = data.coachCertificate
}catch(e){
//TODO handle the exception
}
}
},
data() {
return {
list: [{
title:'姓名',
type: 0,// 012
content: ''
}, {
title:'性别',
type: 1,
content: ''
}, {
title:'联系方式',
type: 0,
content: ''
}, {
title:'身份证号',
type: 0,
content: ''
}, {
title:'一寸证件照(图片大小不得大于2M)',
type: 20,
content: 'idPhoto'
}, {
title:'教练证(图片大小不得大于2M)',
type: 21,
content: 'coachCertificate'
}],
che: 0,
isId: 0,
idPhoto: 0,
idPhotoUrl: '',
coachCertificate: '',
coachCertificateUrl: '',
guideId: 0
}
},
methods: {
radioChange(e) {
const that = this
that.che = e.detail.value - 0
console.log(that.che)
},
jump(type) {
const that = this
uni.chooseImage({
count:1,
sizeType: ['compressed'], //
sourceType: ['album', 'camera'], //
success: function (res) {
if (res.tempFiles[0].size > 2*1024*1024) {
uni.showToast({
title: '图片超过2M,请重新选择',
icon: 'none',
duration: 1500
})
} else {
const tempFilePaths = res.tempFilePaths[0]
that[`${type}`] = tempFilePaths
uni.uploadFile({
url: '//www.tall.wiki/gateway/mt/file/upload/photo',
filePath: tempFilePaths,
header:{
"Authorization" : "Bearer " + that.$store.state.user.user.token
},
name: 'file',
success: (res) => {
// console.log(JSON.stringify());
const jsondata = JSON.parse(res.data)
console.log(jsondata)
that[`${type}Url`] = jsondata.data.visitUrl
that[`${type}`] = jsondata.data.id
}
});
}
// that.urlTobase64(res.tempFilePaths[0],type);
}
})
},
// urlTobase64(url,type){
// const that = this
// uni.request({
// url: url,
// method: 'GET',
// responseType: 'arraybuffer',
// success: async res => {
// let base64 = wx.arrayBufferToBase64(res.data); //arraybufferbase64
// base64 = 'data:image/jpeg;base64,' + base64; //
// // console.log(base64);
// try{
// const params = {
// fileBase64: base64
// }
// const data = await photoBase64(params)
// that[`${type}Url`] = data.visitUrl
// that[`${type}`] = data.id
// }catch(e){
// }
// }
// });
// },
async submit() {
const that = this
// console.log('')
if (that.list[0].content === '') {
uni.showToast({
title: '姓名不能为空',
icon: 'none',
duration: 1500
})
} else if (that.list[2].content === '') {
uni.showToast({
title: '联系方式不能为空',
icon: 'none',
duration: 1500
})
} else if (!/^1([3-9])[0-9]{9}$/.test(that.list[2].content)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none',
duration: 2000
})
}else if (that.list[3].content === '') {
uni.showToast({
title: '身份证号不能为空',
icon: 'none',
duration: 1500
})
} else if (that.idPhoto - 0 === 0) {
uni.showToast({
title: '请上传一寸证件照',
icon: 'none',
duration: 1500
})
} else {
try{
const params = {
param: {
companyId: that.$store.state.project.companyId,
identity: 1,
name: that.list[0].content,
gender: that.che,
phone: that.list[2].content,
idCard: that.list[3].content,
idPhoto: that.idPhoto,
coachCertificate: that.coachCertificate
}
}
const data = await savecoach(params)
uni.showToast({
title: '添加成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
setTimeout(function(){
uni.navigateBack()
},1000)
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
},
async change() {
const that = this
if (that.list[0].content === '') {
uni.showToast({
title: '姓名不能为空',
icon: 'none',
duration: 1500
})
} else if (that.list[2].content === '') {
uni.showToast({
title: '联系方式不能为空',
icon: 'none',
duration: 1500
})
} else if (!/^1([3-9])[0-9]{9}$/.test(that.list[2].content)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none',
duration: 2000
})
}else if (that.list[3].content === '') {
uni.showToast({
title: '身份证号不能为空',
icon: 'none',
duration: 1500
})
} else if (that.idPhoto - 0 === 0) {
uni.showToast({
title: '请上传一寸证件照',
icon: 'none',
duration: 1500
})
} else {
try{
const params = {
param: {
coachId: that.guideId,
companyId: that.$store.state.project.companyId,
identity: 1,
name: that.list[0].content,
gender: that.che,
phone: that.list[2].content,
idCard: that.list[3].content,
idPhoto: that.idPhoto,
coachCertificate: that.coachCertificate
}
}
const data = await savecoach(params)
uni.showToast({
title: '修改成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
setTimeout(function(){
uni.navigateBack()
},1000)
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
}
}
}
</script>
<style lang="scss" scoped>
.infor-box {
width: 680rpx;
margin-left: 35rpx;
margin-top: 25px;
border-bottom: 1px solid #eee;
}
.infor-title {
font-size: 16px;
color: #505050;
margin-bottom: 10px;
}
.w100 {
width: 680rpx;
display: flex;
radio {
margin-right: 50rpx;
}
text {
flex: 1;
}
}
.btn {
width: 600rpx;
margin: 50px 0 0 75rpx;
}
.tips {
color: $gray;
font-size: 14px;
}
.fbt {
color: $red;
}
.img-box {
width: 150rpx;
height: 150rpx;
}
</style>

214
pages/First/First.vue

@ -0,0 +1,214 @@
<template>
<view>
<swiper class="screen-swiper" :class="dotStyle?'square-dot':'round-dot'" :indicator-dots="true" :circular="true"
:autoplay="true" interval="3000" duration="500">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<img class="img" :src="item.url" mode="aspectFill" v-if="item.type=='img'"></img>
</swiper-item>
</swiper>
<view class="item-box" v-for="(item,index) in list" :key="index" @click="jump(index)">
<view class="item-num">{{ index + 1 }}</view>
<text class="item-content">{{ item.title }}</text>
<view v-show="item.complete + 1" class="item-infor">
<text v-if="datalist.companyId === null">未填写</text>
<text v-else>已填写</text>
</view>
<view v-show="item.leader + 1" class="item-infor">
<text>领队{{ datalist.guideNum }}教练{{ datalist.coachNum }}</text>
</view>
<view v-show="item.athletes + 1" class="item-infor">
<text>运动员{{ datalist.playerNum }}</text>
</view>
<view v-show="item.project + 1" class="item-infor">
<text>参赛项目{{ datalist.projectNum }}</text>
</view>
</view>
</view>
</template>
<script>
import { competeTime } from 'api/competeTime'
import { overview } from 'api/overview'
export default {
onLoad () {
this.query()
},
data() {
return {
list: [{
title:'基础信息',
complete: 1
}, {
title:'领队及教练信息',
leader: 2,
coach: 0
}, {
title:'运动员注册',
athletes: 4
}, {
title:'参赛项目',
project: 0
}],
projectId: 0,
companyId: 0,
datalist: {},
dotStyle: true,
swiperList: [{
id: 0,
type: 'img',
url: 'static/title.png'
}, {
id: 1,
type: 'img',
url: 'static/item01.png',
}, {
id: 2,
type: 'img',
url: 'static/item02.png'
}]
}
},
methods: {
async query () {
const that = this
try{
const params = {
param: {
type: 0
}
}
const data = await competeTime(params)
that.projectId = data.id
that.$store.state.project.data = data
console.log(that.$store.state.project.data)
if (data.id) {
const params = {
param : {
competeTimeId: data.id
}
}
const datalist = await overview(params)
if (datalist.companyId) {
that.companyId = datalist.companyId
}
console.log(datalist.companyId)
that.datalist = datalist
that.$store.state.project.companyId = datalist.companyId
console.log(that.$store.state.project.companyId)
}
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none'
})
}
},
jump (num) {
const that = this
if (that.$store.state.project.companyId) {
if (num === 0) {
uni.navigateTo({
url:`../Basics/Basics?id=${that.projectId}&companyId=${that.companyId}`
})
} else if (num + 1 === 2) {
uni.navigateTo({
url:`../Choice/Choice`
})
} else if (num + 1 === 3) {
uni.navigateTo({
url:`../Athletes/Athletes`
})
} else if (num + 1 === 4) {
if (that.datalist.playerNum) {
uni.navigateTo({
url:`../Project/Project`
})
} else {
uni.showToast({
title: '请先注册运动员',
icon:'none',
duration: 1500
})
}
}
} else {
if (num === 0) {
uni.navigateTo({
url:`../Basics/Basics?id=${that.projectId}&companyId=${that.companyId}`
})
} else {
uni.showToast({
title: '请先填写基础信息',
icon: 'none',
duration: 1500
})
}
}
}
},
watch: {
'$store.state.project.num'(val) {
const that = this
that.query()
}
}
}
</script>
<style lang="scss" scoped>
.item-box {
margin-top: 24px;
height: 80px;
line-height: 80px;
font-size: 18px;
width: 670rpx;
margin-left: 40rpx;
padding-left: 56rpx;
position: relative;
box-shadow: 0px 2px 5px $grey;
border-radius: 10px;
}
.item-num {
position: relative;
width: 40px;
height: 40px;
border-radius: 50%;
top: 20px;
text-align: center;
line-height: 40px;
background-color: #709AFC;
color: #fff;
}
.item-content {
position: absolute;
left: 180rpx;
top: -10px;
font-size: 16px;
}
.item-infor {
position: absolute;
left: 180rpx;
font-size: 12px;
color: $gray;
top: 15px;
}
.bingo {
font-size: 30px;
position: absolute;
top: 0;
right: 100rpx;
}
.active {
color: $green;
}
.noactive {
color: $grey;
}
.img {
width: 750rpx;
top: 0;
height: 100%;
z-index: 1000;
}
</style>

227
pages/Leader/Leader.vue

@ -0,0 +1,227 @@
<template>
<view>
<view class="infor-box" v-for="(item,index) in list" :key="index">
<view class="infor-title">{{ item.title }}<text class="fbt">*</text></view>
<input v-if="item.type === 0" placeholder="请输入" name="input" v-model.trim="item.content"></input>
<radio-group v-if="item.type === 1" name="sex" @change="radioChange">
<label class="w100">
<radio value="0" :checked="che===0?true:false" /></radio><text></text>
<radio value="1" :checked="che===1?true:false" /></radio><text></text>
</label>
</radio-group>
</view>
<button v-show="isId-0===0" class="btn cu-btn bg-green margin-tb-sm lg" @click="submit">提交</button>
<button v-show="isId-0===1" class="btn cu-btn bg-blue margin-tb-sm lg" @click="change">修改</button>
</view>
</template>
<script>
import { savecoach } from 'api/savecoach'
import { coachInfo } from 'api/coachInfo'
export default {
async onLoad(option) {
const that = this
if(option.id) {
console.log(option)
try{
const params = {
param: {
coachId: option.id
}
}
const data = await coachInfo(params)
console.log(data)
that.list[0].content = data.name
that.che = data.gender
that.list[2].content = data.phone
that.list[3].content = data.idCard
that.isId = 1
that.coachId = data.coachId
}catch(e){
//TODO handle the exception
}
}
},
data() {
return {
list: [{
title:'姓名',
type: 0 ,// 012
content: ''
}, {
title:'性别',
type: 1,
content: ''
}, {
title:'联系方式',
type: 0,
content: ''
}, {
title:'身份证号',
type: 0,
content: ''
}],
che: 0,
isId: 0,
coachId: 0
// imgList: []
}
},
methods: {
radioChange(e) {
const that = this
that.che = e.detail.value - 0
console.log(that.che)
},
async submit() {
const that = this
if (that.list[0].content === '') {
uni.showToast({
title: '姓名不能为空',
icon: 'none',
duration: 1500
})
} else if (that.list[2].content === '') {
uni.showToast({
title: '联系方式不能为空',
icon: 'none',
duration: 1500
})
} else if (!/^1([3-9])[0-9]{9}$/.test(that.list[2].content)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none',
duration: 2000
})
}else if (that.list[3].content === '') {
uni.showToast({
title: '身份证号不能为空',
icon: 'none',
duration: 1500
})
} else {
try{
const params = {
param: {
companyId: that.$store.state.project.companyId,
identity: 0,
name: that.list[0].content,
gender: that.che,
phone: that.list[2].content,
idCard: that.list[3].content
}
}
const data = await savecoach(params)
uni.showToast({
title: '添加成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
setTimeout(function(){
uni.navigateBack()
},1000)
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
// console.log('')
},
async change() {
const that = this
if (that.list[0].content === '') {
uni.showToast({
title: '姓名不能为空',
icon: 'none',
duration: 1500
})
} else if (that.list[2].content === '') {
uni.showToast({
title: '联系方式不能为空',
icon: 'none',
duration: 1500
})
} else if (!/^1([3-9])[0-9]{9}$/.test(that.list[2].content)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'none',
duration: 2000
})
}else if (that.list[3].content === '') {
uni.showToast({
title: '身份证号不能为空',
icon: 'none',
duration: 1500
})
} else {
try{
const params = {
param: {
coachId: that.coachId,
companyId: that.$store.state.project.companyId,
identity: 0,
name: that.list[0].content,
gender: that.che,
phone: that.list[2].content,
idCard: that.list[3].content
}
}
const data = await savecoach(params)
uni.showToast({
title: '修改成功',
icon: 'success',
duration: 1500
})
that.$store.state.project.num++
setTimeout(function(){
uni.navigateBack()
},1000)
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
}
}
}
</script>
<style lang="scss" scoped>
.infor-box {
width: 680rpx;
margin-left: 35rpx;
margin-top: 25px;
border-bottom: 1px solid #eee;
}
.infor-title {
font-size: 16px;
color: #505050;
margin-bottom: 10px;
}
.fbt {
color: $red;
}
.w100 {
width: 680rpx;
display: flex;
radio {
margin-right: 50rpx;
}
text {
flex: 1;
}
}
.btn {
width: 600rpx;
margin: 50px 0 0 75rpx;
}
</style>

220
pages/Login/Login.vue

@ -0,0 +1,220 @@
<template>
<view class="box">
<swiper class="screen-swiper" :class="dotStyle?'square-dot':'round-dot'" :indicator-dots="true" :circular="true"
:autoplay="true" interval="3000" duration="500">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<img class="img" :src="item.url" mode="aspectFill" v-if="item.type=='img'"></img>
</swiper-item>
</swiper>
<view class="title">
登录
</view>
<view class="ipt-infor">
<view class="ipt-username">
<view>
<text class="justleft">手机号</text>:
</view>
<input type="text" v-model.trim="userphone" placeholder="请输入"/>
</view>
<view class="ipt-password">
<view>
<text class="justleft">密码</text>:
</view>
<input type="password" v-model.trim="password" placeholder="请输入"/>
</view>
</view>
<button type="default" class="btn cu-btn bg-green margin-tb-sm lg" @click="login">登录</button>
<view class="go-register" @click="jump">注册</view>
<view class="go-password" @click="jump1">忘记密码</view>
</view>
</template>
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import { login } from 'api/login'
export default {
data() {
return {
userphone:'',
password:'',
dotStyle: true,
swiperList: [{
id: 0,
type: 'img',
url: 'static/title.png'
}, {
id: 1,
type: 'img',
url: 'static/item01.png',
}, {
id: 2,
type: 'img',
url: 'static/item02.png'
}]
}
},
methods: {
...mapMutations('user', ['setToken', 'setUser']),
jump() {
uni.navigateTo({
url:`./Register`,
})
},
jump1() {
uni.navigateTo({
url:`./Retrieve`,
})
},
async login () {
const that = this
if (that.userphone === '') {
uni.showToast({
title: '请输入用户名',
icon: 'none',
duration: 1500
})
} else if (that.password === '') {
uni.showToast({
title: '请输入密码',
icon: 'none',
duration: 1500
})
} else {
try {
const params = {
client: 1,
data: {
identifier: that.userphone,
credential: that.password,
},
type: 3
}
const data = await login(params)
console.log(data)
that.cacheData(data)
uni.navigateTo({
url:`../First/First`
})
} catch(e){
//TODO handle the exception
console.log(e)
uni.showToast({
title:e,
icon: "none",
duration: 2000
})
}
}
},
cacheData(data) {
const { token } = data;
this.setToken(token);
this.setUser(data);
// console.log(this.$store.state.user)
}
// ,
// replace(e){
// const that = this
// that.userphone=e.detail.value.replace(/\s/g,"")
// },
// replace1(e){
// const that = this
// that.password=e.detail.value.replace(/\s/g,"")
// }
}
}
</script>
<style lang="scss" scoped>
.box {
position: relative;
}
.title {
font-size: 20px;
width: 150rpx;
height: 30px;
line-height: 30px;
margin-top: 30px;
border-bottom: 2px solid $blue;
margin-left: 300rpx;
text-align: center;
}
.ipt-infor {
margin-left: 50rpx;
width: 650rpx;
margin-top: 50px;
view{
position: relative;
padding-left: 30px;
}
}
.justleft {
position: absolute;
left: -25px;
width: 50px;
text-align: justify !important;
text-align-last: justify;
}
.ipt-username {
position: relative;
height: 60px;
line-height: 60px;
border-bottom: 1px solid $grey;
input {
position: absolute;
top: 20px;
left: 90px;
font-size: 14px;
}
}
.ipt-password {
position: relative;
height: 60px;
line-height: 60px;
border-bottom: 1px solid $grey;
input {
position: absolute;
top: 20px;
left: 90px;
height: 20px;
line-height: 20px;
font-size: 14px;
}
}
.go-password {
color: $blue;
position: absolute;
left: 50rpx;
margin-top: 20px;
}
.btn {
width: 650rpx;
margin-left: 50rpx;
background: $blue;
color: $white;
margin-top: 50px;
}
.go-register {
color: $blue;
position: absolute;
right: 50rpx;
margin-top: 20px;
}
// .information {
// height: 30px;
// line-height: 30px;
// text-align: center;
// color: ;
// }
.img {
width: 750rpx;
height: 100%;
top: 0;
z-index: 1000;
}
</style>

806
pages/Login/Register.vue

@ -0,0 +1,806 @@
<template>
<view>
<swiper class="screen-swiper" :class="dotStyle?'square-dot':'round-dot'" :indicator-dots="true" :circular="true"
:autoplay="true" interval="3000" duration="500">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<img class="img" :src="item.url" mode="aspectFill" v-if="item.type=='img'"></img>
</swiper-item>
</swiper>
<view class="title">
注册
</view>
<view class="ipt-infor">
<view class="ipt-username">
<text class="just-box">手机号</text>:<input type="text" v-model.trim="username" placeholder="请输入"/>
<text class="just-tips" v-show="tips === 1">
该手机号已被注册请直接登录
</text>
</view>
<view class="ipt-username">
<text class="just-box">验证码</text>:<input type="text" v-model.trim="code" placeholder="请输入"/>
<button type="default" class="btn-code" :class="cooling? 'active' : ''" @click="cooling? get() : ''">{{ content }}</button>
</view>
<view class="ipt-password">
<text class="just-box">密码</text>:<input type="password" v-model.trim="password" placeholder="请输入"/>
</view>
</view>
<view class="img-box" v-show="imgbox">
<image :src="imgsrc" mode=""></image>
<input type="text" placeholder="请输入验证码" v-model.trim="codeinput"/>
<view class="btn-box">
<button type="default" @click="imgbox = false">关闭</button>
<button type="default" class="success" @click="submit">确定</button>
</view>
</view>
<button v-show="tips !== 1" type="default" class="btn cu-btn bg-green margin-tb-sm lg" @click="showBox">注册</button>
<view class="go-register" @click="jump">登录</view>
<view class="" v-show="safe === 1">
<view class="box111" @click="mask">
</view>
<view class="safe-box">
<view class="safe-title">
隐私条款
</view>
<view class="safe-content">
<view class="fs12">
山西传控电子科技有限公司以下统称传控科技我们一向庄严承诺保护使用传控科技所有产品服务以下统称传控服务之用户以下统称用户的隐私您在使用传控服务时我们可能会收集和使用您的相关信息
</view>
<view class="fs12"> 本隐私条款为传控服务声明的一部分并适用于我们提供的一切传控服务其包括传控科技所有产品服务的PC端App端H5手机网站以及提供基础服务的微信公众号微信小程序另外若您通过使用第三方产品和/或服务如第三方账号来使用传控服务您的信息还应当适用该第三方的隐私政策
</view>
<view class="fs12"> 传控科技将通过本隐私条款向您说明传控科技会如何收集保存使用共享以及保护您的信息本隐私条款与您使用我们的服务关系密切在使用传控服务前请您务必仔细阅读并透彻理解本政策在确认充分理解并同意后使用相关产品服务一旦您使用或在我们更新本隐私条款后我们会及时提示您更新的情况继续使用我们的产品或服务即意味着您已充分理解并同意本隐私条款含更新版本内容授权并接受我们按照本隐私条款的规定收集保存使用共享披露您的信息如您不同意协议中的任何条款您应立即停止使用传控科技相关服务 </view>
<view class="fs12">
您可以通过以下目录阅读相应章节进一步了解本条款的具体内容
</view>
<view class="fs12">
1. 我们收集的信息类型
</view>
<view class="fs12">
2. 我们如何使用所收集的信息
</view>
<view class="fs12">
3. 我们可能共享转让公开披露信息
</view>
<view class="fs12">
4. 您的权利
</view>
<view class="fs12">
5. 第三方网站
</view>
<view class="fs12">
6. 我们如何保存和保护您的信息
</view>
<view class="fs12">
7. 未成年人保护
</view>
<view class="fs12">
8. 隐私条款的变更
</view>
<view class="fs12">
9. 联系我们
</view>
<view class="fs16">
1 我们收集的信息类型
</view>
<view class="fs14">
1.1 您向我们提供的信息
</view>
<view class="fs14">
1.1.1 使用传控服务的必要信息
</view>
<view class="fs12">
当您使用传控服务时我们要求您提供并收集以下您的个人信息此等信息对于充分履行您和我们之间的约定的服务很有必要并使得我们能够遵守我们的法律义务没有此等信息我们可能无法向您提供您所要求的全部服务
</view>
<view class="fs12">
账号信息在您使用传控科技提供的服务时可以在不注册账号或不登录的情形下浏览传控科技网站但您在注册之后可以享受更全面和优质的服务在您注册传控科技账号时我们需要收集您的手机号码或个人邮箱我们将通过发送短信验证码的方式来验证您的手机号码是否真实有效通过发送邮件的方式来验证您的邮箱是否真实有效您也可以使用微博微信和QQ的第三方账号关联登录为保存登录信息并在不同设备登录时能同步数据我们需要您授予我们从第三方账号获得某些信息的权限如使用者第三方关联登录的唯一标识头像昵称有关您在这些第三方账号中所有的隐私控制选项及具体的隐私规定请参阅他们的隐私政策我们将在您首次运行App和您注册时提供隐私条款并获取您的同意
</view>
<view class="fs14">
1.1.2 您选择向我们提供的信息
</view>
<view class="fs12">
您可以选择向我们提供额外的个人信息以便在使用传控服务时获得更好的用户体验此等额外信息将基于您的自愿同意而处理
</view>
<view class="fs12">
个性化信息如果您选择我们为您提供的个性化产品或服务我们会根据相关服务的具体需求要求您提供更多个性化信息例如当您使用我们的比赛服务时根据比赛主办方的具体需求比赛报名还会要求您提供身份证号证件照等信息用于登记报到检录等环节
</view>
<view class="fs12">
授权信息在您使用传控科技的部分功能和/或服务时可能需要您自行开启以下权限您决定开启这些权限即代表您授权我们可以收集和使用这些信息来实现相关功能不开启或关闭这些权限即代表您取消了这些授权则我们将不再继续收集和使用您的这些个人信息也无法为您继续提供与这些权限相对应的功能您关闭权限的决定不会影响此前基于您的授权所进行的个人信息处理这些权限包括但不限于
</view>
<view class="fs12">
1文件存储和访问权限开启文件存储和访问权限方便为您提供保存图片缓存播放视频功能
</view>
<view class="fs12">
2访问位置信息权限开启访问位置信息权限我们可能会收集您的精确或大致位置信息此等信息通过您的IP地址或移动设备的GPS数据来确定以便为您提供更好的用户体验即使当您不使用应用时如果您的设置或设备权限允许此等连接开启我们仍可能会收集此等信息
</view>
<view class="fs12">
3相机权限开启相机权限方便您直接拍摄图片上传和使用视频会议等功能
</view>
<view class="fs12">
4访问和修改日历权限开启访问和修改日历权限方便您将比赛会议日期加入您的日历提醒中防止遗忘
</view>
<view class="fs12">
5麦克风权限开启麦克风权限方便您正常使用在线会议语音聊天等功能
</view>
<view class="fs14">
1.2 您使用传控服务时我们自动收集的信息
</view>
<view class="fs12">
在您使用传控科技提供的相关服务时我们会自动收集关于您所使用的服务以及如何使用相关服务的信息包括个人信息
</view>
<view class="fs14">
1.2.1 使用信息
</view>
<view class="fs12">
我们会收集您与传控科技相关产品和服务的互动信息如您查看的页面或其他内容您搜索的服务项目以及您在传控科技平台上参与的其他活动这些活动包括但不限于
</view>
<view class="fs12">
线上支付在您选择购买我们的服务时我们会根据法律规定记录保存在传控科技上的服务信息交易信息您可以选择第三方支付机构所提供的支付服务根据您的选择您需要提供开户行账户银行账户或者您选择的第三方支付时必要信息如果需要发票您还需要提交纳税人识别号支付功能本身并不收集您的信息但我们需要将您的订单号与交易金额信息与支付机构共享以实现其确认您的支付指令并完成支付
</view>
<view class="fs12">
关注并使用传控科技微信公众号小程序当您关注使用我们的微信公众号时我们会收集您的微信昵称头像当您使用该微信小程序时我们可能会收集您的微信UnionIDOpenIDUserID微信昵称微信头像登录记录信息当您使用微信账号或微信小程序的特定功能或服务时您可能需要注册登录传控科技账号或使用微信账号授权登录当您选择后者时我们可能会收集您的手机号码对于此类消息我们将按照微信公众号和微信小程序的个人信息收集规则根据相应提示获取您的同意当您参加线上课程等活动时我们将收集您主动填写的个人信息且仅用于该次活动的开展和统计工作
</view>
<view class="fs14">
1.2.2 登录数据和设备信息
</view>
<view class="fs12">
为了保障您使用传控科技服务时的人身财产安全更好地预防求职诈骗财产风险人身安全风险以及更准确地识别违反法律法规及传控科技相关协议规则的行为我们会记录整合使用您的常用设备信息网络标识信息以及我们关联公司合作第三方取得您授权或依法共享的信息即使您未创建或登录传控科技账户当您访问并使用传控科技网站时我们也会自动收集登录数据和设备信息此等信息包括您使用传控科技服务包括指向第三方应用的链接的详细信息IP地址访问日期和次数硬件和软件信息设备信息设备事件信息唯一识别符崩溃数据cookie数据以及您在使用传控科技服务前后查看或接触过的页面收集此等信息用于判断您的账号风险进行身份验证检测我们认为存在风险的行为以及防范平台安全时间并采取必要的记录审计分析处置措施
</view>
<view class="fs14">
1.2.3 Cookie及类似技术
</view>
<view class="fs12">
我们使用Cookie及类似技术如网络信标像素及移动标识符借助于 Cookie网站能够存储您的偏好等数据我们不会将 Cookie 用于本政策所述目的之外的任何用途您可根据自己的偏好管理或删除 Cookie
</view>
<view class="fs12">
您有权选择接受或拒绝接受Cookie在您未拒绝接受Cookie的情况下www.ccsens.com会在您的计算机上设定或取用Cookie以便您能登录或使用依赖于Cookie的www.ccsens.com平台服务或功能同时www.ccsens.com将自动接收并记录您的浏览器端数据包括但不限于IP地址网站Cookie中的资料及您要求取用的网页记录
</view>
<view class="fs12">
您可以通过修改浏览器设置的方式拒绝接受Cookie但如果您选择拒绝接受Cookie则您可能无法登录或使用依赖于Cookie的www.ccsens.com平台服务或功能
</view>
<view class="fs12">
通过www.ccsens.com所设Cookie所取得的有关信息将适用本政策
</view>
<view class="fs14">
1.3 我们通过第三方收集的信息
</view>
<view class="fs12">
传控科技可能会收集其他方在使用传控科技服务时提供的关于您的信息包括个人信息或从其他渠道获取信息并与我们通过传控科技服务收集的信息整合我们不会控制监督或回应提供您的信息的第三方如何处理您的个人数据任何发给我们的关于披露您的个人信息的信息请求应直接发送给此类第三方
</view>
<view class="fs12">
第三方服务我们提供部分功能和/或服务时需要使用第三方SDK技术这些第三方SDK在配合我们向您提供更全面的服务的同时可能会收集或使用您的个人信息我们会以弹窗提示等方式明确告知您在您授权同意后再获取相关信息相关隐私实践请详见该第三方的隐私政策如未取得您的授权我们将不会收集和使用相关信息也不会再反复弹窗向您获取授权目前这些SDK包括
</view>
<view class="fs12">
第三方登录新浪微博SDK微信SDKQQ登录SDK如果您将您的传控科技账户与第三方服务相关联或使用第三方服务登录您的传控科技账户相关第三方服务可能会向我们发送您在该服务中的注册信息和个人资料信息信息因服务而异由相关服务控制或由您通过相关服务的隐私设置授权使用第三方登录功能和一键快速登录功能时在您授权同意后新浪微博登录SDK微信SDKQQ登录SDK可能获取访问您的读写入外部存储WiFi权限网络状态电话状态检索正在运行的应用用于保证对应功能的正常使用
</view>
<view class="fs12">
支付支付宝微信SDK为实现用户在线支付购买增值服务产品在您授权同意后微信支付宝SDK可能获取访问您的网络状态读取电话状态Wi-Fi状态确保您在服务中正常使用在线支付的功能您可选择关闭相应的授权但可能造成您无法购买我们的增值产品或服务
</view>
<view class="fs12">
定位腾讯地图SDK在您授权同意后腾讯地图SDK可能获取访问您所在的城市地区以及位置信息网络状态读写外部存储权限Wi-Fi状态您可选择关闭相应的授权但可能造成您无法接收附近的职位信息
</view>
<view class="fs12">
分享微信QQ新浪微博SDK为了实现分享到第三方的功能在您授权同意后微信SDKQQSDK新浪微博SDK可能获取访问您的读写入外部存储WiFi权限网络状态电话状态用于保证对应功能的正常使用
</view>
<view class="fs12">
其他来源在适用法律允许的情况下我们可以从第三方服务提供商和/或合作伙伴处获得您的额外信息并将此类信息与我们所拥有的您的信息整合我们可以通过合作伙伴获得您的信息以及您在传控科技内外的活动信息或您在合作伙伴广告网络的体验与互动信息
</view>
<view class="fs16">
2 我们如何使用所收集的信息
</view>
<view class="fs12">
我们遵循合法正当必要的原则使用存储和处理您的信息包括个人信息以提供了解改进和发展传控科技创建和维护一个受到信任的更安全的环境并遵守我们的法律义务其中如涉及您的个人化信息我们将通过技术手段对数据进行去标识化处理去标识化处理的信息将无法识别主体我们有权使用已经去标识化的信息并在不透露您个人信息的前提下对用户数据库进行分析利用
</view>
<view class="fs16">
3 我们可能共享转让公开披露信息
</view>
<view class="fs12">
获得您的明确同意后我们会与其他方共享您的个人信息
</view>
<view class="fs12">
为便于我们基于关联账号共同向您提供服务推荐您可能感兴趣的信息或保护传控科技关联公司或其他用户或公众的人身财产安全免遭侵害您的个人信息可能会与我们的关联公司共享
</view>
<view class="fs12">
仅为实现本隐私权政策中声明的目的我们的某些服务将由我们和授权合作伙伴共同提供我们仅会出于合法正当必要特定明确的目的共享您的个人信息并且只会共享提供服务所必要的个人信息以提供更好的客户服务和用户体验我们的合作伙伴必须遵守我们的数据隐私和安全要求并且无权将共享的个人信息用于与产品或服务无关的其他用途
</view>
在传控科技服务提供者发生合并收购或破产清算情形或其他涉及合并收购或破产清算情形时如涉及到个人信息转让我们会要求新的持有您个人信息的公司组织继续受本政策的约束否则我们将要求该公司组织和个人重新向您征求授权同意
<view class="fs12">
尤其注意以下情形中共享转让公开披露您的个人信息无需事先征得您的授权同意
</view>
<view class="fs12">
依照法律法规法院命令监管机构命令的要求或根据政府行为监管要求或请求
</view>
<view class="fs12">
为执行相关服务协议或本政策维护社会公共利益为保护使用者我们的客户我们或我们的关联公司其他用户或雇员的人身财产安全或其他合法权益合理且必要的用途
</view>
<view class="fs12">
为提供和优化我们的服务我们的服务中内嵌了第三方的SDK比如您需要确定比赛或会议的地理位置可以调用腾讯地图的定位和导航功能在您调用相关功能时第三方SDK可能与我们收集您的个人常用设备信息硬件序列号设备MAC地址唯一设备识别码网络身份标识信息和其他可识别的信息上述所有信息将去标识化传输
</view>
<view class="fs16">
4 您的权利
</view>
<view class="fs14">
4.1 管理您的信息
</view>
<view class="fs12">
除法律规定外您有权访问和管理您的信息我们鼓励您更新和修改您的信息以使其更准确有效
</view>
<view class="fs14">
4.2 更正不准确或不完整的信息
</view>
<view class="fs12">
您在账户内可修改您的原有个人信息设置当您发现我们处理的关于您的个人信息有错误且您无法在账户内自行修改时您有权通过联系我们要求我们更正您的不准确或不完整的个人信息
</view>
<view class="fs14">
4.3 数据保留与删除
</view>
<view class="fs12">
通常我们只在履行您与我们之间的约定的服务并遵守我们的法律义务的必要时间段内保留您的不可识别个人信息如果您不再希望我们使用您的信息来为您提供服务您可以通过联系我们要求我们删除您的个人信息
</view>
<view class="fs14">
4.4 撤销同意和处理限制
</view>
<view class="fs12">
当您撤销同意或授权后我们将不再处理相应的信息但请您理解当您撤销同意或授权后我们无法继续为您提供撤销同意或授权所对应的全部或部分功能和服务您也可以通过联系我们向传控科技发送消息来撤销您的同意同时说明您要撤销哪一项同意请注意撤销您的同意不会影响任何在此撤销之前依据此类同意的处理活动的合法性
</view>
<view class="fs12">
请您理解在您访问修改和删除相关信息时我们可能会要求您进行身份验证以保障账号的安全同时由于技术所限法律或监管要求我们可能无法满足您的所有要求原则上我们会在15个工作日内答复您的请求
</view>
<view class="fs14">
4.5 注销权
</view>
<view class="fs12">
一般情况下您可以通过网上自助或联系客服方式注销您此前注册的账号我们将尽快删除该账号账户注销可能会导致清除您的所有用户数据和账户信息且不可恢复但在特定的情形下如合理必要地履行我们的法律义务解决争议防止欺诈和滥用我们将在您的账号注销后保留不可识别个人的信息
</view>
<view class="fs12">
您可根据以下联系我们部分所列的方式与我们联络以行使上述权利或者对于通过传控科技网站和传控科技App收集的个人信息您可通过传控科技网站或App中的帮助与反馈功能提交相关权利要求当您请求行使上述权利或进行其他申诉时原则上我们将于15个工作日内回复处理意见或结果
</view>
<view class="fs14">
4.6 例外情况
</view>
<view class="fs12">
依据相关法律法规及国家相关标准在以下情形中我们可能无法响应您的请求
</view>
<view class="fs12">
1与国家安全国防安全直接相关的
</view>
<view class="fs12">
2与公共安全公共卫生重大公共利益直接相关的
</view>
<view class="fs12">
3与犯罪侦查起诉审判和执行判决等直接相关的
</view>
<view class="fs12">
4有充分证据表明您存在主观恶意或滥用权利的
</view>
<view class="fs12">
5响应您的请求将导致其他个人组织的合法权益受到严重损害的
</view>
<view class="fs12">
6涉及商业秘密的
</view>
<view class="fs16">
5 第三方网站
</view>
<view class="fs12">
我们的网站中可能包含第三方运营网站的链接对于与传控科技网站链接或传控科技网站包含的第三方运营网站的相关产品或服务需受他们的隐私政策约束但这并不意味着传控科技认可或负责该第三方运营网站的隐私政策此外我们的网站中还存在网络110报警服务以响应您的紧急安全请求
</view>
<view class="fs16">
6 我们如何保存和保护您的信息
</view>
<view class="fs12">
我们高度重视您的信息安全我们将严格遵守相关法律法规规定采取业内认可的合理可行的措施保存和保护您的信息防止信息遭到未经授权的访问披露使用修改避免信息损坏或丢失
</view>
<view class="fs14">
6.1 保存期限
</view>
<view class="fs12">
我们仅为实现本隐私条款的目的所需的期限和法律法规及监管规定的最短时限内保留您的信息超出上述期限后我们将删除您的个人信息或对您的个人信息进行匿名化处理但如出现下列情况下我们将更改信息的存储时间
</view>
<view class="fs12">
1法律法规等有关规定的要求
</view>
<view class="fs12">
2法院判决裁定或其他法律程序规定的要求
</view>
<view class="fs12">
3相关行政机关的强制要求
</view>
<view class="fs12">
4我们有理由确信需要遵守法律法规等有关规定
</view>
<view class="fs12">
5为执行相关服务协议或本隐私条款维护社会公共利益为保护们的客户我们或我们的关联公司其他用户或雇员的人身财产安全或其他合法权益所合理必需的用途
</view>
<view class="fs12">
6当我们的产品或服务发生停止运营的情形时我们将采取例如邮件信函电话推送通知公告等形式通知您并在合理的期限内删除或匿名化处理您的信息
</view>
<view class="fs14">
6.2 保存地域
</view>
<view class="fs12">
我们在中华人民共和国境内运营中收集和产生的个人信息存储在中国境内以下情形除外
</view>
<view class="fs12">
1法律法规有明确规定
</view>
<view class="fs12">
2获得您的明确授权
</view>
<view class="fs12">
针对以上情形我们会确保依据本隐私条款对您的个人信息提供足够的保护
</view>
vi<view class="fs14">
6.3 技术措施与数据安全措施
</view>
<view class="fs12">
1我们努力采取各种合理可行的措施来保护您的信息安全我们积极建立数据分类分级制度数据安全管理规范数据安全开发规范来管理规范信息的存储和使用确保未收集与我们提供的服务无关的信息
</view>
<view class="fs12">
2我们通过与信息接触者签署保密协议监控和审计机制来对数据进行全面安全控制防止您的信息遭到未经授权的访问公开披露使用修改损坏或丢失
</view>
<view class="fs12">
3我们已使用符合业界标准的安全防护措施保护您提供的信息防止数据遭到未经授权的访问公开披露使用修改防止数据发生损坏或丢失我们会采取一切合理可行的措施保护您的信息例如在您的浏览器与服务之间交换数据时受SSL加密保护我们同时对传控科技网站提供https安全浏览方式我们会使用加密技术确保数据的保密性我们会使用受信赖的保护机制防止数据遭到恶意攻击我们会部署访问控制机制确保只有授权人员才可以访问信息以及我们会举办安全和隐私保护培训课程加强员工对于保护信息重要性的认识
</view>
<view class="fs14">
6.4 安全事件通知
</view>
<view class="fs12">
我们会制定网络安全事件应急预案及时处置系统漏洞计算机病毒网络攻击网络侵入等安全风险在发生危害网络安全的事件时我们会立即启动应急预案采取相应的补救措施并按照适用法律及法规向有关主管部门报告
</view>
<view class="fs12">
在发生安全事件后我们将按照法律法规的要求及时向您告知安全事件的基本情况和可能的影响我们已采取或将要采取的处理措施您可自主防范和降低的风险的建议对您的补救措施等我们将及时将事件相关情况以短信通知电话邮件等您预留的联系方式告知您难以逐一告知时我们会采取合理有效的方式发布公告
</view> <view class="fs16">
7 未成年人保护
</view>
<view class="fs12">
除了适用法律要求外我们不会在明知的情况下收集未成年人未满十八周岁的个人信息我们将在信息收集的相关功能中对年龄进行限定不再提供十八周岁以下的年龄区间选项十六周岁至十八周岁的未成年人使用传控科技服务应当由监护人仔细阅读本隐私条款注册账户和填写相关信息并应确保已征得其监护人同意的前提下使用我们的产品和服务并向我们提供您的信息如未经其监护人同意未成年人请勿向我们提供个人信息如您的监护人不同意您按照本隐私条款使用我们的产品和服务并向我们提供信息请您立即终止使用我们的服务并及时通知我们以便我们采取相应的措施如果监护人发现我们对您所监护的未成年人的信息处理有任何疑问请通过本隐私条款公布的联系方式及时联系我们我们将根据国家相关法律法规及本隐私条款的规定重点保护未成年人信息的保密性及安全性
</view>
<view class="fs16">
8 隐私条款的变更
</view>
<view class="fs12">
传控科技保留随时根据本条规定修改本隐私条款的权利如果我们对本隐私条款做出变更我们将发布变更后的隐私条款并更新隐私条款顶端的最后更新日期如本政策发生更新我们将以移动端推送通知或者在传控科技官方网站发布公告的方式来通知您为了您能及时接收到通知建议您在联系方式更新时及时通知我们如果您不同意变更后的隐私条款您可以注销您的账户如果您未在变更后的隐私条款生效前注销您的账户您对传控科技的继续访问或使用将受变更后的隐私条款的约束
</view>
<view class="fs16">
9 联系我们
</view>
<view class="fs12">
公司名称山西传控电子科技有限公司
</view>
<view class="fs12">
注册地址山西综改示范区太原学府园区发展路15号中绿大厦2-5层创时代孵化器508室
</view>
<view class="fs12">
联系方式如果您对本隐私条款或传控科技的信息处理方法有任何疑问意见建议以及申诉您可以通过产品的帮助与反馈功能或如下方式同我们联系
</view>
<view class="fs12">
电话01088850886-803工作日 930-1800
</view>
<view class="fs12">
官网www.ccsens.com
</view>
<view class="fs12">
邮箱who@ccsens.com
</view>
<view class="fs12">
为了核查您的问题并及时向您反馈我们可能需要您提交身份证明有效联系方式和书面请求及相关证据我们会妥善处理并及时反馈您的疑问意见建议以及申诉一般情况下我们会在15个工作日内对您的请求予以答复
</view>
</view>
<view class="safe-sure">
<icon type="icon" class="cuIcon-roundcheckfill green-icon safe-icon" :class="active === 1 ? 'active111' : ''" @click="changeSafe"></icon>
<text @click="changeSafe">同意</text>
<button class="safe-btn" type="default" @click="register">确认</button>
</view>
</view>
</view>
</view>
</template>
<script>
import { register } from 'api/register'
import { getbase } from 'api/getbase'
import { getcode } from 'api/getcode'
import { phone } from 'api/phone'
export default {
data() {
return {
username:'',
password:'',
code: '',
cooling: true,
content: '获取验证码',
imgbox: false,
imgsrc: '',
codeId: '',
codeinput: '',
safe: 0,
active: 0,
tips: 0,
dotStyle: true,
swiperList: [{
id: 0,
type: 'img',
url: 'static/title.png'
}, {
id: 1,
type: 'img',
url: 'static/item01.png',
}, {
id: 2,
type: 'img',
url: 'static/item02.png'
}]
}
},
methods: {
jump() {
uni.navigateTo({
url:`./Login`
})
},
changeSafe() {
const that = this
if (that.active === 0) {
that.active = 1
} else {
that.active = 0
}
},
async get() {
const that = this
if (!/^1([3-9])[0-9]{9}$/.test(that.username)) {
uni.showToast({
title: '请输入正确的手机号',
icon: 'loading',
duration: 2000
})
} else {
const params = {
}
const data = await getbase(params)
console.log(data)
that.imgsrc = data.imageBase64
that.codeId = data.verificationCodeId
that.imgbox = true
}
},
async register() {
const that = this
if (that.active === 0) {
uni.showToast({
title: '请先同意',
icon: 'none',
duration:1500
})
} else if (that.active === 1) {
try{
const params = {
account: that.username,
phone: that.username,
password: that.password,
smsCode: that.code,
source: 1
}
const data = await register(params)
console.log(data)
if (!data || !data.token) {
uni.showToast({
title: '注册失败',
icon: 'none',
duration: 1500
})
}else if (data.account) {
uni.showToast({
title: '注册成功,自动跳转到登录界面',
icon: 'success',
duration: 1500
})
setTimeout(function () {
uni.navigateTo({
url:`./Login`
})
},1500)
}
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
that.safe = 0
}
}
},
showBox() {
const that = this
if (that.username === '') {
uni.showToast({
title: '请输入手机号',
icon: 'none',
duration: 1500
})
} else if (that.password === '') {
uni.showToast({
title: '请输入密码',
icon: 'none',
duration: 1500
})
} else if (that.code === '') {
uni.showToast({
title: '请输入验证码',
icon: 'none',
duration: 1500
})
} else {
that.safe = 1
}
},
mask() {
const that = this
that.safe = 0
},
async submit() {
const that = this
try{
const params = {
params: {
phone: that.username,
verificationCodeId: that.codeId,
verificationCodeValue: that.codeinput
}
}
const data = await getcode(params)
console.log(data)
if (data.expiredInSeconds) {
that.imgbox = false
uni.showToast({
title: '正在获取',
icon: 'success',
duration: 3000
})
if (that.cooling) {
that.cooling = false
var a = 60
that.content = a + 's'
var aaa = setInterval(function () {
a -= 1
that.content = a + 's'
if (a - 0 === 0) {
that.cooling = true
that.content = '获取验证码'
clearInterval(aaa)
}
},1000)
}
}
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
},
watch:{
async username(val){
const that = this
that.tips = 0
if (that.username.length - 0 === 11) {
const params = {
params: {
phone : that.username
}
}
const data = await phone(params)
if (data) {
that.tips = 1
}
}
}
}
}
</script>
<style lang="scss" scoped>
.title {
font-size: 20px;
width: 150rpx;
height: 30px;
line-height: 30px;
margin-top: 30px;
border-bottom: 2px solid $blue;
margin-left: 300rpx;
text-align: center;
}
.ipt-infor {
margin-left: 50rpx;
width: 650rpx;
margin-top: 50px;
view{
padding-left: 75px;
}
}
.ipt-username {
position: relative;
height: 60px;
line-height: 60px;
border-bottom: 1px solid $grey;
input {
position: absolute;
top: 20px;
left: 90px;
font-size: 14px;
}
}
.ipt-password {
position: relative;
height: 60px;
line-height: 60px;
border-bottom: 1px solid $grey;
input {
position: absolute;
top: 20px;
left: 90px;
height: 20px;
line-height: 20px;
font-size: 14px;
}
}
.btn {
width: 650rpx;
margin-left: 50rpx;
background: $blue;
color: $white;
margin-top: 50px;
}
.go-register {
color: $blue;
position: absolute;
right: 50rpx;
margin-top: 20px;
}
.btn-code {
width: 100px;
height: 30px;
line-height: 30px;
font-size: 12px;
position: absolute;
right: 0;
top: 15px;
}
.active {
background: $green;
color: $white;
}
.img-box {
height: 650rpx;
width: 650rpx;
background: white;
border: 1px solid $gray;
border-radius: 10px;
position: fixed;
padding: 25rpx;
top: 0;
left: 50rpx;
margin-top: 45%;
z-index: 1;
image {
width: 500rpx;
height: 390rpx;
margin-left: 50rpx;
background-color: red;
}
input {
border: 1px solid $blue;
width: 500rpx;
margin-left: 50rpx;
margin-top: 40rpx;
height: 80rpx;
font-size: 18px;
padding-left: 5%;
border-radius: 10px;
}
}
.btn-box {
display: flex;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80rpx;
button {
// flex: 1;
width: 40%;
height: 80rpx;
line-height: 80rpx;
}
.success {
background: $green;
color: $white;
}
}
.img {
width: 750rpx;
top: 0;
height: 100%;
z-index: 1000;
}
.safe-box {
position: absolute;
width: 680rpx;
padding: 20px;
border-radius: 10px;
height: 680rpx;
top: 20%;
z-index: 10;
background: white;
left: 35rpx;
}
.safe-sure {
font-size: 14px;
padding-top: 5px;
}
.active111 {
color: $green !important;
}
.green-icon {
font-size: 16px !important;
color: $gray;
}
.box111 {
position: fixed;
height: 100%;
width: 100%;
top: 0;
left: 0;
background: rgba(0,0,0,0.5);
}
.safe-title {
text-align: center;
font-size: 16px;
}
.safe-content {
height: 70%;
overflow: auto;
}
.safe-btn {
margin-top: 5px;
}
.just-box{
position: absolute;
width: 50px;
font-size: 14px;
left: 20px;
text-align: center;
text-align: justify !important;
text-align-last: justify;
}
.just-tips {
position: absolute;
bottom: 0;
color: $red;
height: 14px;
line-height: 14px;
font-size: 14px;
left: 90px;
}
.fs16{
font-size: 16px;
}
.fs14{
font-size: 14px;
text-indent: 1em;
}
.fs12{
font-size: 12px;
text-indent: 2em;
}
</style>

282
pages/Login/Retrieve.vue

@ -0,0 +1,282 @@
<template>
<view>
<swiper class="screen-swiper" :class="dotStyle?'square-dot':'round-dot'" :indicator-dots="true" :circular="true"
:autoplay="true" interval="3000" duration="500">
<swiper-item v-for="(item,index) in swiperList" :key="index">
<img class="img" :src="item.url" mode="aspectFill" v-if="item.type=='img'"></img>
</swiper-item>
</swiper>
<view class="title">
找回密码
</view>
<view class="ipt-infor">
<view class="ipt-username">
<text>手机号</text><input type="text" v-model.trim="username" placeholder="请输入"/>
</view>
<view class="ipt-username">
<text>验证码</text><input type="text" v-model.trim="code" placeholder="请输入"/>
<button type="default" class="btn-code" :class="cooling? 'active' : ''" @click="get">{{ content }}</button>
</view>
<view class="ipt-password">
<text>新密码</text><input type="password" v-model.trim="password" placeholder="请输入"/>
</view>
</view>
<view class="img-box" v-show="imgbox">
<image :src="imgsrc" mode=""></image>
<input type="text" placeholder="请输入验证码" v-model.trim="codeinput"/>
<view class="btn-box">
<button type="default" @click="imgbox = false">关闭</button>
<button type="default" class="success" @click="submit">确定</button>
</view>
</view>
<button type="default" class="btn cu-btn bg-green margin-tb-sm lg" @click="register">确定修改</button>
<view class="go-register" @click="jump">登录</view>
</view>
</template>
<script>
import { password } from 'api/password'
import { getbase } from 'api/getbase'
import { getcode } from 'api/getcode'
export default {
data() {
return {
username:'',
password:'',
code: '',
cooling: true,
content: '获取验证码',
imgbox: false,
imgsrc: '',
codeId: '',
codeinput: '',
dotStyle: true,
swiperList: [{
id: 0,
type: 'img',
url: 'static/title.png'
}, {
id: 1,
type: 'img',
url: 'static/item01.png',
}, {
id: 2,
type: 'img',
url: 'static/item02.png'
}]
}
},
methods: {
jump() {
uni.navigateTo({
url:`./Login`
})
},
async get() {
const that = this
const params = {
}
const data = await getbase(params)
console.log(data)
that.imgsrc = data.imageBase64
that.codeId = data.verificationCodeId
that.imgbox = true
},
async register() {
const that = this
try{
const params = {
phone: that.username,
password: that.password,
code: that.code
}
const data = await password(params)
console.log(data)
if (!data || !data.token) {
uni.showToast({
title: '修改成功',
icon: 'success',
duration: 1500
})
}
}catch(e){
//TODO handle the exception
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
},
async submit() {
const that = this
try{
const params = {
params: {
phone: that.username,
verificationCodeId: that.codeId,
verificationCodeValue: that.codeinput
}
}
const data = await getcode(params)
console.log(data)
if (data.expiredInSeconds) {
that.imgbox = false
uni.showToast({
title: '正在获取',
icon: 'success',
duration: 3000
})
if (that.cooling) {
that.cooling = false
var a = 60
that.content = a + 's'
var aaa = setInterval(function () {
a -= 1
that.content = a + 's'
if (a - 0 === 0) {
that.cooling = true
that.content = '获取验证码'
clearInterval(aaa)
}
},1000)
}
}
}catch(e){
//TODO handle the exception
}
}
}
}
</script>
<style lang="scss" scoped>
.title {
font-size: 20px;
width: 200rpx;
height: 30px;
line-height: 30px;
margin-top: 30px;
border-bottom: 2px solid $blue;
margin-left: 275rpx;
text-align: center;
}
.ipt-infor {
margin-left: 50rpx;
width: 650rpx;
margin-top: 50px;
view{
padding-left: 50rpx;
}
}
.ipt-username {
position: relative;
height: 60px;
line-height: 60px;
border-bottom: 1px solid $grey;
input {
position: absolute;
top: 20px;
left: 90px;
font-size: 14px;
}
}
.ipt-password {
position: relative;
height: 60px;
line-height: 60px;
border-bottom: 1px solid $grey;
input {
position: absolute;
top: 20px;
left: 90px;
height: 20px;
line-height: 20px;
font-size: 14px;
}
}
.btn {
width: 650rpx;
margin-left: 50rpx;
background: $blue;
color: $white;
margin-top: 50px;
}
.go-register {
color: $blue;
position: absolute;
right: 50rpx;
margin-top: 20px;
}
.btn-code {
width: 100px;
height: 30px;
line-height: 30px;
font-size: 12px;
position: absolute;
right: 0;
top: 15px;
}
.active {
background: $green;
color: $white;
}
.img-box {
height: 650rpx;
width: 650rpx;
background: white;
border: 1px solid $gray;
border-radius: 10px;
position: fixed;
padding: 25rpx;
top: 0;
left: 50rpx;
margin-top: 45%;
z-index: 1;
image {
width: 500rpx;
height: 390rpx;
margin-left: 50rpx;
background-color: red;
}
input {
border: 1px solid $blue;
width: 500rpx;
margin-left: 50rpx;
margin-top: 40rpx;
height: 80rpx;
font-size: 18px;
padding-left: 5%;
border-radius: 10px;
}
}
.btn-box {
display: flex;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80rpx;
button {
// flex: 1;
width: 40%;
height: 80rpx;
line-height: 80rpx;
}
.success {
background: $green;
color: $white;
}
}
.img {
width: 750rpx;
top: 0;
height: 100%;
z-index: 1000;
}
</style>

209
pages/Project/MatchDetail/MatchDetail.vue

@ -0,0 +1,209 @@
<template>
<view>
<view class="sign-up" v-show="submit === 0" @click="changeSub">
添加报名
</view>
<!-- <view class="sign-up" v-show="submit === 0" @click="changeSub">
已报名{{}}/{{ objdata.memberMax }}
</view> -->
<view class="member-box" v-for="(item,index) in list" :key="index">
<view class="member-title">{{ item.groupName }}</view>
<view class="member-con" v-for="(a,b) in item.playerList" :key="b" :class="a.joinProject ? 'active' : ''" v-show="(a.joinProject === 1||submit === 1)? true : false" @click="submit === 1? change(index,b) : ''">
<icon v-show="submit === 1" type="icon" class="cuIcon-roundcheckfill back" :class="a.joinProject ? 'backactive' : ''"></icon>{{ a.playerName }}
</view>
</view>
<button @click="trans" v-show="submit === 1" type="default" class="btn cu-btn bg-green margin-tb-sm lg">提交</button>
</view>
</template>
<script>
import { groupplayer } from 'api/groupplayer'
import { join } from 'api/join'
export default {
onLoad(option) {
const obj = JSON.parse(option.obj)
const that = this
that.objdata = obj
that.query()
},
data() {
return {
list: [],
objdata: {},
submit: 2,
changeList: [],
originalList: []
}
},
methods: {
async query () {
const that = this
try{
const params = {
param: {
companyId: that.$store.state.project.companyId,
projectId: that.objdata.id
}
}
const data = await groupplayer(params)
// console.log(data)
that.list = data
if (that.list[0]) {
that.submit = 0
} else {
that.submit = 2
}
}catch(e){
//TODO handle the exception
}
},
change (a,b) {
const that = this
// console.log(that.list[a].playerList[b]) //
var obj = {}
if (that.originalList.length === 0) {
obj.playerId = that.list[a].playerList[b].playerId
obj.joinProject = that.list[a].playerList[b].joinProject
obj.playerName = that.list[a].playerList[b].playerName
that.originalList.push(obj)
} else {
// console.log('0')
for (var i=0; i<that.originalList.length; i++) {
// console.log(i)
if (that.originalList[i].playerId === that.list[a].playerList[b].playerId) {
// console.log('')
break
}
if (i + 1 === that.originalList.length) {
// console.log('for')
obj.playerId = that.list[a].playerList[b].playerId
obj.joinProject = that.list[a].playerList[b].joinProject
obj.playerName = that.list[a].playerList[b].playerName
that.originalList.push(obj)
break
}
}
}
if (that.list[a].playerList[b].joinProject - 0 === 0) {
that.list[a].playerList[b].joinProject = 1
} else {
that.list[a].playerList[b].joinProject = 0
}
if (that.changeList.length === 0) {
that.changeList.push(that.list[a].playerList[b])
} else {
for (var i=0; i<that.changeList.length; i++) {
if (that.changeList[i].playerId === that.list[a].playerList[b].playerId) {
that.changeList[i].joinProject = that.list[a].playerList[b].joinProject
break
}
if (i + 1 === that.changeList.length) {
that.changeList.push(that.list[a].playerList[b])
break
}
}
}
for (var i=0; i<that.originalList.length; i++) {
for (var j=0; j<that.changeList.length; j++) {
if (that.originalList[i].playerId === that.changeList[j].playerId) {
if (that.originalList[i].joinProject === that.changeList[j].joinProject) {
that.changeList.splice(j,1)
break
}
}
}
}
console.log(that.changeList)
},
changeSub() {
const that = this
that.submit = 1
},
async trans() {
const that = this
try{
const params = {
param: {
companyId: that.$store.state.project.companyId,
competeTimeId: that.$store.state.project.data.id,
players: that.changeList,
projectId: that.objdata.id
}
}
const data = await join(params)
uni.showToast({
title: '报名成功',
icon: 'success',
duration: 1500
})
that.changeList = []
that.originalList = []
that.query()
that.$store.state.project.num++
that.submit = 0
}catch(e){
//TODO handle the exception
that.submit = 1
uni.showToast({
title: e,
icon: 'none',
duration: 1500
})
}
}
}
}
</script>
<style lang="scss" scoped>
.member-box {
margin-left: 35rpx;
width: 680rpx;
}
.member-title {
height: 40px;
line-height: 40px;
font-size: 18px;
margin-top: 10px;
border-bottom: 1px solid $grey;
}
.member-con {
position: relative;
height: 60px;
margin-top: 10px;
line-height: 60px;
border-radius: 10px;
padding-left: 50%;
box-shadow: 0 0 4px $gray;
}
.active {
box-shadow: 0 0 4px #709AFC !important;
}
.btn {
width: 680rpx;
margin-left: 35rpx;
margin-top: 30px;
background: #709AFC;
color: $white;
}
.back {
position: absolute;
color: $grey;
font-size: 30px;
left: 20px;
}
.backactive {
color: #709AFC !important;
}
.sign-up {
position: absolute;
right: 35rpx;
padding: 5px 10px;
border-radius: 5px;
box-shadow: 0 0 5px $gray;
}
</style>

225
pages/Project/MatchDetail/TeamDetail.vue

@ -0,0 +1,225 @@
<template>
<view>
<ms-dropdown-menu class="select">
<ms-dropdown-item v-model="value" :list="list"></ms-dropdown-item>
<ms-dropdown-item v-model="value1" :list="list1"></ms-dropdown-item>
</ms-dropdown-menu>
<!-- <button type="default" @click="btn">123</button> -->
<view class="player-box">
<view class="player" :class="item.isjoin?'player-active':''" v-show="item.isjoin || edit - 0 === 1" v-for="(item,index) in list2" :key="index" @click="choice(index)">
<icon type="icon" :class="item.isjoin?'backactive':''" class="cuIcon-roundcheckfill back"></icon>
{{ item.name }}
</view>
</view>
<button class="modify" type="default" @click="editData" :class="edit - 0 === 1?'mit':''">
<text v-if="edit - 0 === 0">编辑</text>
<text v-else-if="edit - 0 === 1">提交</text>
</button>
</view>
</template>
<script>
import msDropdownMenu from '@/components/ms-dropdown/dropdown-menu.vue'
import msDropdownItem from '@/components/ms-dropdown/dropdown-item.vue'
export default {
components: {
msDropdownMenu,
msDropdownItem
},
onLoad(option) {
const that = this
that.obj = JSON.parse(option.obj)
console.log(that.obj)
},
data() {
return {
obj:{},
list: [
{
text: '小学',
value: 0
},
{
text: '中学',
value: 1
},
{
text: '高职院校',
value: 2
},
{
text: '本科院校',
value: 3
},
{
text: '俱乐部',
value: 4
}
],
list1: [
{
text: '男子组',
value: 0
},
{
text: '女子组',
value: 1
},
{
text: '混合组',
value: 2
}
],
list2: [
{
name: '男子组',
isjoin: 0
},
{
name: '女子组',
isjoin: 0
},
{
name: '混合组',
isjoin: 0
},
{
name: '女子组',
isjoin: 0
},
{
name: '混合组',
isjoin: 0
},
{
name: '女子组',
isjoin: 0
},
{
name: '混合组',
isjoin: 0
},
{
name: '女子组',
isjoin: 0
},
{
name: '混合组',
isjoin: 0
},
{
name: '女子组',
isjoin: 0
},
{
name: '混合组',
isjoin: 0
}
],
value: 0,
value1: 0,
edit: 0,
joinNum:0
}
},
methods: {
choose() {
const that = this
let obj = {
value: 'test'
}
this.$refs.dropdownItem.choose(obj)
},
close() {
this.$refs.dropdownItem.closePopup()
},
editData() {
const that = this
if(that.edit - 0 === 0) {
that.edit = 1
} else {
if (that.joinNum - 0 === 0) {
that.edit = 0
} else if (that.joinNum < that.obj.memberMin) {
uni.showToast({
title:`小于最少人数${that.obj.memberMin}`,
icon: 'none',
duration: 1500
})
} else if (that.joinNum > that.obj.memberMax) {
uni.showToast({
title:`大于最多人数${that.obj.memberMin}`,
icon: 'none',
duration: 1500
})
} else {
that.edit = 0
}
}
},
choice(index) {
const that = this
that.joinNum = 0
if(that.list2[`${index}`].isjoin - 0 === 0) {
that.list2[`${index}`].isjoin = 1
} else {
that.list2[`${index}`].isjoin = 0
}
for(var i=0; i<that.list2.length;i++) {
if(that.list2[i].isjoin - 0 === 1) {
that.joinNum++
}
}
console.log(that.joinNum)
}
}
}
</script>
<style lang="scss" scoped>
.select {
position: fixed;
z-index: 10;
top: 44px;
width: 750rpx !important;
box-shadow: 0 2px 10px #aaa;
margin-bottom: 20px;
}
.player-box {
width: 670rpx;
margin-top: 70px;
margin-left: 35rpx;
}
.player {
position: relative;
height: 60px;
margin-top: 10px;
line-height: 60px;
border-radius: 10px;
text-align: center;
box-shadow: 0 0 5px $gray;
}
.player-active{
box-shadow: 0 0 4px #709AFC !important;
}
.back {
position: absolute;
color: $grey;
font-size: 30px;
left: 20px;
}
.backactive {
color: #709AFC !important;
}
.modify {
width: 670rpx;
margin-left: 35rpx;
margin-top: 50px;
box-shadow: 2px 2px 5px #C0C0C0;
margin-bottom: 100px;
}
.mit {
background: $blue;
color: $white;
}
</style>

181
pages/Project/MatchDetail/TeamMatch.vue

@ -0,0 +1,181 @@
<template>
<view>
<view class="match-name">
{{ obj.name }}
</view>
<view class="match-Name"></view>
<view v-if="list[0]">
<view class="match-team" v-for="(item,index) in list" :key="index">
<view class="team-name">
{{ item.name }}
<icon type="icon" class="cuIcon-close icon" @click="del"></icon>
</view>
<view class="team-player">
<view class="team-man" v-for="(a,b) in item.player" :key="b">
{{ a }}
</view>
</view>
<button type="default" class="change" @click="edit">编辑</button>
</view>
</view>
<view class="img-B" v-else>
<img src="static/item.png" class="img-box" />
<view>
暂无运动员信息
</view>
</view>
<view class="add" @click="addTeam">添加</view>
</view>
</template>
<script>
export default {
onLoad(option) {
const that = this
var OBJ = JSON.parse(option.obj)
console.log(option.TypeNum,OBJ)
that.obj = OBJ
},
data() {
return {
obj: {},
list: [{
name:'小学女子组',
player: ['王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺']
},{
name:'小学男子组',
player: ['王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺','王旺旺']
},{
name:'小学混合组',
player: ['王旺旺','王旺旺','王旺旺']
}]
}
},
methods: {
del() {
uni.showModal({
title: '提示',
content: '确定要删除整组么?',
success: (res) => {
if (res.confirm) {
console.log('确定删除')
}
if (res.cancel) {
console.log('取消')
}
}
})
},
edit() {
const that = this
const obj = that.obj
uni.navigateTo({
url: `./TeamDetail?obj=${JSON.stringify(obj)}`
})
},
addTeam() {
const that = this
const obj = that.obj
uni.navigateTo({
url: `./TeamDetail?obj=${JSON.stringify(obj)}`
})
}
}
}
</script>
<style lang="scss" scoped>
.match-name {
position: fixed;
text-align: center;
width: 100%;
top: 44px;
height: 40px;
line-height: 40px;
background: $white;
z-index: 10;
font-size: 20px;
}
.match-Name {
height: 40px;
}
.match-team {
// background: $yellowLight;
box-shadow: 0 0 10px #C0C0C0;
width: 670rpx;
padding-bottom: 30px;
margin-left: 35rpx;
height: auto;
max-height: 240px;
margin-top: 20px;
border-radius: 25px;
position: relative;
}
.team-name {
height: 40px;
line-height: 40px;
width: 100%;
text-align: center;
font-size: 16px;
font-weight: 600;
border-bottom: 1px solid $blueShadow;
}
.change {
width: 60px;
height: 30px;
font-size: 14px;
line-height: 30px;
position: absolute;
right: 10px;
bottom: 6px;
}
.team-player {
width: 100%;
max-height: 160px;
padding: 10px;
overflow: auto;
}
.team-man {
padding: 2px 10px;
float: left;
height: 40px;
margin-right: 10px;
margin-bottom: 10px;
line-height: 40px;
font-size: 12px;
border-radius: 10px;
box-shadow: 0 0 5px $blue;
}
.icon {
position: absolute;
top: 0;
right: 10px;
font-size: 24px;
color: #C0C0C0;
}
.add {
position: fixed;
width: 150rpx;
height: 150rpx;
background: #709AFC;
box-shadow: 0 0 20px #709AFC;
border-radius: 50%;
color: $white;
right: 10px;
bottom: 100px;
text-align: center;
line-height: 150rpx;
font-size: 18px;
}
.img-box {
width: 70%;
height: 70%;
}
.img-B {
width: 100%;
margin-top: 100px;
height: 450rpx;
text-align: center;
color: #aaa;
}
</style>

86
pages/Project/NumMatch.vue

@ -0,0 +1,86 @@
<template>
<view>
<view class="item-box" v-for="(item,index) in list" :key="index" @click="jump(index,item)">
<view class="item-num">{{ index + 1 }}</view>
<text class="item-content">{{ item.name }}</text>
</view>
</view>
</template>
<script>
import { secondproject } from 'api/secondproject'
export default {
async onLoad (options) {
const that = this
try{
const params = {
param: {
firstProjectId: options.id
}
}
const data = await secondproject(params)
that.list = data
console.log(data)
}catch(e){
//TODO handle the exception
}
},
data() {
return {
list: [],
}
},
methods: {
jump (num,obj) {
if (obj.team - 0 === 0) {
uni.navigateTo({
url:`./MatchDetail/MatchDetail?TypeNum=${num}&obj=${JSON.stringify(obj)}`
})
} else {
uni.navigateTo({
url:`./MatchDetail/TeamMatch?TypeNum=${num}&obj=${JSON.stringify(obj)}`
})
}
}
}
}
</script>
<style lang="scss" scoped>
.title {
text-align: center;
font-size: 12px;
color: $red;
overflow: auto;
}
.item-box {
margin-top: 24px;
height: 80px;
line-height: 80px;
font-size: 18px;
width: 670rpx;
margin-left: 40rpx;
padding-left: 56rpx;
position: relative;
box-shadow: 0px 2px 5px $grey;
border-radius: 10px;
}
.item-num {
position: relative;
width: 20px;
height: 20px;
border-radius: 5px;
top: 30px;
font-size: 12px;
text-align: center;
line-height: 20px;
background-color: #709AFC;
color: #fff;
}
.item-content {
position: absolute;
left: 150rpx;
font-size: 16px;
top: 0;
}
</style>

89
pages/Project/PatternMatch.vue

@ -0,0 +1,89 @@
<template>
<view class="box">
<view class="item-box" v-for="(item,index) in list" :key="index" @click="jump(index,item)">
<view class="item-num">{{ index + 1 }}</view>
<text class="item-content">{{ item.name }}</text>
</view>
</view>
</template>
<script>
import { secondproject } from 'api/secondproject'
export default {
async onLoad (options) {
const that = this
try{
const params = {
param: {
firstProjectId: options.id
}
}
const data = await secondproject(params)
that.list = data
console.log(data)
}catch(e){
//TODO handle the exception
}
},
data() {
return {
list: [],
}
},
methods: {
jump (num,obj) {
if (obj.team - 0 === 0) {
uni.navigateTo({
url:`./MatchDetail/MatchDetail?TypeNum=${num}&obj=${JSON.stringify(obj)}`
})
} else {
uni.navigateTo({
url:`./MatchDetail/TeamMatch?TypeNum=${num}&obj=${JSON.stringify(obj)}`
})
}
}
}
}
</script>
<style lang="scss" scoped>
.box {
padding-bottom: 100px;
}
.title {
text-align: center;
font-size: 12px;
color: $red;
overflow: auto;
}
.item-box {
margin-top: 24px;
height: 80px;
line-height: 80px;
font-size: 18px;
width: 670rpx;
margin-left: 40rpx;
padding-left: 56rpx;
position: relative;
box-shadow: 0px 2px 5px $grey;
border-radius: 10px;
}
.item-num {
position: relative;
width: 20px;
height: 20px;
border-radius: 5px;
top: 30px;
font-size: 12px;
text-align: center;
line-height: 20px;
background-color: #709AFC;
color: #fff;
}
.item-content {
position: absolute;
left: 150rpx;
font-size: 16px;
top: 0;
}
</style>

97
pages/Project/Project.vue

@ -0,0 +1,97 @@
<template>
<view>
<view class="title">
<text v-for="(item,index) in tips" :key="index" class="flex flex-direction">
{{ item }}
</text>
</view>
<view class="item-box" v-for="(item,index) in list" :key="index" @click="jump(index,item.projectId)">
<view class="item-num">{{ index + 1 }}</view>
<text class="item-content">{{ item.projectName }}</text>
</view>
</view>
</template>
<script>
import { firstproject } from 'api/firstproject'
export default {
async onLoad () {
const that = this
try{
const params = {
param: {
competeTimeId: that.$store.state.project.data.id
}
}
const data = await firstproject(params)
that.list = data
console.log(data)
}catch(e){
//TODO handle the exception
}
},
data() {
return {
tips: ['参赛项目(各项各组报名不满3人 / 队)', '取消该组该项比赛'],
list: [{
projectId: "2001",
projectName: "计数赛"
}, {
projectId: "2002",
projectName: "花样赛"
}],
}
},
methods: {
jump (num,id) {
if (num === 0) {
uni.navigateTo({
url:`./NumMatch?id=${id}`
})
} else if (num === 1) {
uni.navigateTo({
url:`./PatternMatch?id=${id}`
})
}
}
}
}
</script>
<style lang="scss" scoped>
.title {
text-align: center;
font-size: 12px;
color: $red;
overflow: auto;
}
.item-box {
margin-top: 24px;
height: 80px;
line-height: 80px;
font-size: 18px;
width: 670rpx;
margin-left: 40rpx;
padding-left: 56rpx;
position: relative;
box-shadow: 0px 2px 5px $grey;
border-radius: 10px;
}
.item-num {
position: relative;
width: 40px;
height: 40px;
border-radius: 50%;
top: 20px;
text-align: center;
line-height: 40px;
background-color: #709AFC;
color: #fff;
}
.item-content {
position: absolute;
left: 180rpx;
font-size: 16px;
top: 0;
}
</style>

17
pages/index/index.vue

@ -0,0 +1,17 @@
<template>
<view class="container">
<!-- 测试组件 不需要引入 注册 -->
<test />
</view>
</template>
<script>
export default {
};
</script>
<style scoped lang="scss">
</style>

29
pages/read/Privacy.vue

@ -0,0 +1,29 @@
<template>
<view>
<web-view :src="src"></web-view>
</view>
</template>
<script>
export default {
onLoad(option) {
const that = this
that.type = option.type
that.token = that.$store.state.user.user.token
that.src = `http://test.tall.wiki/compete/index/index.html?type=` + option.type + `&token=` + that.token
},
data() {
return {
type: '',
token: ''
}
},
methods: {
}
}
</script>
<style>
</style>

67
pages/read/read.vue

@ -0,0 +1,67 @@
<template>
<view class="box">
<view class="title">
2020年山西省学生跳绳比赛
</view>
<view class="title-safe">
安全责任书
</view>
<view class="content">
本人自愿报名参加2020年山西省学生跳绳比赛并签署本责任书
</view>
<view class="content">
本人已全面了解并同意遵守大会所制订的各项竞赛规程规则要求及采取的安全措施
</view>
<view class="content">
本人已完全了解自己的身体状况确认自己身体健康状况良好具备参赛条件已为参赛做好充分准备并在比赛前购买了人身意外伤害保险监护人经审慎评估确认被监护人身体状况符合参赛条件并自愿承担相应风险
</view>
<view class="content">
本人充分了解本次比赛可能出现的风险且已准备必要的防范措施以对自己学生安全负责的态度参赛
</view>
<view class="content">
本人愿意承担比赛期间发生的自身意外风险责任且同意对于非大会原因造成的伤害等任何形式的损失大会不承担任何形式的赔偿
</view>
<view class="content">
本人同意接受大会在比赛期间提供的现场急救性质的医务治疗但在离开现场后在医院救治等发生的相关费用由本队负担
</view>
<view class="content">
本人承诺以自己的名义参赛决不冒名顶替否则自愿承担全部法律责任
</view>
<view class="content">
本人及家长监护人已认真阅读并全面理解以上内容且对上述所有内容予以确认并承担相应的法律责任
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.box {
padding: 20px 35rpx 35px 35rpx;
}
.title {
font-size: 18px;
text-align: center;
}
.title-safe {
font-size: 16px;
text-align: center;
margin-top: 10px;
margin-bottom: 10px;
}
.content {
margin-bottom: 10px;
font-size: 14px;
text-indent: 2em;
}
</style>

112
plugins/request/index.js

@ -0,0 +1,112 @@
import { BASE_URL, ERR_CODE } from 'api/base';
import Request from './request';
import Fingerprint from 'fingerprintjs'
const test = new Request();
/**
* 获取token
* @return {string} 本地保存的token
*/
export const getToken = () => uni.getStorageSync('token');
test.setConfig(config => {
/* 设置全局配置 */
config.baseUrl = BASE_URL;
config.header = {
...config.header,
};
return config;
});
test.interceptor.request((config, cancel) => {
/* 请求之前拦截器 */
config.header = {
...config.header,
};
/*
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
}
*/
return config;
});
/**
* 自定义验证器如果返回true 则进入响应拦截器的响应成功函数(resolve)否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode只读
* @return { Boolean } 如果为true, resolve, 否则 reject
*/
test.validateStatus = statusCode => {
return statusCode === ERR_CODE;
};
test.interceptor.response(
response => {
/* 请求之后拦截器 */
return response;
},
response => {
// 请求错误做点什么
return response;
},
);
const http = new Request();
http.setConfig(config => {
// 设置全局配置
config.baseUrl = BASE_URL; // 根域名不同
config.header = {
...config.header,
};
return config;
});
/**
* 自定义验证器如果返回true 则进入响应拦截器的响应成功函数(resolve)否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode只读
* @return { Boolean } 如果为true, resolve, 否则 reject
*/
http.validateStatus = statusCode => {
return statusCode === ERR_CODE;
};
// console.log(fingerprint)
http.interceptor.request((config, cancel) => {
/* 请求之前拦截器 */
const token = getToken() ? { Authorization: `Bearer ${getToken()}` } : {};
const fingerprint = { fingerprint: new Fingerprint().get() };
config.header = {
...config.header,
...token,
...fingerprint,
};
/*
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
}
*/
return config;
});
http.interceptor.response(
response => {
/* 请求之后拦截器 */
if (response.data.code !== ERR_CODE) {
// 服务端返回的状态码不等于200,则reject()
return Promise.reject(response.data.msg);
}
return response.data.data;
},
response => {
// 请求错误做点什么
return response;
},
);
export { http, test };

273
plugins/request/readme.md

@ -0,0 +1,273 @@
**插件使用说明**
- 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
- 支持全局挂载
- 支持多个全局配置实例
- 支持自定义验证器
- 支持文件上传(如不使用可以删除class里upload 方法)
- 支持` typescript `、` javascript ` 版本(如果不使用ts版本,则可以把luch-request-ts 文件夹删除)
- 下载后把 http-request 文件夹放到项目 utils/ 目录下
**Example**
---
创建实例
``` javascript
const http = new Request();
```
执行` GET `请求
``` javascript
http.get('/user/login', {params: {userName: 'name', password: '123456'}}).then(res => {
}).catch(err => {
})
// 局部修改配置,局部配置优先级高于全局配置
http.get('/user/login', {
params: {userName: 'name', password: '123456'}, /* 会加在url上 */
header: {}, /* 会覆盖全局header */
dataType: 'json',
responseType: 'text'
}).then(res => {
}).catch(err => {
})
```
执行` POST `请求
``` javascript
http.post('/user/login', {userName: 'name', password: '123456'} ).then(res => {
}).catch(err => {
})
// 局部修改配置,局部配置优先级高于全局配置
http.post('/user/login', {userName: 'name', password: '123456'}, {
params: {}, /* 会加在url上 */
header: {}, /* 会覆盖全局header */
dataType: 'json',
responseType: 'text'
}).then(res => {
}).catch(err => {
})
```
执行` upload `请求
``` javascript
http.upload('api/upload/img', {
files: [], // 仅5+App支持
fileType:'image/video/audio', // 仅支付宝小程序,且必填。
filePath: '', // 要上传文件资源的路径。
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {},
formData: {}, // HTTP 请求中其他额外的 form data
}).then(res => {
}).catch(err => {
})
```
**luch-request API**
--
``` javascript
http.request({
method: 'POST', // 请求方法必须大写
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
},
params: { // 会拼接到url上
token: '1111'
}
})
具体参数说明:[uni.uploadFile](https://uniapp.dcloud.io/api/request/network-file)
http.upload('api/upload/img', {
files: [], // 仅5+App支持
fileType:'image/video/audio', // 仅支付宝小程序,且必填。
filePath: '', // 要上传文件资源的路径。
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {}, // 如填写,会覆盖全局header
formData: {}, // HTTP 请求中其他额外的 form data
})
```
请求方法别名 / 实例方法
``` javascript
http.request(config)
http.get(url[, config])
http.upload(url[, config])
http.delete(url[, data[, config]])
http.head(url[, data[, config]])
http.post(url[, data[, config]])
http.put(url[, data[, config]])
http.connect(url[, data[, config]])
http.options(url[, data[, config]])
http.trace(url[, data[, config]])
```
**全局请求配置**
--
``` javascript
{
baseUrl: '', /* 全局根路径,需要注意,如果请求的路径为绝对路径,则不会应用baseUrl */
header: {
'Content-Type': 'application/json;charset=UTF-8'
},
method: 'GET',
dataType: 'json',
responseType: 'text'
}
```
全局配置修改` setConfig `
``` javascript
/**
* @description 修改全局默认配置
* @param {Function}
*/
http.setConfig((config) => { /* config 为默认全局配置*/
config.baseUrl = 'http://www.bbb.cn'; /* 根域名 */
config.header = {
a: 1,
b: 2
}
return config
})
```
自定义验证器` validateStatus `
``` javascript
/**
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode(只读)
* @return { Boolean } 如果为true,则 resolve, 否则 reject
*/
http.validateStatus = (statusCode) => { // 默认
return statusCode === 200
}
// 举个栗子
http.validateStatus = (statusCode) => {
return statusCode >= 200 && statusCode < 300
}
```
**拦截器**
--
在请求之前拦截
``` javascript
/**
* @param { Function} cancel - 取消请求,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行; 不会进入响应拦截器
*
* @param {String} text ['handle cancel'| any] - catch((err) => {}) err.errMsg === 'handle cancel'。非必传,默认'handle cancel'
* @cancel {Object} config - catch((err) => {}) err.config === config; 非必传,默认为request拦截器修改之前的config
* function cancel(text, config) {}
*/
http.interceptor.request((config, cancel) => { /* cancel 为函数,如果调用会取消本次请求。需要注意:调用cancel,本次请求的catch仍会执行。必须return config */
config.header = {
...config.header,
a: 1
}
/*
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
cancel('token 不存在', config) // 把修改后的config传入,之后响应就可以拿到修改后的config。 如果调用了cancel但是不传修改后的config,则catch((err) => {}) err.config 为request拦截器修改之前的config
}
*/
return config;
})
```
在请求之后拦截
``` javascript
http.interceptor.response((response) => { /* 对响应成功做点什么 (statusCode === 200),必须return response*/
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
// return Promise.reject(response)
// }
console.log(response)
return response
}, (response) => { /* 对响应错误做点什么 (statusCode !== 200),必须return response*/
console.log(response)
return response
})
```
**typescript使用**
--
在` request.ts `里还暴露了五个接口
```javascript
{
options, // request 方法配置参数
handleOptions, // get/post 方法配置参数
config, // init 全局config接口(setConfig 参数接口)
requestConfig, // 请求之前参数配置项
response // 响应体
}
```
**常见问题**
--
1. 为什么会请求两次?
- 总有些小白问这些很那啥的问题,有两种可能,一种是‘post三次握手’(不知道的请先给个五星好评,然后打自己一巴掌,并问自己,为什么这都不知道),还有一种可能是`本地访问接口时跨域请求,所以浏览器会先发一个option 去预测能否成功,然后再发一个真正的请求`(没有自己观察请求头,Request Method,就跑来问的,请再打自己一巴掌,并问自己,为什么这都不知道,不知道也行,为什么不百度)。
2. 如何跨域?
- 问的人不少,可以先百度了解一下。<a href="https://ask.dcloud.net.cn/article/35267" target="_blank">如何跨域</a>
3. post 怎么传不了数组的参数啊?
- <a href="https://uniapp.dcloud.io/api/request/request">uni-request</a> <br>
可以点击看一下uni-request 的api 文档,data支持的文件类型只有<code>Object/String/ArrayBuffer</code>这个真跟我没啥关系 0.0
4. 'Content-Type' 为什么要小写?
- hbuilderX 更新至‘2.3.0.20190919’ 后,uni.request post请求,如果 ‘Content-Type’ 大写,就会在后面自动拼接‘ application/json’,请求头变成
`Content-Type: application/json;charset=UTF-8 application/json`,导致后端无法解析类型,`Status Code 415`,post 请求失败。但是小写就不会出现这个问题。至于为什么我也没有深究,我现在也不清楚这是他们的bug,还是以后就这样规范了。我能做的只有立马兼容,至于后边uni官方会不会继续变动也不清楚。
**tip**
--
- 不想使用upload 可把class 里的upload 删除
**issue**
--
有任何问题或者建议可以=> <a href="https://ask.dcloud.net.cn/question/74922" target="_blank">issue提交</a>,先给个五星好评QAQ!!
**作者想说**
--
- 主体代码3kb
- 目前该插件已经上项目,遇到任何问题请先检查自己的代码(排除新版本发布的情况)。最近新上了` typescript ` 版本,因为本人没使用过ts,所以写的不好的地方,还请见谅~
- 写代码很容易,为了让你们看懂写文档真的很lei 0.0
- 最近发现有插件与我雷同,当初接触uni-app 就发现插件市场虽然有封装的不错的request库,但是都没有对多全局配置做处理,都是通过修改源码的方式配置。我首先推出通过class类,并仿照axios的api实现request请求库,并起名‘仿axios封装request网络请求库,支持拦截器全局配置’。他们虽然修改了部分代码,但是功能与性能并没有优化,反而使代码很冗余。希望能推出新的功能,和性能更加强悍的请求库。
- 任何形式的‘参考’、‘借鉴’,请标明作者
```javascript
<a href="https://ext.dcloud.net.cn/plugin?id=392">luch-request</a>
```
- 关于问问题
1. 首先请善于利用搜索引擎,不管百度,还是Google,遇到问题请先自己尝试解决。自己尝试过无法解决,再问。
2. 不要问类似为什么我的xx无法使用这种问题。请仔细阅读文档,检查代码,或者说明运行环境,把相关代码贴至评论或者发送至我的邮箱,还可以点击上面的issue提交,在里面提问,可能我在里面已经回答了。
3. 我的代码如果真的出现bug,或者你有好的建议、需求,可以提issue,我看到后会立即解决
4. 不要问一些弱智问题!!!
- 如何问问题
1. 仔细阅读文档,检查代码
2. 说明运行环境,比如:app端 ios、android 版本号、手机机型、普遍现象还是个别现象(越详细越好)
3. 发出代码片段或者截图至邮箱(很重要)
4. 或者可以在上方的'issue提交' 里发出详细的问题描述
5. 以上都觉得解决不了你的问题,可以加QQ:`370306150`
**土豪赞赏**
--
<img src="https://img-cdn-qiniu.dcloud.net.cn/uploads/answer/20191014/0d9fff1e6a57a83024787224593b39c1.png" width="150">
####创作不易,五星好评你懂得!

316
plugins/request/request.js

@ -0,0 +1,316 @@
/**
* Request 1.0.2
* @Class Request
* @description luch-request 1.0.2 http请求插件
* @Author lu-ch
* @Date 2019-10-14
* @Email webwork.s@qq.com
* http://ext.dcloud.net.cn/plugin?id=392
*/
export default class Request {
config = {
baseUrl: '',
header: {
'content-type': 'application/json;charset=UTF-8'
},
method: 'GET',
dataType: 'json',
responseType: 'text'
}
static posUrl (url) { /* 判断url是否为绝对路径 */
return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
}
static addQueryString (params) {
let paramsData = ''
Object.keys(params).forEach(function (key) {
paramsData += key + '=' + params[key] + '&'
})
return paramsData.substring(0, paramsData.length - 1)
}
/**
* @property {Function} request 请求拦截器
* @property {Function} response 响应拦截器
* @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
*/
interceptor = {
/**
* @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数config, cancel=> {return config}第一个参数为全局config,第二个参数为函数调用则取消本次请求
*/
request: (cb) => {
if (cb) {
this.requestBeforeFun = cb
}
},
/**
* @param {Request~responseCallback} cb 响应拦截器对响应数据做点什么
* @param {Request~responseErrCallback} ecb 响应拦截器对响应错误做点什么
*/
response: (cb, ecb) => {
if (cb && ecb) {
this.requestComFun = cb
this.requestComFail = ecb
}
}
}
requestBeforeFun (config) {
return config
}
requestComFun (response) {
return response
}
requestComFail (response) {
return response
}
/**
* 自定义验证器如果返回true 则进入响应拦截器的响应成功函数(resolve)否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode只读
* @return { Boolean } 如果为true, resolve, 否则 reject
*/
validateStatus (statusCode) {
return statusCode === 200
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig (f) {
this.config = f(this.config)
}
/**
* @Function
* @param {Object} options - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
async request (options = {}) {
options.baseUrl = this.config.baseUrl
options.dataType = options.dataType || this.config.dataType
options.responseType = options.responseType || this.config.responseType
options.url = options.url || ''
options.data = options.data || {}
options.params = options.params || {}
options.header = options.header || this.config.header
options.method = options.method || this.config.method
return new Promise((resolve, reject) => {
let next = true
let handleRe = {}
options.complete = (response) => {
response.config = handleRe
if (this.validateStatus(response.statusCode)) { // 成功
response = this.requestComFun(response)
resolve(response)
} else {
response = this.requestComFail(response)
reject(response)
}
}
const cancel = (t = 'handle cancel', config = options) => {
const err = {
errMsg: t,
config: config
}
reject(err)
next = false
}
handleRe = { ...this.requestBeforeFun(options, cancel) }
const _config = { ...handleRe }
if (!next) return
let mergeUrl = Request.posUrl(options.url) ? options.url : (options.baseUrl + options.url)
if (JSON.stringify(options.params) !== '{}') {
const paramsH = Request.addQueryString(options.params)
mergeUrl += mergeUrl.indexOf('?') === -1 ? `?${paramsH}` : `&${paramsH}`
}
_config.url = mergeUrl
uni.request(_config)
})
}
get (url, options = {}) {
return this.request({
url,
method: 'GET',
...options
})
}
post (url, data, options = {}) {
return this.request({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY
put (url, data, options = {}) {
return this.request({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete (url, data, options = {}) {
return this.request({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN
connect (url, data, options = {}) {
return this.request({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
head (url, data, options = {}) {
return this.request({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options (url, data, options = {}) {
return this.request({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN
trace (url, data, options = {}) {
return this.request({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload (url, {
// #ifdef APP-PLUS
files,
// #endif
// #ifdef MP-ALIPAY
fileType,
// #endif
filePath,
name,
header,
formData
}) {
return new Promise((resolve, reject) => {
let next = true
let handleRe = {}
const pubConfig = {
baseUrl: this.config.baseUrl,
url,
// #ifdef APP-PLUS
files,
// #endif
// #ifdef MP-ALIPAY
fileType,
// #endif
filePath,
method: 'UPLOAD',
name,
header: header || this.config.header,
formData,
complete: (response) => {
response.config = handleRe
if (response.statusCode === 200) { // 成功
response = this.requestComFun(response)
resolve(response)
} else {
response = this.requestComFail(response)
reject(response)
}
}
}
const cancel = (t = 'handle cancel', config = pubConfig) => {
const err = {
errMsg: t,
config: config
}
reject(err)
next = false
}
handleRe = { ...this.requestBeforeFun(pubConfig, cancel) }
const _config = { ...handleRe }
if (!next) return
_config.url = Request.posUrl(url) ? url : (this.config.baseUrl + url)
uni.uploadFile(_config)
})
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/
/**
* 请求拦截器回调
* @return {Object} - 返回操作后的config
* @callback Request~requestCallback
* @param {Object} config - 全局config
* @param {Function} [cancel] - 取消请求钩子调用会取消本次请求
*/
/**
* 响应拦截器回调
* @return {Object} - 返回操作后的response
* @callback Request~responseCallback
* @param {Object} response - 请求结果 response
*/
/**
* 响应错误拦截器回调
* @return {Object} - 返回操作后的response
* @callback Request~responseErrCallback
* @param {Object} response - 请求结果 response
*/

11
static/html/01.html

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body>
这是html界面
</body>
</html>

BIN
static/item.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/item01.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 KiB

BIN
static/item02.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

BIN
static/location.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

BIN
static/title.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

13
store/index.js

@ -0,0 +1,13 @@
import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user/index';
import project from './modules/project/index';
Vue.use(Vuex);
const store = new Vuex.Store({
// modules:sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')): { user, project }
modules: { user, project },
});
export default store;

9
store/modules/project/actions.js

@ -0,0 +1,9 @@
import { showLoading, hideLoading, showModal, showToast } from 'utils/ui';
import { mpLogin, signIn, ddLogin } from 'utils/user';
import { http } from 'plugins/request/index';
const actions = {
};
export default actions;

5
store/modules/project/index.js

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

5
store/modules/project/mutations.js

@ -0,0 +1,5 @@
const mutations = {
};
export default mutations;

7
store/modules/project/state.js

@ -0,0 +1,7 @@
const state = {
data: null,
companyId: 0,
num: 0
};
export default state;

9
store/modules/user/actions.js

@ -0,0 +1,9 @@
import { showLoading, hideLoading, showModal, showToast } from 'utils/ui';
import { mpLogin, signIn, ddLogin } from 'utils/user';
import { http } from 'plugins/request/index';
const actions = {
};
export default actions;

5
store/modules/user/index.js

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

26
store/modules/user/mutations.js

@ -0,0 +1,26 @@
const mutations = {
/**
* 设置存储token
* @param {object} state
* @param {string} token
*/
setToken(state, token) {
if (!token) return;
state.token = token;
uni.setStorageSync('token', token);
},
/**
* 设置user数据
* @param {object} state
* @param {object} user
*/
setUser(state, user) {
if (!user) return;
state.user = { ...user };
uni.setStorageSync('user', JSON.stringify(user));
},
};
export default mutations;

7
store/modules/user/state.js

@ -0,0 +1,7 @@
const state = {
token: '',
user: null,
fingerprint: 0
};
export default state;

133
uni.scss

@ -0,0 +1,133 @@
/* 颜色变量 */
/* Color 可以自定义相关配色 */
/* 标准色 */
$red: #F26A1B;
$orange: #f37b1d;
$yellow: #fbbd08;
$olive: #76AC96;
$green: #11A20D; // 健康打卡 绿
$cyan: #0897C7; // 按钮蓝
$blue: #6D99F2;
$darkBlue: #0076FF;
$purple: #677DBF; // 校园打卡
$mauve: #9c26b0;
$pink: #e03997;
$brown: #a5673f;
$grey: #CDCDCD;
$black: #333333;
$darkGray: #666666;
$gray: #808080;
$ghostWhite: #f1f1f1;
$white: #ffffff;
/* 浅色 */
$redLight: #fadbd9;
$orangeLight: #fde6d2;
$yellowLight: #fef2ce;
$oliveLight: #e8f4d9;
$greenLight: #d7f0db;
$cyanLight: #d2f1f0;
$blueLight: #cce6ff;
$purpleLight: #e1d7f0;
$mauveLight: #ebd4ef;
$pinkLight: #f9d7ea;
$brownLight: #ede1d9;
$greyLight: #F5f5f5;
/* 渐变色 */
$gradualRed: linear-gradient(45deg, #f43f3b, #ec008c);
$gradualOrange: linear-gradient(45deg, #ff9700, #ed1c24);
$gradualGreen: linear-gradient(45deg, #39b54a, #8dc63f);
$gradualPurple: linear-gradient(45deg, #ec008c, #5e00ff);
$gradualPink: linear-gradient(45deg, #ec008c, #6739b6);
$gradualBlue: linear-gradient(45deg, #0081ff, #1cbbb4);
/* 阴影透明色 */
$ShadowSize: 6rpx 6rpx 8rpx;
$redShadow: rgba(204, 69, 59, 0.2);
$orangeShadow: rgba(217, 109, 26, 0.2);
$yellowShadow: rgba(224, 170, 7, 0.2);
$oliveShadow: rgba(124, 173, 55, 0.2);
$greenShadow: rgba(48, 156, 63, 0.2);
$cyanShadow: rgba(28, 187, 180, 0.2);
$blueShadow: rgba(0, 102, 204, 0.2);
$purpleShadow: rgba(88, 48, 156, 0.2);
$mauveShadow: rgba(133, 33, 150, 0.2);
$pinkShadow: rgba(199, 50, 134, 0.2);
$brownShadow: rgba(140, 88, 53, 0.2);
$greyShadow: rgba(114, 130, 138, 0.2);
$grayShadow: rgba(114, 130, 138, 0.2);
$blackShadow: rgba(26, 26, 26, 0.2);
$whiteShadow: rgba(255, 255, 255, 0.2);
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 首页四个配色 */
$iconGreen: #62D4A6;
$iconCyan: #9376F5;
$iconBlue: #58C9D1;
$iconPurple: #CF7FE6;
/* 文字基本颜色 */
$uni-text-color: #101010; //基本色
$uni-text-color-inverse: #fff; //反色
$uni-text-color-grey: #747474; //辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #ffffff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; //点击状态颜色
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size: 20rpx;
$uni-font-size-sm: 24rpx;
$uni-font-size-base: 28rpx;
$uni-font-size-lg: 32rpx;
$uni-font-size-xl: 36rpx;
$uni-font-size-xxl: 44rpx;
$uni-font-size-sl: 80rpx;
$uni-font-size-xsl: 120rpx;
/* 图片尺寸 */
$uni-img-size-sm: 40rpx;
$uni-img-size-base: 52rpx;
$uni-img-size-lg: 80rpx;
/* Border Radius */
$uni-border-radius-sm: 4rpx;
$uni-border-radius-base: 6rpx;
$uni-border-radius-lg: 12rpx;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 10px;
$uni-spacing-row-base: 20rpx;
$uni-spacing-row-lg: 30rpx;
/* 垂直间距 */
$uni-spacing-col-sm: 8rpx;
$uni-spacing-col-base: 16rpx;
$uni-spacing-col-lg: 24rpx;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 40rpx;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle: 36rpx;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 30rpx;

31
utils/ui.js

@ -0,0 +1,31 @@
/**
* 显示模态框
* @param {string} msg
* @param {boolean} showCancel
* @param {string} confirmText
*/
export const showModal = (msg, showCancel = false, confirmText = '知道了') =>
uni.showModal({
title: '提示',
content: msg,
showCancel,
confirmText,
confirmColor: '#1890ff',
});
/**
* 显示toast
* @param {string} msg
*/
export const showToast = msg =>
uni.showToast({
title: msg,
icon: 'none',
duration: 3000,
});
// 显示加载动画
export const showLoading = (msg = '努力加载中...') => uni.showLoading({ title: msg });
// 隐藏加载动画
export const hideLoading = () => uni.hideLoading();

132
utils/user.js

@ -0,0 +1,132 @@
import { SIGN_IN } from 'api/user';
import { ERR_CODE } from 'config/config.default';
import { http as axios } from 'plugins/request/index';
import { SIGN_IN_CLIENTS, SIGN_IN_TYPES } from 'config/config.user';
/**
* 保存token
* @param {string} token 服务端返回的token数据
*/
export const setToken = token => {
uni.setStorageSync('token', token);
};
/**
* 获取token
* @return {string} 本地保存的token
*/
export const getToken = () => uni.getStorageSync('token');
/**
* 提交登录信息
* @param {object} params 提交的参数
*/
export const signIn = params => {
return new Promise((resolve, reject) => {
axios
.post(SIGN_IN, params)
.then(res => {
const { success, code, data, msg } = res.data;
if (success && code === ERR_CODE) {
if (!data || !data.token) {
reject('服务端返回数据不正确');
} else {
resolve(data);
}
} else {
reject(`登录请求失败 ${msg}`);
}
})
.catch(err => {
reject(err);
});
});
};
// 微信登录
export const wxLogin = () => {
return new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success(response) {
if (response.code) {
const params = {
client: SIGN_IN_CLIENTS['mp'],
type: SIGN_IN_TYPES['mp'],
data: { identifier: response.code, credential: 'health' },
// redirect: 'https://test.tall.wiki/gateway/health/initMsg',
redirect: 'https://www.tall.wiki/gateway/health/initMsg',
};
resolve(params);
} else {
reject(response.errMsg);
}
},
fail() {
console.log('fail');
reject('微信登录失败');
},
});
});
};
// 企业微信登录
export const wxWorkLogin = () => {
return new Promise((resolve, reject) => {
wx.qy.login({
provider: 'weixin',
success(response) {
if (response.code) {
const params = {
client: SIGN_IN_CLIENTS['wx_work'],
type: SIGN_IN_TYPES['wx_work'],
data: { identifier: response.code, credential: 'health' },
// redirect: 'https://test.tall.wiki/gateway/health/initMsg',
redirect: 'https://www.tall.wiki/gateway/health/initMsg',
};
resolve(params);
} else {
reject(response.errMsg);
}
},
fail() {
console.log('fail');
reject('微信登录失败');
},
});
});
};
// 小程序登录
export const mpLogin = () => {
try {
const res = uni.getSystemInfoSync();
if (res.environment === 'wxwork') {
return wxWorkLogin();
} else {
return wxLogin();
}
} catch (err) {
console.log('err: ', err);
}
};
// 钉钉登录
export const ddLogin = () => {
console.log('dingtalk login.........');
try {
dd.getAuthCode({
success(res) {
console.log('res: ', res);
/*{
authCode: 'hYLK98jkf0m' // string authCode
}*/
},
fail: function(err) {
console.log('err: ', err);
},
});
} catch (error) {
console.log('error: ', error);
}
};

20
utils/util.js

@ -0,0 +1,20 @@
/**
* 格式化 querystring
* taskId=3&scene=1011 ->
* {taskId:3,scene:1011}
*/
export const formatQuery = str => {
if (!str) return;
const result = {};
if (str.includes('&')) {
const arr = str.split('&');
arr.forEach(item => {
const arr1 = item.split('=');
result[arr1[0]] = arr1[1];
});
} else {
const arr = str.split('=');
result[arr[0]] = arr[1];
}
return result;
};

21
vue.config.js

@ -0,0 +1,21 @@
const path = require('path');
const resolve = dir => path.join(__dirname, dir);
module.exports = {
lintOnSave: true,
configureWebpack: {
resolve: {
alias: {
'~': __dirname,
config: resolve('config'),
api: resolve('api'),
store: resolve('store'),
components: resolve('components'),
pages: resolve('pages'),
common: resolve('common'),
plugins: resolve('plugins'),
utils: resolve('utils'),
},
},
},
};

8
yarn.lock

@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
moment@^2.24.0:
version "2.24.0"
resolved "https://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s=
Loading…
Cancel
Save