Browse Source

first commit

master
unknown 1 year ago
commit
c7ccd6a193
  1. 7
      .gitignore
  2. 20
      App.vue
  3. 0
      README.md
  4. 139
      common/css/app.scss
  5. 8
      common/js/uniExport.js
  6. 96
      common/js/utils.js
  7. 741
      common/sdk/qqmap-wx-jssdk.js
  8. 3
      common/sdk/qqmap-wx-jssdk.min.js
  9. 130
      components/UserTab/UserTab - 副本.vue
  10. 117
      components/UserTab/UserTab.vue
  11. 65
      components/cancelReservation/cancelReservation.vue
  12. 59
      components/commentItem/commentItem.vue
  13. 24
      components/moreRight/moreRight.vue
  14. 59
      components/pozCard/pozCard.vue
  15. 78
      components/privacyPopup/privacyPopup.vue
  16. 35
      components/privacyRadion/privacyRadion.vue
  17. 51
      components/searchRow/searchRow.vue
  18. 27
      components/topNavbar/topNavbar.vue
  19. 10
      config/api.js
  20. 52
      config/request.js
  21. 20
      index.html
  22. 35
      main.js
  23. 100
      manifest.json
  24. 15
      package.json
  25. 153
      pages.json
  26. 120
      pages/indexEntry/settlement/detail/detail.vue
  27. 264
      pages/indexEntry/settlement/settlement.vue
  28. 27
      pages/other/webView/webView.vue
  29. 30
      pages/tabbar/examSimulation/index.vue
  30. 31
      pages/tabbar/mine/index.vue
  31. 31
      pages/tabbar/operateTrain/index.vue
  32. 51
      pages/tabbar/statistics/comp/stage.vue
  33. 205
      pages/tabbar/statistics/index.vue
  34. 71
      pages/userCenter/login/login.vue
  35. 187
      pages/userCenter/login/loginByPhone.vue
  36. 28
      project.config.json
  37. 7
      project.private.config.json
  38. 12
      site.config.js
  39. BIN
      static/images/bigImg/indexTopBanner.png
  40. BIN
      static/images/bigImg/topPageBg.png
  41. BIN
      static/images/index/ic_jiaxiao.png
  42. BIN
      static/images/index/ic_shuaxin.png
  43. BIN
      static/images/index/radio_cli.png
  44. BIN
      static/images/index/radio_nor.png
  45. BIN
      static/images/index/searchIcon.png
  46. BIN
      static/images/index/searchIconHui.png
  47. BIN
      static/images/logo.png
  48. BIN
      static/images/tabbar/btn_xueche_cli.png
  49. BIN
      static/images/tabbar/btn_xueche_nor.png
  50. BIN
      static/images/tabbar/kc.png
  51. BIN
      static/images/tabbar/kcActive.png
  52. BIN
      static/images/tabbar/sc.png
  53. BIN
      static/images/tabbar/scActive.png
  54. BIN
      static/images/tabbar/sy.png
  55. BIN
      static/images/tabbar/syActive.png
  56. BIN
      static/images/tabbar/tj.png
  57. BIN
      static/images/tabbar/tjActive.png
  58. BIN
      static/images/tabbar/tk.png
  59. BIN
      static/images/tabbar/tkActive.png
  60. BIN
      static/images/tabbar/wd.png
  61. BIN
      static/images/tabbar/wdActive.png
  62. BIN
      static/images/tabbar/xy.png
  63. BIN
      static/images/tabbar/xyActive.png
  64. BIN
      static/images/tabbar/zx.png
  65. BIN
      static/images/tabbar/zxActive.png
  66. BIN
      static/images/userCenter/loginTopBg.png
  67. BIN
      static/images/userCenter/title_1.png
  68. BIN
      static/images/登录流程切图/__MACOSX/登录流程切图/._.DS_Store
  69. BIN
      static/images/登录流程切图/__MACOSX/登录流程切图/._btn_1.png
  70. BIN
      static/images/登录流程切图/登录流程切图/.DS_Store
  71. BIN
      static/images/登录流程切图/登录流程切图/bg_1.png
  72. BIN
      static/images/登录流程切图/登录流程切图/btn_1.png
  73. BIN
      static/images/登录流程切图/登录流程切图/btn_2.png
  74. BIN
      static/images/登录流程切图/登录流程切图/title_1.png
  75. BIN
      static/logo.png
  76. 21
      store/getters.js
  77. 90
      store/index.js
  78. 91
      store/modules/add.js
  79. 31
      store/modules/user.js
  80. 10
      uni.promisify.adaptor.js
  81. 80
      uni.scss
  82. 6
      uni_modules/uni-config-center/changelog.md
  83. 81
      uni_modules/uni-config-center/package.json
  84. 93
      uni_modules/uni-config-center/readme.md
  85. 1
      uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js
  86. 9
      uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/package.json
  87. 25
      uni_modules/uni-open-bridge-common/changelog.md
  88. 84
      uni_modules/uni-open-bridge-common/package.json
  89. 5
      uni_modules/uni-open-bridge-common/readme.md
  90. 26
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/bridge-error.js
  91. 124
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/config.js
  92. 30
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/consts.js
  93. 317
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/index.js
  94. 15
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/package.json
  95. 111
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/storage.js
  96. 324
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/uni-cloud-cache.js
  97. 31
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/validator.js
  98. 203
      uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/weixin-server.js
  99. 19
      uni_modules/uni-open-bridge-common/uniCloud/database/opendb-open-data.schema.json
  100. 8
      uni_modules/uni-scss/changelog.md

7
.gitignore

@ -0,0 +1,7 @@
node_modules
package-lock.json
unpackage/dist
unpackage/cache
.hbuilderx
unpackage

20
App.vue

