Browse Source

init

remotes/origin/HEAD
wally 5 years ago
parent
commit
055b87996d
  1. 59
      App.vue
  2. 111
      README.md
  3. 0
      api/base.js
  4. 5
      api/user.js
  5. 24
      common/script/filters.js
  6. 34
      common/script/util.js
  7. 88
      common/style/iconfont.scss
  8. 105
      components/auth/auth.vue
  9. 45
      components/history-map/history-map.vue
  10. 42
      components/nav-bottom/nav-bottom.vue
  11. 167
      components/rattenking-dtpicker/GetDate.js
  12. 221
      components/rattenking-dtpicker/rattenking-dtpicker.vue
  13. 69
      components/rattenking-dtpicker/readme.md
  14. 102
      components/school-map/school-map.vue
  15. 19
      components/test/test.vue
  16. 44
      components/timeline/timeline.vue
  17. 546
      components/uni-calendar/calendar.js
  18. 152
      components/uni-calendar/uni-calendar-item.vue
  19. 434
      components/uni-calendar/uni-calendar.vue
  20. 327
      components/uni-calendar/util.js
  21. 124
      components/uni-segmented-control/uni-segmented-control.vue
  22. 103
      components/user-agreement/user-agreement.vue
  23. 49
      config/api/api.js
  24. 10
      config/api/user.js
  25. 4
      package.json
  26. 80
      pages.json
  27. 291
      pages/add-stroke/add-stroke.vue
  28. 73
      pages/add-stroke/components/end-date-selector.vue
  29. 73
      pages/add-stroke/components/start-date-selector.vue
  30. 489
      pages/apply-code/apply-code.vue
  31. 281
      pages/basic-info/basic-info.vue
  32. 238
      pages/get-code/get-code.vue
  33. 175
      pages/index/components/home.vue
  34. 100
      pages/index/components/mine.vue
  35. 78
      pages/index/index.vue
  36. 175
      pages/my-code/my-code.vue
  37. 157
      pages/my-signs/my-signs.vue
  38. 195
      pages/my-trips/my-trips.vue
  39. 398
      pages/privacy-aolicy/privacy-aolicy.vue
  40. 559
      pages/punch-the-clock/punch-the-clock.vue
  41. 79
      pages/service-agreement/service-agreement.vue
  42. 143
      pages/sign/sign.vue
  43. 124
      pages/statistics/components/date-selector.vue
  44. 90
      pages/statistics/components/health-data.vue
  45. 57
      pages/statistics/components/location-map.vue
  46. 80
      pages/statistics/statistics.vue
  47. 196
      pages/user-code/user-code.vue
  48. 4
      plugins/request/index.js
  49. BIN
      static/head-portrait.png
  50. BIN
      static/img/home.png
  51. BIN
      static/img/homeHL.png
  52. BIN
      static/img/shanda1.png
  53. BIN
      static/img/shanda2.png
  54. BIN
      static/img/user.png
  55. BIN
      static/img/userHL.png
  56. 5
      store/index.js
  57. 74
      store/modules/site/actions.js
  58. 5
      store/modules/site/index.js
  59. 21
      store/modules/site/mutations.js
  60. 6
      store/modules/site/state.js
  61. 79
      store/modules/statistics/actions.js
  62. 5
      store/modules/statistics/index.js
  63. 36
      store/modules/statistics/mutations.js
  64. 12
      store/modules/statistics/state.js
  65. 138
      store/modules/user/actions.js
  66. 58
      store/modules/user/mutations.js
  67. 5
      store/modules/user/state.js
  68. 3
      vue.config.js

59
App.vue

@ -1,68 +1,11 @@
<script>
import { mapState, mapMutations, mapActions } from 'vuex';
import { GET_USER_INFO, HEALTH_TYPE_STATUS } from 'api/api';
import { showLoading, hideLoading, showToast, showModal } from 'utils/ui';
import { formatQuery } from 'utils/util';
export default {
async onLaunch(options) {
console.log('options: ', options);
// #ifdef MP-WEIXIN
uni.getSetting({
success(res) {
if (!res.authSetting['scope.userLocation']) {
uni.authorize({
scope: 'scope.userLocation',
success() {},
});
}
},
});
// #endif
try {
if (options.query && options.query.scene) {
const query = formatQuery(decodeURIComponent(options.query.scene));
if (options.path === 'pages/index/index' && query && query.d) {
await this.getTokenByUserId({ params: { userId: query.d } });
this.initCommon();
} else {
await this.login();
this.initCommon();
return;
}
} else {
await this.login();
this.initCommon();
}
} catch (error) {
console.log('onLaunch error: ', error);
}
},
computed: mapState('user', ['token']),
methods: {
...mapActions('user', ['login', 'getUserInfo', 'getHealthTypeStatus', 'getTokenByUserId']),
async initCommon() {
const startTime = +this.$moment()
.startOf('year')
.format('x');
const endTime = +this.$moment()
.endOf('day')
.format('x');
const params = {
param: {
startTime,
endTime,
token: this.token,
},
};
await this.getUserInfo(params);
await this.getHealthTypeStatus();
},
},
};
</script>

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

0
config/api/base.js → api/base.js

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);

24
common/script/filters.js

@ -1,24 +0,0 @@
// 定义过滤器 时间戳转化
export function formatDate(date, fmt) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
};
function padLeftZero(str) {
return ('00' + str).substr(str.length);
};

34
common/script/util.js

@ -1,34 +0,0 @@
/**
* 显示模态框
* @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 = () =>
setTimeout(() => {
uni.hideLoading();
}, 1000);

88
common/style/iconfont.scss

@ -1,88 +0,0 @@
@font-face {font-family: "iconfont";
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAooAAsAAAAAFQgAAAnYAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCFRgqZFJQeATYCJANQCyoABCAFhG0HgWobnBEzo/aLcspA9l8mcGMo9gb0lUEcr9BhQpCSJK7HqIWTDrU0ai135rQs5oP3fGb0CRKyhu0ftp9rggfS63uD5Ada/eogdCKqqwD2ZlHBHV1m9swEwI9zny/tMO+Ok/bGKNSBGvEHUgPCxiaWhgQFYUbzj6f96t+fCg1iXjqwWSZlB2ZsBvG6E9seoJ5h0HHmFST4/WyWc+spmo2Vac96be8R9iNBZjMMkQIIGNzvW31yLkQdOjwUUqTE2Ze9vW+y88GJe0ItE0lEcUsXSWQJhW4lF36G1+/VCeVRDb4PPh0C8KhGE0S3Hv2G4aAhloBe82bPnIwTCKEzcgWO1064q0bswMKRG+UZYLv++eINrYQDEktBbjpoRvdpdEop/1crsqiNkxrMnAQO9wIFNAE0iJeJpY9RDm+ihZc0UHMKqpHgD49S0jDNpYw5bs6a6+a+eWRemLflbuWp5f9ZlpV7zJwx7+30ZrqkUlCN5tagllnHrNcZNIp4sHRm/oNXgYNLDo88PgEFQiIQNlgEKLHcnIcUDqEhDSEUpDmIGNJZEBLMMQgLzHEIAeYMhA3mLEQFmOsQDph7EC6Y+xA5MI8gPDAvIPJg3kD4YN5CBFDuFqCg7akQIAT7PwwhAqAoBzYAOoD2IPwekrFLShSMw0K9twgVOvDwFgEy0U2DXMsoSAqJbpRLigWdqyHWKQq3he3UFwo1RIA6FrvjYF3MkfIg1MxfJYClKCxukMe0qtEFfOoScXGx1tC9sZok+Swrpun8Lm7UJREUrDwfcd7+NJ1MMJR7ke6OFzAhjhflRNKdgy6FLCujS3ETgQ1q3GyNTGfbq/3cHf4lQZWRFJGmyHgggMqwnJZmlcMkTVETL1fRp4MCOfToAZA3B5a9y4adTCe/jgefSKoqwQPg/u/hZ/8WxL3jIqPCqrO9qGe5gg3UiTTfNA6+q1OKPsuGIdzDhhNJB4t1B8PRhacMkBrFkMCjpUvlx583xa1fn9SHHzei69/f72ro/ryZZt0Mb3QhsWtNWMzzxmUu6LTtIXgdzXctHWslW3z+xBNMr9FH9UwspgSL3vjJDF0BoD5dtmQXJWYiUKcXp90MIfQhQupRGHPDMqidH2lwUX0nWGmHbjSLaHRhPb6QPF+caNnUwsGH9Mj7bbnplo3XfFZ/DTIymMqFgxoHGOcFiUQwcI7LAZ8MalxLQoeVb8ibuMympVYDEV+yWJHRthPbiLDMj6PrGkcQwdLgrrrhOcYievRX+ghPvTTxVa1I0FHX8eD44kF2ACJ6ZcXwJn9B/d5nzraZFGlpcTqILnRODFa5cBTpInFnph/h2DP1ZIA34kEC9+PCftifTNowhyj6Tjb3Rz35MwRVxWa41+nHbWDWZZcefDpyrr3LjZYeeDIil1hUN4kfyvHcs37EFpt8VvM9sKquTSkNXzTp21KXYzlcvrjJ10nLm60pU4xtrNT+xcX9+OX3ev1mGtfC+kHu84se37Xpne/qaLn0tlOG54vqwsp4G+cIH/BFfB7Hrk8hFwMUxdW/f7+Fo+fIm3L1YEiUVit8p7UMtNQmp+CPEWShe0dcKnM7Yj2d72DlwL8jEFuJBWnWK4W3Xy3dpeGcOsUR7tkIA/EQuDXwMWe3T34gGuDAL1srsLe2F5zj24OImJQJfvOi/U/8Z/q5VS7hiX04w+B9MxlSyLVnnh820bgWhgROmKA2V4+fEBgCa425+/7+2ScU988fLn6ZP3+5zq3bru8uv8WjKPmW6UOOl4D/GqUkxCLO7XSKSHzTgFNES8CYMQEthPScm0tLAwRuIoAiv3VLANj4dq7t2ouOw3Ger3Du3Jtk7pwPvjyFY0jtpDwMtrHXSqlSKa1YHzEVn82MH88IN3uP4CX4mOojUFqqVHgfKw7SYTQ092Kyskq8mhlSyBXm0Wuz4upcHW+CeeQ8cLOH/kjgnczGSkgraLgR0h9oqIDJOmXWwEBHB0VIxEFYQ5by6VPfMoLavu0TgVD/sd1dWrWFes4cB16glQepJz0tCWzCkMozlTebSN0T0VLRQt7uiY6kqyppF4CzT6yMqjsSBTRfOR6PZly6pmnylPhOv/iIH8BfrHP2EhyRs85FDY1FOA93bFtHCskrjgwHLfsRDSlIu9HQaaWTNYSC3U1uokIH+o28m9WI5Vqn3sgaJNtsMXEg8Yz1QqzRJWvzdWn3UcbvjQiD2+5LHOrMbxsQRu+NR/Gk1zdnNdFvp7z9NYj/I+OtTTwf77m4rouDv5pDYwJ6eCmIUplBOwql8HoCIq07bcDlw+7CnZl4tCVpoGpYwu0hhJcg+hG2F/Lh4KZBYQgdQHKPfzRa6FLoD/YP77fNpj5+xEtk2x8UXGbCLpsytYRdzp4+lV0WhZWeK0xlFTKs1NAIdaVWqEP5s2daBEQgjVCDIhwikFagE90nDf4jhIEz3c6A9YZqr4xAVmoxc+hCnoQbGxsVFUtyJHnrdO7WKZOV6rXarQZj/28fa+OhP60/kG2sh3Cbn6Y16o2zD+hj94sqI/v9saNCKVoqDFCpwOUsqNWLBUE6mYbJMMLiDikdmKpIpThw4lIsTZ/GKUTVIpsucABvy6qaX5f+1H5I77vjQP6X1EK1D86w2Au/mLP9YH9Ebo3UUJ3OfQAg4d67x+DylGpw1/H9vGzXWkfFVh93krm7bu5K+rVnefYRkFI7lFD/2pNaqUZl/7NzxlKdINspSi3JuvRntUA1AKgV8oG1y31dV003AnVZfi/x5D9Ik/fHw/eOkv8hWy83tSAJgQmsFTieXRT1dPo/dTVqfWoSE+P2b47/t6v26bpf2zHup75+0ALAQUIsYYD8VwpoyNAy191JwkV9dfD9ZP+DswzUeHmRzb/Zdck9uhf8V4rm+W9WDqXC7F+ip+QBSQVVgcKhjq4TmwALnzbAxqEj8GhMz3v7FJiPQmgXaMRaCwgSzgJJxAWgSLih68QXwKKSd8AmEQp4jBCtD+lTb/3h8pWhBb1a7lUwVCyLyqXd+wuuJc1+p17+B66hVs3G03DrJwrYxC3qxs1FrLJMWX1Yz4aUSA1MKxgZe5FhMZnYpFuODeXR5UjJ0CLX75XlfsdgqNjjy2Xkx3/BtaQ5Y9Lbjn/gGs5fmRmbFtA/RSk0aV8G1o2bEySt4iOZsvKBJBJtImVIvtIKRsZ8hYZhYYIWs0XFuH1PfgsINt+U5BGKDxGGEwwmi19UXzGH4vL4Hz5++vzF6ZK4B59IyOAzH6oQ74+ZKF8bDxO7um6acdwq+CKFAuM1y7GnjHOjmffU5Mj+ZCccnJfjBCsXNmnX2ZDS8ZBafdqi9G0V/sWDcxUp4DNPCXfTfFyNLqdrNtTjobEuJrBJqBocjQAAAAA=') format('woff2'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 26px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-border:before {
content: "\e7a9";
}
.icon-timer:before {
content: "\e668";
}
.icon-history:before {
content: "\e60d";
}
.icon-room:before {
content: "\e600";
}
.icon-check-square:before {
content: "\e7a8";
}
.icon-user:before {
content: "\e7ae";
}
.icon-linechart:before {
content: "\e7af";
}
.icon-home:before {
content: "\e7c6";
}
.icon-carryout:before {
content: "\e7d3";
}
.icon-car:before {
content: "\e7d7";
}
.icon-right:before {
content: "\e7eb";
}
.icon-left:before {
content: "\e7ec";
}
.icon-flag-fill:before {
content: "\e863";
}
.icon-plus:before {
content: "\e8fe";
}
.icon-wenduji_thermometer:before {
content: "\e611";
}
.icon-calendar:before {
content: "\e7d3";
}
.icon-scan:before {
content: "\e7d4";
}
.icon-qrcode:before {
content: "\e7dd";
}
.icon-check-circle-fill:before {
content: "\e844";
}

105
components/auth/auth.vue

@ -1,105 +0,0 @@
<template>
<view class="mask" v-if="show">
<view class="content shadow">
<view class="text-black text-bold text-lg margin-bottom-lg">健康码小程序</view>
<view class="text-df text-gray">为了您的正常使用</view>
<view class="text-df text-gray margin-bottom-lg">小程序需要获得您的基本公开信息</view>
<button
@tap="getuserinfo"
class="text-purple"
open-type="getUserInfo"
plain
style="border: none;"
>极速登录</button>
</view>
</view>
</template>
<script>
import { mapActions, mapState } from 'vuex';
export default {
data() {
return { show: false };
},
computed: {
...mapState('user', ['user']),
},
watch: {
//
//
user(value) {
if (value) {
if (!value.wxInfo || !value.wxInfo.nickname) {
this.show = true;
} else {
this.show = false;
}
} else {
this.show = false;
}
},
},
methods: {
...mapActions('user', ['updateUserInfo']),
//
getuserinfo() {
this.show = false;
uni.getUserInfo({
provider: 'weixin',
success: infoRes => {
const {
nickName,
avatarUrl,
city,
country,
gender,
language,
province,
} = infoRes.userInfo;
const params = {
nickname: nickName,
headImgUrl: avatarUrl,
city,
country,
sex: gender,
language,
province,
};
this.updateUserInfo(params);
},
});
},
},
};
</script>
<style scoped>
.mask {
z-index: 9998;
position: fixed;
left: 0;
top: 0;
bottom: 0;
right: 0;
background: rgba(0, 0, 0, 0.2);
}
.content {
z-index: 9999;
position: absolute;
left: 50upx;
top: 50%;
box-sizing: border-box;
width: 650upx;
padding: 20px;
font-size: 14px;
transform: translate3d(0, -50%, 0);
background: #fff;
border-radius: 20upx;
text-align: center;
}
</style>

45
components/history-map/history-map.vue

@ -1,45 +0,0 @@
<template>
<map
:latitude="latitude"
:longitude="longitude"
:markers="markers"
:polygons="polygons"
:polyline="polyline"
id="map"
ref="map"
scale="17"
show-location="true"
style="width: 100%; min-height: 1000rpx;height:100%"
/>
</template>
<script>
export default {
props: {
markers: { type: Array, default: () => [] },
polyline: { type: Array, default: () => [] },
polygons: { type: Array, default: () => [] },
},
data() {
return {
latitude: 37.87059,
longitude: 112.55067,
};
},
watch: {
markers: {
deep: true,
handler(value) {
if (value && value.length) {
const { latitude, longitude } = value[0];
this.latitude = latitude;
this.longitude = longitude;
} else {
this.latitude = 37.87059;
this.longitude = 112.55067;
}
},
},
},
};
</script>

42
components/nav-bottom/nav-bottom.vue

@ -1,42 +0,0 @@
<template>
<view class="cu-bar tabbar bg-white" style="width: 750rpx">
<view
:class="[index === 0 ? 'text-blue': 'text-gray']"
@tap="handleClickButton(0)"
class="action"
hover-class="cc-active"
>
<view class="cuIcon-home"></view>首页
</view>
<view @tap="$emit('scan')" class="action text-gray add-action">
<button class="cu-btn cuIcon-scan bg-blue shadow"></button>
扫一扫
</view>
<view
:class="[index === 1 ? 'text-blue': 'text-gray']"
@tap="handleClickButton(1)"
class="action"
hover-class="cc-active"
>
<view class="cuIcon-my">
<!-- <view class="cu-tag badge"></view> -->
</view>我的
</view>
</view>
</template>
<script>
export default {
data() {
return {
index: 0,
};
},
methods: {
handleClickButton(i) {
this.index = i;
this.$emit('change', i);
},
},
};
</script>

167
components/rattenking-dtpicker/GetDate.js

@ -1,167 +0,0 @@
const GetDate = {
withData: (num) => {
let param = parseInt(num);
return param < 10 ? '0' + param : '' + param;
},
getTimes(str){
return new Date(str.replace(/-/g,'/')).getTime();
},
getCurrentTimes(){
const date = new Date();
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
return {
detail: {
year: year,
month: month,
day: day,
hour: hour,
minute: minute,
second: second
}
}
},
format(arr){
let curarr = [];
let curarr0 = [];
let str = '';
arr.forEach((cur,index) => {
let o = GetDate.withData(cur);
if(index > 2){
curarr.push(o);
}else{
curarr0.push(o);
}
})
if(arr.length < 4){
str = curarr0.join('-');
}else{
str = curarr0.join('-') + ' ' + curarr.join(':');
}
return str;
},
getCurrentStringValue(str){
let newstr = str.split(' ');
if(newstr && newstr[1]){
let arr = [...newstr[0].split('-'),...newstr[1].split(':')];
return arr;
}
return newstr[0].split('-');
},
getCompare(curp,startp,endp,timesp){
let cur = GetDate.getTimes(curp);
let start = GetDate.getTimes(startp);
let end = GetDate.getTimes(endp);
if(cur < start){
return GetDate.getTimeIndex(timesp,GetDate.getCurrentStringValue(startp));
}else if(cur > end){
return GetDate.getTimeIndex(timesp,GetDate.getCurrentStringValue(endp));
}else{
return GetDate.getTimeIndex(timesp,GetDate.getCurrentStringValue(curp));
}
},
getChooseArr(times,indexs){
let arr = [];
times.forEach((cur,index) => arr.push(cur[indexs[index]]));
return arr;
},
getNewArray(arr){
let newarr = [];
arr.forEach(cur => newarr.push(cur));
return newarr;
},
getLoopArray: (start, end) => {
var start = start || 0;
var end = end || 1;
var array = [];
for (var i = start; i <= end; i++) {
array.push(GetDate.withData(i));
}
return array;
},
getMonthDay: (year, month) => {
var flag = year % 400 == 0 || (year % 4 == 0 && year % 100 != 0), array = null;
switch (month) {
case '01':
case '03':
case '05':
case '07':
case '08':
case '10':
case '12':
array = GetDate.getLoopArray(1, 31)
break;
case '04':
case '06':
case '09':
case '11':
array = GetDate.getLoopArray(1, 30)
break;
case '02':
array = flag ? GetDate.getLoopArray(1, 29) : GetDate.getLoopArray(1, 28)
break;
default:
array = '月份格式不正确,请重新输入!'
}
return array;
},
getDateTimes: (opts) => {
var years = GetDate.getLoopArray(opts.start, opts.end);
var months = GetDate.getLoopArray(1, 12);
var days = GetDate.getMonthDay(opts.curyear, opts.curmonth);
var hours = GetDate.getLoopArray(0, 23);
var minutes = GetDate.getLoopArray(0, 59);
var seconds = GetDate.getLoopArray(0, 59);
var times = null;
switch (opts.fields) {
case 'year':
times = [years]
break;
case 'month':
times = [years, months]
break;
case 'day':
times = [years, months, days]
break;
case 'hour':
times = [years, months, days, hours]
break;
case 'minute':
times = [years, months, days, hours, minutes]
break;
case 'second':
times = [years, months, days, hours, minutes, seconds]
break;
default:
times = [years, months, days, hours, minutes, seconds]
}
return times;
},
getIndex(arr,target){
let len = arr.length;
for(let i = 0; i < len; i++){
if(arr[i] == target){
return i;
}
}
},
getTimeIndex(arrs, targets){
let len = arrs.length;
let arr = [];
for(let i = 0; i < len; i++){
arr.push(GetDate.getIndex(arrs[i], targets[i]))
}
return arr;
},
error(str){
console.error(str);
}
}
module.exports = GetDate;

221
components/rattenking-dtpicker/rattenking-dtpicker.vue

@ -1,221 +0,0 @@
<template>
<picker class='rui-picker rui-class' mode="multiSelector" :range="times" :value="timesIndex" :disabled="curDisabled" @change='changeDate' @cancel="cancelDate" @columnchange="columnchangeDate">
{{curValue}}
</picker>
</template>
<script>
import GetDate from './GetDate.js';
export default {
name: 'rattenking-dtpicker',
props: {
/**
* picker允许选中的最小值
*/
start: {
type: String,
default: '1900-00-00 00:00:00'
},
/**
* picker允许选中的最大值
*/
end: {
type: String,
default: '2500-12-30 23:59:59'
},
/**
* picker默认展示的值
*/
value: {
type: String,
default: ''
},
/**
* picker的时间粒度
*/
fields: {
type: String,
default: 'second'
},
/**
* picker是否禁止
*/
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
times:[['2019','2020'],['01','02']],
timesIndex: [1,1],
timesString: null,
curValue: this.value,
curDisabled: this.disabled,
cancel: null
}
},
watch: {
value(val) {
this.curValue = val;
},
disabled(val){
this.curDisabled = val;
},
curDisabled(val){
this.curDisabled = val;
},
curValue(val) {
this.curValue = val;
this.$emit('change', val);
},
times(val){
this.times = val;
},
timesIndex(val){
this.timesIndex = val;
},
cancel(val) {
this.$emit('cancel', val);
}
},
created() {
if(this.value === ''){
let time = GetDate.getCurrentTimes();
let arr = [];
for (let key in time.detail) {
arr.push(time.detail[key]);
if(key === this.fields){
break;
}
}
this.value = GetDate.format(arr);
this.curValue = GetDate.format(arr);
}
switch (this.fields){
case 'year':
if (this.value.length !== 4) {GetDate.error('时间粒度和时间格式不一致');this.curDisabled = true;return false;}
if (this.start.length !== 4) { GetDate.error('时间粒度和开始时间格式不一致'); this.curDisabled = true; return false;}
if (this.end.length !== 4) { GetDate.error('时间粒度和结束时间格式不一致'); this.curDisabled = true; return false;}
break;
case 'month':
if (this.value.length !== 7) { GetDate.error('时间粒度和时间格式不一致'); this.curDisabled = true; return false;}
if (this.start.length !== 7) { GetDate.error('时间粒度和开始时间格式不一致'); this.curDisabled = true; return false;}
if (this.end.length !== 7) { GetDate.error('时间粒度和结束时间格式不一致'); this.curDisabled = true; return false;}
break;
case 'day':
if (this.value.length !== 10) { GetDate.error('时间粒度和时间格式不一致'); this.curDisabled = true; return false;}
if (this.start.length !== 10) { GetDate.error('时间粒度和开始时间格式不一致'); this.curDisabled = true; return false;}
if (this.end.length !== 10) { GetDate.error('时间粒度和结束时间格式不一致'); this.curDisabled = true; return false;}
break;
case 'hour':
if (this.value.length !== 13) { GetDate.error('时间粒度和时间格式不一致'); this.curDisabled = true; return false;}
if (this.start.length !== 13) { GetDate.error('时间粒度和开始时间格式不一致'); this.curDisabled = true; return false;}
if (this.end.length !== 13) { GetDate.error('时间粒度和结束时间格式不一致'); this.curDisabled = true; return false;}
break;
case 'minute':
if (this.value.length !== 16) { GetDate.error('时间粒度和时间格式不一致'); this.curDisabled = true; return false;}
if (this.start.length !== 16) { GetDate.error('时间粒度和开始时间格式不一致'); this.curDisabled = true; return false;}
if (this.end.length !== 16) { GetDate.error('时间粒度和结束时间格式不一致'); this.curDisabled = true; return false;}
break;
case 'second':
if (this.value.length !== 19) { GetDate.error('时间粒度和时间格式不一致'); this.curDisabled = true; return false;}
if (this.start.length !== 19) { GetDate.error('时间粒度和开始时间格式不一致'); this.curDisabled = true; return false;}
if (this.end.length !== 19) { GetDate.error('时间粒度和结束时间格式不一致'); this.curDisabled = true; return false;}
break;
default:
GetDate.error('时间粒度不存在')
break;
}
let values = this.value.split(' ');
let targets = GetDate.getCurrentStringValue(this.value);
if (GetDate.getTimes(this.value) < GetDate.getTimes(this.start)){
GetDate.error('默认时间不能小于开始时间')
this.curDisabled = true
return false;
}
if (GetDate.getTimes(this.value) > GetDate.getTimes(this.end)) {
GetDate.error('默认时间不能大于开始时间')
this.curDisabled = true
return false;
}
let times = GetDate.getDateTimes({
start: this.start.substring(0, 4),
end: this.end.substring(0, 4),
curyear: this.value.substring(0, 4),
curmonth: this.value.substring(5, 7),
fields: this.fields
})
let timesIndex = GetDate.getTimeIndex(times, targets);
let timesString = [];
timesIndex.forEach(o => timesString.push(o));
this.times = times;
this.timesIndex = timesIndex;
this.timesString = timesString;
},
methods: {
changeDate(e){
let values = e.detail.value;
let times = this.times;
let curarr = [];
for (let i = 0, len = values.length; i < len; i++) {
curarr.push(times[i][values[i]])
}
let str = GetDate.format(curarr);
this.curValue = str;
},
columnchangeDate(e){
// index
if ((this.fields === 'year' || this.fields === 'month')){
let timesIndex = GetDate.getNewArray(this.timesIndex);
timesIndex[e.detail.column] = e.detail.value;
// let arr = GetDate.getCompare(GetDate.format(GetDate.getChooseArr(this.times,timesIndex)),this.start,this.end,this.times);
// console.log(arr)
this.timesIndex = timesIndex;
return false;
}else{
// index
if(e.detail.column === 0 || e.detail.column === 1){
let times = GetDate.getNewArray(this.times);
let timesIndex = GetDate.getNewArray(this.timesIndex);
timesIndex[e.detail.column] = e.detail.value;
let days = GetDate.getMonthDay(times[0][timesIndex[0]], times[1][timesIndex[1]]);
times[2] = days;
if(timesIndex[2] > days.length - 1){
timesIndex[2] = days.length - 1;
}
this.times = times;
// let arr = GetDate.getCompare(GetDate.format(GetDate.getChooseArr(this.times,timesIndex)),this.start,this.end,this.times);
this.timesIndex = timesIndex;
}else{
let timesIndex = GetDate.getNewArray(this.timesIndex);
timesIndex[e.detail.column] = e.detail.value;
// let arr = GetDate.getCompare(GetDate.format(GetDate.getChooseArr(this.times,timesIndex)),this.start,this.end,this.times);
// console.log(arr)
this.timesIndex = timesIndex;
}
}
},
cancelDate(e){
this.cancel = e
}
}
}
</script>
<style>
.rui-picker{
height: 10vw;
font-size: 4vw;
color: #000;
display: -webkit-flex;
display: flex;
align-items: center;
padding: 0 10px;
box-sizing: border-box;
border: 1px solid #aaa;
border-radius: 3px;
}
</style>

