diff --git a/README.md b/README.md index 80d49f3..26f1c99 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,24 @@ yarn build ### Customize configuration See [Configuration Reference](https://cli.vuejs.org/config/). + +### 库 + +``` + vuex、numeral + + 依赖-> sass-loader/ node-sass/ +``` + +*sass 需要cnpm装,npm安装报错* + +> 原型链接:https://org.modao.cc/app/d33151880c5a6a975b9f3af1868bf3a125a8923b?simulator_type=outside_artboard&sticky +> 设计稿链接:https://lanhuapp.com/web/#/item/project/board?pid=ea91cb92-0502-40c9-a746-e68d121e26fd + +### ipx 底部适配高度函数 + +```css + padding-bottom: 0; + padding-bottom: calc( 0 + constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */ + padding-bottom: calc( 0 + env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */ +``` \ No newline at end of file diff --git a/package.json b/package.json index 2a5eee7..a0908ec 100644 --- a/package.json +++ b/package.json @@ -80,8 +80,12 @@ "jest": "^25.4.0", "mini-types": "*", "miniprogram-api-typings": "*", + "node-sass": "^5.0.0", + "numeral": "^2.0.6", "postcss-comment": "^2.0.0", - "vue-template-compiler": "^2.6.11" + "sass-loader": "^10.1.1", + "vue-template-compiler": "^2.6.11", + "vuex": "^3.6.0" }, "browserslist": [ "Android >= 4", diff --git a/src/App.vue b/src/App.vue index 8c2b732..08441c9 100644 --- a/src/App.vue +++ b/src/App.vue @@ -14,4 +14,27 @@ diff --git a/src/components/authorize_modal/authorize_modal.vue b/src/components/authorize_modal/authorize_modal.vue new file mode 100644 index 0000000..a1252a8 --- /dev/null +++ b/src/components/authorize_modal/authorize_modal.vue @@ -0,0 +1,149 @@ + + + \ No newline at end of file diff --git a/src/js/api.js b/src/js/api.js new file mode 100644 index 0000000..67f4729 --- /dev/null +++ b/src/js/api.js @@ -0,0 +1,12 @@ +export const ORIGIN = ``; // 测试 +// export const ORIGIN = ``; // 正式 + +export const API = { + ex: `${ORIGIN}/test/test`, +} + + +export default { ORIGIN, API }; + + + diff --git a/src/js/server.js b/src/js/server.js new file mode 100644 index 0000000..e5cebad --- /dev/null +++ b/src/js/server.js @@ -0,0 +1,91 @@ +import util from '../utils/util'; +// import { app as vm } from '../main'; +const islog = true; + +export class Server { + request(url,data,method,header,isDefaultGet,failMsg){ + return new Promise(async (rs,rj)=>{ + // const _store = vm.$store; + // let storeInfo = _store.state.storeInfo || null; + // if(storeInfo)data['brand_id'] = storeInfo.brand_id; + + const _token = uni.getStorageSync('token') || ''; + if(_token)data['token'] = _token; + if(method === 'POST'&&_token)url = url + `?token=${_token}` + uni.request({ + url, + data, + method, // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT + header: {...header}, // 设置请求的 header // {'custom-header': 'application/json'} + success: res=>{ + if(islog)console.log('req success---->',{ + link: url, + query: data, + method: method, + data: res, + }) + if(isDefaultGet){ + if(failMsg == '')throw Error('默认回调,失败提示不能为空 key -> failMsg'); + defaultGet({ + url, + data, + res, + failMsg, + resolve: rs, + reject: rj + }); + return + } + rs(res); + }, + fail: err=>{ + if(islog)console.log('req fail---->',{ + link: url, + query: data, + method: method, + data: err, + }) + util.hideLoad(); + if(typeof(err.errMsg) == 'string'&&(err.errMsg.indexOf('timeout')!=-1 || err.errMsg.indexOf('interrupted')!=-1 || err.errMsg.indexOf('请求超时')!=-1)){ + util.showNone('网络超时!'); + }else if(typeof(err.errMsg) == 'string'){ + util.showNone(err.errMsg); + }else{ + util.showNone('请求数据失败!请检查当前网络状态。'); + } + rj(err); + }, + }) + }) + + function defaultGet({res,failMsg,resolve,reject,url,data}){ + if(res.data.code == 0){ + resolve(res.data.data); + }else{ + util.hideLoad(); + util.showNone(res.data.message || failMsg || ''); + reject({url,res,data}); + } + } + } + get({url,data={},header={},isDefaultGet=true,failMsg=''}){ + return this.request(url,data,'GET',header,isDefaultGet,failMsg); + } + + post({url,data={},header={'custom-header': 'application/json'},isDefaultGet=true,failMsg=''}){ + return this.request(url,data,'POST',header,isDefaultGet,failMsg); + } + + uploadFile({url,filePath,onProgressCallBack,formData={}}){ + return new Promise((rs,rj)=>{ + let uploadTask = wx.uploadFile({ + url,filePath,formData,name:'file',success:rs,fail:rj + }) + uploadTask.onProgressUpdate(res=>onProgressCallBack&&onProgressCallBack(res)) + }) + } +} + +export const servers = new Server(); + +export default { servers, Server }; \ No newline at end of file diff --git a/src/pages.json b/src/pages.json index 1aea851..bcbc40e 100644 --- a/src/pages.json +++ b/src/pages.json @@ -3,14 +3,31 @@ { "path": "pages/index/index", "style": { - "navigationBarTitleText": "uni-app" + "navigationBarTitleText": "欧轩智能教裁助手" + } + }, + { + "path": "pages/feedback/feedback", + "style": { + } + }, + + { + "path": "pages/message_list/message_list", + "style": { + } + }, + { + "path": "pages/login/login", + "style": { } } + ], "globalStyle": { - "navigationBarTextStyle": "black", - "navigationBarTitleText": "uni-app", - "navigationBarBackgroundColor": "#F8F8F8", - "backgroundColor": "#F8F8F8" + "navigationBarTextStyle": "white", + "navigationBarTitleText": "欧轩智能教裁助手", + "navigationBarBackgroundColor": "#009874", + "backgroundColor": "#f2f2f7" } } diff --git a/src/pages/feedback/feedback.vue b/src/pages/feedback/feedback.vue new file mode 100644 index 0000000..5293459 --- /dev/null +++ b/src/pages/feedback/feedback.vue @@ -0,0 +1,151 @@ + + + + + \ No newline at end of file diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue index bd118ba..54ab78c 100644 --- a/src/pages/index/index.vue +++ b/src/pages/index/index.vue @@ -1,49 +1,215 @@ - - diff --git a/src/pages/login/login.vue b/src/pages/login/login.vue new file mode 100644 index 0000000..db8ff22 --- /dev/null +++ b/src/pages/login/login.vue @@ -0,0 +1,205 @@ + + + + + \ No newline at end of file diff --git a/src/pages/message_list/message_list.vue b/src/pages/message_list/message_list.vue new file mode 100644 index 0000000..ba2f994 --- /dev/null +++ b/src/pages/message_list/message_list.vue @@ -0,0 +1,94 @@ + + + + + \ No newline at end of file diff --git a/src/static/images/authorize.png b/src/static/images/authorize.png new file mode 100644 index 0000000..bc7f1fb Binary files /dev/null and b/src/static/images/authorize.png differ diff --git a/src/static/logo.png b/src/static/logo.png deleted file mode 100644 index b5771e2..0000000 Binary files a/src/static/logo.png and /dev/null differ diff --git a/src/store/actions.js b/src/store/actions.js new file mode 100644 index 0000000..73e1a6d --- /dev/null +++ b/src/store/actions.js @@ -0,0 +1,16 @@ +// 异步方法 +import { servers } from '../js/server'; +import { API } from '../js/api'; +export default { + getBrandInfo({commit, state}){ + return servers.get({ + url: API.calc, + data: {}, + failMsg: '加载数据失败!' + }) + .then(res=>{ + commit('setBrandInfo',res); + return res; + }) + } +} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 0000000..a776071 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,54 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import mutations from './mutations'; +import actions from './actions'; +import device from './device'; +Vue.use(Vuex); + +export default new Vuex.Store({ + modules: { + device + }, + state: { + // #ifdef H5 + APPID: uni.getAccountInfoSync().miniProgram.appId, + // #endif + brandInfo: { + brand: {} + }, + permissionObj: { // 权限代号对应 + '1001': '营业额', + '1002': '收款记录', + '1003': '经营分析', + '1004': '预约订单', + '1005': '会员卡订单', + '1006': '积分订单', + '1007': '员工管理', + '1008': '查询核销', + '1009': '场地管理', + '1010': '设备管理', + }, + + // 场地占用提交页面信息 + occupyInfo: { + storeInfo: {}, // 店铺信息 + dateInfo: {}, // 时间信息 + typeInfo: {}, // 球场类型 + venueList: [], // 选择场地列表 + } + }, + mutations, + actions, + getters: { + permissionArr: state=>{ + let _arr = [], _obj = state.permissionObj; + for(let key in _obj){ + _arr.push({ + key, + name: _obj[key], + }) + } + return _arr; + } + } +}); diff --git a/src/store/mutations.js b/src/store/mutations.js new file mode 100644 index 0000000..99d4083 --- /dev/null +++ b/src/store/mutations.js @@ -0,0 +1,15 @@ +// 同步方法 + + +export default { + // 设置品牌信息 + setBrandInfo(state, brandInfo){ + // console.log(storeInfo,'-----') + state.brandInfo = brandInfo + }, + + // 场地占用信息 + setOccupyInfo(state, _occupyInfo){ + state.occupyInfo = _occupyInfo; + } +} \ No newline at end of file diff --git a/src/style/public.scss b/src/style/public.scss new file mode 100644 index 0000000..60305d8 --- /dev/null +++ b/src/style/public.scss @@ -0,0 +1,89 @@ +@charset "utf-8"; + +$themeColor: #009874; + +/*每个页面公共css */ +/* view,scroll-view,text,picker{ + box-sizing: border-box; + } */ + +@mixin textHide($line) { + display: -webkit-box; + word-break: break-all; + text-overflow: ellipsis; + overflow: hidden; + -webkit-box-orient: vertical; + -webkit-line-clamp:$line; +} + +@mixin centerFlex($justtify){ + display: flex; + align-items: center; + justify-content: $justtify; +} + +@mixin botLine{ + border-bottom: 2upx solid #e5e5e5; +} + +@mixin picBgc{ + background-color: #f9f9f9; +} + + +@mixin closeIcon($width,$height,$color) { + transform: rotateZ(45deg); + &::before{ + content: ''; + display: block; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%,-50%); + width: $width; + height: $height; + background-color: $color; + border-radius: $height; + } + &::after{ + content: ''; + display: block; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%,-50%); + width: $height; + height: $width; + background-color: $color; + border-radius: $height; + } +} + +@mixin hover { + position: relative; + overflow: hidden; + &::after{ + content: ''; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 2; + background-color: rgba(0,0,0,.1); + } +} + +@mixin arrowIcon($size,$line,$radius,$angle,$color) { + &::after{ + content:''; + display: inline-block; + vertical-align: middle; + transform: rotateZ($angle); + width: $size; + height: $size; + border-right: $line solid $color; + border-bottom: $line solid $color; + border-bottom-right-radius: $radius; + } +} diff --git a/src/utils/util.js b/src/utils/util.js new file mode 100644 index 0000000..7c444a1 --- /dev/null +++ b/src/utils/util.js @@ -0,0 +1,338 @@ +// import {emojiMap,emojiUrl} from './emojiMap'; + +export const phoneReg = new RegExp(/^1(3|4|5|6|7|8|9)\d{9}$/); +export const mailReg = new RegExp(/^\w+((.\w+)|(-\w+))@[A-Za-z0-9]+((.|-)[A-Za-z0-9]+).[A-Za-z0-9]+$/); +export const specialReg = new RegExp("[^a-zA-Z0-9\_\u4e00-\u9fa5]","i"); + +export const formatTime = date => { + const year = date.getFullYear() + const month = date.getMonth() + 1 + const day = date.getDate() + const hour = date.getHours() + const minute = date.getMinutes() + const second = date.getSeconds() + + return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':') +} + +export const formatDate = ({ date= new Date(), partition= '-' }) => { + let _date; + if(typeof (date) === 'string'&&date.indexOf('-')!=-1){ + _date = new Date(date.replace(/\-/g,'/')) + }else{ + _date = new Date(date) + } + const year = _date.getFullYear(); + const month = formatNumber(_date.getMonth() + 1); + const day = formatNumber(_date.getDate()); + + if(partition == 'zh')return `${year}年${month}月${day}日` + return [year, month, day].map(formatNumber).join(partition); +} + +export const formatNumber = n => { + n = n.toString() + return n[1] ? n : '0' + n +} +// 路由跳转 +export const routeTo = (url,type) => { + switch(type){ + case 'nT': uni.navigateTo({url}); + break + case 'rT': uni.redirectTo({url}); + break + case 'rL': uni.reLaunch({url}); + break + case 'sT': uni.switchTab({url}); + break + default: uni.navigateBack({delta: 1}) + break + } +} + +function showNone(txt,duration=1500){ + uni.hideToast(); + uni.hideLoading(); + uni.showToast({ + mask: true, + title: txt, + icon: 'none', + duration, + }) +} + +function showLoad(title='加载中', mask=true){ + uni.showLoading({ + mask, + title, + }) +} +function hideLoad(){ + uni.hideLoading() +} +function showModal({ + title='提示', + content='', + showCancel=false, + cancelText='取消', + confirmText='确定', + confirmColor='#009874', + success, + fail, + complete +}){ + uni.showModal({ + title, + content, + showCancel, + cancelText, + confirmColor, + confirmText, + success, + fail, + complete + }) +} + +function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result; + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp; + // 上次被包装函数被调用时间间隔last小于设定时间间隔wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args); + if (!timeout) context = args = null; + } + } + } + return function(...args) { + context = this; + timestamp = +new Date(); + const callNow = immediate && !timeout; + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + return result; + } +} + +function jsonStr(data){ + return encodeURIComponent(JSON.stringify(data)) +} +function jsonPar(json){ + return JSON.parse(decodeURIComponent(json)) +} + +export let promisify = api => { + return (options, ...params) => { + return new Promise((resolve, reject) => { + api(Object.assign({}, options, { success: resolve, fail: reject }), ...params); + }); + } +} + +function previousPageFunction({fnName,query}){ + return new Promise((rs,rj)=>{ + try{ + if(getCurrentPages().length>1){ + console.log(getCurrentPages()) + getCurrentPages()[getCurrentPages().length-2]['$vm'][fnName](query); + rs('success'); + }else{ + console.error('当前路由栈为一,无法调取上一页数据'); + rj('当前路由栈为一,无法调取上一页数据'); + } + }catch(err){ + console.error('调用上一页面栈方法失败!',err); + rj('调用上一页面栈方法失败!'); + } + }) + +} + +// 获取节点信息 +function getNodeMes(selector,_this=null){ + return new Promise(rs=>{ + let query = _this ? uni.createSelectorQuery().in(_this) : uni.createSelectorQuery(); + query.select(selector).boundingClientRect(res=>{ + rs(res) + // this.height = uni.getSystemInfoSync().windowHeight - res.height; + }).exec() + }) +} + +export const convertBase64 = function(base64data){ + const fsm = uni.getFileSystemManager(); + const FILE_BASE_NAME = 'tmp_base64src';//临时文件名 + return new Promise((resolve, reject) => { + const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || []; //去掉 base64 的头信息: + if (!format) { + reject(new Error('ERROR_BASE64SRC_PARSE')); + } + const filePath = `${uni.env.USER_DATA_PATH}/${FILE_BASE_NAME}.${format}`; + const buffer = bodyData&&uni.base64ToArrayBuffer(bodyData); //将 base64 数据转换为 ArrayBuffer 数据 + // console.log(uni.base64ToArrayBuffer(bodyData)) + fsm.writeFile({ //将 ArrayBuffer 写为本地用户路径的二进制图片文件 //图片文件路径在 uni.env.USER_DATA_PATH 中 + filePath, + data: buffer, + encoding: 'binary', + success() { + // console.log(buffer) + resolve(filePath); + }, + fail() { + reject(new Error('ERROR_BASE64SRC_WRITE')); + }, + }); + }); +} +export function failAuthorizeTosetting(content='调用失败,请打开检查授权状态'){ + showModal({ + title:'提示', + content, + showCancel:true, + confirmText:'打开', + success:res=>{if(res.confirm)uni.openSetting()} + }) +} + +// 解析小程序码 scene 参数 +export function getSceneQuery(scene){ + const queryStr = decodeURIComponent(scene) || null; + let queryObj = queryStr.split('&').reduce((obj,el)=>{ + let arr = el.split('='); + obj[`${arr[0]}`] = arr[1]; + return obj; + },{}) || null; + return queryObj; +} + +//获取地址栏里(URL)传递的参数 +function getUrlQuery(key,url){ + var _url = decodeURI(url).split('?a=1').join(''); //剪切'?a=1'安卓兼容,再获取参数 + if(_url.indexOf(`${key}=`) !=-1){ + return _url.split(`${key}=`)[1].split('&')[0] + } + return null +} + +//获取地址字段栏信息 腾讯获取授权码时 +function getQueryString(name) { + var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); + var r = window.location.search.substr(1).match(reg); + if (r != null) return (r[2]); // 编码的字符串进行解码 + return null; +} + +function getRandom(min, max) { + return Math.round(Math.random() * (max - min)) + min; +} + +export function get_zh_day(date){ + if(isSameDay(new Date().getTime(),new Date(date).getTime()))return '今天' + const Arr = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; + return Arr[new Date(date).getDay()] || ''; +} + +// 判断是否同一天 +export function isSameDay(timeStampA, timeStampB) { + let dateA = new Date(timeStampA); + let dateB = new Date(timeStampB); + return (dateA.setHours(0, 0, 0, 0) == dateB.setHours(0, 0, 0, 0)); +} + +// 获取中文日期 +export function get_zh_date(date = new Date()){ + return `${new Date(date).getMonth()+1}月${new Date(date).getDate()}日` +} + +export function substrDate(date){ + if(!date)return; + return date.substr(0,10) || '-' +} + +// package 貌似为保留字,作为参数时 编译报错 +export function requestPayment(query = {}){ + showLoad(); + uni.requestPayment({ + timeStamp: query.timeStamp || '', + nonceStr: query.nonceStr || '', + package: query.package || '', + signType: query.signType || '', + paySign: query.paySign || '', + success: function(res){ + query.success&&query.success(res) + }, + fail: function(res){ + query.fail&&query.fail(res) + }, + complete: function(res){ + hideLoad(); + query.complete&&query.complete(res) + }, + }) +} + +function getQueryStr({url,name}){ + let queryArr = (url.split('?')[1] || '').split("&") || []; + // let vars = query.split("&"); + for (let i=0;i '{}' +function formatScene(sceneStr = ''){ + let scene = decodeURIComponent(sceneStr); + let queryArr = scene.split('&'); + return queryArr.reduce((obj,e,i)=>{ + let _arr = e.split('='); + obj[_arr[0]] = _arr[1]; + return obj; + },{}) || {}; +} + +function changeLowerCase(str){ + return str.toString().toLocaleLowerCase(); +} + +export default { + formatTime, + formatNumber, + formatDate, + routeTo, + showNone, + showLoad, + hideLoad, + showModal, + debounce, + jsonStr, + jsonPar, + promisify, + previousPageFunction, + getNodeMes, + failAuthorizeTosetting, + getSceneQuery, + getUrlQuery, + getQueryString, + getRandom, + get_zh_day, + isSameDay, + get_zh_date, + substrDate, + requestPayment, + getQueryStr, + formatScene, + changeLowerCase +}