@ -0,0 +1,20 @@
<script>
export default {
onLaunch: function() {
console.log('App Launch')
// uni.hideTabBar();
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style lang="scss">
/*每个页面公共css */
@import '@/uni_modules/uview-ui/index.scss';
@import 'common/css/app.scss'
</style>

0
README.md

139
common/css/app.scss

@ -0,0 +1,139 @@
page {
background-color: #fff;
font-size: 32rpx;
font-family: -apple-system-font, Helvetica Neue, Helvetica, sans-serif;
}
view {
box-sizing: border-box;
}
.oneRowText {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.towRowText {
display: -webkit-box;
overflow: hidden;
white-space: normal;
text-overflow: ellipsis;
word-wrap: break-word;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical
}
.bgLinear {
background: linear-gradient(180deg, #3593FB 0%, #53D3E5 100%);
}
.flex-b {
justify-content: space-between;
display: flex;
align-items: center;
}
.flex {
display: flex;
align-items: center;
}
.pageBg {
background: #F6F6F6;
color: #333;
font-size: 28rpx;
min-height: 100vh;
}
.pageBgImg {
font-size: 28rpx;;
color: #333;
background: url('http://192.168.1.20:81/zhili/image/20230824/afabc8b8a39544dc9ee4aea739b716cc.png') #F6F6F6 no-repeat;
background-size: 100% 324rpx;
min-height: 100vh;
}
.pad {
padding: 0 28rpx;
}
.card {
width: 100%;
background: #fff;
border-radius: 16rpx;
}
.status_bar {
height: var(--status-bar-height);
width: 100%;
}
image {
display: block;
width: 100%;
height: 100%;
}
.placeholderClassFFF {
color: #fff !important;
}
.starBox {
display: flex;
.num {
color: $themC;
font-size: 24rpx;
}
}
.my .u-input {
height: 100%;
}
.h1 {
font-size: 32rpx;
color: #333;
font-weight: 500;
position: relative;
padding: 0 0 0 32rpx;
&::before {
position: absolute;
content: '';
width: 8rpx;
height: 32rpx;
background: #1F6EFA;
border-radius: 4rpx;
top: 50%;
transform: translateY(-50%);
left: 0;
}
}
.btnBg {
height: 72rpx;
background: #1989FA;
border-radius: 8rpx;
line-height: 72rpx;
text-align: center;
font-size: 28rpx;
color: #fff;
}
.btnBorder {
height: 72rpx;
background: #DFEAF5;
border-radius: 8rpx;
line-height: 72rpx;
text-align: center;
font-size: 28rpx;
color:$themC;
border: 2rpx solid #1989FA;
}
/* 通用 */
::-webkit-input-placeholder { color:#ADADAD; }
::-moz-placeholder { color:#ADADAD; } /* firefox 19+ */
:-ms-input-placeholder { color:#ADADAD; } /* ie */
input:-moz-placeholder { color:#ADADAD; }
input::-webkit-input-placeholder { color: #CDCDCD !important; }
input::-moz-input-placeholder { color: #CDCDCD !important; }
input::-ms-input-placeholder { color: #CDCDCD !important; }
// /* webkit专用 */
.input1::-webkit-input-placeholder { color:#00f; }
// #input2::-webkit-input-placeholder { color:#090; background:lightgreen; text-transform:uppercase; }
// #input3::-webkit-input-placeholder { font-style:italic; text-decoration:overline; color:#999; }
// /* mozilla专用 */
// #input1::-moz-placeholder { color:#00f; }
// #input2::-moz-placeholder { color:#090; background:lightgreen; text-transform:uppercase; }
// #input3::-moz-placeholder { font-style:italic; text-decoration:overline; color:#999; }

8
common/js/uniExport.js

@ -0,0 +1,8 @@
export let goPage = (url, params={}, type='navigateTo')=> {
uni.$u.route({
url,
params,
type
})
}

96
common/js/utils.js

@ -0,0 +1,96 @@
import store from '@/store';
const install = (Vue, vm) => {
// 打开地图
const openMap = (lat, lng) => {
uni.openLocation({
latitude: lat,
longitude: lng
})
}
// 距离换算
const distanceFn = (val) => {
if (val * 1 < 1000) {
return val + '米'
} else {
return (val / 1000).toFixed(2) + '公里'
}
}
// 价格计算
const priceTo = (price = 0) => {
// return (price / 100).toFixed(2)
return (parseInt(price * 100) / 100 / 100).toFixed(2)
}
const distanceLatLng = (lat1, lng1) => {
var that = this;
let lat2 = store.state.latLng.lat;
let lng2 = store.state.latLng.lng;
let rad1 = lat1 * Math.PI / 180.0;
let rad2 = lat2 * Math.PI / 180.0;
let a = rad1 - rad2;
let b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rad1) *
Math.cos(
rad2) * Math.pow(
Math.sin(b / 2), 2)));
s = s * 6378.137;
s = Math.round(s * 10000) / 10000;
s = s.toString();
s = s.substring(0, s.indexOf('.') + 2);
return s
}
const getLocation = () => {
return new Promise((resolve, reject) => {
uni.getLocation({
type: 'wgs84',
success: function(res) {
console.log('当前位置的经度:' + res.longitude);
console.log('当前位置的纬度:' + res.latitude);
let obj = {
lat: res.latitude,
lng: res.longitude
}
store.commit('updateLatLng', obj)
resolve(obj)
}
});
}).catch((e) => {})
}
function addZeroPrefix(number) {
return number < 10 ? `0${number}` : number
}
let getDate = (date, splitor = '-') => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
return `${year}${splitor}${addZeroPrefix(month)}${splitor}${addZeroPrefix(day)}`
}
vm.$u.utils = {
openMap,
getLocation,
priceTo,
distanceLatLng,
distanceFn,
getDate,
}
}
export default {
install
}

741
common/sdk/qqmap-wx-jssdk.js

@ -0,0 +1,741 @@
/**
* 微信小程序JavaScriptSDK
*
* @version 1.1
* @date 2019-01-20
*/
var ERROR_CONF = {
KEY_ERR: 311,
KEY_ERR_MSG: 'key格式错误',
PARAM_ERR: 310,
PARAM_ERR_MSG: '请求参数信息有误',
SYSTEM_ERR: 600,
SYSTEM_ERR_MSG: '系统错误',
WX_ERR_CODE: 1000,
WX_OK_CODE: 200
};
var BASE_URL = 'https://apis.map.qq.com/ws/';
var URL_SEARCH = BASE_URL + 'place/v1/search';
var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
var URL_CITY_LIST = BASE_URL + 'district/v1/list';
var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
var URL_DISTANCE = BASE_URL + 'distance/v1/';
var EARTH_RADIUS = 6378136.49;
var Utils = {
/**
* 得到终点query字符串
* @param {Array|String} 检索数据
*/
location2query(data) {
if (typeof data == 'string') {
return data;
}
var query = '';
for (var i = 0; i < data.length; i++) {
var d = data[i];
if (!!query) {
query += ';';
}
if (d.location) {
query = query + d.location.lat + ',' + d.location.lng;
}
if (d.latitude && d.longitude) {
query = query + d.latitude + ',' + d.longitude;
}
}
return query;
},
/**
* 计算角度
*/
rad(d) {
return d * Math.PI / 180.0;
},
/**
* 处理终点location数组
* @return 返回终点数组
*/
getEndLocation(location){
var to = location.split(';');
var endLocation = [];
for (var i = 0; i < to.length; i++) {
endLocation.push({
lat: parseFloat(to[i].split(',')[0]),
lng: parseFloat(to[i].split(',')[1])
})
}
return endLocation;
},
/**
* 计算两点间直线距离
* @param a 表示纬度差
* @param b 表示经度差
* @return 返回的是距离单位m
*/
getDistance(latFrom, lngFrom, latTo, lngTo) {
var radLatFrom = this.rad(latFrom);
var radLatTo = this.rad(latTo);
var a = radLatFrom - radLatTo;
var b = this.rad(lngFrom) - this.rad(lngTo);
var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));
distance = distance * EARTH_RADIUS;
distance = Math.round(distance * 10000) / 10000;
return parseFloat(distance.toFixed(0));
},
/**
* 使用微信接口进行定位
*/
getWXLocation(success, fail, complete) {
wx.getLocation({
type: 'gcj02',
success: success,
fail: fail,
complete: complete
});
},
/**
* 获取location参数
*/
getLocationParam(location) {
if (typeof location == 'string') {
var locationArr = location.split(',');
if (locationArr.length === 2) {
location = {
latitude: location.split(',')[0],
longitude: location.split(',')[1]
};
} else {
location = {};
}
}
return location;
},
/**
* 回调函数默认处理
*/
polyfillParam(param) {
param.success = param.success || function () { };
param.fail = param.fail || function () { };
param.complete = param.complete || function () { };
},
/**
* 验证param对应的key值是否为空
*
* @param {Object} param 接口参数
* @param {String} key 对应参数的key
*/
checkParamKeyEmpty(param, key) {
if (!param[key]) {
var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误');
param.fail(errconf);
param.complete(errconf);
return true;
}
return false;
},
/**
* 验证参数中是否存在检索词keyword
*
* @param {Object} param 接口参数
*/
checkKeyword(param){
return !this.checkParamKeyEmpty(param, 'keyword');
},
/**
* 验证location值
*
* @param {Object} param 接口参数
*/
checkLocation(param) {
var location = this.getLocationParam(param.location);
if (!location || !location.latitude || !location.longitude) {
var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
param.fail(errconf);
param.complete(errconf);
return false;
}
return true;
},
/**
* 构造错误数据结构
* @param {Number} errCode 错误码
* @param {Number} errMsg 错误描述
*/
buildErrorConfig(errCode, errMsg) {
return {
status: errCode,
message: errMsg
};
},
/**
*
* 数据处理函数
* 根据传入参数不同处理不同数据
* @param {String} feature 功能名称
* search 地点搜索
* suggest关键词提示
* reverseGeocoder逆地址解析
* geocoder地址解析
* getCityList获取城市列表父集
* getDistrictByCityId获取区县列表子集
* calculateDistance距离计算
* @param {Object} param 接口参数
* @param {Object} data 数据
*/
handleData(param,data,feature){
if (feature === 'search') {
var searchResult = data.data;
var searchSimplify = [];
for (var i = 0; i < searchResult.length; i++) {
searchSimplify.push({
id: searchResult[i].id || null,
title: searchResult[i].title || null,
latitude: searchResult[i].location && searchResult[i].location.lat || null,
longitude: searchResult[i].location && searchResult[i].location.lng || null,
address: searchResult[i].address || null,
category: searchResult[i].category || null,
tel: searchResult[i].tel || null,
adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
})
}
param.success(data, {
searchResult: searchResult,
searchSimplify: searchSimplify
})
} else if (feature === 'suggest') {
var suggestResult = data.data;
var suggestSimplify = [];
for (var i = 0; i < suggestResult.length; i++) {
suggestSimplify.push({
adcode: suggestResult[i].adcode || null,
address: suggestResult[i].address || null,
category: suggestResult[i].category || null,
city: suggestResult[i].city || null,
district: suggestResult[i].district || null,
id: suggestResult[i].id || null,
latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
province: suggestResult[i].province || null,
title: suggestResult[i].title || null,
type: suggestResult[i].type || null
})
}
param.success(data, {
suggestResult: suggestResult,
suggestSimplify: suggestSimplify
})
} else if (feature === 'reverseGeocoder') {
var reverseGeocoderResult = data.result;
var reverseGeocoderSimplify = {
address: reverseGeocoderResult.address || null,
latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
};
if (reverseGeocoderResult.pois) {//判断是否返回周边poi
var pois = reverseGeocoderResult.pois;
var poisSimplify = [];
for (var i = 0;i < pois.length;i++) {
poisSimplify.push({
id: pois[i].id || null,
title: pois[i].title || null,
latitude: pois[i].location && pois[i].location.lat || null,
longitude: pois[i].location && pois[i].location.lng || null,
address: pois[i].address || null,
category: pois[i].category || null,
adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
city: pois[i].ad_info && pois[i].ad_info.city || null,
district: pois[i].ad_info && pois[i].ad_info.district || null,
province: pois[i].ad_info && pois[i].ad_info.province || null
})
}
param.success(data,{
reverseGeocoderResult: reverseGeocoderResult,
reverseGeocoderSimplify: reverseGeocoderSimplify,
pois: pois,
poisSimplify: poisSimplify
})
} else {
param.success(data, {
reverseGeocoderResult: reverseGeocoderResult,
reverseGeocoderSimplify: reverseGeocoderSimplify
})
}
} else if (feature === 'geocoder') {
var geocoderResult = data.result;
var geocoderSimplify = {
title: geocoderResult.title || null,
latitude: geocoderResult.location && geocoderResult.location.lat || null,
longitude: geocoderResult.location && geocoderResult.location.lng || null,
adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
province: geocoderResult.address_components && geocoderResult.address_components.province || null,
city: geocoderResult.address_components && geocoderResult.address_components.city || null,
district: geocoderResult.address_components && geocoderResult.address_components.district || null,
street: geocoderResult.address_components && geocoderResult.address_components.street || null,
street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null,
level: geocoderResult.level || null
};
param.success(data,{
geocoderResult: geocoderResult,
geocoderSimplify: geocoderSimplify
});
} else if (feature === 'getCityList') {
var provinceResult = data.result[0];
var cityResult = data.result[1];
var districtResult = data.result[2];
param.success(data,{
provinceResult: provinceResult,
cityResult: cityResult,
districtResult: districtResult
});
} else if (feature === 'getDistrictByCityId') {
var districtByCity = data.result[0];
param.success(data, districtByCity);
} else if (feature === 'calculateDistance') {
var calculateDistanceResult = data.result.elements;
var distance = [];
for (var i = 0; i < calculateDistanceResult.length; i++){
distance.push(calculateDistanceResult[i].distance);
}
param.success(data, {
calculateDistanceResult: calculateDistanceResult,
distance: distance
});
} else {
param.success(data);
}
},
/**
* 构造微信请求参数公共属性处理
*
* @param {Object} param 接口参数
* @param {Object} param 配置项
* @param {String} feature 方法名
*/
buildWxRequestConfig(param, options, feature) {
var that = this;
options.header = { "content-type": "application/json" };
options.method = 'GET';
options.success = function (res) {
var data = res.data;
if (data.status === 0) {
that.handleData(param, data, feature);
} else {
param.fail(data);
}
};
options.fail = function (res) {
res.statusCode = ERROR_CONF.WX_ERR_CODE;
param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
};
options.complete = function (res) {
var statusCode = +res.statusCode;
switch(statusCode) {
case ERROR_CONF.WX_ERR_CODE: {
param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
break;
}
case ERROR_CONF.WX_OK_CODE: {
var data = res.data;
if (data.status === 0) {
param.complete(data);
} else {
param.complete(that.buildErrorConfig(data.status, data.message));
}
break;
}
default:{
param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
}
}
};
return options;
},
/**
* 处理用户参数是否传入坐标进行不同的处理
*/
locationProcess(param, locationsuccess, locationfail, locationcomplete) {
var that = this;
locationfail = locationfail || function (res) {
res.statusCode = ERROR_CONF.WX_ERR_CODE;
param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
};
locationcomplete = locationcomplete || function (res) {
if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
}
};
if (!param.location) {
that.getWXLocation(locationsuccess, locationfail, locationcomplete);
} else if (that.checkLocation(param)) {
var location = Utils.getLocationParam(param.location);
locationsuccess(location);
}
}
};
class QQMapWX {
/**
* 构造函数
*
* @param {Object} options 接口参数,key 为必选参数
*/
constructor(options) {
if (!options.key) {
throw Error('key值不能为空');
}
this.key = options.key;
};
/**
* POI周边检索
*
* @param {Object} options 接口参数对象
*
* 参数对象结构可以参考
* @see http://lbs.qq.com/webservice_v1/guide-search.html
*/
search(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (!Utils.checkKeyword(options)) {
return;
}
var requestParam = {
keyword: options.keyword,
orderby: options.orderby || '_distance',
page_size: options.page_size || 10,
page_index: options.page_index || 1,
output: 'json',
key: that.key
};
if (options.address_format) {
requestParam.address_format = options.address_format;
}
if (options.filter) {
requestParam.filter = options.filter;
}
var distance = options.distance || "1000";
var auto_extend = options.auto_extend || 1;
var region = null;
var rectangle = null;
//判断城市限定参数
if (options.region) {
region = options.region;
}
//矩形限定坐标(暂时只支持字符串格式)
if (options.rectangle) {
rectangle = options.rectangle;
}
var locationsuccess = function (result) {
if (region && !rectangle) {
//城市限定参数拼接
requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")";
} else if (rectangle && !region) {
//矩形搜索
requestParam.boundary = "rectangle(" + rectangle + ")";
} else {
requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_SEARCH,
data: requestParam
}, 'search'));
};
Utils.locationProcess(options, locationsuccess);
};
/**
* sug模糊检索
*
* @param {Object} options 接口参数对象
*
* 参数对象结构可以参考
* http://lbs.qq.com/webservice_v1/guide-suggestion.html
*/
getSuggestion(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (!Utils.checkKeyword(options)) {
return;
}
var requestParam = {
keyword: options.keyword,
region: options.region || '全国',
region_fix: options.region_fix || 0,
policy: options.policy || 0,
page_size: options.page_size || 10,//控制显示条数
page_index: options.page_index || 1,//控制页数
get_subpois : options.get_subpois || 0,//返回子地点
output: 'json',
key: that.key
};
//长地址
if (options.address_format) {
requestParam.address_format = options.address_format;
}
//过滤
if (options.filter) {
requestParam.filter = options.filter;
}
//排序
if (options.location) {
var locationsuccess = function (result) {
requestParam.location = result.latitude + ',' + result.longitude;
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_SUGGESTION,
data: requestParam
}, "suggest"));
};
Utils.locationProcess(options, locationsuccess);
} else {
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_SUGGESTION,
data: requestParam
}, "suggest"));
}
};
/**
* 逆地址解析
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-gcoder.html
*/
reverseGeocoder(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
var requestParam = {
coord_type: options.coord_type || 5,
get_poi: options.get_poi || 0,
output: 'json',
key: that.key
};
if (options.poi_options) {
requestParam.poi_options = options.poi_options
}
var locationsuccess = function (result) {
requestParam.location = result.latitude + ',' + result.longitude;
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_GET_GEOCODER,
data: requestParam
}, 'reverseGeocoder'));
};
Utils.locationProcess(options, locationsuccess);
};
/**
* 地址解析
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-geocoder.html
*/
geocoder(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'address')) {
return;
}
var requestParam = {
address: options.address,
output: 'json',
key: that.key
};
//城市限定
if (options.region) {
requestParam.region = options.region;
}
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_GET_GEOCODER,
data: requestParam
},'geocoder'));
};
/**
* 获取城市列表
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-region.html
*/
getCityList(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
var requestParam = {
output: 'json',
key: that.key
};
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_CITY_LIST,
data: requestParam
},'getCityList'));
};
/**
* 获取对应城市ID的区县列表
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-region.html
*/
getDistrictByCityId(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'id')) {
return;
}
var requestParam = {
id: options.id || '',
output: 'json',
key: that.key
};
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_AREA_LIST,
data: requestParam
},'getDistrictByCityId'));
};
/**
* 用于单起点到多终点的路线距离(非直线距离)计算
* 支持两种距离计算方式步行和驾车
* 起点到终点最大限制直线距离10公里
*
* 新增直线距离计算
*
* @param {Object} options 接口参数对象
*
* 请求参数结构可以参考
* http://lbs.qq.com/webservice_v1/guide-distance.html
*/
calculateDistance(options) {
var that = this;
options = options || {};
Utils.polyfillParam(options);
if (Utils.checkParamKeyEmpty(options, 'to')) {
return;
}
var requestParam = {
mode: options.mode || 'walking',
to: Utils.location2query(options.to),
output: 'json',
key: that.key
};
if (options.from) {
options.location = options.from;
}
//计算直线距离
if(requestParam.mode == 'straight'){
var locationsuccess = function (result) {
var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标
var data = {
message:"query ok",
result:{
elements:[]
},
status:0
};
for (var i = 0; i < locationTo.length; i++) {
data.result.elements.push({//将坐标存入
distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
duration:0,
from:{
lat: result.latitude,
lng:result.longitude
},
to:{
lat: locationTo[i].lat,
lng: locationTo[i].lng
}
});
}
var calculateResult = data.result.elements;
var distanceResult = [];
for (var i = 0; i < calculateResult.length; i++) {
distanceResult.push(calculateResult[i].distance);
}
return options.success(data,{
calculateResult: calculateResult,
distanceResult: distanceResult
});
};
Utils.locationProcess(options, locationsuccess);
} else {
var locationsuccess = function (result) {
requestParam.from = result.latitude + ',' + result.longitude;
wx.request(Utils.buildWxRequestConfig(options, {
url: URL_DISTANCE,
data: requestParam
},'calculateDistance'));
};
Utils.locationProcess(options, locationsuccess);
}
}
};
module.exports = QQMapWX;

3
common/sdk/qqmap-wx-jssdk.min.js
File diff suppressed because it is too large
View File

130
components/UserTab/UserTab - 副本.vue