69
components/rattenking-dtpicker/readme.md

@ -1,69 +0,0 @@
### DatePicker 多粒度时间选择器
可进行多粒度的时间选择器,组件名:``rattenking-dtpicker``,代码块: ruiDatePicker。
**使用方式:**
在 ``script`` 中引用组件
```javascript
import ruiDatePicker from '@/components/rattenking-dtpicker/rattenking-dtpicker.vue';
export default {
components: {ruiDatePicker}
}
```
在 ``template`` 中使用组件
```html
<ruiDatePicker
fields="second"
start="2010-00-00 00:00:00"
end="2030-12-30 23:59:59"
:value="value"
@change="bindChange"
@cancel="bindCancel"
></ruiDatePicker>
```
实际效果参考:[https://github.com/Rattenking/rui-uni-components](https://github.com/Rattenking/rui-uni-components)
**DatePicker 属性说明:**
|属性名 |类型 |默认值 |说明 |
|--- |---- |--- |--- |
|start |String |'1900-00-00 00:00:00' |限制选择器选择的最小时间 |
|end |String |'2500-12-30 23:59:59' |限制选择器选择的最大时间 |
|value |String |'' |当前时间选择器显示的时间 |
|fields |String |'second' |时间选择器的粒度 |
|disabled |Boolean|false |是否为禁用状态 |
**fields 值说明:**
|值 |类型 |说明 |
|--- |---- |--- |
|year |String |选择器粒度为年 |
|month |String |选择器粒度为月份 |
|day |String |选择器粒度为天 |
|hour |String |选择器粒度为小时 |
|minute |String |选择器粒度为分钟 |
|second |String |选择器粒度为秒 |
**事件说明:**
|事件名称 |说明 |
|---|---|
|change |时间选择器点击【确定】按钮时时触发的事件,参数为picker的当前的 value|
|cancel |时间选择器点击【取消】按钮时时触发的事件|
**修复BUG说明:**
1. 修复每个月都是 31 天的 bug !
2. 修复 value 值做出改变,在外部赋值才改变的 bug ,当前版本只要 value 改变,显示值就会改变!
3. 优化了默认显示值问题,如果用户不填写,直接默认当前设备的当前时间!
4. 修复二月份能够选择 31 号的 bug!
**感谢:**
> 在这里特别感谢**AimerQAQ**、**icare**、**281245387@qq.com**、**椰子皮**等反馈的 bug 和给出的优化建议。有更多优化建议和需求,请联系作者。谢谢!

102
components/school-map/school-map.vue

@ -1,102 +0,0 @@
<template>
<map
:latitude="latitude"
:longitude="longitude"
:markers="covers"
:polygons="polygons"
:polyline="polyline"
id="map"
ref="map"
show-location="true"
include-points="true"
style="width: 100%; height: 1000rpx;"
/>
</template>
<script>
export default {
data() {
return {
latitude: 37.87059,
longitude: 112.55067,
covers: [
{
latitude: 37.87059,
longitude: 112.55067,
iconPath: '../../static/location.png',
callout: {
content: 'test',
color: '#fff',
padding: 4,
bgColor: '#0A97C6',
borderRadius: 2,
textAlign: 'center',
},
},
{
latitude: 37.87,
longitude: 112.5506,
iconPath: '../../static/location.png',
},
{
latitude: 37.8701,
longitude: 112.55,
iconPath: '../../static/location.png',
},
{
latitude: 37.86,
longitude: 112.5496,
iconPath: '../../static/location.png',
},
],
polyline: [
{
points: [
{ latitude: 37.87059, longitude: 112.55067 },
{ latitude: 37.87, longitude: 112.5506 },
{ latitude: 37.8701, longitude: 112.55 },
{ latitude: 37.86, longitude: 112.5496 },
],
arrowLine: true,
dottedLine: true,
borderColor: '#cccccc',
},
],
polygons: [
{
points: [
{ latitude: 37.87059, longitude: 112.55067 },
{ latitude: 37.87, longitude: 112.5506 },
{ latitude: 37.8701, longitude: 112.55 },
{ latitude: 37.86, longitude: 112.5496 },
],
strokeWidth: 0,
strokeColor: '#00000000',
fillColor: '#cce6ff88',
zIndex: 0,
},
],
};
},
onLoad() {
this.getLocation();
},
methods: {
//
// mapgcj02
getLocation() {
uni.getLocation({
type: 'gcj02',
success: res => {
this.longitude = res.longitude;
this.latitude = res.latitude;
console.log('当前位置的经度:' + res.longitude);
console.log('当前位置的纬度:' + res.latitude);
},
});
},
},
};
</script>

19
components/test/test.vue

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

44
components/timeline/timeline.vue

@ -1,44 +0,0 @@
<template>
<view>
<view class="cu-timeline">
<!-- <view class="cu-time">昨天</view> -->
<view :key="index" class="cu-item cur" v-for="(item, index) in userSigns">
<view class="content bg-green shadow-blur">
<text>{{ generateTime(item.time - 0) }}</text>
{{ item.siteName }} 打卡
</view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
export default {
data() {
return {};
},
computed: mapState('statistics', ['userSigns']),
methods: {
/**
* 格式化时间为文本
* @param {number} time ms数
*/
generateTime(time) {
let type = 'YYYY-MM-DD HH:mm';
if (this.$moment().isSame(time, 'day')) {
return `今天 ${this.$moment(time).format('HH:mm')}`;
}
if (this.$moment().isSame(time, 'year')) {
type = 'MM-DD HH:mm';
}
return this.$moment(time).format(type);
},
},
};
</script>

546
components/uni-calendar/calendar.js

@ -1,546 +0,0 @@
/**
* @1900-2100区间内的公历农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hexAttribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function (y) {
var i; var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function (y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (02930)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function (y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-12930)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function (y, m) {
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-128293031)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function (y, m) {
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function (lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function (cMonth, cDay) {
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function (offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function (y, n) {
if (y < 1900 || y > 2100) { return -1 }
if (n < 1 || n > 24) { return -1 }
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function (m) { // 月 => \u6708
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708'// 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function (d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341'; break
case 20:
s = '\u4e8c\u5341'; break
break
case 30:
s = '\u4e09\u5341'; break
break
default :
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是立春
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function (y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1// undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i; var leap = 0; var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp; i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i)// 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) { isLeap = false }
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true; --i
}
}
if (offset < 0) {
offset += temp; --i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0; var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y); isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) { offset += day }
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

152
components/uni-calendar/uni-calendar-item.vue

@ -1,152 +0,0 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
'uni-calendar-item--multiple': weeks.multiple
}"
@click="choiceDate(weeks)">
<view class="uni-calendar-item__weeks-box-item">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text" :class="{
'uni-calendar-item--isDay-text': weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.date}}</text>
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
}">今天</text>
<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.isDay?'今天': (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--extra':weeks.extraInfo.info,
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
export default {
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
}
.uni-calendar-item__weeks-box-text {
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar-item__weeks-lunar-text {
font-size: $uni-font-size-sm;
color: $uni-text-color;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100rpx;
height: 100rpx;
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: $uni-color-error;
}
.uni-calendar-item--disable {
background-color: rgba(249, 249, 249, $uni-opacity-disabled);
color: $uni-text-color-disable;
}
.uni-calendar-item--isDay-text {
color: $uni-color-primary;
}
.uni-calendar-item--isDay {
background-color: $uni-color-primary;
opacity: 0.8;
color: #fff;
}
.uni-calendar-item--extra {
color: $uni-color-error;
opacity: 0.8;
}
.uni-calendar-item--checked {
background-color: $uni-color-primary;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--multiple {
background-color: $uni-color-primary;
color: #fff;
opacity: 0.8;
}
</style>

434
components/uni-calendar/uni-calendar.vue

@ -1,434 +0,0 @@
<template>
<view class="uni-calendar" @touchmove.stop.prevent="clean">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
<view class="uni-calendar__header-btn-box" @click="close">
<text class="uni-calendar__header-text uni-calendar--fixed-width">取消</text>
</view>
<view class="uni-calendar__header-btn-box" @click="confirm">
<text class="uni-calendar__header-text uni-calendar--fixed-width">确定</text>
</view>
</view>
<view class="uni-calendar__header">
<view class="uni-calendar__header-btn-box" @click="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'') +'月'}}</text>
<view class="uni-calendar__header-btn-box" @click="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<text class="uni-calendar__backtoday" @click="backtoday">回到今天</text>
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<uni-calendar-item :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></uni-calendar-item>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import uniCalendarItem from './uni-calendar-item.vue'
export default {
components: {
uniCalendarItem
},
props: {
/**
* 当前日期
*/
date: {
type: String,
default: ''
},
/**
* 打点日期
*/
selected: {
type: Array,
default () {
return []
}
},
/**
* 是否开启阴历日期
*/
lunar: {
type: Boolean,
default: false
},
/**
* 开始时间
*/
startDate: {
type: String,
default: ''
},
/**
* 结束时间
*/
endDate: {
type: String,
default: ''
},
/**
* 范围
*/
range: {
type: Boolean,
default: false
},
/**
* 插入
*/
insert: {
type: Boolean,
default: true
},
/**
* 是否显示月份背景
*/
showMonth: {
type: Boolean,
default: true
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false
}
},
watch: {
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
}
},
created() {
//
this.cale = new Calendar({
date: this.date,
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
this.init(this.cale.date.fullDate)
},
methods: {
// 穿
clean() {},
init(date) {
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
},
open() {
this.show = true
this.$nextTick(() => {
setTimeout(()=>{
this.aniMaskShow = true
},50)
})
},
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
}, 300)
})
},
confirm() {
this.setEmit('confirm')
this.close()
},
change() {
if (!this.insert) return
this.setEmit('change')
},
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
},
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
//
this.cale.setMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
this.change()
},
backtoday() {
this.cale.setDate(this.date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(this.date)
this.change()
},
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style lang="scss" scoped>
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
// padding: 0 15px;
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
padding-left: 10px;
height: 25px;
line-height: 25px;
font-size: 12px;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
color: $uni-text-color;
background-color: $uni-bg-color-hover;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 10px;
height: 10px;
border-left-color: $uni-text-color-placeholder;
border-left-style: solid;
border-left-width: 2px;
border-top-color: $uni-color-subtitle;
border-top-style: solid;
border-top-width: 2px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 45px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 14px;
}
.uni-calendar__box {
position: relative;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: $uni-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
</style>

327
components/uni-calendar/util.js

@ -1,327 +0,0 @@
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range
} = {}) {
// 当前日期
this.date = this.getDate(date) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
this.range = range
// 多选状态
this.multipleStatus = {
before: '',
after: '',
data: []
}
// 每周日期
this.weeks = {}
this._getWeek(this.date.fullDate)
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let isinfo = false
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
let dateCompBefore = this.dateCompare(this.startDate, fullDate)
disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
}
if (this.endDate) {
let dateCompAfter = this.dateCompare(fullDate, this.endDate)
disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
month: full.month,
lunar: this.getlunar(full.year, full.month, i),
disable: !disableBefore || !disableAfter,
isDay
}
if (info) {
data.extraInfo = info
}
dateArr.push(data)
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this._getWeek(date)
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
geDateAll(begin, end) {
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
this.multipleStatus.before = ''
this.multipleStatus.after = ''
this.multipleStatus.data = []
this._getWeek(fullDate)
} else {
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
this._getWeek(fullDate)
}
}
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
const {
fullDate,
year,
month,
date,
day
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
this.canlender = canlender
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

124
components/uni-segmented-control/uni-segmented-control.vue

@ -1,124 +0,0 @@
<template>
<view :class="[styleType === 'text'?'segmented-control--text' : 'segmented-control--button' ]" :style="{ borderColor: styleType === 'text' ? '' : activeColor }"
class="segmented-control">
<view v-for="(item, index) in values" :class="[ styleType === 'text'?'segmented-control__item--text': 'segmented-control__item--button' , index === currentIndex&&styleType === 'button'?'segmented-control__item--button--active': '' , index === 0&&styleType === 'button'?'segmented-control__item--button--first': '',index === values.length - 1&&styleType === 'button'?'segmented-control__item--button--last': '' ]"
:key="index" :style="{
backgroundColor: index === currentIndex && styleType === 'button' ? activeColor : '',borderColor: index === currentIndex&&styleType === 'text'||styleType === 'button'?activeColor:'transparent'
}"
class="segmented-control__item" @click="_onClick(index)">
<text :style="{color:
index === currentIndex
? styleType === 'text'
? activeColor
: '#fff'
: styleType === 'text'
? '#000'
: activeColor}"
class="segmented-control__text">{{ item }}</text>
</view>
</view>
</template>
<script>
export default {
name: 'UniSegmentedControl',
props: {
current: {
type: Number,
default: 0
},
values: {
type: Array,
default () {
return []
}
},
activeColor: {
type: String,
default: '#007aff'
},
styleType: {
type: String,
default: 'button'
}
},
data() {
return {
currentIndex: 0
}
},
watch: {
current(val) {
if (val !== this.currentIndex) {
this.currentIndex = val
}
}
},
created() {
this.currentIndex = this.current
},
methods: {
_onClick(index) {
if (this.currentIndex !== index) {
this.currentIndex = index
this.$emit('clickItem', {currentIndex:index})
}
}
}
}
</script>
<style lang="scss" scoped>
@import '@/uni.scss';
.segmented-control {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
height: 36px;
overflow: hidden;
}
.segmented-control__item {
/* #ifndef APP-NVUE */
display: inline-flex;
box-sizing: border-box;
/* #endif */
position: relative;
flex: 1;
justify-content: center;
align-items: center;
}
.segmented-control__item--button {
border-style: solid;
border-top-width: 1px;
border-bottom-width: 1px;
border-right-width: 1px;
border-left-width: 0;
}
.segmented-control__item--button--first {
border-left-width: 1px;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}
.segmented-control__item--button--last {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}
.segmented-control__item--text {
border-bottom-style: solid;
border-bottom-width: 3px;
}
.segmented-control__text {
font-size: 16px;
line-height: 20px;
text-align: center;
}
</style>