@ -0,0 +1,130 @@
<template>
<view class="">
<!-- 学生端 -->
<u-tabbar v-if="showWho=='student'" :value="student" @change="studentChange" :fixed="true" :placeholder="true"
:safeAreaInsetBottom="true" activeColor="#31aef1">
<u-tabbar-item v-for="i in studentList" :key='i.id' :text="i.name" :name="i.name">
<image class="u-page__item__slot-icon" slot="active-icon" :src="i.active" mode="widthFix"></image>
<image class="u-page__item__slot-icon" slot="inactive-icon" :src="i.inactive" mode="widthFix"></image>
</u-tabbar-item>
</u-tabbar>
<!-- 教师端 -->
<u-tabbar :value="teacher" @change="teacherChange" :fixed="true" :placeholder="true"
:safeAreaInsetBottom="true" activeColor="#31aef1">
<u-tabbar-item v-for="i in teacherList" :key='i.id' :text="i.name" :name="i.name">
<image class="u-page__item__slot-icon" slot="active-icon" :src="i.active" mode="widthFix"></image>
<image class="u-page__item__slot-icon" slot="inactive-icon" :src="i.inactive" mode="widthFix"></image>
</u-tabbar-item>
</u-tabbar>
</view>
</template>
<script>
export default {
props: ['tabNumber'],
data() {
return {
teacher: '课堂',
student: '',
showWho: 'teacher',
teacherList: [{
id: 1,
name: '课堂',
active: '../../static/images/tabbar/syActive.png',
inactive: '../../static/images/tabbar/sy.png'
},
{
id: 2,
name: '兴趣小组',
active: '../../static/images/tabbar/tkActive.png',
inactive: '../../static/images/tabbar/tk.png'
},
{
id: 3,
name: '我的',
active: '../../static/images/tabbar/zxActive.png',
inactive: '../../static/images/tabbar/zx.png'
}
],
studentList: [{
id: 1,
name: '学员课堂',
active: '../../static/images/tabbar/syActive.png',
inactive: '../../static/images/tabbar/sy.png'
},
{
id: 2,
name: '学员兴趣小组',
active: '../../static/images/tabbar/tkActive.png',
inactive: '../../static/images/tabbar/tk.png'
},
{
id: 3,
name: '学员我的',
active: '../../static/images/tabbar/zxActive.png',
inactive: '../../static/images/tabbar/zx.png'
}
],
}
},
mounted() {
// if (uni.getStorageSync('status') == 'teacher') {
// this.showWho = 'student'
// } else {
// this.showWho = 'teacher'
// }
this.student = this.tabNumber
this.teacher = this.tabNumber
},
methods: {
teacherChange(e) {
this.teacher = e
if (e == '课堂') {
uni.reLaunch({
url: "/pages/tabbar/index/index"
})
// uni.hideHomeButton() //
} else if (e == "兴趣小组") {
uni.reLaunch({
url: "/pages/tabbar/question/index"
})
// uni.hideHomeButton()
} else if (e == "我的") {
uni.reLaunch({
url: "/pages/tabbar/mine/index"
})
// uni.hideHomeButton()
}
},
studentChange(e) {
this.student = e
uni.hideHomeButton()
// if (e == '') {
// uni.reLaunch({
// url: "/pages/index/CourseTeacherIndex"
// })
// uni.hideHomeButton() //
// } else if (e == "") {
// uni.reLaunch({
// url: "/pages/interestGroup/interestGroup"
// })
// uni.hideHomeButton()
// } else if (e == "") {
// uni.reLaunch({
// url: "/pages/mine/mine"
// })
// uni.hideHomeButton()
// }
}
}
}
</script>
<style lang="scss" scoped>
.u-page__item__slot-icon {
display: block;
width: 56rpx;
height: 56rpx;
}
</style>

117
components/UserTab/UserTab.vue

@ -0,0 +1,117 @@
<template>
<view class="tab-bar">
<view v-for="(item,index) in list" :key="index" class="tab-bar-item" @click="switchTab(item, index)">
<image class="tab_img" :src="currentIndex == index ? item.selectedIconPath : item.iconPath"></image>
<view class="tab_text" :style="{color: currentIndex == index ? selectedColor : color}">{{item.text}}</view>
</view>
</view>
</template>
<script>
export default {
props: {
selectedIndex: { // tab index
default: 0
},
},
data() {
return {
color: "#666666",
selectedColor: "#00BAB2",
list: [],
currentIndex:0,
}
},
created() {
this.currentIndex = this.selectedIndex;
var _this = this
if (uni.getStorageSync('identify') == 'nurse') {
//
_this.list = [
{
"pagePath": "/pages/tabbar/index/index",
"text": "首页",
"iconPath": "/static/images/tabbar/sy.png",
"selectedIconPath": "/static/images/tabbar/syActive.png"
},
{
"pagePath": "/pages/tabbar/question/index",
"text": "题库",
"iconPath": "/static/images/tabbar/tk.png",
"selectedIconPath": "/static/images/tabbar/tkActive.png"
},
{
"pagePath": "/pages/tabbar/mine/index",
"text": "我的",
"iconPath": "/static/images/tabbar/wd.png",
"selectedIconPath": "/static/images/tabbar/wdActive.png"
}
]
} else {
//
_this.list = [{
"pagePath": "/pages/tabbar/index/index",
"text": "首1页",
"iconPath": "/static/images/tabbar/sy.png",
"selectedIconPath": "/static/images/tabbar/syActive.png"
},
{
"pagePath": "/pages/tabbar/question/index",
"text": "题2库",
"iconPath": "/static/images/tabbar/tk.png",
"selectedIconPath": "/static/images/tabbar/tkActive.png"
},
{
"pagePath": "/pages/tabbar/mine/index",
"text": "我3的",
"iconPath": "/static/images/tabbar/wd.png",
"selectedIconPath": "/static/images/tabbar/wdActive.png"
}
]
}
},
methods: {
switchTab(item, index) {
this.currentIndex = index;
let url = item.pagePath;
console.log(url)
uni.reLaunch({url:url})
}
}
}
</script>
<style lang="scss">
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background: white;
display: flex;
justify-content: center;
align-items: center;
padding-bottom: env(safe-area-inset-bottom); // iphoneX
.tab-bar-item {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
.tab_img {
width: 48rpx;
height: 48rpx;
}
.tab_text {
font-size: 24rpx;
margin-top: 4rpx;
}
}
}
</style>

65
components/cancelReservation/cancelReservation.vue

@ -0,0 +1,65 @@
<template>
<view class="popupCon">
<view class="h2">确定取消预约</view>
<view class="txt">某个训练预约取消规则</view>
<view class="btnBox">
<view class="btn" @click="$emit('popupBtnClick',0)">取消</view>
<view class="btn right" @click="$emit('popupBtnClick',1)">确定</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.popupCon {
width: 558rpx;
background: linear-gradient(180deg, #C1DFFE 0%, #FFFFFF 20%);
border-radius: 16rpx;
.h2 {
font-size: 36rpx;
color: #333;
font-weight: 600;
text-align: center;
padding: 90rpx 0 0rpx 0;
}
.txt {
text-align: center;
padding: 28rpx;
color: #686B73;
}
.btnBox {
width: 100%;
height: 110rpx;
border-top: 1rpx solid #E8E9EC;
display: flex;
padding: 30rpx 0;
.btn {
flex: 1;
text-align: center;
color: #ADADAD;
font-size: 36rpx;
}
.btn.right {
color: $themC;
position: relative;
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 2rpx;
height: 48rpx;
background: #E8E9EC;
}
}
}
}
</style>

59
components/commentItem/commentItem.vue

@ -0,0 +1,59 @@
<template>
<view class="box">
<view class="flex-b">
<view class="name">匿名用户</view>
<view class="date">2023/08/08</view>
</view>
<view class="starBox">
<u-rate active-color="#1989FA" inactive-color="#1989FA" gutter="1" :size="16" :value="4" disabled></u-rate>
<view class="num">4.9</view>
</view>
<view class="text">教学质量高技术好超有耐心有责任心学车就找这个驾校教练都超好满意满意满意</view>
<view class="imgBox">
<view class="img">
<image src="@/static/images/logo.png" mode=""></image>
</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.flex-b {
align-items: center;
.name {
font-weight: 600;
font-size: 32rpx;
color: #333;
}
.date {
font-size: 24rpx;
color: #686B73;
}
}
.starBox {
padding: 10rpx 0 24rpx 0;
}
.text {
font-size: 24rpx;
}
.imgBox {
display: flex;
flex-wrap: wrap;
padding-top: 20rpx;
.img {
margin-top: 20rpx;
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
overflow: hidden;
margin-right: 24rpx;
}
}
</style>

24
components/moreRight/moreRight.vue

@ -0,0 +1,24 @@
<template>
<view class="moreBox">
<view class="txt">{{text}}</view>
<u-icon name="arrow-right" color="#686B73" size="14"></u-icon>
</view>
</template>
<script>
export default {
props: ['text']
}
</script>
<style lang="scss" scoped>
.moreBox {
display: flex;
align-items: center;
.txt {
font-size: 24rpx;
color: #686B73;
margin-right: 8rpx;
}
}
</style>

59
components/pozCard/pozCard.vue

@ -0,0 +1,59 @@
<template>
<view class="card">
<view class="flex-b">
<view class="left_text">
<view class="adr ">江西省江西市江西区某某镇尚坤丁兰国际1190号820</view>
<view class="distance">距您100km</view>
</view>
<view class="mapEntry">
<view class="icon">
<image src="@/static/images/index/mapIcon.png" mode=""></image>
</view>
<view class="text">地图导航</view>
</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.card {
padding: 24rpx;
.flex-b {
align-items: center;
.left_text {
flex: 1;
.adr {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.distance {
margin-top: 24rpx;
color: #686B73;
font-size: 24rpx;
}
}
.mapEntry {
width: 130rpx;
display: flex;
flex-direction: column;
align-items: center;
.icon {
width: 32rpx;
height: 32rpx;
}
.text {
font-size: 24rpx;
margin-top: 10rpx;
color: $themC;
}
}
}
}
</style>

78
components/privacyPopup/privacyPopup.vue

@ -0,0 +1,78 @@
<template>
<view class="privacyPopup">
<view class="h2">个人信息保护声明</view>
<view class="content">
<view class="nickName">亲爱的用户</view>
<view class="text">
感谢您信任并使用江西驾考公共服
务平台小程序我们深知个人信息对您
的重要性非常重视您的个人信息和隐
私保护并会尽全力保护您的个人信息
安全可靠我们承诺我们将按业界成
熟的安全标准采取相应的安全保护措
隐私政策帮助您了解我们收集使
存储和共享个人信息的情况
在您注册成为学员的过程中您需要
</view>
</view>
<view class="btnBox">
<view class="btn" @click="$emit('disagree')">不同意</view>
<view class="btn right" @click="$emit('agree')">同意</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.privacyPopup {
width: 558rpx;
position: relative;
background: linear-gradient(180deg, #C1DFFE 0%, #FFFFFF 20%);
border-radius: 16rpx;
.h2 {
padding: 42rpx 0 0 0;
font-size: 36rpx;
color: #333;
text-align: center;
font-weight: 600;
}
.content {
padding: 30rpx 30rpx 120rpx 30rpx;
font-size: 28rpx;
color: #333;
.nickName {
z-index: 2em;
}
.text {
margin-top: 16rpx;
z-index: 2em;
}
}
.btnBox {
width: 100%;
height: 110rpx;
border-top: 1rpx solid #E8E9EC;
display: flex;
padding: 30rpx 0;
position: absolute;
left: 0;
bottom: 0;
.btn {
flex: 1;
text-align: center;
color: #ADADAD;
font-size: 36rpx;
}
.btn.right {
color: $themC;
}
}
}
</style>

35
components/privacyRadion/privacyRadion.vue

@ -0,0 +1,35 @@
<template>
<view class="radioWrap">
<u-checkbox-group @change="changeRadio">
<u-checkbox v-model="isCheck" shape="circle" label="已阅读并同意" :labelSize="12" ></u-checkbox>
</u-checkbox-group>
<view class="privacyText">
<text>用户协议</text> <text>隐私协议</text>
</view>
</view>
</template>
<script>
export default {
props: ['isCheck'],
methods: {
changeRadio(val) {
this.$emit('changeRadio', val)
}
}
}
</script>
<style lang="scss" scoped>
.radioWrap {
display: flex;
align-items: center;
.privacyText {
font-size: 24rpx;
color: #888E94;
text {
color: $themC;
}
}
}
</style>

51
components/searchRow/searchRow.vue

@ -0,0 +1,51 @@
<template>
<view class="searchBg">
<view class="flex">
<view class="searchIcon">
<image src="@/static/images/index/searchIcon.png" mode=""></image>
</view>
<view class="inputBox">
<u--input :placeholder="placeholder" border="none" clearable v-model="keywords" :color="'#fff'"
placeholderClass="placeholderClassFFF"></u--input>
</view>
</view>
</view>
</template>
<script>
export default {
props: ['placeholder'],
data() {
return {
keywords: ''
}
}
}
</script>
<style lang="scss" scoped>
.searchBg {
background: #8ABAED;
width: 100%;
height: 72rpx;
border-radius: 16rpx;
line-height: 72rpx;
.flex {
height: 100%;
padding: 0 28rpx;
.searchIcon {
width: 40rpx;
height: 40rpx;
}
.inputBox {
padding-left: 28rpx;
flex: 1;
color: #fff;
font-size: 28rpx;
}
}
}
</style>

27
components/topNavbar/topNavbar.vue

@ -0,0 +1,27 @@
<template>
<view class="topNavbar">
<u-navbar
:leftText="title"
:autoBack="true"
:bgColor="bgColor"
:leftIconColor="leftIconColor"
:fixed="false"
>
</u-navbar>
</view>
</template>
<script>
export default {
props: ['title'],
data() {
return {
bgColor: 'transparent',
leftIconColor: '#fff'
}
}
}
</script>
<style>
</style>

10
config/api.js

@ -0,0 +1,10 @@
const http = uni.$u.http
// 隐私政策
export const getAgreement = (params, config = {}) => http.post('/util/manage/getAgreement.do', params, config)
// 验证码登录
export const loginSMS = (data) => http.post('/account/manage/login.do', data)
// 登录发验证码
export const getLoginCode = (data) => http.post('/util/manage/getLoginRegistCode.do', data)

52
config/request.js

@ -0,0 +1,52 @@
// 此vm参数为页面的实例,可以通过它引用vuex中的变量
module.exports = (vm) => {
// 初始化请求配置
uni.$u.http.setConfig((config) => {
/* config 为默认全局配置*/
config.baseURL = 'http://121.41.97.244:8090'; /* 根域名 */
config.header['content-type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
return config
})
// 请求拦截
uni.$u.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
// 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
config.data = config.data || {}
// 根据custom参数中配置的是否需要token,添加对应的请求头
if(config?.custom?.auth) {
// 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
config.header.token = vm.$store.state.userInfo.token
}
return config
}, config => { // 可使用async await 做异步操作
return Promise.reject(config)
})
// 响应拦截
uni.$u.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/
const data = response.data
console.log('response')
console.log(data)
// 自定义参数
const custom = response.config?.custom
if (data.code !== 0) {
// 如果没有显式定义custom的toast参数为false的话,默认对报错进行toast弹出提示
if (custom.toast !== false) {
uni.$u.toast(data.message)
}
// 如果需要catch返回,则进行reject
if (custom?.catch) {
return Promise.reject(data)
} else {
// 否则返回一个pending中的promise,请求不会进入catch中
return new Promise(() => { })
}
}
return data === undefined ? {} : data
}, (response) => {
// 对响应错误做点什么 (statusCode !== 200)
return Promise.reject(response)
})
}

20
index.html

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

35
main.js

@ -0,0 +1,35 @@
import App from './App'
import Vue from 'vue'
import store from './store';
import { goPage } from './common/js/uniExport.js'
Vue.prototype.$goPage = goPage;
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
// main.js,注意要在use方法之后执行
import uView from '@/uni_modules/uview-ui'
Vue.use(uView)
import tabBar from "components/UserTab/UserTab.vue"
Vue.component('tabBar',tabBar)
const app = new Vue({
...App,
store
})
require('./config/request.js')(app)
import utils from "@/common/js/utils.js"
console.log(utils)
// 自定义工具
Vue.use(utils,app)
app.$mount()

100
manifest.json

@ -0,0 +1,100 @@
{
"name" : "recruitStudent",
"appid" : "__UNI__BD23957",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx2d71605af3b620e6",
"setting" : {
"urlCheck" : false,
"es6" : true
},
"usingComponents" : true,
"permission" : {
"scope.userLocation" : {
"desc" : "查询用户地理位置,推荐本地驾校,提高用户服务"
}
}
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"h5" : {
"devServer" : {
"port" : 8000,
"disableHostCheck" : true,
"proxy" : {
"/api" : {
"target" : "http://121.41.97.244:8090",
"changeOrigin" : true,
"secure" : true,
"pathRewrite" : {
//使
"^/api" : ""
}
}
},
"https" : false
},
"router" : {
"mode" : "hash",
"base" : "/h5"
}
}
}

15
package.json

@ -0,0 +1,15 @@
{
"name": "recruitStudent",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"uview-ui": "^2.0.36"
}
}

153
pages.json

@ -0,0 +1,153 @@
{
"pages": [
{
"path": "pages/tabbar/statistics/index",
"style": {
"navigationBarTitleText": "首页",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}
},
{
"path": "pages/tabbar/examSimulation/index",
"style": {
"navigationBarTitleText": "考场模拟",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}
},
{
"path": "pages/tabbar/operateTrain/index",
"style": {
"navigationBarTitleText": "实操训练",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}
},
{
"path": "pages/tabbar/mine/index",
"style": {
"navigationBarTitleText": "我的",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
"backgroundTextStyle": "dark"
}
}
],
"subPackages": [
{
"root": "pages/userCenter",
"pages": [
{
"path": "login/login",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom",
"enablePullDownRefresh": false,
"backgroundTextStyle": "dark"
}
},
{
"path": "login/loginByPhone",
"style": {
"navigationBarTitleText": "手机号登录",
"navigationStyle": "custom",
"enablePullDownRefresh": false,
"backgroundTextStyle": "dark"
}
}
]
},
{
"root": "pages/indexEntry",
"pages": [
{
"path": "settlement/settlement",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"enablePullDownRefresh": false,
"backgroundTextStyle": "dark"
}
},
{
"path": "settlement/detail/detail",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"enablePullDownRefresh": false,
"backgroundTextStyle": "dark"
}
}
]
},
{
"root": "pages/other",
"pages": [
{
"path": "webView/webView",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"enablePullDownRefresh": false,
"backgroundTextStyle": "dark"
}
}
]
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "学车小程序",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"uniIdRouter": {},
"tabBar": {
"color": "#999999",
"selectedColor": "#218DFF",
"backgroundColor": "#FFFFFF",
"list": [{
"pagePath": "pages/tabbar/statistics/index",
"text": "首页",
"iconPath": "static/images/tabbar/tj.png",
"selectedIconPath": "static/images/tabbar/tjActive.png"
},
{
"pagePath": "pages/tabbar/examSimulation/index",
"text": "考场模拟",
"iconPath": "static/images/tabbar/kc.png",
"selectedIconPath": "static/images/tabbar/kcActive.png"
},
{
"pagePath": "pages/tabbar/operateTrain/index",
"text": "实操训练",
"iconPath": "static/images/tabbar/sc.png",
"selectedIconPath": "static/images/tabbar/scActive.png"
},
{
"pagePath": "pages/tabbar/mine/index",
"text": "我的",
"iconPath": "static/images/tabbar/wd.png",
"selectedIconPath": "static/images/tabbar/wdActive.png"
}
]
},
"easycom": {
"^u-(.*)": "@/uni_modules/uview-ui/components/u-$1/u-$1.vue"
},
"condition" : { //
"current": 0, //(list )
"list": [
{
"name": "", //
"path": "", //
"query": "" //onLoad
}
]
}
}

120
pages/indexEntry/settlement/detail/detail.vue

@ -0,0 +1,120 @@
<template>
<view class="pageBgImg ">
<topNavbar title="结算明细"></topNavbar>
<view class="pad">
<view class="card">
<view class="top_row">
<view class="name">张三三</view>
<view class="tab">第二阶段</view>
<view class="price_row">
<view class="jia">+</view>
<view class="price">12256.33</view>
</view>
</view>
<view class="row">
<view class="text">学员确认</view>
<view class="iconPass">
<image src="@/static/images/index/radio_cli.png" mode=""></image>
</view>
<view class="text">2023/08/08 10:55:21</view>
</view>
<view class="row">
<view class="text">教练确认</view>
<view class="iconPass">
<image src="@/static/images/index/radio_cli.png" mode=""></image>
</view>
<view class="text">2023/08/08 10:55:21</view>
</view>
<view class="row">
<view class="text">驾校确认</view>
<view class="iconPass">
<image src="@/static/images/index/radio_cli.png" mode=""></image>
</view>
<view class="text">2023/08/08 10:55:21</view>
</view>
</view>
<view class="card">
<view class="row">
<view class="text">学员手机号</view>
<view class="text">182671031657</view>
</view>
<view class="row">
<view class="text">到账时间</view>
<view class="text">2023/08/08 10:55:21</view>
</view>
<view class="row">
<view class="text">返款类型</view>
<view class="text">三方确认</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {}
}
}
</script>
<style lang="scss" scoped>
.card {
padding: 0 28rpx;
padding-bottom: 16rpx;
margin-bottom: 20rpx;
.top_row {
display: flex;
align-items: center;
height: 98rpx;
border-bottom: 2px dashed #E8E9EC;
.name {
font-size: 32rpx;
font-weight: 500;
}
.tab {
width: 144rpx;
height: 60rpx;
background: rgba(25,137,250,0.1);
border-radius: 8rpx;
font-size: 28rpx;
text-align: center;
line-height: 60rpx;
color: $themC;
margin-left: 14rpx;
}
.price_row {
color: $themC;
margin-left: auto;
display: flex;
align-items: flex-end;
.jia {
font-size: 24rpx;
}
.price {
font-size: 32rpx;
}
}
}
.row {
height: 80rpx;
display: flex;
align-items: center;
justify-content: space-between;
.text {
color: #686B73;
}
.iconPass {
width: 32rpx;
height: 32rpx;
}
}
}
</style>

264
pages/indexEntry/settlement/settlement.vue

@ -0,0 +1,264 @@
<template>
<!-- 结算明细 -->
<view class="pageBgImg">
<topNavbar title="结算明细"></topNavbar>
<view class="pad">
<searchRow placeholder="搜索学员姓名/学员手机号"/>
</view>
<scroll-view class="scroll-view_w" scroll-x="true" scroll-with-animation :scroll-into-view="'tab'+currentTab" scroll-left="140">
<view class="tabs">
<view class="tab" v-for="(item,index) in tabData" :key="index" @click="changeTab(item)" :class="{active: currentTab==item.id}" :id="'tab'+item.id">{{ item.text }}</view>
<view class="rightPad"></view>
</view>
</scroll-view>
<view class="content pad">
<view class="month_row" @click="showDate=true">
<!-- <view class="month">8</view>
<view class="unit"></view> -->
<view class="">{{ selectDate }}</view>
<view class="iconFont">
<u-icon name="arrow-down" color="#686B73" size="14"></u-icon>
</view>
</view>
<view class="total">结算金额共计</view>
<view class="record">
<view class="card" v-for="(item,index) in 10" :key="index" @click="$goPage('/pages/indexEntry/settlement/detail/detail')">
<stage/>
</view>
</view>
</view>
<u-popup :show="showDate" mode="bottom" :round="20" >
<view class="popupCon">
<view class="popTab">
<view class="tabItem" :class="{active: currentPopTab==1}" @click="changePopTab(1)">月份选择</view>
<view class="tabItem" :class="{active: currentPopTab==2}" @click="changePopTab(2)">自定义时间</view>
</view>
<view class="timer">
<view class="tabCon" v-if="currentPopTab==1">
<view class="dateBtn" @click="showDatePickerFn(1)" :class="{hui: !data1}">{{ date1 }}</view>
<!-- <u-datetime-picker-my
:show="show"
v-model="value1"
mode="year-month"
:showToolbar="false"
:visibleItemCount="4"
@confirm="confirm"
></u-datetime-picker-my> -->
</view>
<view class="tabCon" v-else>
<view class="dateBtn" :class="{hui: !data2}" @click="showDatePickerFn(2)">{{ date2 }}</view>
<view class="to"></view>
<view class="dateBtn" :class="{hui: !data3}" @click="showDatePickerFn(3)">{{ date3 }}</view>
</view>
<view class="btnBg" @click="selectDateClick">确定</view>
<!-- <u-picker-my></u-picker-my> -->
</view>
</view>
</u-popup>
<u-datetime-picker
:show="showDatePicker"
v-model="value1"
mode="year-month"
:visibleItemCount="4"
:closeOnClickOverlay="false"
@confirm="confirmDatePicker"
@cancel="cancelDatePicker"
></u-datetime-picker>
</view>
</template>
<script>
import stage from '../../tabbar/statistics/comp/stage.vue'
export default {
components: { stage },
data() {
return {
date3: '',
date2: '',
date1: '',
value1: '',
showDate: false,
showDatePicker: false,
show: false,
tabData: [{
text: '全部',
id: 10
},
{
text: '阶段一',
id: 0
},
{
text: '阶段二',
id: 1
},
{
text: '阶段三',
id: 2
},
{
text: '阶段四',
id: 3
},
{
text: '阶段五',
id: 4
},
{
text: '阶段六',
id: 5
},
],
currentTab: 2,
currentPopTab: 2,
currentBtnDate: 1,
selectDate: '',//
}
},
methods: {
// tab
changeTab(val) {
this.currentTab = val.id
},
//
changePopTab(num) {
this.currentPopTab = num
},
// 1
showDatePickerFn(num) {
this.showDate = false
this.showDatePicker = true
this.currentBtnDate = num
},
// 2
confirmDatePicker(val) {
this.showDate = true
this.showDatePicker = false
let date = uni.$u.date(val.value, 'yyyy-mm-dd')
if(this.currentBtnDate==1) {
date = uni.$u.date(val.value, 'yyyy-mm')
}
this['date'+this.currentBtnDate] = date
},
// 3使
selectDateClick() {
this.showDate = false
this.selectDate = this['date'+this.currentBtnDate]
}
}
}
</script>
<style lang="scss" scoped>
.scroll-view_w {
width: 100%;
margin: 30rpx 0 40rpx 0;
.tabs {
display: flex;
flex-wrap: nowrap;
padding: 0 0rpx 10rpx 32rpx;
width: auto;
.tab {
width: 108rpx;
height: 60rpx;
border-radius: 8rpx;
border: 2rpx solid #FFFFFF;
font-size: 28rpx;
color: #fff;
text-align: center;
line-height: 60rpx;
margin-right: 28rpx;
flex-shrink: 0;
&.active {
background-color: #fff;
color: $themC;
}
&.all {
width: 96rpx;
}
}
.rightPad {
min-width: 10rpx;
height: 60rpx;
}
}
}
.month_row {
display: flex;
align-items: center;
color: $themC;
.month {
font-size: 50rpx;
font-weight: 600;
}
.unit {
font-size: 30rpx;
margin: 0 4rpx;
}
}
.total {
padding: 20rpx 0;
}
.card {
margin-bottom: 24rpx;
}
.popupCon {
height: 430rpx;
.popTab {
display: flex;
padding: 40rpx 32rpx;
.tabItem {
font-size: 32rpx;
color: #333;
margin-right: 60rpx;
&.active {
color: $themC;
position: relative;
&::before {
content: '';
position: absolute;
bottom: -20rpx;
left: 50%;
transform: translateX(-50%);
width: 128rpx;
height: 4rpx;
background: #1989FA;
border-radius: 3rpx;
}
}
}
}
}
.tabCon {
display: flex;
align-items: center;
padding-left: 32rpx;
padding-top: 20rpx;
.dateBtn {
width: 280rpx;
height: 80rpx;
border-radius: 10rpx;
border: 2rpx solid #1989FA;
line-height: 80rpx;
text-align: center;
color: $themC;
font-size: 32rpx;
&.hui {
border: 2rpx solid #E8E9EC;
}
}
.to {
font-size: 32rpx;
margin: 0 40rpx;
}
}
.btnBg {
width: 396rpx;
margin: 34rpx auto 42rpx auto;
}
</style>

27
pages/other/webView/webView.vue

@ -0,0 +1,27 @@
<template>
<view>
<web-view :webview-styles="webviewStyles" src="http://192.168.1.20/system/menu"></web-view>
<!-- <web-view :webview-styles="webviewStyles" :src="$store.state.webViewUrl"></web-view> -->
</view>
</template>
<script>
export default {
data() {
return {
webviewStyles: {
progress: {
color: '#FF3333'
}
}
}
},
onLoad() {
console.log('噜噜哇'+ this.$store.state.webViewUrl)
}
}
</script>
<style>
</style>