103
components/user-agreement/user-agreement.vue

@ -1,103 +0,0 @@
<template>
<view class="margin flex flex-wrap">
<view class="flex flex-wrap">
<view @click="changeIntentions" class="iconfont agree-box" :class="[agree ? 'text-blue icon-check-square': 'text-gray icon-border']"></view>
<view class="text-df text-black flex-sub agree-text">
请认真阅读
<text class="text-blue" @tap="openPage('/pages/service-agreement/service-agreement')">用户服务协议</text>
<text class="text-blue" @tap="openPage('/pages/privacy-aolicy/privacy-aolicy')">隐私政策</text>,
勾选代表您已同意此协议
</view>
</view>
<view class="flex flex-wrap margin-tb-sm text-black">
<view class="text-df text-bold flex agree-text">
本人郑重承诺
</view>
<view class="flex">
<view @click="changeIntentions1" class="iconfont agree-box" :class="[agree1 ? 'text-blue icon-check-square': 'text-gray icon-border']"></view>
<view class="text-df flex-sub agree-text">
为疫情防控本人同意以上信息依法提交山西大学统筹管理
</view>
</view>
<view class="flex">
<view @click="changeIntentions2" class="iconfont agree-box" :class="[agree2 ? 'text-blue icon-check-square': 'text-gray icon-border']"></view>
<view class="text-df flex-sub agree-text">
上述信息是我本人填写本人对信息内容的真实性和完整性负责如果信息有误或者缺失本人愿承担相应的法律责任同时本人保证遵守防疫管控的各项规定配合并听从各项措施和要求
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
agree: false,
agree1: false,
agree2: false,
};
},
onLoad(op) {},
methods: {
changeIntentions(){
if(!this.agree){
this.agree = true;
}else{
this.agree = false;
}
this.$emit('changeIntentions',this.agree)
},
changeIntentions1(){
if(!this.agree1){
this.agree1 = true;
}else{
this.agree1 = false;
}
this.$emit('changeIntentions1',this.agree1)
},
changeIntentions2(){
if(!this.agree2){
this.agree2 = true;
}else{
this.agree2 = false;
}
this.$emit('changeIntentions2',this.agree2)
},
//
serviceAgreement(){
uni.navigateToMiniProgram({
appId: 'wx5b97b0686831c076',
path: 'pages/preview/preview?sid=&fid=60774991254',
success(res) {
console.log(res);
},
});
},
//
privacyAolicy(){
uni.navigateToMiniProgram({
appId: 'wx5b97b0686831c076',
path: 'pages/preview/preview?sid=&fid=60774976650',
success(res) {
console.log(res);
},
});
}
},
};
</script>
<style lang="scss" scoped>
.agree-box {
width: 70rpx;
}
.agree-text {
line-height: 60rpx;
}
</style>

49
config/api/api.js

@ -1,49 +0,0 @@
const HEALTH = '/health';
const users = `${HEALTH}/users`;
const health = `${HEALTH}/health`;
const sites = `${HEALTH}/sites`;
const journeys = `${HEALTH}/journeys`;
// 保存个人信息
export const SUBMIT_USER_INFO = `${users}/addInfo`;
// 获取个人信息
export const GET_USER_INFO = `${users}/info`;
// 获取健康打卡记录
export const HEALTH_SIGN_HISTORY = `${health}/info`;
// 健康上报数目统计
export const HEALTH_TYPE_STATISTICS = `${health}/statistics`;
// 查询健康状态类型
export const HEALTH_TYPE_STATUS = `${health}/type`;
// 健康打卡
export const HEALTH_SIGN = `${health}/upload`;
// 上传备注图片
export const HEALTH_FILE = `${health}/file`;
// 查看自己的打卡记录
export const USER_SIGNS = `${sites}/info`;
// 获取所有场景信息
export const SITES_INFO = `${sites}/siteInfo`;
// 根据二维码的id获取场所的信息
export const SITE = `${sites}/site`;
// 扫码统计
export const SCHOOL_SIGNS = `${sites}/statistics`;
// 扫码打卡
export const SCAN_SIGN = `${sites}/upload`;
// 查询行程
export const GET_JOURNEYS = `${journeys}/info`;
// 上报行程
export const SUBMIT_JOURNEYS = `${journeys}/upload`;

10
config/api/user.js

@ -1,10 +0,0 @@
const proxyUrl = '/tall/v1.0';
// 登录api
export const SIGN_IN = `${proxyUrl}/users/signin`;
// 上传用户微信信息
export const UPDATE_USER = `${proxyUrl}/users/userInfo`;
// 通过userId 获取token
export const USER_ID_EXCHANGE_TOKEN = `${proxyUrl}/users/userId`;

4
package.json

@ -1,5 +1,5 @@
{
"name": "healthycode",
"name": "uniTemplete",
"version": "1.0.0",
"description": "",
"main": "main.js",
@ -21,7 +21,7 @@
},
"repository": {
"type": "git",
"url": "https://gitee.com/ccsens_s/healthyCode.git"
"url": ""
},
"author": "wally",
"license": "ISC",

80
pages.json

@ -3,87 +3,13 @@
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "山大健康码"
"navigationBarTitleText": "模板"
}
},
{
"path": "pages/sign/sign",
"style": {
"navigationBarTitleText": "打卡"
}
},
{
"path": "pages/user-code/user-code",
"style": {"navigationBarTitleText": "健康码"}
},
{
"path": "pages/statistics/statistics",
"style": { "navigationBarTitleText": "统计" }
},
{
"path": "pages/my-signs/my-signs",
"style": { "navigationBarTitleText": "我的校园打卡" }
},
{
"path": "pages/get-code/get-code",
"style": {
"navigationBarTitleText": "领取健康码"
}
},
{
"path": "pages/punch-the-clock/punch-the-clock",
"style": {
"navigationBarTitleText": "每日健康打卡"
}
},
{
"path": "pages/basic-info/basic-info",
"style": {
"navigationBarTitleText": "基本信息填写"
}
},
{
"path": "pages/add-stroke/add-stroke",
"style": {
"navigationBarTitleText": "添加行程"
}
},
{
"path": "pages/apply-code/apply-code",
"style": {
"navigationBarTitleText": "申请健康码"
}
},
{
"path": "pages/my-code/my-code",
"style": {
"navigationBarTitleText": "我的健康打卡"
}
},
{
"path": "pages/my-trips/my-trips",
"style": {
"navigationBarTitleText": "我的行程"
}
},
{
"path": "pages/service-agreement/service-agreement",
"style": {
"navigationBarTitleText": "服务协议"
}
},
{
"path": "pages/privacy-aolicy/privacy-aolicy",
"style": {
"navigationBarTitleText": "隐私政策"
}
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "山大健康码",
"navigationBarTitleText": "模板项目",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},

291
pages/add-stroke/add-stroke.vue

@ -1,291 +0,0 @@
<template>
<view>
<form class="padding-lr cu-form-group flex-direction">
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>出发时间</view>
<start-date-selector @change="getStartData" />
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>抵达时间</view>
<end-date-selector @change="getEndData" />
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>行程类型</view>
<radio-group class="block" @change="TypeChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(journey,index) in journeys" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === journeyType" :value="journey.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ journey.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="text-xl padding-tb-sm"><span class="text-red padding-right-xs">*</span>出行交通方式</view>
<radio-group class="block" @change="RadioChange">
<view class="cu-list menu text-left">
<view class="cu-item" v-for="(transport,index) in transports" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === tripMode" :value="transport.value"></radio>
<view class="flex-sub" style="font-size: 34rpx;">{{ transport.name }}</view>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>乘坐航班车次或车牌号码及座位号没有填无</view>
<input placeholder="请输入" name="input" type="text" v-model="carNo" maxlength="12"/>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">同行人</view>
<input placeholder="请输入同行人" name="input" type="text" v-model="together" maxlength="255" />
</view>
</form>
<user-agreement @changeIntentions="changeIntentions" @changeIntentions1="changeIntentions1" @changeIntentions2="changeIntentions2"></user-agreement>
<button class="bg-cyan margin primary-btn" hover-class="cc-active" @tap="handleAddStroke">确认提交</button>
<!-- 历史 -->
<button class="shadow round bg-cyan history-btn iconfont icon-history" hover-class="cc-active" @tap="openPage('/pages/my-trips/my-trips')"></button>
</view>
</template>
<script>
import { showToast } from 'common/script/util';
import UniCalendar from 'components/uni-calendar/uni-calendar.vue';
import StartDateSelector from './components/start-date-selector.vue';
import EndDateSelector from './components/end-date-selector.vue';
import { SUBMIT_JOURNEYS } from 'api/api';
export default {
components: {UniCalendar,StartDateSelector,EndDateSelector},
data() {
return {
startTime: this.$moment().format('YYYY-MM-DD HH:mm'),
endTime: this.$moment().format('YYYY-MM-DD HH:mm'),
transports: [
{
value: '0',
name: '火车',
},
{
value: '1',
name: '飞机',
},
{
value: '2',
name: '客运车辆',
},
{
value: '3',
name: '自驾',
},
{
value: '4',
name: '船',
},
{
value: '5',
name: '其他',
}
],
carNo: '',
journeys: [
{
value: '0',
name: '返校行程',
},
{
value: '1',
name: '日常外出',
}
],
together: '',
tripMode: 0,
journeyType: 0,
agree: false,
agree1: false,
agree2: false
};
},
methods: {
RadioChange: function(evt) {
for (let i = 0; i < this.transports.length; i++) {
if (this.transports[i].value === evt.target.value) {
this.tripMode = i;
break;
}
}
},
TypeChange: function(evt) {
for (let i = 0; i < this.journeys.length; i++) {
if (this.journeys[i].value === evt.target.value) {
this.journeyType = i;
break;
}
}
},
//
changeIntentions(data) {
this.agree = data;
},
changeIntentions1(data) {
this.agree1 = data;
},
changeIntentions2(data) {
this.agree2 = data;
},
/**
* 获取出发时间
* @param {string} start 开始时间
* @param {string} end 截止时间
*/
getStartData(start) {
console.log('出发时间 start: ', start);
this.startTime = start;
},
/**
* 获取抵达时间
* @param {string} start 开始时间
* @param {string} end 截止时间
*/
getEndData(end) {
console.log('抵达时间 end: ', end);
this.endTime = end;
},
/**
* 添加行程
*/
async handleAddStroke() {
try {
if (!this.checkRules()) return;
const {
carNo,
endTime,
journeyType,
startTime,
together,
tripMode,
agree1,
agree2,
} = this;
const params = {
param: {
carNo,
endTime: +this.$moment(endTime).format('x'),
journeyType: journeyType+1,
startTime: +this.$moment(startTime).format('x'),
together,
tripMode,
healthAgreement: agree1 ? 1 : 0,
selfFill: agree2 ? 1 : 0,
}
};
const res = await this.$http.post(SUBMIT_JOURNEYS, params);
const {
success,
code,
msg,
data
} = res.data;
if (success && code === 200) {
uni.showToast({
title: '行程添加成功',
duration: 2000
});
this.success = true;
uni.reLaunch({
url: `/pages/index/index`,
});
} else {
uni.showToast({
title: msg || '行程添加失败',
icon: 'none'
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '行程添加失败',
icon: 'none'
});
}
}
},
//
checkRules() {
const { startTime,endTime,journeyType,tripMode,carNo,agree,agree1,agree2 } = this;
if (!this.startTime) {
showToast('请选择出发时间');
return;
}
if (!this.endTime) {
showToast('请选择抵达时间');
return;
}
if (this.startTime >= this.endTime){
showToast('开始时间不能和抵达时间相同或者晚于抵达时间');
return;
}
if (this.journeyType<0) {
showToast('请选择行程类型');
return;
}
if (this.tripMode<0) {
showToast('请选择出行交通方式');
return;
}
if (!this.carNo) {
showToast('乘坐航班车次或车牌号码及座位号');
return;
}
if (!agree) {
showToast('请选择是否同意《用户服务协议》和《隐私政策》');
return;
}
if (!this.agree1) {
showToast('请选择是否同意信息提交山西大学管理');
return;
}
if (!this.agree2) {
showToast('请确定是否为本人填写');
return;
}
return true;
},
},
};
</script>
<style lang="scss" scoped>
.agree-box{
width: 70rpx;
}
.agree-text{
line-height: 60rpx;
}
.primary-btn{
border-radius: 15rpx;
}
.history-btn{
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
}
.history-btn::after{
border: none;
}
</style>

73
pages/add-stroke/components/end-date-selector.vue

@ -1,73 +0,0 @@
<template>
<view class="date-bg">
<view hover-class="cc-active">
<view class="iconfont icon-calendar timer"><text class="padding-left-xs">{{ endDate }}</text></view>
</view>
<view class="startTime">
<ruiDatePicker
fields="minute"
start="2010-01-01 00:00"
end="2030-12-30 23:59"
:value="valueEnd"
@change="bindChangeEnd"
class="rui-date-picker"
></ruiDatePicker>
</view>
</view>
</template>
<script>
import ruiDatePicker from 'components/rattenking-dtpicker/rattenking-dtpicker.vue';
export default {
name: 'EndDateSelector',
components: {ruiDatePicker},
data() {
return {
valueEnd: this.$moment(new Date()).format('YYYY-MM-DD HH:mm')
};
},
computed: {
endDate() {
const {
valueEnd
} = this;
return valueEnd;
},
},
methods: {
bindChangeEnd(value){
// console.log('',value)
this.valueEnd = value;
this.$emit('change', this.valueEnd);
},
},
};
</script>
<style lang="scss" scoped>
.timer {
font-size: 34rpx !important;
color: $gray;
}
.date-bg{
position: relative;
}
.startTime{
opacity: 0;
position: absolute;
top: -14rpx;
left: 50rpx;
}
.rui-date-picker{
text-indent: 12em;
}
</style>

73
pages/add-stroke/components/start-date-selector.vue

@ -1,73 +0,0 @@
<template>
<view class="date-bg">
<view hover-class="cc-active">
<view class="iconfont icon-calendar timer"><text class="padding-left-xs">{{ startDate }}</text></view>
</view>
<view class="startTime">
<ruiDatePicker
fields="minute"
start="2010-01-01 00:00"
end="2030-12-30 23:59"
:value="valueStart"
@change="bindChangeStart"
class="rui-date-picker"
></ruiDatePicker>
</view>
</view>
</template>
<script>
import ruiDatePicker from 'components/rattenking-dtpicker/rattenking-dtpicker.vue';
export default {
name: 'StartDateSelector',
components: {ruiDatePicker},
data() {
return {
valueStart: this.$moment(new Date()).format('YYYY-MM-DD HH:mm')
};
},
computed: {
startDate() {
const {
valueStart
} = this;
return valueStart;
},
},
methods: {
bindChangeStart(value){
// console.log('',value)
this.valueStart = value;
this.$emit('change', this.valueStart);
},
},
};
</script>
<style lang="scss" scoped>
.timer {
font-size: 34rpx !important;
color: $gray;
}
.date-bg{
position: relative;
}
.startTime{
opacity: 0;
position: absolute;
top: -14rpx;
left: 50rpx;
}
.rui-date-picker{
text-indent: 12em;
}
</style>

489
pages/apply-code/apply-code.vue

@ -1,489 +0,0 @@
<template>
<view>
<form class="padding-lr cu-form-group flex-direction">
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs">*</span>当前所在地区
</view>
<view class="flex align-center">
<input @tap="handleSelectLocation" class="flex-sub padding-left-xs" name="input" placeholder="请选择当前所在地区" type="btn"
v-model="district" maxlength="128" />
<text class="cuIcon-location timer"></text>
</view>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs">*</span>当前所在详细地址
</view>
<input name="input" placeholder="请输入详细地址(含门牌号)" type="text" v-model="address" maxlength="128" />
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs">*</span>当前状态
</view>
<radio-group @change="StateChange" class="block">
<view class="cu-list menu text-left">
<view :key="index" class="cu-item" v-for="(state,index) in status">
<label class="flex justify-between align-center">
<radio :checked="index === healthTypeId" :value="state.id" class="round margin-right-xs"></radio>
<view class="flex-sub" style="font-size: 34rpx;">{{ state.name }}</view>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">就诊医院若无填无</view>
<input name="input" placeholder="请输入就诊医院" type="text" v-model="hospital" maxlength="32" />
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>最近14天是否有武汉居住史旅游史或武汉亲戚来访</view>
<radio-group @change="TourChange" class="block">
<view class="flex">
<view :key="index" class="flex-sub margin-tb-sm" v-for="(tour,index) in tours">
<label class="flex justify-between align-center">
<radio :checked="index === touchHubei" :value="tour.value" class="round margin-right-xs"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ tour.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>最近14天是否有新冠肺炎患者或疑似患者接触史</view>
<radio-group @change="TouchChange" class="block">
<view class="flex">
<view :key="index" class="flex-sub margin-tb-sm" v-for="(touch,index) in touches">
<label class="flex justify-between align-center">
<radio :checked="index === touchSick" :value="touch.value" class="round margin-right-xs"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ touch.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>最近14天有无接触过近期境外返回人员</view>
<radio-group class="block" @change="TouchOverseasChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(touch,index) in overseas" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === touchOverseas" :value="touch.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ touch.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>是否在学校所在地</view>
<radio-group class="block" @change="SchoolLocationChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(location,index) in locations" :key="location.value">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === schoolLocation" :value="location.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ location.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>当前体温</view>
<input placeholder="0" name="input" type="digit" v-model="animalHeat" />
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">紧急联系人</view>
<view class="flex">
<input placeholder="姓名" maxlength="6" class="flex flex-sub" name="input" type="text" v-model="emergencyName" />
<input placeholder="联系方式" maxlength="11" class="flex flex-sub" name="input" type="number" v-model="emergencyPhone" />
</view>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">备注选填</view>
<textarea placeholder="请填写具体情况..." name="input" v-model="remark" />
<!-- 上传图片 -->
<view class="grid col-4 grid-square flex-sub">
<view class="bg-img" v-for="(item,index) in imgList" :key="index" @tap="ViewImage" :data-url="imgList[index]">
<image :src="imgList[index]" mode="aspectFill"></image>
<view class="cu-tag bg-red" @tap.stop="DelImg" :data-index="index">
<text class='cuIcon-close'></text>
</view>
</view>
<view class="solids" @tap="ChooseImage" v-if="imgList.length<9">
<text class='cuIcon-cameraadd'></text>
</view>
</view>
</view>
</form>
<user-agreement @changeIntentions="changeIntentions" @changeIntentions1="changeIntentions1" @changeIntentions2="changeIntentions2"></user-agreement>
<button @tap="handleHealthSign" class="bg-cyan margin primary-btn" hover-class="cc-active">确认提交</button>
<!-- 历史 -->
<button
@tap="openPage('/pages/my-code/my-code')"
class="shadow round bg-cyan history-btn iconfont icon-history"
hover-class="cc-active"
></button>
</view>
</template>
<script>
import { showToast } from 'common/script/util';
import { HEALTH_SIGN, HEALTH_FILE } from 'api/api';
import { mapState, mapMutations } from 'vuex';
export default {
data() {
return {
district: '请选择当前所在地区',
address: '',
// status: [],
hospital: '',
tours: [
{
value: '0',
name: '否',
},
{
value: '1',
name: '是',
},
],
touches: [
{
value: '0',
name: '否',
},
{
value: '1',
name: '是',
},
],
overseas: [{
value: '0',
name: '无'
}, {
value: '1',
name: '有'
}],
locations: [{
value: '0',
name: '否'
}, {
value: '1',
name: '是'
}],
animalHeat: '',
healthTypeId: 0,
touchHubei: 0,
touchSick: 0,
touchOverseas: 0,
schoolLocation: 1,
emergencyName: '',
emergencyPhone: '',
remark: '',
fileIdList: [],
imgList: [],
agree: false,
agree1: false,
agree2: false,
};
},
computed: mapState('user', ['token', 'status']),
methods: {
...mapMutations('user', ['setHealthCode']),
handleSelectLocation() {
const that = this;
uni.chooseLocation({
success: function(res) {
console.log('位置名称:' + res.name);
console.log('详细地址:' + res.address);
that.district = res.address;
},
});
},
changeIntentions(data) {
this.agree = data;
},
changeIntentions1(data) {
this.agree1 = data;
},
changeIntentions2(data) {
this.agree2 = data;
},
//
StateChange: function(evt) {
for (let i = 0; i < this.status.length; i++) {
if (this.status[i].id === evt.target.value) {
this.healthTypeId = i;
break;
}
}
},
//
TourChange: function(evt) {
for (let i = 0; i < this.tours.length; i++) {
if (this.tours[i].value === evt.target.value) {
this.touchHubei = i;
break;
}
}
},
//
TouchChange(evt) {
for (let b = 0; b < this.touches.length; b++) {
if (this.touches[b].value === evt.target.value) {
this.touchSick = b;
break;
}
}
},
//
TouchOverseasChange(evt) {
for (let i = 0; i < this.overseas.length; i++) {
if (this.overseas[i].value === evt.target.value) {
this.touchOverseas = i;
break;
}
}
},
//
SchoolLocationChange(evt) {
for (let i = 0; i < this.locations.length; i++) {
if (this.locations[i].value === evt.target.value) {
this.schoolLocation = i;
break;
}
}
},
ChooseImage() {
uni.chooseImage({
count: 1, //9
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success: (res) => {
if (this.imgList.length != 0) {
this.imgList = this.imgList.concat(res.tempFilePaths)
} else {
this.imgList = res.tempFilePaths
}
uni.uploadFile({
url: `https://www.tall.wiki/gateway${HEALTH_FILE}`,
// url: `https://test.tall.wiki/gateway${HEALTH_FILE}`,
filePath: res.tempFilePaths[0],
fileType: 'image',
name: 'file',
success: (res) => {
const resData = JSON.parse(res.data)
const {
success,
code,
msg,
data
} = resData;
this.fileIdList.splice(-1,0,resData.data.fileId);
uni.showToast({
title: '图片提交成功',
duration: 1000,
});
},fail: (err) => {
console.log('uploadImage fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
});
}
});
},
});
},
ViewImage(e) {
uni.previewImage({
urls: this.imgList,
current: e.currentTarget.dataset.url
});
},
DelImg(e) {
uni.showModal({
title: '删除',
content: '确定要删除这张图片吗?',
cancelText: '再想想',
confirmText: '再见',
success: res => {
if (res.confirm) {
this.imgList.splice(e.currentTarget.dataset.index, 1)
this.fileIdList.splice(e.currentTarget.dataset.index, 1)
}
}
})
},
/**
* 申请健康码
*/
async handleHealthSign() {
try {
if (!this.checkRules()) return;
const {
address,
animalHeat,
district,
healthTypeId,
hospital,
token,
touchHubei,
touchSick,
touchOverseas,
schoolLocation,
emergencyName,
emergencyPhone,
remark,
fileIdList,
agree1,
agree2,
} = this;
const params = {
param: {
address,
animalHeat,
district,
healthTypeId: healthTypeId + 1,
hospital,
token,
touchHubei,
touchSick,
touchOverseas,
schoolLocation,
emergencyName,
emergencyPhone,
remark,
fileIdList,
healthAgreement: agree1 ? 1 : 0,
selfFill: agree2 ? 1 : 0,
},
};
const res = await this.$http.post(HEALTH_SIGN, params);
const { success, code, msg, data } = res.data;
if (success && code === 200) {
uni.showToast({
title: '申请健康码成功',
duration: 2000,
});
this.success = true;
this.setHealthCode(data.healthCode);
uni.reLaunch({
url: `/pages/index/index`,
});
} else {
uni.showToast({
title: msg || '申请健康码失败',
icon: 'none',
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '申请健康码失败',
icon: 'none',
});
}
}
},
//
checkRules() {
const { district, address, healthTypeId, animalHeat, agree,agree1,agree2 } = this;
if (!district || district === '请选择当前所在地区') {
showToast('请选择当前所在地区');
return;
}
if (!address) {
showToast('请输入当前所在地址');
return;
}
if (healthTypeId < 0) {
showToast('请选择状态');
return;
}
if (!animalHeat || animalHeat<30 || animalHeat>45) {
showToast('请输入正确的体温值(范围:30℃~45℃)');
return;
}
if (!agree) {
showToast('请选择是否同意《用户服务协议》和《隐私政策》');
return;
}
if (!agree1) {
showToast('请选择是否同意信息提交山西大学管理');
return;
}
if (!agree2) {
showToast('请确定是否为本人填写');
return;
}
return true;
},
/**
* 验证手机号格式
* @param {string} phone 手机号
*/
verifyPhone(phone) {
const phoneExg = /^1\d{10}$/;
return phoneExg.test(phone);
},
},
};
</script>
<style lang="scss" scoped>
.agree-box {
width: 70rpx;
}
.agree-text {
line-height: 60rpx;
}
.timer {
font-size: 34rpx !important;
color: $gray;
}
.primary-btn {
border-radius: 15rpx;
}
.history-btn {
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
}
.history-btn::after {
border: none;
}
</style>

281
pages/basic-info/basic-info.vue

@ -1,281 +0,0 @@
<template>
<view>
<form class="padding-lr cu-form-group flex-direction">
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs" v-show="!(userInfo && userInfo.id)">*</span>姓名
</view>
<input
:disabled="userInfo && userInfo.id ? true : false"
name="input"
placeholder="请输入真实姓名"
type="text"
v-model="name"
maxlength="6"
/>
</view>
<!-- <view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs" v-show="!(userInfo && userInfo.id)">*</span>身份证
</view>
<input
:disabled="userInfo && userInfo.id ? true : false"
name="input"
placeholder="请输入身份证号"
type="idcard"
v-model="idCard"
maxlength="18"
/>
</view> -->
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs" v-show="!(userInfo && userInfo.id)">*</span>联系方式
</view>
<input
:disabled="userInfo && userInfo.id ? true : false"
name="input"
placeholder="请输入手机号码"
type="number"
v-model="phone"
maxlength="11"
/>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs" v-show="!(userInfo && userInfo.id)">*</span>身份
</view>
<radio-group @change="RadioChange" class="block" v-if="!(userInfo && userInfo.id)">
<view class="flex">
<view :key="index" class="flex-sub margin-tb-sm" v-for="(identity, index) in identitys">
<label class="flex justify-between align-center">
<radio
:checked="index === current"
:value="identity.value"
class="round margin-right-xs"
></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ identity.name }}</text>
</label>
</view>
</view>
</radio-group>
<input
:value="current === 0 ? '学生' : current === 1 ? '教师' : '工作人员'"
disabled
name="input"
type="text"
v-else
/>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs" v-show="!(userInfo && userInfo.id)">*</span>学号/工号
</view>
<input
:disabled="userInfo && userInfo.id ? true : false"
name="input"
placeholder="请输入学号/工号"
type="text"
v-model="studentID"
maxlength="16"
/>
</view>
</form>
<user-agreement @changeIntentions="changeIntentions" @changeIntentions1="changeIntentions1" @changeIntentions2="changeIntentions2" v-if="!(userInfo && userInfo.id)"></user-agreement>
<view v-if="!(userInfo && userInfo.id)" class="flex text-df margin-bottom margin-lr agree-text">
微信小程序和企业微信应用同时实名后可以绑定微信小程序帐号和企业微信应用帐号方便操作
</view>
<button
@tap="handleSubmitUserInfo"
class="bg-cyan margin primary-btn"
hover-class="cc-active"
v-show="!(userInfo && userInfo.id)"
>确认提交</button>
</view>
</template>
<script>
import { showToast } from 'common/script/util';
import { SUBMIT_USER_INFO } from 'api/api';
import { mapState, mapMutations } from 'vuex';
export default {
data() {
return {
name: '',
// idCard: '',
phone: '',
identitys: [
{
value: '0',
name: '学生',
},
{
value: '1',
name: '教师',
},
{
value: '2',
name: '工作人员',
},
],
studentID: '',
current: 0,
agree: false,
agree1: false,
agree2: false
};
},
created() {
if (this.userInfo) {
this.name = this.userInfo.name;
// this.idCard = this.userInfo.idCard;
this.phone = this.userInfo.phone;
this.studentID = this.userInfo.no;
this.current = this.userInfo.post;
}
},
computed: mapState('user', ['userInfo', 'pagePath']),
methods: {
...mapMutations('user', ['setUserInfo']),
RadioChange: function(evt) {
for (let i = 0; i < this.identitys.length; i++) {
if (this.identitys[i].value === evt.target.value) {
this.current = i;
break;
}
}
},
changeIntentions(data) {
this.agree = data;
},
changeIntentions1(data) {
this.agree1 = data;
},
changeIntentions2(data) {
this.agree2 = data;
},
/**
* 提交基本信息
*/
async handleSubmitUserInfo() {
try {
if (!this.checkRules()) return;
const { name, phone, studentID, current, pagePath } = this;
const params = {
param: {
// idCard,
name,
no: studentID,
phone,
post: current,
},
};
const res = await this.$http.post(SUBMIT_USER_INFO, params);
const { success, code, msg, data } = res.data;
if (success && code === 200) {
uni.showToast({
title: '基本信息提交成功',
duration: 2000,
});
this.success = true;
this.setUserInfo(data);
if (pagePath === '/pages/basic-info/basic-info') {
uni.reLaunch({
url: `/pages/index/index`,
});
} else {
uni.redirectTo({ url: pagePath });
}
} else {
uni.showToast({
title: msg || '基本信息提交失败',
icon: 'none',
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '基本信息提交失败',
icon: 'none',
});
}
}
},
//
checkRules() {
const { name, phone, identitys, studentID, agree,agree1,agree2 } = this;
if (!name) {
showToast('请输入姓名');
return;
}
// if (!this.verifyIdCard(idCard)) {
// showToast('');
// return;
// }
if (!this.verifyPhone(phone)) {
showToast('请输入正确的手机号');
return false;
}
if (!identitys) {
showToast('请选择身份');
return;
}
if (!studentID) {
showToast('请输入学号/工号');
return;
}
if (!agree) {
showToast('请选择是否同意《用户服务协议》和《隐私政策》');
return;
}
if (!agree1) {
showToast('请选择是否同意信息提交山西大学管理');
return;
}
if (!agree2) {
showToast('请确定是否为本人填写');
return;
}
return true;
},
/**
* 验证手机号格式
* @param {string} phone 手机号
*/
verifyPhone(phone) {
const phoneExg = /^1\d{10}$/;
return phoneExg.test(phone);
},
/**
* 验证身份证号格式
* @param {string} idCard 身份证号
*/
// verifyIdCard(idCard) {
// const idCardExg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
// return idCardExg.test(idCard);
// },
},
};
</script>
<style lang="scss" scoped>
.primary-btn {
border-radius: 15rpx;
}
.agree-text {
line-height: 60rpx;
}
</style>

238
pages/get-code/get-code.vue