30
pages/tabbar/examSimulation/index.vue

@ -0,0 +1,30 @@
<template>
<view class="content">
考场模拟
<!-- <UserTab selectedIndex ='1'></UserTab> -->
</view>
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {
},
onShow() {
// uni.hideTabBar();
},
methods: {
goPage() {}
}
}
</script>
<style lang="scss" scoped>
.content {
width: 100vw;
height: 100vh;
}
</style>

31
pages/tabbar/mine/index.vue

@ -0,0 +1,31 @@
<template>
<view class="content">
我的
<!-- <UserTab selectedIndex ='2'></UserTab> -->
</view>
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {
console.log('我的页面')
},
onShow() {
// uni.hideTabBar();
},
methods: {
goPage() {}
}
}
</script>
<style lang="scss" scoped>
.content {
width: 100vw;
height: 100%;
}
</style>

31
pages/tabbar/operateTrain/index.vue

@ -0,0 +1,31 @@
<template>
<view class="content">
实操训练
<!-- <UserTab selectedIndex ='2'></UserTab> -->
</view>
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {
console.log('我的页面')
},
onShow() {
// uni.hideTabBar();
},
methods: {
goPage() {}
}
}
</script>
<style lang="scss" scoped>
.content {
width: 100vw;
height: 100%;
}
</style>

51
pages/tabbar/statistics/comp/stage.vue

@ -0,0 +1,51 @@
<template>
<view class="li">
<view class="name_row">
<view class="name">张三三</view>
<view class="price">+¥1,130.86</view>
</view>
<view class="stage">
<view>第一阶段</view>
<view>2023/08/08 10:55:21</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.li {
height: 168rpx;
padding: 0 34rpx;
.name_row {
display: flex;
justify-content: space-between;
height: 98rpx;
align-items: center;
.name {
font-weight: 500;
font-size: 32rpx;
}
.price {
font-size: 32rpx;
color: $themC;
font-weight: 500;
}
}
.stage {
display: flex;
justify-content: space-between;
height: 68rpx;
align-items: center;
font-size: 24rpx;
color: #686B73;
border-top: 2rpx dashed #E8E9EC;
;
}
}
</style>

205
pages/tabbar/statistics/index.vue

@ -0,0 +1,205 @@
<template>
<view class="content pageBg">
<view class="userInfo">
<view class="tit">Hi,大乔教练</view>
<view class="flex userRow">
<view class="schoolIcon">
<image src="@/static/images/index/ic_jiaxiao.png" mode=""></image>
</view>
<view class="schoolName oneRowText">翔力驾校</view>
<view class="tag">合作教练</view>
</view>
</view>
<view class="card priceBox">
<view class="blueLab">今日已结算金额</view>
<view class="price">36333.66</view>
<view class="flex-b">
<view class="data">截止2023/08/08 11:00:00</view>
<view class="refresh">
<view class="text">刷新</view>
<view class="icon">
<image src="@/static/images/index/ic_shuaxin.png" mode=""></image>
</view>
</view>
</view>
</view>
<view class="h1">结算统计</view>
<view class="tabs">
<view class="tab">按日</view>
<view class="tab">按月</view>
<view class="tab">按年</view>
<view class="tab long">自定义日期</view>
</view>
<view class="card">
<view class="chart">
图表到时候一起调
</view>
</view>
<view class="flex-b">
<view class="h1">结算明细</view>
<moreRight text="更多"/>
</view>
<view class="record">
<view class="card">
<stage/>
</view>
</view>
<view class="moreBtn" @click="$goPage('/pages/indexEntry/settlement/settlement')">查看更多</view>
<!-- <UserTab selectedIndex ='2'></UserTab> -->
</view>
</template>
<script>
import stage from './comp/stage'
export default {
components: { stage },
data() {
return {
}
},
onLoad() {
console.log('我的页面')
},
onShow() {
// uni.hideTabBar();
},
methods: {
goPage() {}
}
}
</script>
<style lang="scss" scoped>
.content {
width: 100%;
background: url('http://192.168.1.20:81/zhili/image/20230824/30073140957f4349b6579cb0ff00d4b1.png') #F6F6F6 no-repeat;
background-size: 100% 492rpx;
padding: 0 28rpx;
padding-top: 142rpx;
.userInfo {
.tit {
font-size: 48rpx;
color: #fff;
font-weight: 500;
}
.userRow {
align-items: center;
margin-bottom: 20rpx;
.schoolIcon {
width: 28rpx;
height: 28rpx;
}
.schoolName {
font-size: 28rpx;
padding: 20rpx;
max-width: 220rpx;
color: #fff;
}
.tag {
width: 112rpx;
height: 44rpx;
background: #82AFDD;
border-radius: 22rpx;
font-size: 20rpx;
color: #fff;
line-height: 44rpx;
text-align: center;
}
}
}
.priceBox {
padding: 32rpx;
color: $themC;
.blueLab {
font-weight: 500;
font-size: 28rpx;
}
.price {
font-size: 56rpx;
font-weight: 600;
padding: 12rpx 0 24rpx 0;
}
.flex-b {
.data {
font-size: 24rpx;
color: #363A44;
}
.refresh {
width: 130rpx;
height: 60rpx;
background: rgba(25,137,250,0.1);
border-radius: 8rpx;
border: 2rpx solid #1989FA;
line-height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
.text {
font-size: 28rpx;
}
.icon {
width: 24rpx;
height: 24rpx;
margin-left: 6rpx;
}
}
}
}
.h1 {
margin: 32rpx 0 24rpx 0;
}
.tabs {
display: flex;
justify-content: space-between;
padding-bottom: 24rpx;
.tab {
width: 96rpx;
height: 60rpx;
background: rgba(25,137,250,0.1);
border-radius: 8rpx;
border: 2rpx solid #1989FA;
font-size: 28rpx;
text-align: center;
line-height: 60rpx;
color: $themC;
&.active {
color: #fff;
background-color: $themC;
}
&.long {
width: 336rpx;
}
}
}
.card {
.chart {
}
}
.moreBtn {
width: 200rpx;
height: 60rpx;
background: #FFFFFF;
border-radius: 8rpx;
border: 2rpx solid #E8E9EC;
color: #ADADAD;
text-align: center;
line-height: 60rpx;
margin: 32rpx auto 8rpx auto;
}
}
</style>

71
pages/userCenter/login/login.vue

@ -0,0 +1,71 @@
<template>
<view class="main">
登录方式
</view>
</template>
<script>
export default {
data() {
return {
}
},
onLoad() {
// this.init()
},
methods: {
init() {
uni.login({
provider: 'weixin',
"onlyAuthorize": true,
success: function (loginRes) {
console.log('11')
console.log(loginRes)
//
uni.getUserInfo({
provider: 'weixin',
success: function(info) {
// , info.authResult
console.log('11')
console.log(info)
}
})
},
fail: function (err) {
//
// err.code
}
});
},
}
}
</script>
<style lang="scss" scoped>
// .radioWrap {
// display: flex;
// justify-content: flex-start;
// align-items: center;
// /deep/.radio-btn {
// margin-right: -9rpx !important;
// }
// radio {
// zoom: .8;
// }
// view:nth-child(2) {
// font-size: 24rpx;
// color: #bfbfbf;
// }
// text {
// color: #218DFF;
// font-size: 24rpx;
// }
// }
</style>

187
pages/userCenter/login/loginByPhone.vue

@ -0,0 +1,187 @@
<template>
<view class="main">
<view class="u-back-top">
<view class="backBox">
<u-icon name="arrow-left" color="#333" size="28"></u-icon>
</view>
</view>
<view class="title">短信验证码登录</view>
<view class="form">
<view class="form-item">
<view class="prefix">
<view class="jia">+</view>
<view class="num">86</view>
<view class="" style="margin: 0 32rpx 0 12rpx;">
<u-icon name="arrow-down" color="#333" size="16" ></u-icon>
</view>
</view>
<view class="inputBox my">
<u--input placeholder="请输入手机号" border="none" clearable type="number" maxlength="11" v-model="FormData.phone"></u--input>
</view>
</view>
<view class="form-item">
<view class="inputBox my">
<u--input placeholder="请输入验证码" border="none" clearable style="height: 100%;" :clearable="false" v-model="FormData.code"></u--input>
</view>
<view class="code" @tap='goSms' :class="{active: isPhone&&!codeOn}">{{codeText}}</view>
</view>
<view class="loginBtn" :class="{active: btnHighlight}" @click="submitFn"> </view>
<view class="radioWrap">
<u-checkbox-group >
<u-checkbox v-model="isCheck" shape="circle" label="已阅读并同意" :labelSize="12" ></u-checkbox>
</u-checkbox-group>
<view class="privacyText">
<text>用户协议</text> <text>隐私协议</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { getLoginCode } from '@/config/api.js'
export default {
data() {
return {
isCheck: false,
codeText: '获取验证码',
FormData: {},
codeOn: false
}
},
onLoad() {
},
computed: {
isPhone() {
return uni.$u.test.mobile(this.FormData.phone)
},
btnHighlight() {
return this.isPhone&&uni.$u.test.code(this.FormData.code, 4)
}
},
methods: {
//
groupChangeEnvnt(e) {
this.isCheck = e.value
console.log('是否选择协议', this.isCheck)
},
//
async goSms() {
const {
FormData
} = this
if (!FormData.phone) return this.$u.toast('请输入手机号');
if (!this.isPhone) return this.$u.toast('手机号格式有误');
if (this.codeOn) return
const data = await getLoginCode({
codeType: 1,
phone: FormData.phone,
})
console.log(data)
//
var time = 60;
var timer = setInterval(() => {
time--;
this.codeText = time + "秒后重新发送"
this.codeOn = true;
if (time == 0) {
clearInterval(timer);
this.codeText = "获取验证码";
this.codeOn = false;
}
}, 1000);
},
submitFn() {
uni.switchTab({
url: '/pages/tabbar/index/index'
})
}
}
}
</script>
<style lang="scss" scoped>
.main {
width: 100%;
min-height: 100vh;
background: url('../../../static/images/userCenter/loginTopBg.png') no-repeat;
background-size: 100% 360rpx;
.u-back-top {
padding: 32rpx 0 0 0;
.backBox {
padding: 24rpx;
}
}
.title {
font-size: 48rpx;
color: #333;
padding: 92rpx 0;
text-align: center;
font-weight: 600;
}
.form {
padding: 0 46rpx;
.form-item {
height: 112rpx;
background: #F4F7FF;
border-radius: 16rpx;
width: 100%;
line-height: 112rpx;
display: flex;
margin-bottom: 40rpx;
padding: 0 40rpx;
.prefix {
display: flex;
align-items: center;
font-size: 32rpx;
color: #333;
font-weight: 600;
}
.inputBox {
flex: 1;
}
.code {
color: #BBBBBB;
margin-left: 30rpx;
&.active {
color: $themC
}
}
}
.loginBtn {
width: 100%;
height: 112rpx;
background: rgba(25,137,250,0.3);
border-radius: 16rpx;
text-align: center;
line-height: 112rpx;
font-size: 32rpx;
font-weight: 600;
color: #fff;
margin-top: 100rpx;
&.active {
background: rgba(25,137,250,1);
}
}
.radioWrap {
display: flex;
align-items: center;
margin-top: 40rpx;
.privacyText {
font-size: 24rpx;
color: #888E94;
text {
color: $themC;
}
}
}
}
}
</style>

28
project.config.json

@ -0,0 +1,28 @@
{
"appid": "wx0820ae1a0b635ae2",
"compileType": "miniprogram",
"libVersion": "2.32.2",
"packOptions": {
"ignore": [],
"include": []
},
"setting": {
"coverView": true,
"es6": true,
"postcss": true,
"minified": true,
"enhance": true,
"showShadowRootInWxmlPanel": true,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
}
},
"condition": {},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
}
}

7
project.private.config.json

@ -0,0 +1,7 @@
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"projectname": "recruitStudent",
"setting": {
"compileHotReLoad": true
}
}

12
site.config.js

@ -0,0 +1,12 @@
const VUE_APP_PLATFORM = process.env.VUE_APP_PLATFORM;
module.exports = {
APP_API: VUE_APP_PLATFORM === 'h5' ? 'http://121.41.97.244:8090' : '',
APP_HOST: VUE_APP_PLATFORM === 'h5' ? '' : 'https://www.jaxc.cn/api',
TEMP_HOST: VUE_APP_PLATFORM === 'h5' ? '' : 'http://121.41.97.244:8090 https://www.jaxc.cn/api',
ADD_API: VUE_APP_PLATFORM === 'h5' ? '/addApi': 'http://121.41.97.244:48084', //http://121.41.97.244:48084
APP_NAME: '',
VERSION: '1.0.0',
gaodeMapUrl: 'https://webapi.amap.com/maps?v=1.4.15&key=4545202996c625152b7f2c1aa0ffb8ea&plugin=AMap.DistrictSearch,AMap.CustomLayer,AMap.MarkerClusterer',
locationIcon: 'http://3dtest.hzhuishi.cn/images/location.png',
AThreeFace : true, //是否启用人脸识别
};

BIN
static/images/bigImg/indexTopBanner.png

After

Width: 750  |  Height: 492  |  Size: 394 KiB

BIN
static/images/bigImg/topPageBg.png

After

Width: 750  |  Height: 324  |  Size: 5.0 KiB

BIN
static/images/index/ic_jiaxiao.png

After

Width: 28  |  Height: 28  |  Size: 706 B

BIN
static/images/index/ic_shuaxin.png

After

Width: 24  |  Height: 26  |  Size: 754 B

BIN
static/images/index/radio_cli.png

After

Width: 32  |  Height: 32  |  Size: 1.1 KiB

BIN
static/images/index/radio_nor.png

After

Width: 32  |  Height: 32  |  Size: 919 B

BIN
static/images/index/searchIcon.png

After

Width: 40  |  Height: 40  |  Size: 737 B