@ -1,238 +0,0 @@
<template>
<view>
<form class="padding-lr cu-form-group flex-direction">
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>当前所在地区</view>
<input placeholder="请输入当前所在地区" name="input" type="text" v-model="area" />
<button @tap="handleSelectLocation">选择位置</button>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>当前所在详细地址</view>
<input placeholder="请输入详细地址(含门牌号)" name="input" type="text" v-model="address" />
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>当前状态</view>
<radio-group class="block" @change="StateChange">
<view class="cu-list menu text-left">
<view class="cu-item" v-for="(state,index) in status" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === current" :value="state.value"></radio>
<view class="flex-sub" style="font-size: 34rpx;">{{ state.name }}</view>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">就诊医院若无填无</view>
<input placeholder="请输入就诊医院" name="input" type="text" v-model="hospital" />
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm">最近14天是否有武汉居住史旅游史或武汉亲戚来访</view>
<radio-group class="block" @change="TourChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(tour,index) in tours" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === tourCurrent" :value="tour.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ tour.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm">最近14天是否有新冠肺炎患者或疑似患者接触史</view>
<radio-group class="block" @change="TouchChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(touch,index) in touches" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === touchCurrent" :value="touch.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ touch.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>当前体温</view>
<input placeholder="请输入当前真实体温" name="input" type="number" v-model="temperature" />
</view>
</form>
<view class="margin flex flex-wrap">
<view @click="agree = !agree" class="iconfont agree-box" :class="[agree ? 'text-blue icon-check-square': 'text-gray icon-border']"></view>
<view class="text-df text-black flex-sub agree-text">
以上信息是我本人填写本人对信息的真实性和完整性负责
</view>
</view>
<button class="bg-cyan margin primary-btn" hover-class="cc-active" @tap="addStroke">确认提交</button>
<!-- 历史 -->
<button class="shadow round bg-cyan history-btn iconfont icon-history" hover-class="cc-active" @tap="openPage('/pages/my-code/my-code')"></button>
</view>
</template>
<script>
import { showToast } from 'common/script/util';
export default {
data() {
return {
area: '',
address: '',
status: [
{
value: '0',
name: '正常',
},
{
value: '1',
name: '发烧(377.3度以上)',
},
{
value: '2',
name: '咳嗽',
},
{
value: '3',
name: '咽喉疼痛',
},
{
value: '4',
name: '流鼻涕',
},
{
value: '5',
name: '头痛',
},
{
value: '6',
name: '其他',
}
],
hospital: '',
tours: [
{
value: 1,
name: '是'
},
{
value: 0,
name: '否'
}
],
touches: [
{
value: 1,
name: '是'
},
{
value: 0,
name: '否'
}
],
temperature: '',
current: 0,
tourCurrent: 0,
touchCurrent: 0,
agree: false,
};
},
methods: {
handleSelectLocation() {
uni.chooseLocation({
success: function(res) {
console.log('位置名称:' + res.name);
console.log('详细地址:' + res.address);
console.log('纬度:' + res.latitude);
console.log('经度:' + res.longitude);
},
});
},
//
StateChange: function(evt) {
for (let i = 0; i < this.status.length; i++) {
if (this.status[i].value === evt.target.value) {
this.current = i;
break;
}
}
},
//
TourChange: function(evt) {
for (let i = 0; i < this.tours.length; i++) {
if (this.tours[i].value === evt.target.value) {
this.tourCurrent = i;
break;
}
}
},
//
TouchChange: function(evt) {
for (let i = 0; i < this.touches.length; i++) {
if (this.touches[i].value === evt.target.value) {
this.touchCurrent = i;
break;
}
}
},
/**
* 提交基本信息
*/
addStroke() {
if (!this.area) {
showToast('请输入当前所在地区');
return;
}
if (!this.address) {
showToast('请输入当前所在地址');
return;
}
if (!this.status) {
showToast('请选择状态');
return;
}
if (!this.temperature) {
showToast('请输入当前体温');
return;
}
if (!this.agree) {
showToast('请确定是否为本人填写');
return;
}
uni.reLaunch({
url: `/pages/index/index`,
});
},
},
};
</script>
<style lang="scss" scoped>
.agree-box{
width: 70rpx;
}
.agree-text{
line-height: 60rpx;
}
.primary-btn{
border-radius: 15rpx;
}
.history-btn{
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
}
.history-btn::after{
border: none;
}
</style>

175
pages/index/components/home.vue

@ -1,175 +0,0 @@
<template>
<view class="content flex flex-direction">
<view v-if="healthCode" style="position: relative;">
<image
src="/static/img/shanda2.png"
class="bg1"
></image>
<view class="bg-code" @click="getPreviewImg(healthCode)">
<view class="healthy-code-bg shadow-lg">
<image class="healthy-code" :src="healthCode"></image>
</view>
</view>
</view>
<image
v-else
src="/static/img/shanda1.png"
class="bg"
></image>
<view :class="healthCode ? 'box1' : 'box'">
<view
@tap="openPage('/pages/apply-code/apply-code')"
class="cu-card flex margin bg-white shadow card-radius"
hover-class="cc-active"
v-show="!healthCode"
>
<view class="flex align-center padding">
<view class="round lg cu-avatar icon1">
<view class="iconfont icon-qrcode"></view>
</view>
<view class="padding-left-sm flex-sub">
<view class="text-xl">领取健康码</view>
<view class="text-df padding-top-xs description">领取健康码查看健康状态</view>
</view>
</view>
</view>
<view
@tap="openPage('/pages/punch-the-clock/punch-the-clock')"
class="cu-card flex margin bg-white shadow card-radius"
hover-class="cc-active"
>
<view class="flex align-center padding">
<view class="round lg cu-avatar icon2">
<view class="iconfont icon-carryout"></view>
</view>
<view class="padding-left-sm flex-sub">
<view class="text-xl">每日健康打卡</view>
<view class="text-df padding-top-xs description">山大人员健康情况打卡</view>
</view>
</view>
</view>
<view
@tap="openPage('/pages/add-stroke/add-stroke')"
class="cu-card flex margin bg-white shadow card-radius"
hover-class="cc-active"
>
<view class="flex align-center padding">
<view class="round lg cu-avatar icon3">
<view class="iconfont icon-car"></view>
</view>
<view class="padding-left-sm flex-sub">
<view class="text-xl">行程上报</view>
<view class="text-df padding-top-xs description">外出行程及时上报</view>
</view>
</view>
</view>
<view
@tap="openPage('/pages/statistics/statistics')"
class="cu-card flex margin bg-white shadow card-radius"
hover-class="cc-active"
>
<view class="flex align-center padding">
<view class="round lg cu-avatar icon4">
<view class="iconfont icon-linechart"></view>
</view>
<view class="padding-left-sm flex-sub">
<view class="text-xl">统计</view>
<view class="text-df padding-top-xs description">山大校园疫情统计</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'Home',
data() {
return {};
},
computed: mapState('user', ['userInfo', 'healthCode']),
methods: {
//
getPreviewImg(image) {
var imgArr = [];
imgArr.push(image);
uni.previewImage({
urls: imgArr,
current: imgArr[0],
});
},
},
};
</script>
<style scoped lang="scss">
.bg {
width: 100%;
height: 495rpx;
}
.bg1 {
width: 100%;
height: 635rpx;
}
.bg-code {
display: flex;
width: 100%;
position: absolute;
top: 370rpx;
.healthy-code-bg {
margin: 0 auto;
width: 230rpx;
height: 230rpx;
border-radius: 6px;
overflow: hidden;
background: $whiteShadow;
.healthy-code {
margin: 5rpx;
width: 220rpx;
height: 220rpx;
border-radius: 6px;
}
}
}
.box {
position: relative;
top: -100rpx;
}
.box1 {
position: relative;
top: 0;
}
.card-radius {
border-radius: 15rpx;
}
.description {
color: $grey;
}
.icon1 {
background: $iconGreen;
}
.icon2 {
background: $iconCyan;
}
.icon3 {
background: $iconBlue;
}
.icon4 {
background: $iconPurple;
}
</style>

100
pages/index/components/mine.vue

@ -1,100 +0,0 @@
<template>
<view>
<!-- 头部 -->
<view class="cu-card flex bg-white" v-if="user && user.wxInfo">
<view class="flex flex-sub align-center padding-xl">
<view class="round xl cu-avatar portrait bg-white">
<image class="xl cu-avatar" :src="user.wxInfo.headImgUrl"></image>
</view>
<view class="flex-sub padding-left">
<view class="text-xxl">{{ user.wxInfo.nickname }}</view>
<!-- <view class="text-df padding-top-xs">{{ user.wxInfo.classes }}</view> -->
</view>
<image class="healthy-code" :src="healthCode" @tap="getPreviewImg(healthCode)"></image>
</view>
</view>
<view class="cu-card flex bg-white" v-else>
<view class="flex flex-sub align-center padding-xl">
<view class="round xl cu-avatar portrait">
<image class="xl cu-avatar" :src="userInfo.avatarUrl"></image>
</view>
<view class="flex-sub padding-left">
<view class="text-xxl">{{ userInfo.nickName }}</view>
<!-- <view class="text-df padding-top-sm">{{ userInfo.classes }}</view> -->
</view>
</view>
</view>
<!-- 列表 -->
<view class="bg-white margin-top">
<view class="cu-list menu margin-lr">
<view
class="cu-item"
hover-class="cc-active"
@tap="openPage('/pages/basic-info/basic-info')"
>
<text class="text-xl">个人信息</text>
<view class="iconfont icon-right more"></view>
</view>
<view class="cu-item" hover-class="cc-active" @tap="openPage('/pages/my-code/my-code')">
<text class="text-xl">我的健康打卡</text>
<view class="iconfont icon-right more"></view>
</view>
<view class="cu-item" hover-class="cc-active" @tap="openPage('/pages/my-trips/my-trips')">
<text class="text-xl">我的行程</text>
<view class="iconfont icon-right more"></view>
</view>
<view class="cu-item" hover-class="cc-active" @tap="openPage('/pages/my-signs/my-signs')">
<text class="text-xl">我的校园打卡</text>
<view class="iconfont icon-right more"></view>
</view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'Mine',
data() {
return {
userInfo: {
avatarUrl: '/static/head-portrait.png',
nickName: '健康码',
// classes: '19',
},
};
},
computed: mapState('user', ['healthCode', 'user']),
methods: {
//
getPreviewImg(image) {
var imgArr = [];
imgArr.push(image);
uni.previewImage({
urls: imgArr,
current: imgArr[0],
});
},
},
};
</script>
<style lang="scss" scoped>
.portrait {
overflow: hidden;
}
.healthy-code {
width: 70rpx;
height: 70rpx;
}
.more {
font-size: 18px;
color: $grey;
}
</style>

78
pages/index/index.vue

@ -1,87 +1,17 @@
<template>
<view class="container">
<view class="content" style="width: 100%;">
<home v-if="navIndex === 0" />
<mine v-else />
</view>
<!-- 底部菜单 -->
<nav-bottom @change="onNavChange" @scan="onScan" class="nav-bottom"></nav-bottom>
<!-- 获取用户信息的授权组件 -->
<auth />
模板首页
<!-- 测试组件 不需要引入 注册 -->
<test />
</view>
</template>
<script>
import Home from './components/home.vue';
import Mine from './components/mine.vue';
export default {
components: { Home, Mine },
data() {
return {
navIndex: 0,
};
},
//
onShareAppMessage() {
return {
title: '健康码小程序',
path: '/pages/index/index?scene=1008',
};
},
methods: {
/**
* 监听nav-bottom的点击事件
* @param {number} index nav-bottom按钮的索引值
*/
onNavChange(index) {
this.navIndex = index;
},
//
//
onScan() {
wx.scanCode({
onlyFromCamera: true,
scanType: ['qrCode', 'wxCode'],
success: res => {
//
if (res && res.path) {
const pathArr = res.path.split('?scene=');
const query = encodeURIComponent(pathArr[1]);
const path = `/${pathArr[0]}?scene=${query}&scan=true`;
this.openPage(path);
} else {
uni.showModal({
title: '提示',
content: '您的微信版本不支持此功能, 请使用微信APP的扫码功能',
success: function(res) {},
});
}
},
});
},
},
};
</script>
<style scoped lang="scss">
.container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100vh;
.content {
flex: 1;
overflow-y: auto;
}
.nav-bottom {
width: 100%;
}
}
</style>

175
pages/my-code/my-code.vue

@ -1,175 +0,0 @@
<template>
<view>
<!-- 添加 -->
<button v-if="tableList && tableList.length === 0" class="shadow round bg-cyan add-btn iconfont icon-plus" hover-class="cc-active" @tap="openPage('/pages/apply-code/apply-code')"></button>
<button v-if="tableList && tableList.length > 0" class="shadow round bg-cyan add-btn iconfont icon-plus" hover-class="cc-active" @tap="openPage('/pages/punch-the-clock/punch-the-clock')"></button>
<view v-if="tableList && tableList.length > 0" class="cu-timeline" :key="index" v-for="(item, index) in tableList">
<view class="cu-time">{{ +item.time | formatDate }}</view>
<view class="cu-item text-blue cuIcon-locationfill">
<view class="content shadow-blur bg-blue light">{{ item.district }}{{ item.address }}</view>
</view>
<view class="cu-item text-green cuIcon-wefill">
<view class="content shadow-blur light" :class="item.healthLevel === 0 ? 'bg-green' : item.healthLevel === 1 ? 'bg-yellow' : 'bg-red'">
<view class="margin-bottom flex">
体温{{ item.animalHeat }}
<text v-for="state in status" :key="state.id">
<text v-if="state.id === item.healthTypeId">状态{{ state.name }}</text>
</text>
</view>
<view>
<text class="radius bg-red margin-tb margin-right padding-xs" v-show="item.touchHubei === 1">武汉接触史</text>
<text class="radius bg-red margin-tb margin-right padding-xs" v-show="item.touchSick === 1">患者接触史</text>
</view>
<view class="margin-tb-sm">
<text class="radius bg-red margin-tb margin-right padding-xs" v-show="item.touchOverseas === 1">境外人员接触史</text>
<text class="radius bg-green margin-tb margin-right padding-xs" v-if="item.schoolLocation === 1">在校</text>
<text class="radius bg-green margin-tb margin-right padding-xs" v-else>不在校</text>
</view>
<view class="margin-top" v-if="item.emergencyName || item.emergencyPhone">紧急联系人:{{ item.emergencyName }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ item.emergencyPhone }}</view>
<view class="margin-top" v-show="item.remark || item.filePath.length > 0">
备注:{{ item.remark }}
<view class="padding-top">
<image @tap="viewImage(item.filePath, i)" :key="i" v-for="(file, i) in item.filePath" :src="file" style="width: 69px;height: 75px;"
class="margin-lr-xs"></image>
</view>
</view>
</view>
</view>
</view>
<view v-if="tableList && tableList.length === 0" class="padding-lg">您还没有申请健康码请点击右下角加号申请健康码</view>
</view>
</template>
<script>
import {
formatDate
} from 'common/script/filters';
import {
showToast
} from 'common/script/util';
import {
HEALTH_SIGN_HISTORY
} from 'api/api';
import {
mapState,
mapMutations
} from 'vuex';
export default {
data() {
return {
tableList: []
};
},
computed: mapState('user', ['token', 'userInfo', 'status']),
//
filters: {
formatDate(time) {
var data = new Date(time);
return formatDate(data, 'MM-dd');
}
},
onLoad() {
const startTime = +this.$moment()
.startOf('year')
.format('x');
const endTime = +this.$moment()
.endOf('day')
.format('x');
const params = {
param: {
startTime,
endTime,
token: this.token
}
};
this.getHealthSignHistory(params);
},
methods: {
...mapMutations('user', ['setStatus', 'setUserInfo']),
//
async getHealthSignHistory(params) {
try {
const res = await this.$http.post(HEALTH_SIGN_HISTORY, params);
const {
success,
code,
msg,
data
} = res.data;
if (success && code === 200) {
this.success = true;
this.tableList = data;
} else {
uni.showToast({
title: msg || '获取健康打卡记录失败',
icon: 'none'
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '获取健康打卡记录失败',
icon: 'none'
});
}
}
},
//
viewImage(filePath, index) {
if (filePath && filePath.length > 0) {
uni.previewImage({
urls: filePath,
current: filePath[index],
indicator: 'number',
});
}
}
}
};
</script>
<style lang="scss" scoped>
.data-wrap {
display: flex;
flex-direction: column;
align-items: left;
justify-content: flex-start;
padding: 40rpx 0;
.data-item {
display: flex;
flex-wrap: wrap;
align-items: left;
font-size: 36rpx;
margin-bottom: 20rpx;
.data-title {
line-height: 60rpx;
color: $black;
}
}
}
.add-btn {
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
z-index: 1;
}
.add-btn::after {
border: none;
}
</style>

157
pages/my-signs/my-signs.vue

@ -1,157 +0,0 @@
<template>
<view class="wrap">
<!-- 头部菜单 -->
<view class="nav-wrap slide-bottom">
<uni-segmented-control
:active-color="activeColor"
:current="current"
:values="values"
@clickItem="handleClickNav"
class="nav"
/>
</view>
<!-- 内容区 -->
<view class="content">
<!-- 今日打卡地图显示 -->
<history-map
:markers="markers"
:polygons="polygons"
:polyline="polyline"
v-if="current === 0"
/>
<!-- 打卡记录 时间轴显示 -->
<timeline v-else />
</view>
</view>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
data() {
return {
current: 0,
values: ['今日打卡', '打卡记录'],
activeColor: '#0897C7',
};
},
computed: {
...mapState('statistics', ['userSigns']),
...mapState('user', ['token']),
//
markers() {
if (!this.userSigns) return null;
const result = [];
this.userSigns.forEach(item => {
const content = `${this.$moment(+item.time).format('YY-MM-DD HH:mm')} \n ${item.siteName}`;
const obj = {
latitude: item.latitude,
longitude: item.longitude,
iconPath: '/static/location.png',
callout: {
content,
color: '#fff',
padding: 4,
bgColor: '#0A97C6',
borderRadius: 2,
textAlign: 'center',
},
};
result.push(obj);
});
return result;
},
// 线
polyline() {
if (!this.userSigns) return null;
const points = [];
this.userSigns.forEach(item => {
const obj = {
latitude: item.latitude,
longitude: item.longitude,
};
points.push(obj);
});
return [{ points, arrowLine: true, dottedLine: true, borderColor: '#cccccc' }];
},
//
polygons() {
if (!this.userSigns) return null;
const points = [];
this.userSigns.forEach(item => {
const obj = {
latitude: item.latitude,
longitude: item.longitude,
};
points.push(obj);
});
return [
{ points, strokeWidth: 0, strokeColor: '#00000000', fillColor: '#cce6ff88', zIndex: 0 },
];
},
},
onLoad() {
const startTime = +this.$moment()
.startOf('day')
.format('x');
const endTime = +this.$moment()
.endOf('day')
.format('x');
const params = { param: { startTime, endTime, token: this.token } };
this.getUserSigns(params);
},
methods: {
...mapActions('statistics', ['getUserSigns']),
handleClickNav({ currentIndex }) {
this.current = currentIndex;
const endTime = +this.$moment()
.endOf('day')
.format('x');
const startTime = +this.$moment('2020-01-01')
.startOf('day')
.format('x');
let param = { startTime, endTime, token: this.token };
if (currentIndex === 0) {
param.startTime = +this.$moment()
.startOf('day')
.format('x');
}
const params = { param };
this.getUserSigns(params);
},
},
};
</script>
<style lang="scss" scoped>
.wrap {
padding-top: 88rpx;
min-height: 100vh;
background-color: $white;
.nav-wrap {
z-index: 999;
position: fixed;
left: 0;
right: 0;
top: 0;
display: flex;
justify-content: center;
align-items: center;
height: 88rpx;
background-color: $white;
.nav {
width: 540rpx;
}
}
}
</style>

195
pages/my-trips/my-trips.vue

@ -1,195 +0,0 @@
<template>
<view>
<!-- 添加 -->
<button class="shadow round bg-cyan add-btn iconfont icon-plus" hover-class="cc-active" @tap="openPage('/pages/add-stroke/add-stroke')"></button>
<view v-if="tableList && tableList.length>0" class="cu-timeline" :key="index" v-for="(item,index) in tableList">
<view class="cu-time">{{ +item.startTime | formatDate }}</view>
<view class="cu-item cuIcon-timefill text-blue">
<view class="content shadow-blur bg-blue light">
{{ +item.startTime | formatDate1 }} {{ +item.endTime | formatDate1 }}
</view>
</view>
<view class="cu-item cuIcon-tagfill text-green">
<view class="content shadow-blur bg-green light">
<view>
<text class="radius bg-green margin-tb padding-xs" v-if="item.journeyType === '0'">返校行程</text>
<text class="radius bg-green margin-tb padding-xs" v-if="item.journeyType === '1'">日常外出</text>
</view>
<view class="margin-bottom" v-if="item.together">
同行人{{ item.together }}
</view>
<view v-for="trip in transports" :key="trip.id" class="margin-bottom" v-if="trip.value === item.tripMode">
出行方式
{{ trip.name }}
</view>
<view>
乘坐航班车次或车牌号码及座位号
{{ item.carNo }}
</view>
</view>
</view>
</view>
<view v-if="tableList && tableList.length === 0" class="padding-lg">
您还没有添加行程请点击右下角加号添加行程
</view>
</view>
</template>
<script>
import {
formatDate
} from 'common/script/filters';
import {
showToast
} from 'common/script/util';
import {
mapState,
mapMutations
} from 'vuex';
import {
GET_JOURNEYS
} from 'api/api';
export default {
data() {
return {
tableList: [],
transports: [{
value: 0,
name: '火车',
},
{
value: 1,
name: '飞机',
},
{
value: 2,
name: '客运车辆',
},
{
value: 3,
name: '自驾',
},
{
value: 4,
name: '船',
},
{
value: 5,
name: '其他',
}
]
};
},
computed: mapState('user', ['token']),
//
filters: {
formatDate(time) {
var data = new Date(time);
return formatDate(data, 'MM-dd');
},
formatDate1(time) {
var data = new Date(time);
return formatDate(data, 'MM月dd日 hh:mm');
},
},
onLoad() {
const startTime = +this.$moment()
.startOf('year')
.format('x');
const endTime = +this.$moment()
.endOf('day')
.format('x');
const params = {
param: {
startTime,
endTime,
token: this.token
}
};
this.getJourneys(params);
},
methods: {
//
async getJourneys(params) {
try {
const res = await this.$http.post(GET_JOURNEYS, params);
const {
success,
code,
msg,
data
} = res.data;
if (success && code === 200) {
this.success = true;
this.tableList = data;
const fff = this.$moment(1583683200000).format('YY-MM-DD')
} else {
uni.showToast({
title: msg || '查询行程失败',
icon: 'none'
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '查询行程失败',
icon: 'none'
});
}
}
},
},
};
</script>
<style lang="scss" scoped>
.data-wrap {
display: flex;
flex-direction: column;
align-items: left;
justify-content: flex-start;
padding: 40rpx 0;
.data-item {
display: flex;
flex-wrap: wrap;
align-items: left;
font-size: 36rpx;
margin-bottom: 20rpx;
.data-title {
line-height: 60rpx;
color: $black;
}
}
}
.add-btn {
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
z-index: 1;
}
.add-btn::after {
border: none;
}
</style>

398
pages/privacy-aolicy/privacy-aolicy.vue

@ -1,398 +0,0 @@
<template>
<view class="margin-lr-lg">
<view class="text-xxl margin-tb-lg text-center text-bold">隐私政策</view>
<view class="margin-bottom-lg">
感谢您信任并使用传控科技的产品和服务我们根据最新的法律法规监管政策要求更新了传控科技隐私政策请您仔细阅读并充分理解以下条款
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
引言
</view>
<view>
传控科技严格遵守法律法规遵循以下隐私保护原则为您提供更加安全可靠的服务
</view>
<view class="margin-left">
<view>
1安全可靠
</view>
<view>
我们竭尽全力通过合理有效的信息安全技术及管理流程防止您的信息泄露损毁丢失
</view>
<view>
2自主选择
</view>
<view>
我们为您提供便利的信息管理选项以便您做出合适的选择管理您的个人信息
</view>
<view>
3保护通信秘密
</view>
<view>
我们严格遵照法律法规保护您的通信秘密为您提供安全的通信服务
</view>
<view>
4合理必要
</view>
<view>
为了向您和其他用户提供更好的服务我们仅收集必要的信息
</view>
<view>
5清晰透明
</view>
<view>
我们努力使用简明易懂的表述向您介绍隐私政策以便您清晰地了解我们的信息处理方式
</view>
<view>
6将隐私保护融入产品设计
</view>
<view>
我们在产品或服务开发的各个环节综合法律产品设计等多方因素融入隐私保护的理念
</view>
</view>
<view class="margin-top">
隐私政策主要向您说明
</view>
<view>
我们收集哪些信息
</view>
<view>
我们收集信息的用途
</view>
<view>
您所享有的权利
</view>
<view class="margin-top">
希望您仔细阅读隐私政策以下简称本政策详细了解我们对信息的收集使用方式以便您更好地了解我们的服务并作出适当的选择
</view>
<view class="margin-top">
若您使用传控科技服务即表示您认同我们在本政策中所述内容除另有约定外本政策所用术语与用户服务协议中的术语具有相同的涵义如您有问题请联系我们
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
我们收集的信息
</view>
<view>我们根据合法正当必要的原则仅收集实现产品功能所必要的信息</view>
<view class="margin-left">
<view>您在使用我们服务时主动提供的信息</view>
<view>您在登录系统时经授权产生的信息比如昵称头像等</view>
<view>您在使用服务时上传的信息</view>
<view>例如您在使用我们的微信小程序时上传的头像分享的照片</view>
<view>我们的部分服务可能需要您提供特定的个人敏感信息来实现特定功能若您选择不提供该类信息则可能无法正常使用服务中的特定功能但不影响您使用服务中的其他功能若您主动提供您的个人敏感信息即表示您同意我们按本政策所述目的和方式来处理您的个人敏感信息</view>
</view>
<view class="margin-top">
我们在您使用服务时获取的信息
</view>
<view>
<text class="text-bold margin-tb">日志信息</text>
<view>当您使用我们的服务时我们可能会自动收集相关信息并存储为服务日志信息</view>
</view>
<view class="margin-left">
<view>1)设备信息</view>
<view>例如设备型号操作系统版本唯一设备标识符电池信号强度等信息</view>
<view>2)软件信息</view>
<view>例如软件的版本号浏览器类型为确保操作环境的安全或提供服务所需我们会收集有关您使用的移动应用和其他软件的信息</view>
<view>3)IP地址</view>
<view>4)服务日志信息</view>
<view>例如您在使用我们服务时搜索查看的信息服务故障信息引荐网址等信息</view>
<view>5)通讯日志信息</view>
<view>例如您在使用我们服务时曾经通讯的账户通讯时间和时长</view>
</view>
<view>
<text class="text-bold margin-tb">位置信息</text>
<view>当您使用与位置有关的服务时我们可能会记录您设备所在的位置信息以便为您提供相关服务</view>
<view>在您使用服务时我们可能会通过IP地址 GPSWLAN WiFi)或基站等途径获取您的地理位置信息</view>
<view>您或其他用户在使用服务时提供的信息中可能包含您所在地理位置信息例如您提供的帐号信息中可能包含的您所在地区信息您或其他人共享的照片包含的地理标记信息</view>
</view>
<view>
<text class="text-bold margin-tb">其他相关信息</text>
<view>为了帮助您更好地使用我们的产品或服务我们会收集相关信息例如我们收集的好友列表群列表信息声纹特征值信息为确保您使用我们服务时能与您认识的人进行联系如您选择开启导入通讯录功能我们可能对您联系人的姓名和电话号码进行加密并仅收集加密后的信息</view>
</view>
<view class="margin-top">
其他用户分享的信息中含有您的信息
</view>
<view class="text-sm">
例如其他用户发布的照片或分享的视频中可能包含您的信息
</view>
<view class="margin-top">
从第三方合作伙伴获取的信息
</view>
<view class="text-sm">
我们可能会获得您在使用第三方合作伙伴服务时所产生或分享的信息例如您使用微信或QQ帐户登录第三方合作伙伴服务时我们会获得您登录第三方合作伙伴服务的名称登录时间方便您进行授权管理请您仔细阅读第三方合作伙伴服务的用户协议或隐私政策
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
我们如何使用收集的信息
</view>
<view>
我们严格遵守法律法规的规定及与用户的约定将收集的信息用于以下用途若我们超出以下用途使用您的信息我们将再次向您进行说明并征得您的同意
</view>
<view class="margin-left">
<view>
向您提供服务
</view>
<view>
满足您的个性化需求
</view>
<view class="text-sm">
例如语言设定位置设定个性化的帮助服务
</view>
<view>
产品开发和服务优化
</view>
<view class="text-sm">
例如当我们的系统发生故障时我们会记录和分析系统故障时产生的信息优化我们的服务
</view>
<view>
安全保障
</view>
<view class="text-sm">
例如我们会将您的信息用于身份验证安全防范反诈骗监测存档备份客户的安全服务等用途例如您下载或安装的安全软件会对恶意程序或病毒进行检测或为您识别诈骗信息
</view>
<view>
向您推荐您可能感兴趣的广告资讯等
</view>
<view>
评估改善我们的广告投放和其他促销及推广活动的效果
</view>
<view>
管理软件
</view>
<view class="text-sm">
例如进行软件认证软件升级等
</view>
<view>
邀请您参与有关我们服务的调查
</view>
</view>
<view>
为了让您有更好的体验改善我们的服务或经您同意的其他用途在符合相关法律法规的前提下我们可能将通过某些服务所收集的信息用于我们的其他服务例如将您在使用我们某项服务时的信息用于另一项服务中向您展示个性化的内容或广告用于用户研究分析与统计等服务
</view>
<view>
为了确保服务的安全帮助我们更好地了解我们应用程序的运行情况我们可能记录相关信息例如您使用应用程序的频率故障信息总体使用情况性能数据以及应用程序的来源我们不会将我们存储在分析软件中的信息与您在应用程序中提供的个人身份信息相结合
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
您分享的信息
</view>
<view>
您可以通过我们的服务与您的好友家人及其他用户分享您的相关信息例如您在微信朋友圈中公开分享的文字和照片
</view>
<view>
请注意这其中可能包含您的个人身份信息个人财产信息等敏感信息请您谨慎考虑披露您的相关个人敏感信息
</view>
<view>
您可通过我们服务中的隐私设置来控制您分享信息的范围也可通过服务中的设置或我们提供的指引删除您公开分享的信息但请您注意这些信息仍可能由其他用户或不受我们控制的非关联第三方独立地保存
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
您如何管理自己的信息
</view>
<view>
您可以在使用我们服务的过程中访问修改和删除您提供的注册信息和其他个人信息也可按照通知指引与我们联系您访问修改和删除个人信息的范围和方式将取决于您使用的具体服务
</view>
<view>
例如若您在使用地理位置相关服务时希望停止分享您的地理位置信息您可通过手机定位关闭功能软硬件服务商及通讯服务提供商的关闭方式停止分享建议您仔细阅读相关指引
</view>
<view>
我们将按照本政策所述仅为实现我们产品或服务的功能收集使用您的信息
</view>
<view>
如您发现我们违反法律行政法规的规定或者双方的约定收集使用您的个人信息您可以要求我们删除
</view>
<view>
如您发现我们收集存储的您的个人信息有错误的您也可以要求我们更正
</view>
<view>
请通过本政策列明的联系方式与我们联系
</view>
<view>
在您访问修改和删除相关信息时我们可能会要求您进行身份验证以保障帐号的安全
</view>
<view>
请您理解由于技术所限法律或监管要求我们可能无法满足您的所有要求我们会在合理的期限内答复您的请求
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
我们分享的信息
</view>
<view>
我们遵照法律法规的规定对信息的分享进行严格的限制例如
</view>
<view>
<view>
经您事先同意我们可能与第三方分享您的个人信息
</view>
<view>
仅为实现外部处理的目的我们可能会与第三方合作伙伴第三方服务供应商承包商代理广告合作伙伴应用开发者等例如代表我们发出电子邮件或推送通知的通讯服务提供商为我们提供位置服务的地图服务供应商他们可能并非位于您所在的法域分享您的个人信息让他们按照我们的说明隐私政策以及其他相关的保密和安全措施来为我们处理上述信息并用于以下用途
<view class="text-sm margin-left">
向您提供我们的服务
</view>
<view class="text-sm margin-left">
实现我们如何使用收集的信息部分所述目的
</view>
<view class="text-sm margin-left">
履行我们在用户服务协议或本政策中的义务和行使我们的权利
</view>
<view class="text-sm margin-left">
理解维护和改善我们的服务
</view>
</view>
<view>
如我们与上述第三方分享您的信息我们将会采用加密匿名化处理等手段保障您的信息安全
</view>
<view>
随着我们业务的持续发展当发生合并收购资产转让等交易导致向第三方分享您的个人信息时我们将通过推送通知公告等形式告知您相关情形按照法律法规及不低于本政策所要求的标准继续保护或要求新的管理者继续保护您的个人信息
</view>
<view>
我们会将所收集到的信息用于大数据分析
<view class="text-sm">
例如我们将收集到的信息用于分析形成不包含任何个人信息的城市热力图或行业洞察报告
</view>
<view class="text-sm">
我们可能对外公开并与我们的合作伙伴分享经统计加工后不含身份识别内容的信息用于了解用户如何使用我们服务或让公众了解我们服务的总体使用趋势
</view>
</view>
<view>
我们可能基于以下目的披露您的个人信息
<view class="text-sm margin-left">
遵守适用的法律法规等有关规定
</view>
<view class="text-sm margin-left">
遵守法院判决裁定或其他法律程序的规定
</view>
<view class="text-sm margin-left">
遵守相关政府机关或其他法定授权组织的要求
</view>
<view class="text-sm margin-left">
我们有理由确信需要遵守法律法规等有关规定
</view>
<view class="text-sm margin-left">
为执行相关服务协议或本政策维护社会公共利益为保护我们的客户我们或我们的关联公司其他用户或雇员的人身财产安全或其他合法权益合理且必要的用途
</view>
</view>
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
我们可能向您发送的信息
</view>
<view>
信息推送
</view>
<view class="text-sm">
您在使用我们的服务时我们可能向您发送电子邮件短信资讯或推送通知
</view>
<view class="text-sm">
您可以按照我们的相关提示在设备上选择取消订阅
</view>
<view>
与服务有关的公告
</view>
<view class="text-sm">
我们可能在必要时例如因系统维护而暂停某一项服务时向您发出与服务有关的公告
</view>
<view class="text-sm">
您可能无法取消这些与服务有关性质不属于广告的公告
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
信息安全
</view>
<view>
我们为您的信息提供相应的安全保障以防止信息的丢失不当使用未经授权访问或披露
</view>
<view class="margin-left">
<view>
我们严格遵守法律法规保护用户的通信秘密
</view>
<view>
我们将在合理的安全水平内使用各种安全保护措施以保障信息的安全
</view>
<view class="text-sm">
例如我们使用加密技术例如TLSSSL匿名化处理等手段来保护您的个人信息
</view>
<view>
我们建立专门的管理制度流程和组织确保信息安全
</view>
<view class="text-sm">
例如我们严格限制访问信息的人员范围要求他们遵守保密义务并进行审查
</view>
<view>
若发生个人信息泄露等安全事件我们会启动应急预案阻止安全事件扩大并以推送通知公告等形式告知您
</view>
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
广告
</view>
<view>
我们可能使用您的相关信息在相关网站应用及其他渠道向您提供与您更加相关的广告
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
适用范围
</view>
<view>
我们的所有服务均适用本政策
</view>
<view class="margin-left">
<view>
某些服务有其特定的隐私指引/声明该特定隐私指引/声明更具体地说明我们在该服务中如何处理您的信息
</view>
<view>
如本政策与特定服务的隐私指引/声明有不一致之处请以该特定隐私指引声明为准
</view>
<view>
请您注意本政策不适用由其他公司或个人提供的服务
</view>
<view class="text-sm">
例如您通过使用微信帐号登录其他公司或个人提供的服务
</view>
<view>
您使用该等第三方服务须受其隐私政策而非本政策约束您需要仔细阅读其政策内容
</view>
</view>
</view>
<view class="text-content margin-bottom-xl">
<view class="text-xl text-bold">
联系我们
</view>
<view>
如您对本政策或其他相关事宜有疑问请通过 https://www.ccsens.com
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
}
</script>

559
pages/punch-the-clock/punch-the-clock.vue