BIN
static/images/index/searchIconHui.png

After

Width: 40  |  Height: 40  |  Size: 1.3 KiB

BIN
static/images/logo.png

After

Width: 100  |  Height: 100  |  Size: 10 KiB

BIN
static/images/tabbar/btn_xueche_cli.png

After

Width: 56  |  Height: 56  |  Size: 1.6 KiB

BIN
static/images/tabbar/btn_xueche_nor.png

After

Width: 56  |  Height: 56  |  Size: 1.9 KiB

BIN
static/images/tabbar/kc.png

After

Width: 56  |  Height: 56  |  Size: 943 B

BIN
static/images/tabbar/kcActive.png

After

Width: 56  |  Height: 56  |  Size: 767 B

BIN
static/images/tabbar/sc.png

After

Width: 56  |  Height: 56  |  Size: 2.7 KiB

BIN
static/images/tabbar/scActive.png

After

Width: 56  |  Height: 56  |  Size: 1.7 KiB

BIN
static/images/tabbar/sy.png

After

Width: 58  |  Height: 56  |  Size: 1.0 KiB

BIN
static/images/tabbar/syActive.png

After

Width: 58  |  Height: 56  |  Size: 861 B

BIN
static/images/tabbar/tj.png

After

Width: 56  |  Height: 56  |  Size: 1.0 KiB

BIN
static/images/tabbar/tjActive.png

After

Width: 56  |  Height: 56  |  Size: 955 B

BIN
static/images/tabbar/tk.png

After

Width: 58  |  Height: 56  |  Size: 929 B

BIN
static/images/tabbar/tkActive.png

After

Width: 58  |  Height: 56  |  Size: 826 B

BIN
static/images/tabbar/wd.png

After

Width: 56  |  Height: 56  |  Size: 2.1 KiB

BIN
static/images/tabbar/wdActive.png

After

Width: 56  |  Height: 56  |  Size: 1.3 KiB

BIN
static/images/tabbar/xy.png

After

Width: 56  |  Height: 56  |  Size: 2.2 KiB

BIN
static/images/tabbar/xyActive.png

After

Width: 56  |  Height: 56  |  Size: 1.7 KiB

BIN
static/images/tabbar/zx.png

After

Width: 58  |  Height: 56  |  Size: 587 B

BIN
static/images/tabbar/zxActive.png

After

Width: 58  |  Height: 56  |  Size: 547 B

BIN
static/images/userCenter/loginTopBg.png

After

Width: 375  |  Height: 180  |  Size: 36 KiB

BIN
static/images/userCenter/title_1.png

After

Width: 329  |  Height: 47  |  Size: 8.8 KiB

BIN
static/images/登录流程切图/__MACOSX/登录流程切图/._.DS_Store

BIN
static/images/登录流程切图/__MACOSX/登录流程切图/._btn_1.png

BIN
static/images/登录流程切图/登录流程切图/.DS_Store

BIN
static/images/登录流程切图/登录流程切图/bg_1.png

After

Width: 375  |  Height: 180  |  Size: 36 KiB

BIN
static/images/登录流程切图/登录流程切图/btn_1.png

After

Width: 16  |  Height: 16  |  Size: 461 B

BIN
static/images/登录流程切图/登录流程切图/btn_2.png

After

Width: 16  |  Height: 16  |  Size: 567 B

BIN
static/images/登录流程切图/登录流程切图/title_1.png

After

Width: 329  |  Height: 47  |  Size: 8.8 KiB

BIN
static/logo.png

After

Width: 72  |  Height: 72  |  Size: 3.9 KiB

21
store/getters.js

@ -0,0 +1,21 @@
// export default {
// userInfo: state => state.user.userInfo,
// pushMessage: state => state.push.pushMessage,
// currentAdd: state=>{
// };
export default {
pushMessage: state => state.push.pushMessage,
getCurrentAdd: function (state) {
//返回一个函数用于接收
return function (id) {
let add = state.add.addList.find(item => item.id == id)
if(add) {
return add
}else {
return {}
}
}
}
};

90
store/index.js

@ -0,0 +1,90 @@
import Vue from 'vue';
import Vuex from 'vuex';
import add from './modules/add';
import user from './modules/user';
import getters from './getters';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
currentSchool: '',
userInfo: {},
latLng: {},
classChooseItem: {},
pdfUrl: '',
webViewUrl: '',
havePay: 0, //tabbar 页面是否显示报错小图标
currentMyMsg: {},
billPrice: 0, //开发票的id
TrainingOrderId: '', //退款流程里的驾校id
bankInfo: {
realName: uni.getStorageSync('userInfo').realName,
phoneCode: ''
},
chooseCoachItem: {
coachId: ''
}, //报名时选择的教练
realAuthsuccee: false,
},
getters,
mutations: {
// 选择学校
upDateTrainingSchoolId(state, currentSchool) {
state.currentSchool = currentSchool
},
// webViewUrl
updateWebVeiwUrl(state, url) {
state.webViewUrl = url
},
// 更新用户信息
upDateUserInfo(state, userInfo) {
state.userInfo = userInfo
},
updatePushMyMsg(state, item) {
state.currentMyMsg = item
},
upDateBillPrice(state, billPrice) {
state.billPrice = billPrice
},
upDateTrainingOrderId(state, trainingOrderId) {
state.trainingOrderId = trainingOrderId
},
// tabbar 页面是否显示报错小图标
updateHavePay(state, havePay) {
state.havePay = havePay
},
// 报名时选择教练
upDateCoachItem(state, item) {
state.chooseCoachItem = item
},
// 申请退款时的银行卡信息
upDateBankInfo(state, obj) {
for (let k in obj) {
if (state.bankInfo[k]) {
state.bankInfo[k] = obj[k]
} else {
uni.$set(state.bankInfo, k, obj[k])
}
}
},
// 更新经纬度
updateLatLng(state, item) {
state.latLng = item
},
},
actions: {
},
modules: {
add,
user,
},
});
export default store;

91
store/modules/add.js

@ -0,0 +1,91 @@
// import addApi from '@/api/add.js'; // 引入
const add = {
state: {
addList: []
},
mutations: {
// 更新广告列表
upDateAddList(state, list) {
state.addList = list
},
// 更新当前广告点击量
upDateViews(state, id) {
let add = state.addList.find(item=>item.id==id)
add.clicks ++
},
},
actions: {
// 点击广告
async addClick({commit, dispatch}, curAdd) {
// await dispatch('updateStatistics')
curAdd.clicks ++
if(!curAdd.adBannerDO.jumpUrl) {
return false
}
commit('updateWebVeiwUrl', curAdd.adBannerDO.jumpUrl)
uni.navigateTo({
url:'/pages/commeWebView/addWebView'
})
},
// 获取当前广告
getCurrentAdd({state},id) {
console.log(id)
console.log(state.addList)
let curAdd = state.addList.find(item=>item.id==id)
if(curAdd) {
curAdd.views ++
return curAdd
}else {
return {}
}
},
// // 广告
// async addPageFn({commit,state, dispatch}) {
// await dispatch('updateStatistics')
// let obj = {
// pageNo: 1,
// pageSize: 30,
// adClient: 1
// }
// const [err, res] = await addApi.addPage(obj)
// let list = res.data.records.map(item=>{
// item.views = 0
// item.clicks = 0
// return item
// })
// commit('upDateAddList', list)
// console.log('广告列表')
// console.log(list)
// },
// 更新广告点击量
async updateStatistics({ dispatch,state, commit }) {
let statistics = state.addList.filter(item=>item.views)
console.log('调用更新广告接口')
if(statistics.length) {
let stcsList = statistics.map(add=>{
let obj = {
"adPositionId": add.id,
"adId": add.adId,
"views": add.views,
"clicks": add.clicks
}
return obj
})
const [err, res] = await addApi.batchUpdate(stcsList)
console.log('更新广告点击量请求结果')
console.log(res)
}
}
}
}
export default add

31
store/modules/user.js

@ -0,0 +1,31 @@
import addApi from '../../common/sdk/qqmap-wx-jssdk.min.js'; // 引入
const user = {
state: {
addList: []
},
mutations: {
// 更新广告列表
upDateAddList(state, list) {
state.addList = list
},
},
actions: {
// 点击广告
async addClick({commit, dispatch}, curAdd) {
// await dispatch('updateStatistics')
curAdd.clicks ++
if(!curAdd.adBannerDO.jumpUrl) {
return false
}
commit('updateWebVeiwUrl', curAdd.adBannerDO.jumpUrl)
uni.navigateTo({
url:'/pages/commeWebView/addWebView'
})
},
}
}
export default user

10
uni.promisify.adaptor.js

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

80
uni.scss

@ -0,0 +1,80 @@
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场https://ext.dcloud.net.cn上很多三方插件均使用了这些样式变量
* 如果你是插件开发者建议你使用scss预处理并在插件代码中直接使用这些变量无需 import 这个文件方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者插件使用者你可以通过修改这些变量来定制自己的插件主题实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理你也可以直接在你的 scss 代码中使用如下变量同时无需 import 这个文件
*/
/* 颜色变量 */
@import '@/uni_modules/uview-ui/theme.scss';
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color:#333;//基本色
$uni-text-color-inverse:#fff;//反色
$uni-text-color-grey:#999;//辅助灰色如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable:#c0c0c0;
/* 背景颜色 */
$uni-bg-color:#ffffff;
$uni-bg-color-grey:#f8f8f8;
$uni-bg-color-hover:#f1f1f1;//点击状态颜色
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
/* 边框颜色 */
$uni-border-color:#c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm:12px;
$uni-font-size-base:14px;
$uni-font-size-lg:16;
/* 图片尺寸 */
$uni-img-size-sm:20px;
$uni-img-size-base:26px;
$uni-img-size-lg:40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2C405A; // 文章标题颜色
$uni-font-size-title:20px;
$uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
// 自已定义的
$themC: #1989FA,

6
uni_modules/uni-config-center/changelog.md

@ -0,0 +1,6 @@
## 0.0.3(2022-11-11)
- 修复 config 方法获取根节点为数组格式配置时错误的转化为了对象的Bug
## 0.0.2(2021-04-16)
- 修改插件package信息
## 0.0.1(2021-03-15)
- 初始化项目

81
uni_modules/uni-config-center/package.json

@ -0,0 +1,81 @@
{
"id": "uni-config-center",
"displayName": "uni-config-center",
"version": "0.0.3",
"description": "uniCloud 配置中心",
"keywords": [
"配置",
"配置中心"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "",
"type": "unicloud-template-function"
},
"directories": {
"example": "../../../scripts/dist"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "u"
}
}
}
}
}

93
uni_modules/uni-config-center/readme.md