@ -1,559 +0,0 @@
<template>
<view>
<form class="padding-lr cu-form-group flex-direction">
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs">*</span>当前所在地区
</view>
<view class="flex align-center">
<input @tap="handleSelectLocation" class="flex-sub padding-left-xs" name="input" placeholder="请选择当前所在地区" type="btn"
v-model="district" maxlength="128" />
<text class="cuIcon-location timer"></text>
</view>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs">*</span>当前所在详细地址
</view>
<input name="input" placeholder="请输入详细地址(含门牌号)" type="text" v-model="address" maxlength="128" />
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm">
<span class="text-red padding-right-xs">*</span>当前状态
</view>
<radio-group @change="StateChange" class="block">
<view class="cu-list menu text-left">
<view :key="index" class="cu-item" v-for="(state,index) in status">
<label class="flex justify-between align-center">
<radio :checked="index === healthTypeId" :value="state.id" class="round margin-right-xs"></radio>
<view class="flex-sub" style="font-size: 34rpx;">{{ state.name }}</view>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">就诊医院若无填无</view>
<input name="input" placeholder="请输入就诊医院" type="text" v-model="hospital" maxlength="32" />
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>最近14天是否有武汉居住史旅游史或武汉亲戚来访</view>
<radio-group class="block" @change="TourChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(tour,index) in tours" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === touchHubei" :value="tour.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ tour.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>最近14天是否有新冠肺炎患者或疑似患者接触史</view>
<radio-group class="block" @change="TouchChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(touch,index) in touches" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === touchSick" :value="touch.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ touch.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>最近14天有无接触过近期境外返回人员</view>
<radio-group class="block" @change="TouchOverseasChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(touch,index) in overseas" :key="index">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === touchOverseas" :value="touch.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ touch.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-top">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>是否在学校所在地</view>
<radio-group class="block" @change="SchoolLocationChange">
<view class="flex">
<view class="flex-sub margin-tb-sm" v-for="(location,index) in locations" :key="location.value">
<label class="flex justify-between align-center">
<radio class="round margin-right-xs" :checked="index === schoolLocation" :value="location.value"></radio>
<text class="flex-sub" style="font-size: 34rpx;">{{ location.name }}</text>
</label>
</view>
</view>
</radio-group>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm"><span class="text-red padding-right-xs">*</span>当前体温</view>
<input placeholder="0" name="input" type="digit" v-model="animalHeat" />
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">紧急联系人</view>
<view class="flex">
<input placeholder="姓名" maxlength="6" class="flex flex-sub" name="input" type="text" v-model="emergencyName" />
<input placeholder="联系方式" maxlength="11" class="flex flex-sub" name="input" type="number" v-model="emergencyPhone" />
</view>
</view>
<view class="cu-form-group flex flex-direction padding-tb">
<view class="title padding-bottom-sm">备注选填</view>
<textarea placeholder="请填写具体情况..." name="input" v-model="remark" />
<!-- 上传图片 -->
<view class="grid col-4 grid-square flex-sub">
<view class="bg-img" v-for="(item,index) in imgList" :key="index" @tap="ViewImage" :data-url="imgList[index]">
<image :src="imgList[index]" mode="aspectFill"></image>
<view class="cu-tag bg-red" @tap.stop="DelImg" :data-index="index">
<text class='cuIcon-close'></text>
</view>
</view>
<view class="solids" @tap="ChooseImage" v-if="imgList.length<9">
<text class='cuIcon-cameraadd'></text>
</view>
</view>
</view>
</form>
<user-agreement @changeIntentions="changeIntentions" @changeIntentions1="changeIntentions1" @changeIntentions2="changeIntentions2"></user-agreement>
<button class="bg-cyan margin primary-btn" hover-class="cc-active" @tap="handleHealthSign">确认提交</button>
<!-- 历史 -->
<button class="shadow round bg-cyan history-btn iconfont icon-history" hover-class="cc-active" @tap="openPage('/pages/my-code/my-code')"></button>
</view>
</template>
<script>
import {
showToast
} from 'common/script/util';
import {
HEALTH_SIGN,HEALTH_FILE,HEALTH_SIGN_HISTORY
} from 'api/api';
import {
mapState,
mapMutations
} from 'vuex';
export default {
data() {
return {
district: '请选择当前所在地区',
address: '',
// status: [],
hospital: '',
tours: [{
value: '0',
name: '否'
}, {
value: '1',
name: '是'
}],
touches: [{
value: '0',
name: '否'
}, {
value: '1',
name: '是'
}],
overseas: [{
value: '0',
name: '无'
}, {
value: '1',
name: '有'
}],
locations: [{
value: '0',
name: '否'
}, {
value: '1',
name: '是'
}],
animalHeat: '',
healthTypeId: 0,
touchHubei: 0,
touchSick: 0,
touchOverseas: 0,
schoolLocation: 1,
emergencyName: '',
emergencyPhone: '',
remark: '',
fileIdList: [],
imgList: [],
agree: false,
agree1: false,
agree2: false,
};
},
onLoad() {
const startTime = +this.$moment()
.startOf('year')
.format('x');
const endTime = +this.$moment()
.endOf('day')
.format('x');
const params = {
param: {
startTime,
endTime,
token: this.token
}
};
this.getHealthSignHistory(params);
},
computed: mapState('user', ['token', 'status','healthyInfo']),
methods: {
...mapMutations('user', ['setHealthCode','setHealthyInfo']),
handleSelectLocation() {
const that = this;
uni.chooseLocation({
success: function(res) {
console.log('位置名称:' + res.name);
console.log('详细地址:' + res.address);
that.district = res.address;
},
});
},
changeIntentions(data) {
this.agree = data;
},
changeIntentions1(data) {
this.agree1 = data;
},
changeIntentions2(data) {
this.agree2 = data;
},
//
StateChange: function(evt) {
for (let i = 0; i < this.status.length; i++) {
if (this.status[i].id === evt.target.value) {
this.healthTypeId = i;
break;
}
}
},
//
TourChange: function(evt) {
for (let i = 0; i < this.tours.length; i++) {
if (this.tours[i].value === evt.target.value) {
this.touchHubei = i;
break;
}
}
},
//
TouchChange(evt) {
for (let b = 0; b < this.touches.length; b++) {
if (this.touches[b].value === evt.target.value) {
this.touchSick = b;
break;
}
}
},
//
TouchOverseasChange(evt) {
for (let i = 0; i < this.overseas.length; i++) {
if (this.overseas[i].value === evt.target.value) {
this.touchOverseas = i;
break;
}
}
},
//
SchoolLocationChange(evt) {
for (let i = 0; i < this.locations.length; i++) {
if (this.locations[i].value === evt.target.value) {
this.schoolLocation = i;
break;
}
}
},
ChooseImage() {
uni.chooseImage({
count: 1, //9
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success: (res) => {
if (this.imgList.length != 0) {
this.imgList = this.imgList.concat(res.tempFilePaths)
} else {
this.imgList = res.tempFilePaths
}
uni.uploadFile({
usrl: `https://www.tall.wiki/gateway${HEALTH_FILE}`,
// url: `https://test.tall.wiki/gateway${HEALTH_FILE}`,
filePath: res.tempFilePaths[0],
fileType: 'image',
name: 'file',
success: (res) => {
const resData = JSON.parse(res.data)
const {
success,
code,
msg,
data
} = resData;
this.fileIdList.splice(-1,0,resData.data.fileId);
uni.showToast({
title: '图片提交成功',
duration: 1000,
});
},fail: (err) => {
console.log('uploadImage fail', err);
uni.showModal({
content: err.errMsg,
showCancel: false
});
}
});
},
});
},
ViewImage(e) {
uni.previewImage({
urls: this.imgList,
current: e.currentTarget.dataset.url
});
},
DelImg(e) {
uni.showModal({
title: '删除',
content: '确定要删除这张图片吗?',
cancelText: '再想想',
confirmText: '再见',
success: res => {
if (res.confirm) {
this.imgList.splice(e.currentTarget.dataset.index, 1)
this.fileIdList.splice(e.currentTarget.dataset.index, 1)
}
}
})
},
/**
* 申请健康码
*/
async handleHealthSign() {
try {
if (!this.checkRules()) return;
const {
address,
animalHeat,
district,
healthTypeId,
hospital,
token,
touchHubei,
touchSick,
touchOverseas,
schoolLocation,
emergencyName,
emergencyPhone,
remark,
fileIdList,
agree1,
agree2,
} = this;
const params = {
param: {
address,
animalHeat,
district,
healthTypeId: healthTypeId + 1,
hospital,
token,
touchHubei,
touchSick,
touchOverseas,
schoolLocation,
emergencyName,
emergencyPhone,
remark,
fileIdList,
healthAgreement: agree1 ? 1 : 0,
selfFill: agree2 ? 1 : 0,
}
};
const res = await this.$http.post(HEALTH_SIGN, params);
const {
success,
code,
msg,
data
} = res.data;
if (success && code === 200) {
uni.showToast({
title: '申请健康码成功',
duration: 2000
});
this.success = true;
this.setHealthCode(data.healthCode)
uni.reLaunch({
url: `/pages/index/index`,
});
} else {
uni.showToast({
title: msg || '申请健康码失败',
icon: 'none'
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '申请健康码失败',
icon: 'none'
});
}
}
},
//
checkRules() {
const {
district,
address,
healthTypeId,
animalHeat,
agree,
agree1,
agree2
} = this;
if (!district || district === '请选择当前所在地区') {
showToast('请选择当前所在地区');
return;
}
if (!address) {
showToast('请输入当前所在地址');
return;
}
if (healthTypeId < 0) {
showToast('请选择状态');
return;
}
if (!animalHeat || animalHeat<30 || animalHeat>45) {
showToast('请输入正确的体温值(范围:30℃~45℃)');
return;
}
if (!agree) {
showToast('请选择是否同意《用户服务协议》和《隐私政策》');
return;
}
if (!agree1) {
showToast('请选择是否同意信息提交山西大学管理');
return;
}
if (!agree2) {
showToast('请确定是否为本人填写');
return;
}
return true;
},
/**
* 验证手机号格式
* @param {string} phone 手机号
*/
verifyPhone(phone) {
const phoneExg = /^1\d{10}$/;
return phoneExg.test(phone);
},
//
async getHealthSignHistory(params) {
try {
const res = await this.$http.post(HEALTH_SIGN_HISTORY, params);
const {
success,
code,
msg,
data
} = res.data;
if (success && code === 200) {
this.success = true;
this.setHealthyInfo(data[0]);
if(this.healthyInfo){
this.address = this.healthyInfo.address;
this.district = this.healthyInfo.district;
this.healthTypeId = this.healthyInfo.healthTypeId - 1;
this.hospital = this.healthyInfo.hospital;
this.touchHubei = this.healthyInfo.touchHubei;
this.touchSick = this.healthyInfo.touchSick;
this.touchOverseas = this.healthyInfo.touchOverseas;
this.schoolLocation = this.healthyInfo.schoolLocation;
this.emergencyName = this.healthyInfo.emergencyName;
this.emergencyPhone = this.healthyInfo.emergencyPhone;
}
} else {
uni.showToast({
title: msg || '获取健康打卡记录失败',
icon: 'none'
});
}
} catch (error) {
console.log('error: ', error);
if (error.msg) {
uni.showToast({
title: error.msg || '获取健康打卡记录失败',
icon: 'none'
});
}
}
},
},
};
</script>
<style lang="scss" scoped>
.agree-box {
width: 70rpx;
}
.agree-text {
line-height: 60rpx;
}
.timer {
font-size: 34rpx !important;
color: $gray;
}
.primary-btn {
border-radius: 15rpx;
}
.history-btn {
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
}
.history-btn::after {
border: none;
}
</style>

79
pages/service-agreement/service-agreement.vue

@ -1,79 +0,0 @@
<template>
<view class="margin-lr-lg">
<view class="text-xxl margin-tb-lg text-center text-bold">用户服务协议</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
特别提示
</view>
<view>
在此特别提醒您用户在使用tall健康码之前请认真阅读本tall健康码用户服务协议 以下简称协议确保您充分理解本协议中各条款请您审慎阅读并选择接受或不接 受本协议除非您接受本协议所有条款否则您无权注册登录或使用本协议所涉服务您的注册登录使用等行为将视为对本协议的接受并同意接受本协议各项条款的约束
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
账号注册
</view>
<view>1tall健康码以微信小程序方式提供服务不需要用户刻意注册我们会通过调用微信接口自动使用您在微信平台的身份信息</view>
<view>2本系统面向特定用户群体在登录系统后需要您填写在该群体下的特定用户信息tall健康码需要搜集能识别用户身份的个人信息以便传控科技可以在必要时联系用户或为用户提供更好的使用体验</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
账户安全
</view>
<view>
1用户一旦注册/登录成功成为tall健康码的用户传控科技会尽最大限度保证用户的账户信息安全
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
服务内容
</view>
<view>
1上报健康信息
</view>
<view>
2查询疫情情况
</view>
<view>
3查询个人打卡轨迹
</view>
</view>
<view class="text-content margin-bottom">
<view class="text-xl text-bold">
服务的终止
</view>
<view>
1在下列情况下传控科技有权终止向用户提供服务
</view>
<view>
1在用户违反本服务协议相关规定时传控科技有权终止向该用户提供服务
</view>
<view>
2用户不得通过程序或人工方式进行恶意注册若发现用户有该类行为传控科技将立即终止服务并有权扣留账户内金额
</view>
</view>
<view class="text-content margin-bottom-xl">
<view class="text-xl text-bold">
免责与赔偿声明
</view>
<view>
1请用户在使用过程中对自己的账号密码妥善保管不要告知他人避免给您带来不必要的损失
</view>
<view>
2本协议最终解释权归安庆公共交通有限公司简称传控科技所有
</view>
<view>
3本协议从 2020年3月1日起适用
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
}
</script>

143
pages/sign/sign.vue

@ -1,143 +0,0 @@
<template>
<view class="padding-top-lg">
<template v-if="!success">
<view class="text-xxl padding text-center margin-top-xl">
<text class="text-black text-bold">{{ site.siteName }}</text>
</view>
<button @tap="handleSign(siteId)" class="cu-btn lg bg-purple margin sign-btn">{{ typeText }}</button>
</template>
<view class="success" v-else>
<view class="cuIcon-roundcheckfill text-green"></view>
<text class="text-black margin-top">打卡成功</text>
<button @tap="goHome" class="cu-btn lg bg-purple margin-lg sign-btn">返回首页</button>
</view>
</view>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { formatQuery } from 'utils/util';
import { showToast, showLoading, hideLoading, showModal } from 'utils/ui';
import { SCAN_SIGN } from 'api/api';
export default {
name: 'SignScan',
data() {
return {
siteId: '',
siteName: '',
success: false,
type: 0, // 0 / 1
timer: null,
latitude: '',
longitude: '',
};
},
computed: {
...mapState('site', ['site']),
...mapState('user', ['token']),
typeText() {
return this.type === 0 ? '进场打卡' : '出场打卡';
},
},
onLoad(options) {
this.getLocation();
this.init(options);
},
methods: {
...mapActions('site', ['sign', 'getSiteByQrId']),
init(options) {
try {
const query = formatQuery(decodeURIComponent(options.scene));
const { d, t } = query;
if (!d || !t) {
uni.showToast({
title: '二维码参数错误',
icon: 'none',
duration: 3000,
});
}
this.siteId = d;
this.type = +t;
this.getSignInfo({ param: { id: d, type: t } });
} catch (error) {
console.log('error: ', error);
}
},
/**
* 获取场所的基本信息
* @param {object} params 提交给后端的参数
*/
getSignInfo(params) {
this.timer && clearInterval(this.timer);
if (!this.token) {
this.timer = setTimeout(() => {
this.getSignInfo(params);
}, 100);
} else {
this.getSiteByQrId(params);
}
},
//
// mapgcj02
getLocation() {
showLoading();
uni.getLocation({
// type: 'gcj02',
success: res => {
this.longitude = res.longitude;
this.latitude = res.latitude;
hideLoading();
console.log('当前位置的经度:' + res.longitude);
console.log('当前位置的纬度:' + res.latitude);
},
fail: err => {
showModal('获取定位失败, 请打开GPS后重试');
console.error(err);
},
});
},
/**
* 扫码打卡
* @param {string} siteId 场所id
*/
async handleSign(siteId) {
try {
if (!this.latitude || !this.longitude) {
showToast('位置信息有误, 请打开定位后重试');
}
const params = {
param: { siteId, locationLatitude: this.latitude, locationLongitude: this.longitude },
};
await this.sign(params);
this.success = true;
} catch (error) {
console.log('error: ', error);
}
},
},
};
</script>
<style lang="scss" scoped>
.sign-btn {
display: flex;
}
.success {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 100rpx;
.cuIcon-roundcheckfill {
font-size: 200rpx;
}
}
</style>

124
pages/statistics/components/date-selector.vue

@ -1,124 +0,0 @@
<template>
<view class="card radius shadow-warp bg-white">
<view @tap="$refs.calendar.open()" class="card-head solid-bottom" hover-class="cc-active">
<view class="card-head-avatar bg-orange">
<view class="cuIcon-calendar"></view>
</view>
<view class="card-head-title">选择时间</view>
<view class="card-head-action" style="letter-spacing: 0.6">{{ date }}</view>
</view>
<view class="menus-wrap">
<button
:class="[ menu === item ? 'bg-purple': 'line-gray' ]"
:key="index"
@tap="handleClickMenu(item)"
class="cu-btn round"
v-for="(item, index) in menus"
>{{ item }}</button>
</view>
<uni-calendar
:insert="false"
:range="true"
:show-month="true"
@confirm="handleChange"
ref="calendar"
/>
</view>
</template>
<script>
const menus = ['昨天', '今天', '近7天', '近30天'];
export default {
name: 'DateSelector',
data() {
const start = this.$moment().format('YYYY-MM-DD');
const end = this.$moment().format('YYYY-MM-DD');
return {
start,
end,
menu: '今天',
menus,
};
},
computed: {
date() {
const { start, end } = this;
return start === end ? start : `${start} - ${end}`;
},
},
methods: {
/**
* 日历确认选择了时间段
* @param {object} value 日历返回对象
*/
handleChange(value) {
const { before, after } = value.range;
//
//
if (before && !after) {
this.start = before;
this.end = before;
} else if (!before && after) {
this.end = after;
this.start = after;
} else if (before && after) {
this.start = before;
this.end = after;
}
this.menu = '';
this.$emit('change', this.start, this.end);
},
/**
* 点选了快捷菜单
* @param {string} menu 快捷时间菜单字符串
*/
handleClickMenu(menu) {
this.menu = menu;
let start = this.$moment().format('YYYY-MM-DD');
let end = this.$moment().format('YYYY-MM-DD');
switch (menu) {
case '昨天':
start = this.$moment()
.subtract(1, 'days')
.format('YYYY-MM-DD');
end = this.$moment()
.subtract(1, 'days')
.format('YYYY-MM-DD');
break;
case '近7天':
start = this.$moment()
.subtract(6, 'days')
.format('YYYY-MM-DD');
end = this.$moment().format('YYYY-MM-DD');
break;
case '近30天':
start = this.$moment()
.subtract(29, 'days')
.format('YYYY-MM-DD');
end = this.$moment().format('YYYY-MM-DD');
break;
default:
break;
}
this.start = start;
this.end = end;
this.$emit('change', start, end);
},
},
};
</script>
<style lang="scss" scoped>
.menus-wrap {
display: flex;
align-items: center;
justify-content: space-around;
padding: 40rpx;
}
</style>

90
pages/statistics/components/health-data.vue

@ -1,90 +0,0 @@
<template>
<!-- 校园轨迹组件 -->
<view class="card radius shadow-warp bg-white">
<view class="card-head solid-bottom">
<view class="card-head-avatar bg-green">
<view class="iconfont icon-wenduji_thermometer"></view>
</view>
<view class="card-head-title">健康上报</view>
<!-- <view class="card-head-action">icon</view> -->
</view>
<view class="card-content">
<view class="data-wrap">
<view :key="index" class="data-item" v-for="(item, index) in shoolSignNumber">
<view :class="[generateColor(item.name)]" class="data-text">{{ item.number }}</view>
<view class="data-title">{{ item.name }}</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'HealthData',
data() {
return {};
},
computed: mapState('statistics', ['shoolSignNumber']),
methods: {
/**
* 生成文本的颜色
* @param {string} name 类目名称
* @returns {string} color 颜色string
*/
generateColor(name) {
let color = 'text-green';
switch (name) {
case '发烧':
color = 'text-red';
break;
case '其他':
color = 'text-purple';
break;
case '未上报':
color = 'text-orange';
break;
default:
color = 'text-green';
break;
}
return color;
},
},
};
</script>
<style lang="scss" scoped>
.data-wrap {
display: flex;
align-items: center;
justify-content: space-around;
padding: 40rpx 0;
.data-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
.data-text {
font-size: 18px;
font-weight: 600;
margin-bottom: 4px;
}
.data-title {
font-size: 13px;
color: $grey;
}
}
}
.icon-wenduji_thermometer {
font-size: 14px;
}
</style>

57
pages/statistics/components/location-map.vue

@ -1,57 +0,0 @@
<template>
<!-- 校园轨迹组件 -->
<view class="card radius shadow-warp bg-white">
<view class="card-head solid-bottom">
<view class="card-head-avatar bg-purple">
<view class="cuIcon-circle"></view>
</view>
<view class="card-head-title">校园打卡</view>
<!-- <view class="card-head-action">icon</view> -->
</view>
<view class="card-content">
<view class="map-wrap">
<history-map :markers="markers" />
</view>
</view>
</view>
</template>
<script>
import { mapState } from 'vuex';
export default {
name: 'LocationMap',
computed: {
...mapState('statistics', ['schoolSigns']),
//
markers() {
if (!this.schoolSigns) return null;
const result = [];
this.schoolSigns.forEach(item => {
const content = `${item.siteName}\n${item.number}`;
const obj = {
id: Math.random(),
latitude: item.latitude,
longitude: item.longitude,
iconPath: '/static/location.png',
callout: {
content,
display: 'ALWAYS',
color: '#fff',
padding: 4,
bgColor: '#0A97C6',
borderRadius: 2,
textAlign: 'center',
},
};
result.push(obj);
});
return result;
},
},
};
</script>

80
pages/statistics/statistics.vue

@ -1,80 +0,0 @@
<template>
<view class="padding">
<!-- 日期组件 -->
<date-selector @change="getData" />
<!-- 健康上报组件 -->
<health-data />
<!-- 校园轨迹组件 -->
<location-map />
</view>
</template>
<script>
import { mapActions } from 'vuex';
import DateSelector from './components/date-selector';
import HealthData from './components/health-data';
import LocationMap from './components/location-map';
export default {
name: 'Statistics',
components: { DateSelector, LocationMap, HealthData },
onLoad() {
//
const start = this.$moment().format('YYYY-MM-DD');
const end = this.$moment().format('YYYY-MM-DD');
this.getData(start, end);
},
methods: {
...mapActions('statistics', ['getSchoolSigns', 'getSchoolSignsNumber']),
/**
* 获取数据
* @param {string} start 开始时间 yy-mm-dd
* @param {string} end 截止时间 yy-mm-dd
*/
getData(start, end) {
const startTime =
this.$moment(start)
.startOf('day')
.format('x') - 0;
const endTime =
this.$moment(end)
.endOf('day')
.format('x') - 0;
//
this.getSchoolSignsData(startTime, endTime);
//
this.getSchoolSignsNumberData(startTime, endTime);
},
/**
* 获取校园打卡的数据
* @param {number} startTime 开始时间
* @param {number} endTime 截止时间
*/
getSchoolSignsData(startTime, endTime) {
try {
const params = { param: { startTime, endTime } };
this.getSchoolSigns(params);
} catch (error) {
console.log('getSchoolSignsData error: ', error);
}
},
/**
* 获取健康上报的数目数据
* @param {number} startTime 开始时间
* @param {number} endTime 截止时间
*/
getSchoolSignsNumberData(startTime, endTime) {
try {
const params = { param: { startTime, endTime } };
this.getSchoolSignsNumber(params);
} catch (error) {
console.log('getSchoolSignsNumberData error: ', error);
}
},
},
};
</script>

196
pages/user-code/user-code.vue

@ -1,196 +0,0 @@
<template>
<view class="padding-lg">
<template v-if="userInfo">
<view class="padding bg-purple shadow-blur radius margin-bottom-xl">
<view>
<view class="text-xl text-bold margin-bottom">个人申报信息</view>
<view class="text-lg text-grey">
<text class="margin-right">{{ userInfo.name }}</text>
<text>{{ post }}</text>
</view>
<!-- <view class="text-lg text-grey">
身份证
<text>{{ userInfo.idCard }}</text>
</view> -->
<view class="text-lg text-grey">
手机号
<text>{{ userInfo.phone }}</text>
</view>
<view class="text-lg text-grey">
学号
<text>{{ userInfo.no }}</text>
</view>
<view class="text-lg text-grey">
登记时间
<text>{{ time }}</text>
</view>
<view class="text-lg text-grey">
健康状态
<text>{{ level }}</text>
</view>
</view>
</view>
<view class="padding-top-xl">
<image
:src="userInfo.healthCodeList[0].healthCode"
class="img solid radius"
mode="aspectFit"
/>
</view>
<!-- 返回首页 -->
<button
@tap="openPage('/pages/index/index')"
class="shadow round bg-purple index-btn iconfont icon-home"
hover-class="cc-active"
>首页</button>
</template>
</view>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { formatQuery } from 'utils/util';
export default {
data() {
return {
userInfo: null,
// userInfo: {
// healthCodeList: [
// {
// healthCode: 'https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/gh_33446d7f7a26_430.jpg',
// healthLevel: 0,
// time: Date.now(),
// },
// ],
// id: '123',
// idCard: '1407241989****019X',
// no: '080800009',
// phone: '18603454788',
// post: 0,
// name: '',
// },
timer: null,
};
},
computed: {
...mapState('user', ['token']),
//
post() {
if (!this.userInfo) return '学生';
let str = '学生';
switch (this.userInfo.post) {
case 0:
str = '学生';
break;
case 1:
str = '老师';
break;
case 2:
str = '工作人员';
break;
default:
break;
}
return str;
},
//
level() {
if (!this.userInfo || !this.userInfo.healthCodeList) return '正常';
let str = '正常';
switch (this.userInfo.healthCodeList[0].healthLevel) {
case 0:
str = '正常';
break;
case 1:
str = '隔离中或疑似';
break;
case 2:
str = '确诊';
break;
default:
break;
}
return str;
},
time() {
if (!this.userInfo || !this.userInfo.healthCodeList) return;
return this.$moment(this.userInfo.healthCodeList[0].time - 0).format('YYYY-MM-DD HH:mm');
},
},
onLoad(options) {
this.init(options);
},
methods: {
...mapActions('user', ['getUserInfo']),
init(options) {
try {
console.log('options: ', options);
const query = formatQuery(decodeURIComponent(options.scene));
const { d } = query;
if (!d) {
uni.showToast({
title: '二维码参数错误',
icon: 'none',
duration: 3000,
});
}
const params = { param: { userId: d } };
this.getUserInfoData(params);
} catch (error) {
console.log('error: ', error);
}
},
/**
* 获取场所的基本信息
* @param {object} params 提交给后端的参数
*/
async getUserInfoData(params) {
this.timer && clearInterval(this.timer);
if (!this.token) {
this.timer = setTimeout(() => {
this.getUserInfoData(params);
}, 100);
} else {
this.userInfo = await this.getUserInfo(params);
if (!this.userInfo.healthCodeList.length) {
showToast('健康码信息有误, 请重新生成');
}
}
},
},
};
</script>
<style lang="scss" scoped>
.img {
display: block;
width: 400rpx;
height: 400rpx;
margin: 0 auto;
}
.index-btn {
position: fixed;
bottom: 40rpx;
right: 40rpx;
width: 96rpx;
height: 96rpx;
line-height: 96rpx;
padding: 0;
}
.index-btn::after {
border: none;
}
</style>

4
plugins/request/index.js

@ -92,9 +92,9 @@ http.interceptor.response(
/* 请求之后拦截器 */
if (response.data.code !== ERR_CODE) {
// 服务端返回的状态码不等于200,则reject()
return Promise.reject(response.data);
return Promise.reject(response.data.msg);
}
return response;
return response.data.data;
},
response => {
// 请求错误做点什么

BIN
static/head-portrait.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

BIN
static/img/home.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

BIN
static/img/homeHL.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

BIN
static/img/shanda1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

BIN
static/img/shanda2.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

BIN
static/img/user.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

BIN
static/img/userHL.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

5
store/index.js

@ -1,12 +1,11 @@
import Vue from 'vue';
import Vuex from 'vuex';
import user from './modules/user/index';
import site from './modules/site/index';
import statistics from './modules/statistics/index';
Vue.use(Vuex);
const store = new Vuex.Store({
modules: { user, site, statistics },
modules: { user },
});
export default store;

74
store/modules/site/actions.js

@ -1,74 +0,0 @@
import { http } from 'plugins/request/index';
import { showLoading, hideLoading, showToast, showModal } from 'utils/ui';
import { SITES_INFO, SCAN_SIGN, SITE } from 'api/api';
const actions = {
/**
* 获取所有场所信息
* @param {*} commit
*/
getSites({ commit }) {
return new Promise((resolve, reject) => {
showLoading();
http
.post(SITES_INFO)
.then(res => {
hideLoading();
const { data } = res.data;
commit('setSites', data);
resolve(data);
})
.catch(data => {
hideLoading();
showToast(data.msg || '获取场所信息失败');
reject(data);
});
});
},
/**
* 根据二维码id获取场所信息
* @param {*} commit
* @param {object} params 提交服务端的数据
*/
getSiteByQrId({ commit }, params) {
return new Promise((resolve, reject) => {
http
.post(SITE, params)
.then(res => {
const { data } = res.data;
commit('setSite', data);
resolve(data);
})
.catch(data => {
showToast(data.msg || '获取场所信息失败');
reject(data);
});
});
},
/**
* 扫码打卡 提交打卡记录
* @param {*} commit
* @param {object} params 提交服务端的数据
*/
sign({ commit }, params) {
return new Promise((resolve, reject) => {
showLoading();
http
.post(SCAN_SIGN, params)
.then(res => {
hideLoading();
const { data } = res.data;
resolve(data);
})
.catch(data => {
hideLoading();
showToast(data.msg || '打卡失败');
reject(data);
});
});
},
};
export default actions;

5
store/modules/site/index.js

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

21
store/modules/site/mutations.js

@ -1,21 +0,0 @@
const mutations = {
/**
* 设置所有的场所值
* @param {object} state
* @param {array} data
*/
setSites(state, data) {
state.sites = data || [];
},
/**
* 设置当前场所的信息
* @param {object} state
* @param {object} data
*/
setSite(state, data) {
state.site = data || null;
},
};
export default mutations;

6
store/modules/site/state.js

@ -1,6 +0,0 @@
const state = {
site: null, // 当前场所信息
sites: [], // 所有的场所信息
};
export default state;

79
store/modules/statistics/actions.js

@ -1,79 +0,0 @@
import { http } from 'plugins/request/index';
import { showLoading, hideLoading, showToast } from 'utils/ui';
import { USER_SIGNS, SCHOOL_SIGNS, HEALTH_TYPE_STATISTICS } from 'api/api';
const actions = {
/**
* 获取用户的打卡记录
* @param {*} commit
* @param {object} params 提交给后台的完整参数
*/
getUserSigns({ commit }, params) {
return new Promise((resolve, reject) => {
showLoading();
http
.post(USER_SIGNS, params)
.then(res => {
hideLoading();
const { data } = res.data;
commit('setUserSigns', data);
resolve(data);
})
.catch(data => {
hideLoading();
showToast(data.msg || '查询数据失败');
reject(data);
});
});
},
/**
* 获取校园打卡记录 展示热力图
* @param {*} commit
* @param {object} params 提交服务端的完整参数
*/
getSchoolSigns({ commit }, params) {
return new Promise((resolve, reject) => {
showLoading();
http
.post(SCHOOL_SIGNS, params)
.then(res => {
hideLoading();
const { data } = res.data;
commit('setSchoolSigns', data);
resolve(data);
})
.catch(data => {
hideLoading();
showToast(data.msg || '获取数据失败');
reject(data);
});
});
},
/**
* 获取校园健康上报的 数据数目
* @param {*} commit
* @param {object} params 提交给服务端的完整数据
*/
getSchoolSignsNumber({ commit }, params) {
return new Promise((resolve, reject) => {
showLoading();
http
.post(HEALTH_TYPE_STATISTICS, params)
.then(res => {
hideLoading();
const { data } = res.data;
commit('setShoolSignNumber', data);
resolve(data);
})
.catch(data => {
hideLoading();
showToast(data.msg || '获取健康上报数据失败');
reject(data);
});
});
},
};
export default actions;

5
store/modules/statistics/index.js

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

36
store/modules/statistics/mutations.js

@ -1,36 +0,0 @@
const mutations = {
/**
* 设置userSigns 自己的打卡记录数据
* @param {*} state
* @param {array} data
*/
setUserSigns(state, data) {
state.userSigns = data || [];
},
/**
* 设置shoolSigns 校园的打卡记录
* @param {*} state
* @param {array} data
*/
setSchoolSigns(state, data) {
state.schoolSigns = data || [];
},
/**
* 设置校园健康统计数目 shoolSignNumbers
* @param {*} state
* @param {array} data
*/
setShoolSignNumber(state, data) {
const arr = [
{ name: '正常', number: '-' },
{ name: '发烧', number: '-' },
{ name: '其他', number: '-' },
{ name: '未上报', number: '-' },
];
state.shoolSignNumber = data || arr;
},
};
export default mutations;

12
store/modules/statistics/state.js

@ -1,12 +0,0 @@
const state = {
userSigns: [], // 用户个人打卡记录
schoolSigns: [], // 校园的打卡记录
shoolSignNumber: [
{ name: '正常', number: '-' },
{ name: '发烧', number: '-' },
{ name: '其他', number: '-' },
{ name: '未上报', number: '-' },
], // 健康上报数目统计
};
export default state;

138
store/modules/user/actions.js

@ -1,145 +1,9 @@
import { showLoading, hideLoading, showModal, showToast } from 'utils/ui';
import { mpLogin, signIn, ddLogin } from 'utils/user';
import { UPDATE_USER, USER_ID_EXCHANGE_TOKEN } from 'api/user';
import { GET_USER_INFO, HEALTH_TYPE_STATUS } from 'api/api';
import { http } from 'plugins/request/index';
const actions = {
// 登录
login({ commit }) {
return new Promise(async (resolve, reject) => {
let params = null;
// #ifdef MP-WEIXIN
params = await mpLogin();
// #endif
// #ifdef MP-DINGTALK
params = await ddLogin();
// #endif
signIn(params)
.then(data => {
console.log('login data: ', data);
commit('setToken', data.token);
commit('setUser', data);
resolve(data);
})
.catch(err => {
console.log('login err: ', err);
showModal(err.msg || '登录失败');
reject(err);
});
});
},
/**
* signIn 提交登录信息
* @param {any} commit
* @param {string} params 登录提交的参数
*/
signIn({ commit }, params) {
return signIn(params)
.then(data => {
commit('setToken', data.token);
commit('setUser', data);
})
.catch(err => showModal(err));
},
/**
* 上传用户的微信信息
* @param {*} commit
* @param {object} params 提交的完整数据
*/
updateUserInfo({ commit }, params) {
return new Promise((resolve, reject) => {
http
.post(UPDATE_USER, params)
.then(res => {
const { data } = res.data;
commit('updateUser', { type: 'wxInfo', value: params });
resolve(data);
})
.catch(data => {
showToast(data.msg || '保存用户信息失败');
reject(data);
});
});
},
/**
* 查询基本信息
* @param {*} commit
* @param {object} params 提交的完整数据
*/
getUserInfo({ commit }, params) {
return new Promise((resolve, reject) => {
showLoading();
http
.post(GET_USER_INFO, params)
.then(res => {
hideLoading();
const { data } = res.data;
resolve(data);
// 获取自己的信息采取设置
// 扫别人健康码的时候不用设置
if (params.param.token) {
commit('setUserInfo', data);
}
if (data.healthCodeList && data.healthCodeList.length > 0) {
const oldCode = data.healthCodeList[0].healthCode;
commit('setHealthCode', oldCode);
}
})
.catch(data => {
hideLoading();
// showToast(data.msg || '查询个人信息失败');
reject(data);
});
});
},
/**
* 查询健康状态类型
* @param {*} commit
*/
async getHealthTypeStatus({ commit }) {
return new Promise((resolve, reject) => {
http
.post(HEALTH_TYPE_STATUS)
.then(res => {
const { data } = res.data;
resolve(data);
commit('setStatus', data);
})
.catch(data => {
showToast(data.msg || '查询健康类型失败');
reject(data);
});
});
},
/**
* 通过userId换取token
* @param {*} commit
* @param {object} params 提交的完整参数
*/
getTokenByUserId({ commit }, params) {
return new Promise((resolve, reject) => {
http
.get(USER_ID_EXCHANGE_TOKEN, params)
.then(res => {
const { data } = res.data;
commit('setToken', data.token);
commit('setUser', data);
resolve(data);
})
.catch(data => {
showToast(data.msg || '获取token失败');
reject(data);
});
});
},
};
export default actions;

58
store/modules/user/mutations.js

@ -21,64 +21,6 @@ const mutations = {
uni.setStorageSync('user', JSON.stringify(user));
},
/**
* 更新手机号
* @param {*} state
* @param {object} option
* @param {string} option.type 修改的数据的key
* @param {any} option.value 新数据的值
*/
updateUser(state, { type, value }) {
const { user } = state;
user[type] = value;
state.user = { ...user };
uni.setStorageSync('user', JSON.stringify(user));
},
/**
* 设置健康码
* @param {object} state
* @param {string} data
*/
setHealthCode(state, data) {
state.healthCode = data;
},
/**
* 设置个人信息 userInfo
* @param {*} state
* @param {string} data
*/
setUserInfo(state, data) {
state.userInfo = data;
},
/**
* 设置健康打卡信息 healthyInfo
* @param {*} state
* @param {string} data
*/
setHealthyInfo(state, data) {
state.healthyInfo = data;
},
/**
* 设置页面跳转参数 pagePath
* @param {*} state
* @param {string} data
*/
setPagePath(state, data) {
state.pagePath = data;
},
/**
* 设置健康状态类型 status
* @param {*} state
* @param {array} data
*/
setStatus(state, data) {
state.status = data || [];
},
};
export default mutations;

5
store/modules/user/state.js

@ -1,11 +1,6 @@
const state = {
token: '',
user: null,
healthCode: '', // 健康码
userInfo: null, // 个人基本信息
healthyInfo: null, // 健康打卡信息
pagePath: '', //页面跳转参数
status: [], // 健康状态类型
};
export default state;

3
vue.config.js

@ -6,8 +6,9 @@ module.exports = {
configureWebpack: {
resolve: {
alias: {
'~': __dirname,
config: resolve('config'),
api: resolve('config/api'),
api: resolve('api'),
store: resolve('store'),
components: resolve('components'),
pages: resolve('pages'),

Loading…
Cancel
Save