@ -0,0 +1,93 @@
# 为什么使用uni-config-center
实际开发中很多插件需要配置文件才可以正常运行,如果每个插件都单独进行配置的话就会产生下面这样的目录结构
```bash
cloudfunctions
└─────common 公共模块
├─plugin-a // 插件A对应的目录
│ ├─index.js
│ ├─config.json // plugin-a对应的配置文件
│ └─other-file.cert // plugin-a依赖的其他文件
└─plugin-b // plugin-b对应的目录
├─index.js
└─config.json // plugin-b对应的配置文件
```
假设插件作者要发布一个项目模板,里面使用了很多需要配置的插件,无论是作者发布还是用户使用都是一个大麻烦。
uni-config-center就是用了统一管理这些配置文件的,使用uni-config-center后的目录结构如下
```bash
cloudfunctions
└─────common 公共模块
├─plugin-a // 插件A对应的目录
│ └─index.js
├─plugin-b // plugin-b对应的目录
│ └─index.js
└─uni-config-center
├─index.js // config-center入口文件
├─plugin-a
│ ├─config.json // plugin-a对应的配置文件
│ └─other-file.cert // plugin-a依赖的其他文件
└─plugin-b
└─config.json // plugin-b对应的配置文件
```
使用uni-config-center后的优势
- 配置文件统一管理,分离插件主体和配置信息,更新插件更方便
- 支持对config.json设置schema,插件使用者在HBuilderX内编写config.json文件时会有更好的提示(后续HBuilderX会提供支持)
# 用法
在要使用uni-config-center的公共模块或云函数内引入uni-config-center依赖,请参考:[使用公共模块](https://uniapp.dcloud.net.cn/uniCloud/cf-common)
```js
const createConfig = require('uni-config-center')
const uniIdConfig = createConfig({
pluginId: 'uni-id', // 插件id
defaultConfig: { // 默认配置
tokenExpiresIn: 7200,
tokenExpiresThreshold: 600,
},
customMerge: function(defaultConfig, userConfig) { // 自定义默认配置和用户配置的合并规则,不设置的情况侠会对默认配置和用户配置进行深度合并
// defaudltConfig 默认配置
// userConfig 用户配置
return Object.assign(defaultConfig, userConfig)
}
})
// 以如下配置为例
// {
// "tokenExpiresIn": 7200,
// "passwordErrorLimit": 6,
// "bindTokenToDevice": false,
// "passwordErrorRetryTime": 3600,
// "app-plus": {
// "tokenExpiresIn": 2592000
// },
// "service": {
// "sms": {
// "codeExpiresIn": 300
// }
// }
// }
// 获取配置
uniIdConfig.config() // 获取全部配置,注意:uni-config-center内不存在对应插件目录时会返回空对象
uniIdConfig.config('tokenExpiresIn') // 指定键值获取配置,返回:7200
uniIdConfig.config('service.sms.codeExpiresIn') // 指定键值获取配置,返回:300
uniIdConfig.config('tokenExpiresThreshold', 600) // 指定键值获取配置,如果不存在则取传入的默认值,返回:600
// 获取文件绝对路径
uniIdConfig.resolve('custom-token.js') // 获取uni-config-center/uni-id/custom-token.js文件的路径
// 引用文件(require)
uniIDConfig.requireFile('custom-token.js') // 使用require方式引用uni-config-center/uni-id/custom-token.js文件。文件不存在时返回undefined,文件内有其他错误导致require失败时会抛出错误。
// 判断是否包含某文件
uniIDConfig.hasFile('custom-token.js') // 配置目录是否包含某文件,true: 文件存在,false: 文件不存在
```

1
uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js
File diff suppressed because it is too large
View File

9
uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/package.json

@ -0,0 +1,9 @@
{
"name": "uni-config-center",
"version": "0.0.3",
"description": "配置中心",
"main": "index.js",
"keywords": [],
"author": "DCloud",
"license": "Apache-2.0"
}

25
uni_modules/uni-open-bridge-common/changelog.md

@ -0,0 +1,25 @@
## 1.2.0(2023-04-27)
- 优化 微信小程序平台 使用微信新增 API getStableAccessToken 获取 access_token, access_token 有效期内重复调用该接口不会更新 access_token, [详情](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html)
## 1.1.5(2023-03-27)
- 修复 微信小程序平台 某些情况下 encrypt_key 插入错误的问题
## 1.1.4(2023-03-13)
- 修复 平台 weixin-web
## 1.1.3(2023-03-13)
- 新增 支持旧版本 uni-id 配置
- 新增 支持平台 weixin-app|qq-mp|qq-app
## 1.1.2(2023-02-28)
- 新增 config 配置错误提示语
## 1.1.1(2023-02-28)
- 新增 支持 provider 参数,和 platform 保持一致
## 1.1.0(2023-02-27)
- 重要更新 调整数据库key格式,兼容旧版本API,如果开发者通过手动拼接key查询数据库需要修改现有逻辑
+ 原格式: uni-id:[dcloudAppid]:[platform]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket]
+ 新格式: uni-id:[provider]:[appid]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket]
## 1.0.4(2022-09-21)
- 新增 支持使用阿里云固定IP获取微信公众号H5凭据 access_token、ticket,开发者需要在微信公众平台配置阿里云固定IP,[固定IP详情](https://uniapp.dcloud.net.cn/uniCloud/cf-functions.html#aliyun-eip)
## 1.0.3(2022-09-06)
- 修复 过期时间问题,容错 AccessToken 默认 fallback 逻辑,当微信服务器没有返回过期时间时设置为2小时后过期
## 1.0.2(2022-09-02)
- 新增 依赖数据表schema opendb-open-data
## 1.0.0(2022-08-22)
- 首次发布

84
uni_modules/uni-open-bridge-common/package.json

@ -0,0 +1,84 @@
{
"id": "uni-open-bridge-common",
"displayName": "uni-open-bridge-common",
"version": "1.2.0",
"description": "统一接管微信等三方平台认证凭据",
"keywords": [
"uni-open-bridge-common",
"access_token",
"session_key",
"ticket"
],
"repository": "",
"engines": {
"HBuilderX": "^3.5.2"
},
"dcloudext": {
"type": "unicloud-template-function",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "u",
"vue3": "u"
},
"App": {
"app-vue": "u",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "u",
"Android Browser": "u",
"微信浏览器(Android)": "u",
"QQ浏览器(Android)": "u"
},
"H5-pc": {
"Chrome": "u",
"IE": "u",
"Edge": "u",
"Firefox": "u",
"Safari": "u"
},
"小程序": {
"微信": "u",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

5
uni_modules/uni-open-bridge-common/readme.md

@ -0,0 +1,5 @@
# uni-open-bridge-common
`uni-open-bridge-common` 是统一接管微信等三方平台认证凭据(包括但不限于`access_token`、`session_key`、`encrypt_key`、`ticket`)的开源库。
文档链接 [https://uniapp.dcloud.net.cn/uniCloud/uni-open-bridge#common](https://uniapp.dcloud.net.cn/uniCloud/uni-open-bridge#common)

26
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/bridge-error.js

@ -0,0 +1,26 @@
'use strict';
class BridgeError extends Error {
constructor(code, message) {
super(message)
this._code = code
}
get code() {
return this._code
}
get errCode() {
return this._code
}
get errMsg() {
return this.message
}
}
module.exports = {
BridgeError
}

124
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/config.js

@ -0,0 +1,124 @@
'use strict';
const {
ProviderType
} = require('./consts.js')
const configCenter = require('uni-config-center')
// 多维数据为兼容uni-id以前版本配置
const OauthConfig = {
'weixin-app': [
['app', 'oauth', 'weixin'],
['app-plus', 'oauth', 'weixin']
],
'weixin-mp': [
['mp-weixin', 'oauth', 'weixin']
],
'weixin-h5': [
['web', 'oauth', 'weixin-h5'],
['h5-weixin', 'oauth', 'weixin'],
['h5', 'oauth', 'weixin']
],
'weixin-web': [
['web', 'oauth', 'weixin-web']
],
'qq-app': [
['app', 'oauth', 'qq'],
['app-plus', 'oauth', 'qq']
],
'qq-mp': [
['mp-qq', 'oauth', 'qq']
]
}
const Support_Platforms = [
ProviderType.WEIXIN_MP,
ProviderType.WEIXIN_H5,
ProviderType.WEIXIN_APP,
ProviderType.WEIXIN_WEB,
ProviderType.QQ_MP,
ProviderType.QQ_APP
]
class ConfigBase {
constructor() {
const uniIdConfigCenter = configCenter({
pluginId: 'uni-id'
})
this._uniIdConfig = uniIdConfigCenter.config()
}
getAppConfig(appid) {
if (Array.isArray(this._uniIdConfig)) {
return this._uniIdConfig.find((item) => {
return (item.dcloudAppid === appid)
})
}
return this._uniIdConfig
}
}
class AppConfig extends ConfigBase {
constructor() {
super()
}
get(appid, platform) {
if (!this.isSupport(platform)) {
return null
}
let appConfig = this.getAppConfig(appid)
if (!appConfig) {
return null
}
return this.getOauthConfig(appConfig, platform)
}
isSupport(platformName) {
return (Support_Platforms.indexOf(platformName) >= 0)
}
getOauthConfig(appConfig, platformName) {
let treePath = OauthConfig[platformName]
let node = this.findNode(appConfig, treePath)
if (node && node.appid && node.appsecret) {
return {
appid: node.appid,
secret: node.appsecret
}
}
return null
}
findNode(treeNode, arrayPath) {
let node = treeNode
for (let treePath of arrayPath) {
for (let name of treePath) {
const currentNode = node[name]
if (currentNode) {
node = currentNode
} else {
node = null
break
}
}
if (node === null) {
node = treeNode
} else {
break
}
}
return node
}
}
module.exports = {
AppConfig
};

30
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/consts.js

@ -0,0 +1,30 @@
'use strict';
const TAG = "UNI_OPEN_BRIDGE"
const HTTP_STATUS = {
SUCCESS: 200
}
const ProviderType = {
WEIXIN_MP: 'weixin-mp',
WEIXIN_H5: 'weixin-h5',
WEIXIN_APP: 'weixin-app',
WEIXIN_WEB: 'weixin-web',
QQ_MP: 'qq-mp',
QQ_APP: 'qq-app'
}
// old
const PlatformType = ProviderType
const ErrorCodeType = {
SYSTEM_ERROR: TAG + "_SYSTEM_ERROR"
}
module.exports = {
HTTP_STATUS,
ProviderType,
PlatformType,
ErrorCodeType
}

317
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/index.js

@ -0,0 +1,317 @@
'use strict';
const {
PlatformType,
ProviderType,
ErrorCodeType
} = require('./consts.js')
const {
AppConfig
} = require('./config.js')
const {
Storage
} = require('./storage.js')
const {
BridgeError
} = require('./bridge-error.js')
const {
WeixinServer
} = require('./weixin-server.js')
const appConfig = new AppConfig()
class AccessToken extends Storage {
constructor() {
super('access-token', ['provider', 'appid'])
}
async update(key) {
super.update(key)
const result = await this.getByWeixinServer(key)
return this.set(key, result.value, result.duration)
}
async fallback(key) {
return this.getByWeixinServer(key)
}
async getByWeixinServer(key) {
const oauthConfig = appConfig.get(key.dcloudAppid, key.provider)
let methodName
if (key.provider === ProviderType.WEIXIN_MP) {
methodName = 'GetMPAccessTokenData'
} else if (key.provider === ProviderType.WEIXIN_H5) {
methodName = 'GetH5AccessTokenData'
} else {
throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, "provider invalid")
}
const responseData = await WeixinServer[methodName](oauthConfig)
const duration = responseData.expires_in || (60 * 60 * 2)
delete responseData.expires_in
return {
value: responseData,
duration
}
}
}
class UserAccessToken extends Storage {
constructor() {
super('user-access-token', ['provider', 'appid', 'openid'])
}
}
class SessionKey extends Storage {
constructor() {
super('session-key', ['provider', 'appid', 'openid'])
}
}
class Encryptkey extends Storage {
constructor() {
super('encrypt-key', ['provider', 'appid', 'openid'])
}
async update(key) {
super.update(key)
const result = await this.getByWeixinServer(key)
return this.set(key, result.value, result.duration)
}
getKeyString(key) {
return `${super.getKeyString(key)}-${key.version}`
}
getExpiresIn(value) {
if (value <= 0) {
return 60
}
return value
}
async fallback(key) {
return this.getByWeixinServer(key)
}
async getByWeixinServer(key) {
const accessToken = await Factory.Get(AccessToken, key)
const userSession = await Factory.Get(SessionKey, key)
const responseData = await WeixinServer.GetUserEncryptKeyData({
openid: key.openid,
access_token: accessToken.access_token,
session_key: userSession.session_key
})
const keyInfo = responseData.key_info_list.find((item) => {
return item.version === key.version
})
if (!keyInfo) {
throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, 'key version invalid')
}
const value = {
encrypt_key: keyInfo.encrypt_key,
iv: keyInfo.iv
}
return {
value,
duration: keyInfo.expire_in
}
}
}
class Ticket extends Storage {
constructor() {
super('ticket', ['provider', 'appid'])
}
async update(key) {
super.update(key)
const result = await this.getByWeixinServer(key)
return this.set(key, result.value, result.duration)
}
async fallback(key) {
return this.getByWeixinServer(key)
}
async getByWeixinServer(key) {
const accessToken = await Factory.Get(AccessToken, {
dcloudAppid: key.dcloudAppid,
provider: ProviderType.WEIXIN_H5
})
const responseData = await WeixinServer.GetH5TicketData(accessToken)
const duration = responseData.expires_in || (60 * 60 * 2)
delete responseData.expires_in
delete responseData.errcode
delete responseData.errmsg
return {
value: responseData,
duration
}
}
}
const Factory = {
async Get(T, key, fallback) {
Factory.FixOldKey(key)
return Factory.MakeUnique(T).get(key, fallback)
},
async Set(T, key, value, expiresIn) {
Factory.FixOldKey(key)
return Factory.MakeUnique(T).set(key, value, expiresIn)
},
async Remove(T, key) {
Factory.FixOldKey(key)
return Factory.MakeUnique(T).remove(key)
},
async Update(T, key) {
Factory.FixOldKey(key)
return Factory.MakeUnique(T).update(key)
},
FixOldKey(key) {
if (!key.provider) {
key.provider = key.platform
}
const configData = appConfig.get(key.dcloudAppid, key.provider)
if (!configData) {
throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, 'appid or provider invalid')
}
key.appid = configData.appid
},
MakeUnique(T) {
return new T()
}
}
// exports
async function getAccessToken(key, fallback) {
return Factory.Get(AccessToken, key, fallback)
}
async function setAccessToken(key, value, expiresIn) {
return Factory.Set(AccessToken, key, value, expiresIn)
}
async function removeAccessToken(key) {
return Factory.Remove(AccessToken, key)
}
async function updateAccessToken(key) {
return Factory.Update(AccessToken, key)
}
async function getUserAccessToken(key, fallback) {
return Factory.Get(UserAccessToken, key, fallback)
}
async function setUserAccessToken(key, value, expiresIn) {
return Factory.Set(UserAccessToken, key, value, expiresIn)
}
async function removeUserAccessToken(key) {
return Factory.Remove(UserAccessToken, key)
}
async function getSessionKey(key, fallback) {
return Factory.Get(SessionKey, key, fallback)
}
async function setSessionKey(key, value, expiresIn) {
return Factory.Set(SessionKey, key, value, expiresIn)
}
async function removeSessionKey(key) {
return Factory.Remove(SessionKey, key)
}
async function getEncryptKey(key, fallback) {
return Factory.Get(Encryptkey, key, fallback)
}
async function setEncryptKey(key, value, expiresIn) {
return Factory.Set(Encryptkey, key, value, expiresIn)
}
async function removeEncryptKey(key) {
return Factory.Remove(Encryptkey, key)
}
async function updateEncryptKey(key) {
return Factory.Update(Encryptkey, key)
}
async function getTicket(key, fallback) {
return Factory.Get(Ticket, key, fallback)
}
async function setTicket(key, value, expiresIn) {
return Factory.Set(Ticket, key, value, expiresIn)
}
async function removeTicket(key) {
return Factory.Remove(Ticket, key)
}
async function updateTicket(key) {
return Factory.Update(Ticket, key)
}
module.exports = {
getAccessToken,
setAccessToken,
removeAccessToken,
updateAccessToken,
getUserAccessToken,
setUserAccessToken,
removeUserAccessToken,
getSessionKey,
setSessionKey,
removeSessionKey,
getEncryptKey,
setEncryptKey,
removeEncryptKey,
updateEncryptKey,
getTicket,
setTicket,
removeTicket,
updateTicket,
ProviderType,
PlatformType,
WeixinServer,
ErrorCodeType
}

15
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/package.json

@ -0,0 +1,15 @@
{
"name": "uni-open-bridge-common",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center"
}
}

111
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/storage.js

@ -0,0 +1,111 @@
'use strict';
const {
Validator
} = require('./validator.js')
const {
CacheKeyCascade
} = require('./uni-cloud-cache.js')
const {
BridgeError
} = require('./bridge-error.js')
class Storage {
constructor(type, keys) {
this._type = type || null
this._keys = keys || []
}
async get(key, fallback) {
this.validateKey(key)
const result = await this.create(key, fallback).get()
return result.value
}
async set(key, value, expiresIn) {
this.validateKey(key)
this.validateValue(value)
const expires_in = this.getExpiresIn(expiresIn)
if (expires_in !== 0) {
await this.create(key).set(this.getValue(value), expires_in)
}
}
async remove(key) {
this.validateKey(key)
await this.create(key).remove()
}
// virtual
async update(key) {
this.validateKey(key)
}
async ttl(key) {
this.validateKey(key)
// 后续考虑支持
}
async fallback(key) {}
getKeyString(key) {
const keyArray = [Storage.Prefix]
this._keys.forEach((name) => {
keyArray.push(key[name])
})
keyArray.push(this._type)
return keyArray.join(':')
}
getValue(value) {
return value
}
getExpiresIn(value) {
if (value !== undefined) {
return value
}
return -1
}
validateKey(key) {
Validator.Key(this._keys, key)
}
validateValue(value) {
Validator.Value(value)
}
create(key, fallback) {
const keyString = this.getKeyString(key)
const options = {
layers: [{
type: 'database',
key: keyString
}, {
type: 'redis',
key: keyString
}]
}
const _this = this
return new CacheKeyCascade({
...options,
fallback: async function() {
if (fallback) {
return fallback(key)
} else if (_this.fallback) {
return _this.fallback(key)
}
}
})
}
}
Storage.Prefix = "uni-id"
module.exports = {
Storage
};

324
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/uni-cloud-cache.js

@ -0,0 +1,324 @@
const db = uniCloud.database()
function getType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
}
const validator = {
key: function(value) {
const err = new Error('Invalid key')
if (typeof value !== 'string') {
throw err
}
const valueTrim = value.trim()
if (!valueTrim || valueTrim !== value) {
throw err
}
},
value: function(value) {
// 仅作简单校验
const type = getType(value)
const validValueType = ['null', 'number', 'string', 'array', 'object']
if (validValueType.indexOf(type) === -1) {
throw new Error('Invalid value type')
}
},
duration: function(value) {
const err = new Error('Invalid duration')
if (value === undefined) {
return
}
if (typeof value !== 'number' || value === 0) {
throw err
}
}
}
/**
* 入库时 expired 为过期时间对应的时间戳永不过期用-1表示
* 返回结果时 与redis对齐-1表示永不过期-2表示已过期或不存在
*/
class DatabaseCache {
constructor({
collection = 'opendb-open-data'
} = {}) {
this.type = 'db'
this.collection = db.collection(collection)
}
_serializeValue(value) {
return value === undefined ? null : JSON.stringify(value)
}
_deserializeValue(value) {
return value ? JSON.parse(value) : value
}
async set(key, value, duration) {
validator.key(key)
validator.value(value)
validator.duration(duration)
value = this._serializeValue(value)
await this.collection.doc(key).set({
value,
expired: duration && duration !== -1 ? Date.now() + (duration * 1000) : -1
})
}
async _getWithDuration(key) {
const getKeyRes = await this.collection.doc(key).get()
const record = getKeyRes.data[0]
if (!record) {
return {
value: null,
duration: -2
}
}
const value = this._deserializeValue(record.value)
const expired = record.expired
if (expired === -1) {
return {
value,
duration: -1
}
}
const duration = expired - Date.now()
if (duration <= 0) {
await this.remove(key)
return {
value: null,
duration: -2
}
}
return {
value,
duration: Math.floor(duration / 1000)
}
}
async get(key, {
withDuration = true
} = {}) {
const result = await this._getWithDuration(key)
if (!withDuration) {
delete result.duration
}
return result
}
async remove(key) {
await this.collection.doc(key).remove()
}
}
class RedisCache {
constructor() {
this.type = 'redis'
this.redis = uniCloud.redis()
}
_serializeValue(value) {
return value === undefined ? null : JSON.stringify(value)
}
_deserializeValue(value) {
return value ? JSON.parse(value) : value
}
async set(key, value, duration) {
validator.key(key)
validator.value(value)
validator.duration(duration)
value = this._serializeValue(value)
if (!duration || duration === -1) {
await this.redis.set(key, value)
} else {
await this.redis.set(key, value, 'EX', duration)
}
}
async get(key, {
withDuration = false
} = {}) {
let value = await this.redis.get(key)
value = this._deserializeValue(value)
if (!withDuration) {
return {
value
}
}
const durationSecond = await this.redis.ttl(key)
let duration
switch (durationSecond) {
case -1:
duration = -1
break
case -2:
duration = -2
break
default:
duration = durationSecond
break
}
return {
value,
duration
}
}
async remove(key) {
await this.redis.del(key)
}
}
class Cache {
constructor({
type,
collection
} = {}) {
if (type === 'database') {
return new DatabaseCache({
collection
})
} else if (type === 'redis') {
return new RedisCache()
} else {
throw new Error('Invalid cache type')
}
}
}
class CacheKey {
constructor({
type,
collection,
cache,
key,
fallback
} = {}) {
this.cache = cache || new Cache({
type,
collection
})
this.key = key
this.fallback = fallback
}
async set(value, duration) {
await this.cache.set(this.key, value, duration)
}
async setWithSync(value, duration, syncMethod) {
await Promise.all([
this.set(this.key, value, duration),
syncMethod(value, duration)
])
}
async get() {
let {
value,
duration
} = await this.cache.get(this.key)
if (value !== null && value !== undefined) {
return {
value,
duration
}
}
if (!this.fallback) {
return {
value: null,
duration: -2
}
}
const fallbackResult = await this.fallback()
value = fallbackResult.value
duration = fallbackResult.duration
if (value !== null && duration !== undefined) {
await this.cache.set(this.key, value, duration)
}
return {
value,
duration
}
}
async remove() {
await this.cache.remove(this.key)
}
}
class CacheKeyCascade {
constructor({
layers, // [{cache, type, collection, key}] 从低级到高级排序,[DbCacheKey, RedisCacheKey]
fallback
} = {}) {
this.layers = layers
this.cacheLayers = []
let lastCacheKey
for (let i = 0; i < layers.length; i++) {
const {
type,
cache,
collection,
key
} = layers[i]
const lastCacheKeyTemp = lastCacheKey
try {
const currentCacheKey = new CacheKey({
type,
collection,
cache,
key,
fallback: i === 0 ? fallback : function() {
return lastCacheKeyTemp.get()
}
})
this.cacheLayers.push(currentCacheKey)
lastCacheKey = currentCacheKey
} catch (e) {}
}
this.highLevelCache = lastCacheKey
}
async set(value, duration) {
return Promise.all(
this.cacheLayers.map(item => {
return item.set(value, duration)
})
)
}
async setWithSync(value, duration, syncMethod) {
const setPromise = this.cacheLayers.map(item => {
return item.set(value, duration)
})
return Promise.all(
[
...setPromise,
syncMethod(value, duration)
]
)
}
async get() {
return this.highLevelCache.get()
}
async remove() {
await Promise.all(
this.cacheLayers.map(cacheKeyItem => {
return cacheKeyItem.remove()
})
)
}
}
module.exports = {
Cache,
DatabaseCache,
RedisCache,
CacheKey,
CacheKeyCascade
}

31
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/validator.js

@ -0,0 +1,31 @@
const Validator = {
Key(keyArray, parameters) {
for (let i = 0; i < keyArray.length; i++) {
const keyName = keyArray[i]
if (typeof parameters[keyName] !== 'string') {
Validator.ThrowNewError(`Invalid ${keyName}`)
}
if (parameters[keyName].length < 1) {
Validator.ThrowNewError(`Invalid ${keyName}`)
}
}
},
Value(value) {
if (value === undefined) {
Validator.ThrowNewError('Invalid Value')
}
if (typeof value !== 'object') {
Validator.ThrowNewError('Invalid Value Type')
}
},
ThrowNewError(message) {
throw new Error(message)
}
}
module.exports = {
Validator
}

203
uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/weixin-server.js

@ -0,0 +1,203 @@
'use strict';
const crypto = require('crypto')
const {
HTTP_STATUS
} = require('./consts.js')
const {
BridgeError
} = require('./bridge-error.js')
class WeixinServer {
constructor(options = {}) {
this._appid = options.appid
this._secret = options.secret
}
getAccessToken() {
return uniCloud.httpclient.request(WeixinServer.AccessToken_Url, {
dataType: 'json',
method: 'POST',
contentType: 'json',
data: {
appid: this._appid,
secret: this._secret,
grant_type: "client_credential"
}
})
}
// 使用客户端获取的 code 从微信服务器换取 openid,code 仅可使用一次
codeToSession(code) {
return uniCloud.httpclient.request(WeixinServer.Code2Session_Url, {
dataType: 'json',
data: {
appid: this._appid,
secret: this._secret,
js_code: code,
grant_type: 'authorization_code'
}
})
}
getUserEncryptKey({
access_token,
openid,
session_key
}) {
console.log(access_token, openid, session_key);
const signature = crypto.createHmac('sha256', session_key).update('').digest('hex')
return uniCloud.httpclient.request(WeixinServer.User_Encrypt_Key_Url, {
dataType: 'json',
method: 'POST',
dataAsQueryString: true,
data: {
access_token,
openid: openid,
signature: signature,
sig_method: 'hmac_sha256'
}
})
}
getH5AccessToken() {
return uniCloud.httpclient.request(WeixinServer.AccessToken_H5_Url, {
dataType: 'json',
method: 'GET',
data: {
appid: this._appid,
secret: this._secret,
grant_type: "client_credential"
}
})
}
getH5Ticket(access_token) {
return uniCloud.httpclient.request(WeixinServer.Ticket_Url, {
dataType: 'json',
dataAsQueryString: true,
method: 'POST',
data: {
access_token
}
})
}
getH5AccessTokenForEip() {
return uniCloud.httpProxyForEip.postForm(WeixinServer.AccessToken_H5_Url, {
appid: this._appid,
secret: this._secret,
grant_type: "client_credential"
}, {
dataType: 'json'
})
}
getH5TicketForEip(access_token) {
return uniCloud.httpProxyForEip.postForm(WeixinServer.Ticket_Url, {
access_token
}, {
dataType: 'json',
dataAsQueryString: true
})
}
}
WeixinServer.AccessToken_Url = 'https://api.weixin.qq.com/cgi-bin/stable_token'
WeixinServer.Code2Session_Url = 'https://api.weixin.qq.com/sns/jscode2session'
WeixinServer.User_Encrypt_Key_Url = 'https://api.weixin.qq.com/wxa/business/getuserencryptkey'
WeixinServer.AccessToken_H5_Url = 'https://api.weixin.qq.com/cgi-bin/token'
WeixinServer.Ticket_Url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi'
WeixinServer.GetMPAccessToken = function(options) {
return new WeixinServer(options).getAccessToken()
}
WeixinServer.GetCodeToSession = function(options) {
return new WeixinServer(options).codeToSession(options.code)
}
WeixinServer.GetUserEncryptKey = function(options) {
return new WeixinServer(options).getUserEncryptKey(options)
}
WeixinServer.GetH5AccessToken = function(options) {
return new WeixinServer(options).getH5AccessToken()
}
WeixinServer.GetH5Ticket = function(options) {
return new WeixinServer(options).getH5Ticket(options.access_token)
}
////////////////////////////////////////////////////////////////
function isAliyun() {
return (uniCloud.getCloudInfos()[0].provider === 'aliyun')
}
WeixinServer.GetResponseData = function(response) {
console.log("WeixinServer::response", response)
if (!(response.status === HTTP_STATUS.SUCCESS || response.statusCodeValue === HTTP_STATUS.SUCCESS)) {
throw new BridgeError(response.status || response.statusCodeValue, response.status || response.statusCodeValue)
}
const responseData = response.data || response.body
if (responseData.errcode !== undefined && responseData.errcode !== 0) {
throw new BridgeError(responseData.errcode, responseData.errmsg)
}
return responseData
}
WeixinServer.GetMPAccessTokenData = async function(options) {
const response = await new WeixinServer(options).getAccessToken()
return WeixinServer.GetResponseData(response)
}
WeixinServer.GetCodeToSessionData = async function(options) {
const response = await new WeixinServer(options).codeToSession(options.code)
return WeixinServer.GetResponseData(response)
}
WeixinServer.GetUserEncryptKeyData = async function(options) {
const response = await new WeixinServer(options).getUserEncryptKey(options)
return WeixinServer.GetResponseData(response)
}
WeixinServer.GetH5AccessTokenData = async function(options) {
const ws = new WeixinServer(options)
let response
if (isAliyun()) {
response = await ws.getH5AccessTokenForEip()
if (typeof response === 'string') {
response = JSON.parse(response)
}
} else {
response = await ws.getH5AccessToken()
}
return WeixinServer.GetResponseData(response)
}
WeixinServer.GetH5TicketData = async function(options) {
const ws = new WeixinServer(options)
let response
if (isAliyun()) {
response = await ws.getH5TicketForEip(options.access_token)
if (typeof response === 'string') {
response = JSON.parse(response)
}
} else {
response = await ws.getH5Ticket(options.access_token)
}
return WeixinServer.GetResponseData(response)
}
module.exports = {
WeixinServer
}

19
uni_modules/uni-open-bridge-common/uniCloud/database/opendb-open-data.schema.json

@ -0,0 +1,19 @@
// : https://uniapp.dcloud.net.cn/uniCloud/schema
{
"bsonType": "object",
"required": ["_id", "value"],
"properties": {
"_id": {
"bsonType": "string",
"description": "key,格式:uni-id:[provider]:[appid]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket]"
},
"value": {
"bsonType": "object",
"description": "字段_id对应的值"
},
"expired": {
"bsonType": "date",
"description": "过期时间"
}
}
}

8
uni_modules/uni-scss/changelog.md

@ -0,0 +1,8 @@
## 1.0.3(2022-01-21)
- 优化 组件示例
## 1.0.2(2021-11-22)
- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
## 1.0.1(2021-11-22)
- 修复 vue3中scss语法兼容问题
## 1.0.0(2021-11-18)
- init

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

Loading…
Cancel
Save