commit
e1d2316ec8
44 changed files with 9691 additions and 0 deletions
@ -0,0 +1,23 @@ |
|||||
|
/* |
||||
|
* Copyright (c) 2019. |
||||
|
* author: wally |
||||
|
* email: 18603454788@163.com |
||||
|
*/ |
||||
|
|
||||
|
module.exports = { |
||||
|
root: true, |
||||
|
|
||||
|
env: { browser: true, node: true }, |
||||
|
extends: ['plugin:vue/recommended', 'plugin:vue/essential', '@vue/prettier'], |
||||
|
rules: { |
||||
|
'vue/html-self-closing': 'off', |
||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
||||
|
'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: ['state'] }], |
||||
|
'max-len': ['error', { code: 120, tabWidth: 2 }], |
||||
|
'object-curly-newline': ['error', { multiline: true }], |
||||
|
'arrow-parens': ['error', 'as-needed'], |
||||
|
}, |
||||
|
|
||||
|
parserOptions: { parser: 'babel-eslint' }, |
||||
|
}; |
@ -0,0 +1,17 @@ |
|||||
|
.DS_Store |
||||
|
node_modules/ |
||||
|
dist/ |
||||
|
unpackage/ |
||||
|
unpackage/* |
||||
|
|
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
|
||||
|
# Editor directories and files |
||||
|
.idea |
||||
|
.vscode |
||||
|
*.suo |
||||
|
*.ntvs* |
||||
|
*.njsproj |
||||
|
*.sln |
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"printWidth": 100, |
||||
|
"singleQuote": true, |
||||
|
"semi": true, |
||||
|
"trailingComma": "all", |
||||
|
"arrowParens": "avoid", |
||||
|
"tabWidth": 2, |
||||
|
"useTabs": false, |
||||
|
"bracketSpacing": true, |
||||
|
"jsxBracketSameLine": false, |
||||
|
"proseWrap": "always" |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
<script> |
||||
|
import { mapState, mapMutations, mapActions } from 'vuex'; |
||||
|
|
||||
|
export default { |
||||
|
async onLaunch(options) { |
||||
|
|
||||
|
}, |
||||
|
created () { |
||||
|
//在页面加载时读取sessionStorage里的状态信息 |
||||
|
if (sessionStorage.getItem("store") ) { |
||||
|
this.$store.replaceState(Object.assign({}, this.$store.state,JSON.parse(sessionStorage.getItem("store")))) |
||||
|
} |
||||
|
//在页面刷新时将vuex里的信息保存到sessionStorage里 |
||||
|
window.addEventListener("beforeunload",()=>{ |
||||
|
sessionStorage.setItem("store",JSON.stringify(this.$store.state)) |
||||
|
}) |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
@import 'colorui/main.scss'; |
||||
|
@import 'colorui/icon.scss'; |
||||
|
@import 'colorui/animation.scss'; |
||||
|
@import 'common/style/global'; |
||||
|
@import 'common/style/iconfont'; |
||||
|
body{ |
||||
|
background: white; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,111 @@ |
|||||
|
# uni-templete使用说明 |
||||
|
|
||||
|
## 环境准备 |
||||
|
|
||||
|
+ 使用HbuilderX作为构建开发环境, 主要使用HX的编译环境, 也可以使用vscode编码 + HX编译的搭配 |
||||
|
+ HX需要安装插件 |
||||
|
+ es6编译 |
||||
|
+ Eslint-js |
||||
|
+ Eslint-plugin-vue |
||||
|
+ git |
||||
|
+ prettier |
||||
|
+ **Scss/sass编译** |
||||
|
+ 微信小程序开发工具 |
||||
|
+ appId申请 |
||||
|
|
||||
|
## 目录说明 |
||||
|
|
||||
|
+ **api** |
||||
|
|
||||
|
+ api接口统一管理文件夹,不允许在组件中直接定义api地址,必须在api文件夹下进行统一管理 |
||||
|
+ 不同的模块,分成不同的js文件进行管理 |
||||
|
+ 采用`export const signin = params => http.post('/api/xxx', params);`格式,封装请求方法及请求地址,方便统一管理 |
||||
|
|
||||
|
+ **colorui** colorui样式库引入,如果不需要直接删除,注意删除app.vue里的引用 |
||||
|
|
||||
|
+ **common** 存放公用的css js font等文件,工具类js建议封装到utils文件里 |
||||
|
|
||||
|
+ **components** |
||||
|
|
||||
|
+ 组件存放文件夹 |
||||
|
+ 统一格式 `组件/组件.vue ` 文件夹与组件同名,在页面或组件中引入就不用再`import`和在`components`注册了 |
||||
|
+ 如果是某个组件特用的子组件,确定不公用的情况下,建议封装到 `组件/components`文件夹里 |
||||
|
|
||||
|
+ **config** 配置信息文件,一些复杂对象或复杂数组的配置信息,不要在组件内直接定义,公用的建议提到config文件夹下(分模块管理),确定非公用的直接写到组件的文件夹内部即可 |
||||
|
|
||||
|
+ **pages** 页面存放目录 |
||||
|
|
||||
|
+ **plugins** 插件,request插件是封装的类似axios请求处理插件,跟axios用法一致,**注意返回值**:成功的返回对象是res.data.data,失败的返回值是res.data.msg,可根据后台接口对应修改。请求做了header里的token处理 |
||||
|
|
||||
|
+ **static** 存放静态文件 |
||||
|
|
||||
|
+ **store** vuex文件,注意分模块处理,参考模板中的user,组件中使用store文件时,优先使用mapState等解构方法 |
||||
|
|
||||
|
+ **utils** 公用工具类,注意分模块,如:`ui.js` `time.js` ` query.js`等 |
||||
|
|
||||
|
+ **.eslintrc.js** eslint代码格式检测配置文件 |
||||
|
|
||||
|
+ **.gitignore** 上传git仓库忽略的文件 |
||||
|
|
||||
|
+ **.prettierrc** prettier自动格式化代码风格的插件配置文件 |
||||
|
|
||||
|
+ App.vue 入口文件 |
||||
|
|
||||
|
+ Main.js 入口文件 |
||||
|
|
||||
|
+ **Manifest.json** 项目配置文件 |
||||
|
|
||||
|
+ **package.json** 项目中有使用到npm包,初始时先运行`npm i` |
||||
|
|
||||
|
+ **uni.scss** scss样式遍历定义文件,在组件中可直接使用其变量而不需要导入 |
||||
|
|
||||
|
+ **Vue.config.js** vue配置文件,定义了常用的alias,使用时尽量使用alias的绝对路径代替相对路径,如:`api/user.js`代替`./api/user.js` |
||||
|
|
||||
|
```js |
||||
|
'~': __dirname, |
||||
|
config: resolve('config'), |
||||
|
api: resolve('api'), |
||||
|
store: resolve('store'), |
||||
|
components: resolve('components'), |
||||
|
pages: resolve('pages'), |
||||
|
common: resolve('common'), |
||||
|
plugins: resolve('plugins'), |
||||
|
utils: resolve('utils'), |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
|
||||
|
## scss |
||||
|
|
||||
|
+ 使用scss代替css样式 |
||||
|
+ HX必须要安装`scss插件` |
||||
|
+ 项目开发前应该先定义好uni.scss里的变量,统一引用这里的变量,有利用界面风格统一及后期维护 |
||||
|
|
||||
|
[官方文档uni.scss](https://uniapp.dcloud.io/collocation/uni-scss) |
||||
|
|
||||
|
## 约定 |
||||
|
|
||||
|
+ Package.json里内置了时间处理插件`moment.js`,统一使用`moment.js`进行时间处理 |
||||
|
|
||||
|
+ 使用scss进行样式开发 |
||||
|
|
||||
|
+ 样式变量(颜色,字体大小,间距等)统一定义到`uni.scss`文件里 |
||||
|
|
||||
|
+ 尽量使用alias定义的绝对路径代替相对路径,如:`api/user.js`代替`./api/user.js` |
||||
|
|
||||
|
+ 保持代码风格统一,建议使用vscode + prettier插件,自动格式化代码 |
||||
|
|
||||
|
+ 代码提交前,进行lint检测,不允许有eslint未通过提交的情况 |
||||
|
|
||||
|
+ `components`里的组件统一格式 `组件/组件.vue ` 文件夹与组件同名,在页面或组件中引入就不用再`import`和在`components`注册了 |
||||
|
|
||||
|
+ 页面及组件中分割出来的子组件,确定不公用的情况下,建议封装到 `组件/components`文件夹里 |
||||
|
|
||||
|
+ 一些复杂对象或复杂数组的配置信息,不要在组件内直接定义,公用的建议提到config文件夹下(分模块管理),确定非公用的直接写到组件的文件夹内部即可 |
||||
|
|
||||
|
+ api 参考上文目录说明中的api项 |
||||
|
|
||||
|
+ git提交规范参考前端规范里的代码提交规范 |
||||
|
|
||||
|
https://kdocs.cn/l/saAjmwvzT?f=130 |
||||
|
[文档] 1-前端技术规范-v1.0-20200618.docx |
@ -0,0 +1,6 @@ |
|||||
|
// api基础地质
|
||||
|
// export const BASE_URL = 'https://www.sxwikionline.com/gateway';
|
||||
|
export const BASE_URL = '/gateway'; |
||||
|
|
||||
|
// 错误码
|
||||
|
export const ERR_CODE = 200; |
@ -0,0 +1,5 @@ |
|||||
|
const proxyUrl = '/tall/v1.0'; |
||||
|
import { http } from 'plugins/request/index'; |
||||
|
|
||||
|
// 请求统一使用如下格式
|
||||
|
// export const signin = params => http.post('/api/xxx', params);
|
@ -0,0 +1,184 @@ |
|||||
|
/* |
||||
|
Animation 微动画 |
||||
|
基于ColorUI组建库的动画模块 by 文晓港 2019年3月26日19:52:28 |
||||
|
*/ |
||||
|
|
||||
|
/* css 滤镜 控制黑白底色gif的 */ |
||||
|
.gif-black{ |
||||
|
mix-blend-mode: screen; |
||||
|
} |
||||
|
.gif-white{ |
||||
|
mix-blend-mode: multiply; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/* Animation css */ |
||||
|
[class*=animation-] { |
||||
|
animation-duration: .5s; |
||||
|
animation-timing-function: ease-out; |
||||
|
animation-fill-mode: both |
||||
|
} |
||||
|
|
||||
|
.animation-fade { |
||||
|
animation-name: fade; |
||||
|
animation-duration: .8s; |
||||
|
animation-timing-function: linear |
||||
|
} |
||||
|
|
||||
|
.animation-scale-up { |
||||
|
animation-name: scale-up |
||||
|
} |
||||
|
|
||||
|
.animation-scale-down { |
||||
|
animation-name: scale-down |
||||
|
} |
||||
|
|
||||
|
.animation-slide-top { |
||||
|
animation-name: slide-top |
||||
|
} |
||||
|
|
||||
|
.animation-slide-bottom { |
||||
|
animation-name: slide-bottom |
||||
|
} |
||||
|
|
||||
|
.animation-slide-left { |
||||
|
animation-name: slide-left |
||||
|
} |
||||
|
|
||||
|
.animation-slide-right { |
||||
|
animation-name: slide-right |
||||
|
} |
||||
|
|
||||
|
.animation-shake { |
||||
|
animation-name: shake |
||||
|
} |
||||
|
|
||||
|
.animation-reverse { |
||||
|
animation-direction: reverse |
||||
|
} |
||||
|
|
||||
|
@keyframes fade { |
||||
|
0% { |
||||
|
opacity: 0 |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes scale-up { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: scale(.2) |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: scale(1) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes scale-down { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: scale(1.8) |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: scale(1) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes slide-top { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: translateY(-100%) |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes slide-bottom { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: translateY(100%) |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: translateY(0) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes shake { |
||||
|
|
||||
|
0%, |
||||
|
100% { |
||||
|
transform: translateX(0) |
||||
|
} |
||||
|
|
||||
|
10% { |
||||
|
transform: translateX(-9px) |
||||
|
} |
||||
|
|
||||
|
20% { |
||||
|
transform: translateX(8px) |
||||
|
} |
||||
|
|
||||
|
30% { |
||||
|
transform: translateX(-7px) |
||||
|
} |
||||
|
|
||||
|
40% { |
||||
|
transform: translateX(6px) |
||||
|
} |
||||
|
|
||||
|
50% { |
||||
|
transform: translateX(-5px) |
||||
|
} |
||||
|
|
||||
|
60% { |
||||
|
transform: translateX(4px) |
||||
|
} |
||||
|
|
||||
|
70% { |
||||
|
transform: translateX(-3px) |
||||
|
} |
||||
|
|
||||
|
80% { |
||||
|
transform: translateX(2px) |
||||
|
} |
||||
|
|
||||
|
90% { |
||||
|
transform: translateX(-1px) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes slide-left { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: translateX(-100%) |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: translateX(0) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes slide-right { |
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: translateX(100%) |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
opacity: 1; |
||||
|
transform: translateX(0) |
||||
|
} |
||||
|
} |
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
@ -0,0 +1,34 @@ |
|||||
|
.cc-active { |
||||
|
transform: translate3d(1rpx, 1rpx, 0); |
||||
|
} |
||||
|
|
||||
|
.card { |
||||
|
margin-bottom: 60rpx; |
||||
|
.card-head { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 120rpx; |
||||
|
padding: 0 32rpx; |
||||
|
|
||||
|
.card-head-avatar { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 56rpx; |
||||
|
height: 56rpx; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
|
||||
|
.card-head-title { |
||||
|
flex: 1; |
||||
|
margin: 0 20rpx; |
||||
|
font-size: 16px; |
||||
|
color: $black; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.card-head-action { |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,248 @@ |
|||||
|
<template> |
||||
|
<div> |
||||
|
<div> |
||||
|
<wuc-tab :tab-list="tabList" :tabCur.sync="TabCur" tab-class="text-center bg-white wuc-tab fixed" :tab-style="CustomBar" select-class="text-blue" @change="tabChange"></wuc-tab> |
||||
|
<div class="cu-bar bg-white solid-bottom" style="margin-top:100upx"> |
||||
|
<div class="action"> |
||||
|
<text class="cuIcon-titles text-orange"></text>基本使用(tab固定,只支持点击标签切换) |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="bg-white padding margin text-center text-black">{{tabList[TabCur].name}}</div> |
||||
|
</div> |
||||
|
|
||||
|
<div> |
||||
|
<div class="cu-bar bg-white margin-top solid-bottom"> |
||||
|
<div class="action"> |
||||
|
<text class="cuIcon-titles text-orange"></text>居中选中放大(外部触发切换) |
||||
|
</div> |
||||
|
</div> |
||||
|
<wuc-tab :tab-list="tabList2" :tabCur="TabCur2" @change="tabChange2" tab-class="text-center text-black bg-white" select-class="text-blue text-xl"></wuc-tab> |
||||
|
<swiper :current="TabCur2" class="swiper" duration="300" :circular="true" indicator-color="rgba(255,255,255,0)" indicator-active-color="rgba(255,255,255,0)" @change="swiperChange2"> |
||||
|
<swiper-item v-for="(item,index) in tabList2" :key="index"> |
||||
|
<div class="bg-white padding margin text-center text-black">{{item.name}}</div> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
</div> |
||||
|
|
||||
|
<div> |
||||
|
<div class="cu-bar bg-white margin-top solid-bottom"> |
||||
|
<div class="action"> |
||||
|
<text class="cuIcon-titles text-orange"></text>平分 |
||||
|
</div> |
||||
|
</div> |
||||
|
<wuc-tab :tab-list="tabList3" textFlex :tabCur.sync="TabCur3" tab-class="text-center text-black bg-white" select-class="text-orange"></wuc-tab> |
||||
|
<swiper :current="TabCur3" class="swiper" duration="300" :circular="true" indicator-color="rgba(255,255,255,0)" indicator-active-color="rgba(255,255,255,0)" @change="swiperChange3"> |
||||
|
<swiper-item v-for="(item,index) in tabList3" :key="index"> |
||||
|
<div class="bg-white padding margin text-center text-black">{{item.name}}</div> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
</div> |
||||
|
|
||||
|
<div> |
||||
|
<div class="cu-bar bg-white margin-top solid-bottom"> |
||||
|
<div class="action"> |
||||
|
<text class="cuIcon-titles text-orange"></text>背景 |
||||
|
</div> |
||||
|
</div> |
||||
|
<wuc-tab :tab-list="tabList4" :tabCur.sync="TabCur4" tab-class="text-center text-white bg-blue" select-class="text-white"></wuc-tab> |
||||
|
<swiper :current="TabCur4" class="swiper row" duration="300" :circular="true" indicator-color="rgba(255,255,255,0)" indicator-active-color="rgba(255,255,255,0)" @change="swiperChange4"> |
||||
|
<swiper-item v-for="(item,index) in tabList4" :key="index"> |
||||
|
<div class="bg-white padding margin text-center text-black">{{item.name}}</div> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
</div> |
||||
|
|
||||
|
<div> |
||||
|
<div class="cu-bar bg-white margin-top solid-bottom"> |
||||
|
<div class="action"> |
||||
|
<text class="cuIcon-titles text-orange"></text>图标 |
||||
|
</div> |
||||
|
</div> |
||||
|
<wuc-tab :tab-list="tabList5" :tabCur.sync="TabCur5" tab-class="text-center text-black bg-white" select-class="text-blue" /> |
||||
|
<swiper :current="TabCur5" class="swiper" duration="300" :circular="true" indicator-color="rgba(255,255,255,0)" indicator-active-color="rgba(255,255,255,0)" @change="swiperChange5"> |
||||
|
<swiper-item v-for="(item,index) in tabList5" :key="index"> |
||||
|
<div class="bg-white padding margin text-center text-black">{{item.name}}</div> |
||||
|
</swiper-item> |
||||
|
</swiper> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import WucTab from '@/components/wuc-tab/wuc-tab.vue'; |
||||
|
import { obj2style } from '@/utils/index'; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
tabList: [ |
||||
|
{ name: '选项卡一' }, |
||||
|
{ name: '选项卡二' }, |
||||
|
{ name: '选项卡三' }, |
||||
|
{ name: '选项卡四' }, |
||||
|
{ name: '选项卡五' }, |
||||
|
{ name: '选项卡六' }, |
||||
|
{ name: '选项卡七' }, |
||||
|
{ name: '选项卡八' } |
||||
|
], |
||||
|
tabList2: [{ name: '精选' }, { name: '订阅' }], |
||||
|
tabList3: [{ name: '精选' }, { name: '订阅' }], |
||||
|
tabList4: [ |
||||
|
{ name: '推荐' }, |
||||
|
{ name: '热点' }, |
||||
|
{ name: '视频' }, |
||||
|
{ name: '问答' }, |
||||
|
{ name: '社会' }, |
||||
|
{ name: '娱乐' }, |
||||
|
{ name: '科技' }, |
||||
|
{ name: '汽车' } |
||||
|
], |
||||
|
tabList5: [ |
||||
|
{ name: '短信', icon: 'cuIcon-comment' }, |
||||
|
{ name: '电话', icon: 'cuIcon-dianhua' }, |
||||
|
{ name: 'wifi', icon: 'cuIcon-wifi' } |
||||
|
], |
||||
|
TabCur: 0, |
||||
|
TabCur2: 0, |
||||
|
TabCur3: 0, |
||||
|
TabCur4: 0, |
||||
|
TabCur5: 0 |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
components: { WucTab }, |
||||
|
|
||||
|
computed: { |
||||
|
CustomBar() { |
||||
|
let style = {}; |
||||
|
// #ifdef MP-WEIXIN |
||||
|
const systemInfo = uni.getSystemInfoSync(); |
||||
|
let CustomBar = |
||||
|
systemInfo.platform === "android" |
||||
|
? systemInfo.statusBarHeight + 50 |
||||
|
: systemInfo.statusBarHeight + 45; |
||||
|
style['top'] = CustomBar + 'px'; |
||||
|
// #endif |
||||
|
// #ifdef H5 |
||||
|
style['top'] = 0 + 'px'; |
||||
|
// #endif |
||||
|
return obj2style(style); |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
methods: { |
||||
|
tabChange(index) { |
||||
|
this.TabCur = index; |
||||
|
}, |
||||
|
tabChange2(index) { |
||||
|
this.TabCur2 = index; |
||||
|
}, |
||||
|
swiperChange2(e) { |
||||
|
let { current } = e.target; |
||||
|
this.TabCur2 = current; |
||||
|
}, |
||||
|
swiperChange3(e) { |
||||
|
let { current } = e.target; |
||||
|
this.TabCur3 = current; |
||||
|
}, |
||||
|
swiperChange4(e) { |
||||
|
let { current } = e.target; |
||||
|
this.TabCur4 = current; |
||||
|
}, |
||||
|
swiperChange5(e) { |
||||
|
this.TabCur5 = e.target.current; |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
onReady() {} |
||||
|
}; |
||||
|
</script> |
||||
|
<style> |
||||
|
@import "~@/styles/icon.scss"; |
||||
|
div, |
||||
|
scroll-view, |
||||
|
swiper { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
div { |
||||
|
font-size: 28upx; |
||||
|
background-color: #f1f1f1; |
||||
|
} |
||||
|
.swiper { |
||||
|
height: 140upx; |
||||
|
} |
||||
|
|
||||
|
.cu-bar { |
||||
|
display: flex; |
||||
|
position: relative; |
||||
|
align-items: center; |
||||
|
min-height: 100upx; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.cu-bar .action { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
justify-content: center; |
||||
|
max-width: 100%; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.cu-bar .action:first-child { |
||||
|
margin-left: 30upx; |
||||
|
font-size: 30upx; |
||||
|
} |
||||
|
|
||||
|
.solid, |
||||
|
.solid-bottom { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.solid::after, |
||||
|
.solid-bottom::after{ |
||||
|
content: " "; |
||||
|
width: 200%; |
||||
|
height: 200%; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
border-radius: inherit; |
||||
|
transform: scale(0.5); |
||||
|
transform-origin: 0 0; |
||||
|
pointer-events: none; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
.solid::after { |
||||
|
border: 1upx solid rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.solid-bottom::after { |
||||
|
border-bottom: 1upx solid rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.text-orange{ |
||||
|
color:#f37b1d |
||||
|
} |
||||
|
.text-black{ |
||||
|
color:#333333; |
||||
|
} |
||||
|
.bg-white{ |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.padding { |
||||
|
padding: 30upx; |
||||
|
} |
||||
|
|
||||
|
.margin { |
||||
|
margin: 30upx; |
||||
|
} |
||||
|
|
||||
|
.margin-top { |
||||
|
margin-top: 30upx; |
||||
|
} |
||||
|
.text-center { |
||||
|
text-align: center; |
||||
|
} |
||||
|
</style> |
File diff suppressed because one or more lines are too long
@ -0,0 +1,11 @@ |
|||||
|
/** |
||||
|
* 为样式动态赋值 |
||||
|
* @param {*} style |
||||
|
*/ |
||||
|
export function obj2style(style) { |
||||
|
let str = []; |
||||
|
Object.keys(style).forEach(key => { |
||||
|
str.push(`${key}:${style[key]};`); |
||||
|
}); |
||||
|
return str.join(';'); |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
test组件 |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
|
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
|
||||
|
</style> |
@ -0,0 +1,182 @@ |
|||||
|
## 插件说明 |
||||
|
|
||||
|
> 这是 `v-tabs` 插件的升级版本,参数上有很大变动,支持 `H5` `小程序` `手机端`,如果是在之前的插件上升级的话,请注意参数的变更,触发的事件没有变更。 |
||||
|
|
||||
|
## 使用说明 |
||||
|
|
||||
|
### 1、最基本用法 |
||||
|
|
||||
|
- 视图文件 |
||||
|
|
||||
|
```html |
||||
|
<v-tabs v-model="current" :tabs="tabs" @change="changeTab"></v-tabs> |
||||
|
``` |
||||
|
|
||||
|
- 脚本文件 |
||||
|
|
||||
|
```js |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
current: 0, |
||||
|
tabs: ['军事', '国内', '新闻新闻', '军事', '国内', '新闻', '军事', '国内', '新闻'] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
changeTab(index) { |
||||
|
console.log('当前选中的项:' + index) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 2、平铺整个屏幕 |
||||
|
|
||||
|
- 视图文件 |
||||
|
|
||||
|
```html |
||||
|
<v-tabs v-model="activeTab" :scroll="false" :tabs="['全部', '进行中', '已完成']"></v-tabs> |
||||
|
``` |
||||
|
|
||||
|
- 脚本文件 |
||||
|
|
||||
|
```js |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
activeTab: 0 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 3、胶囊用法 |
||||
|
|
||||
|
- 视图文件 |
||||
|
|
||||
|
```html |
||||
|
<v-tabs v-model="current" :tabs="tabs" :pills="true" line-height="0" activeColor="#fff" @change="changeTab"></v-tabs> |
||||
|
``` |
||||
|
|
||||
|
- 脚本文件 |
||||
|
|
||||
|
```js |
||||
|
data() { |
||||
|
return { |
||||
|
current: 2, |
||||
|
tabs: [ |
||||
|
'军事', |
||||
|
'国内', |
||||
|
'新闻新闻', |
||||
|
'军事', |
||||
|
'国内', |
||||
|
'新闻', |
||||
|
'军事', |
||||
|
'国内', |
||||
|
'新闻', |
||||
|
], |
||||
|
}, |
||||
|
methods: { |
||||
|
changeTab(index) { |
||||
|
console.log('当前选中索引:' + index) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
## 文档说明 |
||||
|
|
||||
|
### 1、属性说明 |
||||
|
|
||||
|
| 参数 | 类型 | 默认值 | 说明 | |
||||
|
| :---------------: | :-----: | :-------: | :----------------------------------------: | |
||||
|
| value | Number | 0 | 必传(双向绑定的值) | |
||||
|
| color | String | '#333' | 默认文字颜色 | |
||||
|
| activeColor | String | '#2979ff' | 选中文字的颜色 | |
||||
|
| fontSize | String | '28rpx' | 默认文字大小(rpx 或 px) | |
||||
|
| bold | Boolean | true | 是否加粗选中项 | |
||||
|
| scroll | Boolean | true | 是否显示滚动条,平铺设置 false | |
||||
|
| height | String | '70rpx' | tab 高度(rpx 或 px) | |
||||
|
| lineHeight | String | '10rpx' | 滑块高度(rpx 或 px) | |
||||
|
| lineColor | String | '#2979ff' | 滑块的颜色 | |
||||
|
| lineScale | Number | 0.5 | 滑块宽度缩放值 | |
||||
|
| lineRadius | String | '10rpx' | 滑块圆角宽度(rpx 或 px) | |
||||
|
| pills | Boolean | false | 是否开启胶囊 | |
||||
|
| pillsColor | String | '#2979ff' | 胶囊背景颜色(rpx 或 px) | |
||||
|
| pillsBorderRadius | String | '10rpx' | 胶囊圆角宽度(rpx 或 px) | |
||||
|
| field | String | '' | 如果 tabs 子项是对象,输入需要展示的键名 | |
||||
|
| bgColor | String | '#fff' | 背景色,支持 linear-gradient 渐变 | |
||||
|
| padding | String | '0' | 整个 tab padding 属性 | |
||||
|
| fixed | Boolean | false | 是否固定在顶部 | |
||||
|
| paddingItem | String | '0 22rpx' | 选项的边距(设置上下不生效,需要设置高度) | |
||||
|
|
||||
|
### 2、事件说明 |
||||
|
|
||||
|
| 名称 | 参数 | 说明 | |
||||
|
| :----: | :---: | :--------------------------------: | |
||||
|
| change | index | 改变选中项触发, index 选中项的下标 | |
||||
|
|
||||
|
## 更新日志 |
||||
|
|
||||
|
### 2020-09-24 |
||||
|
|
||||
|
1. 修复 `v-tabs` 第一次可能出现第一个标签显示不完整的情况 |
||||
|
2. 修改了 `pages/tabs/order` 示例文件 |
||||
|
|
||||
|
### 2020-09-21 |
||||
|
|
||||
|
1. 修复添加 `fixed` 属性后,滚动条无效 |
||||
|
2. 修复选项很少的情况下,下划线计算计算错误 |
||||
|
3. 新增 `paddingItem` 属性,设置选项左右边距(上下边距需要设置 `height` 属性,或者设置 `padding` 属性) |
||||
|
|
||||
|
**写在最后:** |
||||
|
欢迎各位老铁反馈 bug ,本人后端 PHP 一枚,只是应为感兴趣前端,自己琢磨,自己搞。如果你在使用的过程中有什么不合理,需要优化的,都可以在下面评论(或加我 QQ: 1207791534),本人看见后回复、修正,感谢。 |
||||
|
|
||||
|
### 2020-09-17 |
||||
|
|
||||
|
1. 紧急修复 bug,横向滑动不了的情况 |
||||
|
|
||||
|
### 2020-09-16 |
||||
|
|
||||
|
1. 新增 `fixed` 属性,是否固定在顶部,示例地址:`pages/tabs/tabs-static` |
||||
|
2. 优化之前的页面结构 |
||||
|
|
||||
|
**注意:** |
||||
|
|
||||
|
1. 使用 `padding` 属性的时候,尽量不要左右边距,会导致下划线位置不对 |
||||
|
2. 如果不绑定 `v-model` 会导致 `change` 事件改变的时候,下划线不跟随问题 |
||||
|
|
||||
|
### 2020-09-09 |
||||
|
|
||||
|
1. 修复 `width` 错误,dom 加载的时候没有及时获取到 `data` 属性导致的。 |
||||
|
|
||||
|
### 2020-08-29 |
||||
|
|
||||
|
1. 优化异步改变 `tabs` 后,下划线不初始化问题 |
||||
|
2. `github` 地址上有图 2 的源码,需要的自行下载,页面路径:`pages/tabs/order` |
||||
|
|
||||
|
### 2020-08-20 |
||||
|
|
||||
|
1. 优化 `节点查询` 和 `选中渲染` |
||||
|
2. 优化支付宝中 `createSelectorQuery()` 的影响 |
||||
|
|
||||
|
### 2020-08-19 |
||||
|
|
||||
|
1. 优化 `change` 事件触发机制 |
||||
|
|
||||
|
### 2020-08-16 |
||||
|
|
||||
|
1. 修改默认高度为 `70rpx` |
||||
|
2. 新增属性 `bgColor`,可设置背景颜色,默认 `#fff` |
||||
|
3. 新增整个 `tab` 的 `padding` 属性,默认 `0` |
||||
|
|
||||
|
### 2020-08-13 |
||||
|
|
||||
|
1. 全新的 `v-tabs 2.0` |
||||
|
2. 支持 `H5` `小程序` `APP` |
||||
|
3. 属性高度可配置 |
||||
|
|
||||
|
## 预览 |
||||
|
|
||||
|
 |
||||
|
 |
@ -0,0 +1,339 @@ |
|||||
|
<template> |
||||
|
<view :id="elId" class="v-tabs"> |
||||
|
<scroll-view |
||||
|
id="scrollContainer" |
||||
|
:scroll-x="scroll" |
||||
|
:scroll-left="scroll ? scrollLeft : 0" |
||||
|
:scroll-with-animation="scroll" |
||||
|
:style="{ position: fixed ? 'fixed' : 'relative', zIndex: 1993 }" |
||||
|
> |
||||
|
<view |
||||
|
class="v-tabs__container" |
||||
|
:style="{ |
||||
|
display: scroll ? 'inline-flex' : 'flex', |
||||
|
whiteSpace: scroll ? 'nowrap' : 'normal', |
||||
|
background: bgColor, |
||||
|
height, |
||||
|
padding |
||||
|
}" |
||||
|
> |
||||
|
<view |
||||
|
class="v-tabs__container-item" |
||||
|
v-for="(v, i) in tabs" |
||||
|
:key="i" |
||||
|
:style="{ |
||||
|
color: current == i ? activeColor : color, |
||||
|
fontSize: current == i ? fontSize : fontSize, |
||||
|
fontWeight: bold && current == i ? 'bold' : '', |
||||
|
justifyContent: !scroll ? 'center' : '', |
||||
|
flex: scroll ? '' : 1, |
||||
|
padding: paddingItem |
||||
|
}" |
||||
|
@click="change(i)" |
||||
|
> |
||||
|
{{ field ? v[field] : v }} |
||||
|
</view> |
||||
|
<view |
||||
|
v-if="!pills" |
||||
|
class="v-tabs__container-line" |
||||
|
:style="{ |
||||
|
background: lineColor, |
||||
|
width: lineWidth + 'px', |
||||
|
height: lineHeight, |
||||
|
borderRadius: lineRadius, |
||||
|
left: lineLeft + 'px', |
||||
|
transform: `translateX(-${lineWidth / 2}px)` |
||||
|
}" |
||||
|
></view> |
||||
|
<view |
||||
|
v-else |
||||
|
class="v-tabs__container-pills" |
||||
|
:style="{ |
||||
|
background: pillsColor, |
||||
|
borderRadius: pillsBorderRadius, |
||||
|
left: pillsLeft + 'px', |
||||
|
width: currentWidth + 'px', |
||||
|
height |
||||
|
}" |
||||
|
></view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view |
||||
|
class="v-tabs__placeholder" |
||||
|
:style="{ |
||||
|
height: fixed ? height : '0', |
||||
|
padding |
||||
|
}" |
||||
|
></view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
/** |
||||
|
* v-tabs |
||||
|
* @property {Number} value 选中的下标 |
||||
|
* @property {Array} tabs tabs 列表 |
||||
|
* @property {String} bgColor = '#fff' 背景颜色 |
||||
|
* @property {String} color = '#333' 默认颜色 |
||||
|
* @property {String} activeColor = '#2979ff' 选中文字颜色 |
||||
|
* @property {String} fontSize = '28rpx' 默认文字大小 |
||||
|
* @property {String} activeFontSize = '28rpx' 选中文字大小 |
||||
|
* @property {Boolean} bold = [true | false] 选中文字是否加粗 |
||||
|
* @property {Boolean} scroll = [true | false] 是否滚动 |
||||
|
* @property {String} height = '60rpx' tab 的高度 |
||||
|
* @property {String} lineHeight = '10rpx' 下划线的高度 |
||||
|
* @property {String} lineColor = '#2979ff' 下划线的颜色 |
||||
|
* @property {Number} lineScale = 0.5 下划线的宽度缩放比例 |
||||
|
* @property {String} lineRadius = '10rpx' 下划线圆角 |
||||
|
* @property {Boolean} pills = [true | false] 是否胶囊样式 |
||||
|
* @property {String} pillsColor = '#2979ff' 胶囊背景色 |
||||
|
* @property {String} pillsBorderRadius = '10rpx' 胶囊圆角大小 |
||||
|
* @property {String} field 如果是对象,显示的键名 |
||||
|
* @property {Boolean} fixed = [true | false] 是否固定 |
||||
|
* @property {String} paddingItem = '0 22rpx' 选项的边距 |
||||
|
* |
||||
|
* @event {Function(current)} change 改变标签触发 |
||||
|
*/ |
||||
|
export default { |
||||
|
props: { |
||||
|
value: { |
||||
|
type: Number, |
||||
|
default: 0 |
||||
|
}, |
||||
|
tabs: { |
||||
|
type: Array, |
||||
|
default() { |
||||
|
return [] |
||||
|
} |
||||
|
}, |
||||
|
bgColor: { |
||||
|
type: String, |
||||
|
default: '#fff' |
||||
|
}, |
||||
|
padding: { |
||||
|
type: String, |
||||
|
default: '0' |
||||
|
}, |
||||
|
color: { |
||||
|
type: String, |
||||
|
default: '#ccc' |
||||
|
}, |
||||
|
activeColor: { |
||||
|
type: String, |
||||
|
default: 'white' |
||||
|
}, |
||||
|
fontSize: { |
||||
|
type: String, |
||||
|
default: '28rpx' |
||||
|
}, |
||||
|
activeFontSize: { |
||||
|
type: String, |
||||
|
default: '32rpx' |
||||
|
}, |
||||
|
bold: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
scroll: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '70rpx' |
||||
|
}, |
||||
|
lineColor: { |
||||
|
type: String, |
||||
|
default: '#FFD700' |
||||
|
}, |
||||
|
lineHeight: { |
||||
|
type: String, |
||||
|
default: '2px' |
||||
|
}, |
||||
|
lineScale: { |
||||
|
type: Number, |
||||
|
default: 1 |
||||
|
}, |
||||
|
lineRadius: { |
||||
|
type: String, |
||||
|
default: '10rpx' |
||||
|
}, |
||||
|
pills: { |
||||
|
type: Boolean, |
||||
|
deafult: false |
||||
|
}, |
||||
|
pillsColor: { |
||||
|
type: String, |
||||
|
default: '#FE5835' |
||||
|
}, |
||||
|
pillsBorderRadius: { |
||||
|
type: String, |
||||
|
default: '25px' |
||||
|
}, |
||||
|
field: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
fixed: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
}, |
||||
|
paddingItem: { |
||||
|
type: String, |
||||
|
default: '0 22rpx' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
elId: '', |
||||
|
lineWidth: 30, |
||||
|
currentWidth: 0, // 当前选项的宽度 |
||||
|
lineLeft: 0, // 滑块距离左侧的位置 |
||||
|
pillsLeft: 0, // 胶囊距离左侧的位置 |
||||
|
scrollLeft: 0, // 距离左边的位置 |
||||
|
containerWidth: 0, // 容器的宽度 |
||||
|
current: 0 // 当前选中项 |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
value(newVal) { |
||||
|
this.current = newVal |
||||
|
this.$nextTick(() => { |
||||
|
this.getTabItemWidth() |
||||
|
}) |
||||
|
}, |
||||
|
current(newVal) { |
||||
|
this.$emit('input', newVal) |
||||
|
}, |
||||
|
tabs(newVal) { |
||||
|
this.$nextTick(() => { |
||||
|
this.getTabItemWidth() |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 产生随机字符串 |
||||
|
randomString(len) { |
||||
|
len = len || 32 |
||||
|
let $chars = |
||||
|
'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/ |
||||
|
let maxPos = $chars.length |
||||
|
let pwd = '' |
||||
|
for (let i = 0; i < len; i++) { |
||||
|
pwd += $chars.charAt(Math.floor(Math.random() * maxPos)) |
||||
|
} |
||||
|
return pwd |
||||
|
}, |
||||
|
// 切换事件 |
||||
|
change(index) { |
||||
|
if (this.current !== index) { |
||||
|
this.current = index |
||||
|
|
||||
|
this.$emit('change', index) |
||||
|
} |
||||
|
}, |
||||
|
// 获取左移动位置 |
||||
|
getTabItemWidth() { |
||||
|
let query = uni |
||||
|
.createSelectorQuery() |
||||
|
// #ifndef MP-ALIPAY |
||||
|
.in(this) |
||||
|
// #endif |
||||
|
// 获取容器的宽度 |
||||
|
query |
||||
|
.select(`#scrollContainer`) |
||||
|
.boundingClientRect((data) => { |
||||
|
if (!this.containerWidth && data) { |
||||
|
this.containerWidth = data.width |
||||
|
} |
||||
|
}) |
||||
|
.exec() |
||||
|
// 获取所有的 tab-item 的宽度 |
||||
|
query |
||||
|
.selectAll('.v-tabs__container-item') |
||||
|
.boundingClientRect((data) => { |
||||
|
if (!data) { |
||||
|
return |
||||
|
} |
||||
|
let lineLeft = 0 |
||||
|
let currentWidth = 0 |
||||
|
if (data) { |
||||
|
for (let i = 0; i < data.length; i++) { |
||||
|
if (i < this.current) { |
||||
|
lineLeft += data[i].width |
||||
|
} else if (i == this.current) { |
||||
|
currentWidth = data[i].width |
||||
|
} else { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// 当前滑块的宽度 |
||||
|
this.currentWidth = currentWidth |
||||
|
// 缩放后的滑块宽度 |
||||
|
this.lineWidth = currentWidth * this.lineScale * 1 |
||||
|
// 滑块作移动的位置 |
||||
|
this.lineLeft = lineLeft + currentWidth / 2 |
||||
|
// 胶囊距离左侧的位置 |
||||
|
this.pillsLeft = lineLeft |
||||
|
// 计算滚动的距离左侧的位置 |
||||
|
if (this.scroll) { |
||||
|
this.scrollLeft = this.lineLeft - this.containerWidth / 2 |
||||
|
} |
||||
|
}) |
||||
|
.exec() |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.elId = 'xfjpeter_' + this.randomString() |
||||
|
this.current = this.value |
||||
|
this.$nextTick(() => { |
||||
|
this.getTabItemWidth() |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.v-tabs { |
||||
|
width: 100%; |
||||
|
box-sizing: border-box; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
::-webkit-scrollbar { |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
&__container { |
||||
|
min-width: 100%; |
||||
|
position: relative; |
||||
|
display: inline-flex; |
||||
|
align-items: center; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
&-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
position: relative; |
||||
|
z-index: 10; |
||||
|
// padding: 0 11px; |
||||
|
transition: all 0.3s; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
&-line { |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
transition: all 0.3s linear; |
||||
|
} |
||||
|
|
||||
|
&-pills { |
||||
|
position: absolute; |
||||
|
transition: all 0.3s linear; |
||||
|
z-index: 9; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,133 @@ |
|||||
|
<template> |
||||
|
<scroll-view class="wuc-tab" :class="tabClass" :style="tabStyle" scroll-with-animation scroll-x :scroll-left="scrollLeft"> |
||||
|
<div v-if="!textFlex"> |
||||
|
<div class="wuc-tab-item" :class="[index === tabCur ? selectClass + ' cur':'']" v-for="(item,index) in tabList" :key="index" :id="index" @tap="tabSelect(index,$event)"> |
||||
|
<text :class="item.icon"></text> |
||||
|
<span>{{item.name}}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div class="flex text-center" v-if="textFlex"> |
||||
|
<div class="wuc-tab-item flex-sub" :class="index === tabCur ? selectClass + ' cur':''" v-for="(item,index) in tabList" :key="index" :id="index" @tap="tabSelect(index,$event)"> |
||||
|
<text :class="item.icon"></text> |
||||
|
<span>{{item.name}}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</scroll-view> |
||||
|
</template> |
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'wuc-tab', |
||||
|
data() { |
||||
|
return {}; |
||||
|
}, |
||||
|
props: { |
||||
|
tabList: { |
||||
|
type: Array, |
||||
|
default() { |
||||
|
return []; |
||||
|
} |
||||
|
}, |
||||
|
tabCur: { |
||||
|
type: Number, |
||||
|
default() { |
||||
|
return 0; |
||||
|
} |
||||
|
}, |
||||
|
tabClass: { |
||||
|
type: String, |
||||
|
default() { |
||||
|
return ''; |
||||
|
} |
||||
|
}, |
||||
|
tabStyle: { |
||||
|
type: String, |
||||
|
default() { |
||||
|
return ''; |
||||
|
} |
||||
|
}, |
||||
|
textFlex: { |
||||
|
type: Boolean, |
||||
|
default() { |
||||
|
return false; |
||||
|
} |
||||
|
}, |
||||
|
selectClass: { |
||||
|
type: String, |
||||
|
default() { |
||||
|
return 'text-blue'; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
tabSelect(index, e) { |
||||
|
if (this.currentTab === index) return false; |
||||
|
this.$emit('update:tabCur', index); |
||||
|
this.$emit('change', index); |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
scrollLeft() { |
||||
|
return (this.tabCur - 1) * 60; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
<style> |
||||
|
div, |
||||
|
scroll-view, |
||||
|
swiper { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
.wuc-tab { |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
.wuc-tab-item { |
||||
|
height: 90upx; |
||||
|
display: inline-block; |
||||
|
line-height: 90upx; |
||||
|
margin: 0 10upx; |
||||
|
padding: 0 20upx; |
||||
|
} |
||||
|
|
||||
|
.wuc-tab-item.cur { |
||||
|
border-bottom: 4upx solid; |
||||
|
} |
||||
|
|
||||
|
.wuc-tab.fixed { |
||||
|
position: fixed; |
||||
|
width: 100%; |
||||
|
top: 0; |
||||
|
z-index: 1024; |
||||
|
box-shadow: 0 1upx 6upx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.flex { |
||||
|
display: flex; |
||||
|
} |
||||
|
.text-center { |
||||
|
text-align: center; |
||||
|
} |
||||
|
.flex-sub { |
||||
|
flex: 1; |
||||
|
} |
||||
|
.text-blue{ |
||||
|
color:#0081ff; |
||||
|
} |
||||
|
.text-white{ |
||||
|
color:#ffffff; |
||||
|
} |
||||
|
.bg-white{ |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
.bg-blue{ |
||||
|
background-color: #0081ff; |
||||
|
} |
||||
|
.text-orange{ |
||||
|
color: #f37b1d |
||||
|
} |
||||
|
|
||||
|
.text-xl { |
||||
|
font-size: 36upx; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1 @@ |
|||||
|
export const ERR_CODE = 200; |
@ -0,0 +1,20 @@ |
|||||
|
/* |
||||
|
* Copyright (c) 2020. |
||||
|
* author: wally |
||||
|
* email: 18603454788@163.com |
||||
|
*/ |
||||
|
|
||||
|
// 用户登录client
|
||||
|
export const SIGN_IN_CLIENTS = { mp: 0, h5: 1, android: 2, ios: 3, wx_work: 4 }; |
||||
|
|
||||
|
// 用户登录类型
|
||||
|
export const SIGN_IN_TYPES = { |
||||
|
mp: 0, |
||||
|
phone: 1, |
||||
|
email: 2, |
||||
|
username: 3, |
||||
|
wx: 4, |
||||
|
wx_web: 5, |
||||
|
wb: 6, |
||||
|
wx_work: 7, |
||||
|
}; |
@ -0,0 +1,64 @@ |
|||||
|
import Vue from 'vue'; |
||||
|
import moment from 'moment'; |
||||
|
import { http } from 'plugins/request/index'; |
||||
|
import App from './App'; |
||||
|
import store from './store'; |
||||
|
import axios from 'axios' |
||||
|
Vue.prototype.$axios = axios |
||||
|
|
||||
|
import Fingerprint from 'fingerprintjs' |
||||
|
|
||||
|
// 白名单页面
|
||||
|
const whitePathList = [ |
||||
|
'basic-info', |
||||
|
'statistics', |
||||
|
'user-code', |
||||
|
'sign', |
||||
|
'my-signs', |
||||
|
'my-code', |
||||
|
'my-trips', |
||||
|
]; |
||||
|
/** |
||||
|
* 检查url是否在是否在白名单内 |
||||
|
* @param {string} url path+query |
||||
|
*/ |
||||
|
const checkWhitePath = url => { |
||||
|
let str = url.slice(7).split('/')[0]; |
||||
|
return whitePathList.includes(str); |
||||
|
}; |
||||
|
|
||||
|
// var fingerprint = new Fingerprint().get();
|
||||
|
// store.state.user.fingerprint = fingerprint
|
||||
|
// console.log(store.state.user.fingerprint)
|
||||
|
|
||||
|
|
||||
|
Vue.config.productionTip = false; |
||||
|
Vue.prototype.$http = http; |
||||
|
Vue.prototype.$moment = moment; |
||||
|
|
||||
|
moment.locale('zh-cn'); |
||||
|
|
||||
|
Vue.prototype.goHome = () => { |
||||
|
uni.reLaunch({ |
||||
|
url: '/pages/index/index', |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
Vue.prototype.openPage = function(path, query = '') { |
||||
|
let url = query ? `${path}?${query}` : path; |
||||
|
store.commit('user/setPagePath', url); |
||||
|
const isWhite = checkWhitePath(url); |
||||
|
if ((!store.state.user.userInfo || !store.state.user.userInfo.id) && !isWhite) { |
||||
|
url = '/pages/basic-info/basic-info'; |
||||
|
} |
||||
|
|
||||
|
uni.navigateTo({ url }); |
||||
|
}; |
||||
|
|
||||
|
App.mpType = 'app'; |
||||
|
|
||||
|
const app = new Vue({ |
||||
|
store, |
||||
|
...App, |
||||
|
}); |
||||
|
app.$mount(); |
@ -0,0 +1,116 @@ |
|||||
|
{ |
||||
|
"name" : "Loading...", |
||||
|
"appid" : "__UNI__4CABB72", |
||||
|
"description" : "", |
||||
|
"versionName" : "1.0.0", |
||||
|
"versionCode" : "100", |
||||
|
"transformPx" : false, |
||||
|
"networkTimeout" : { |
||||
|
"uploadFile" : 20000, |
||||
|
"request" : 20000 |
||||
|
}, |
||||
|
/* 5+App特有相关 */ |
||||
|
"app-plus" : { |
||||
|
"usingComponents" : true, |
||||
|
"nvueCompiler" : "uni-app", |
||||
|
"compilerVersion" : 3, |
||||
|
"splashscreen" : { |
||||
|
"alwaysShowBeforeRender" : true, |
||||
|
"waiting" : true, |
||||
|
"autoclose" : true, |
||||
|
"delay" : 0 |
||||
|
}, |
||||
|
/* 模块配置 */ |
||||
|
"modules" : {}, |
||||
|
/* 应用发布信息 */ |
||||
|
"distribute" : { |
||||
|
/* android打包配置 */ |
||||
|
"android" : { |
||||
|
"permissions" : [ |
||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>", |
||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>", |
||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>", |
||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>" |
||||
|
] |
||||
|
}, |
||||
|
/* ios打包配置 */ |
||||
|
"ios" : {}, |
||||
|
/* SDK配置 */ |
||||
|
"sdkConfigs" : {} |
||||
|
} |
||||
|
}, |
||||
|
/* 快应用特有相关 */ |
||||
|
"quickapp" : {}, |
||||
|
/* 小程序特有相关 */ |
||||
|
"mp-weixin" : { |
||||
|
"appid" : "wx2f9ef33e08053bbf", |
||||
|
"setting" : { |
||||
|
"urlCheck" : false, |
||||
|
"es6" : true, |
||||
|
"postcss" : true, |
||||
|
"minified" : true |
||||
|
}, |
||||
|
"permission" : { |
||||
|
"scope.userLocation" : { |
||||
|
"desc" : "你的位置信息将用于获取地理位置及地图展示" |
||||
|
} |
||||
|
}, |
||||
|
"usingComponents" : true, |
||||
|
"navigateToMiniProgramAppIdList" : [ "wx5b97b0686831c076" ] |
||||
|
}, |
||||
|
"mp-alipay" : { |
||||
|
"usingComponents" : true |
||||
|
}, |
||||
|
"mp-baidu" : { |
||||
|
"usingComponents" : true |
||||
|
}, |
||||
|
"mp-toutiao" : { |
||||
|
"usingComponents" : true |
||||
|
}, |
||||
|
"h5" : { |
||||
|
"devServer" : { |
||||
|
"proxy" : { |
||||
|
"/gateway" : { |
||||
|
"target" : "https://test.tall.wiki/gateway", |
||||
|
"changeOrigin" : true, |
||||
|
"secure" : true, |
||||
|
"pathRewrite" : { |
||||
|
"^/gateway" : "" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"https" : false, |
||||
|
"port" : "" |
||||
|
}, |
||||
|
"router" : { |
||||
|
"base" : "" |
||||
|
}, |
||||
|
"domain" : "", |
||||
|
"async" : { |
||||
|
//页面js异步加载配置 |
||||
|
"loading" : "AsyncLoading", //页面js加载时使用的组件(需注册为全局组件) |
||||
|
"error" : "AsyncError", //页面js加载失败时使用的组件(需注册为全局组件) |
||||
|
"delay" : 500, //展示 loading 加载组件的延时时间(页面 js 若在 delay 时间内加载完成,则不会显示 loading 组件) |
||||
|
"timeout" : 1000 //页面js加载超时时间(超时后展示 error 对应的组件) |
||||
|
}, |
||||
|
"title" : "tttsss" |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
{ |
||||
|
"name": "uniTemplete", |
||||
|
"version": "1.0.0", |
||||
|
"lockfileVersion": 1, |
||||
|
"requires": true, |
||||
|
"dependencies": { |
||||
|
"axios": { |
||||
|
"version": "0.21.0", |
||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", |
||||
|
"integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", |
||||
|
"requires": { |
||||
|
"follow-redirects": "^1.10.0" |
||||
|
} |
||||
|
}, |
||||
|
"fingerprintjs": { |
||||
|
"version": "0.5.3", |
||||
|
"resolved": "https://registry.npmjs.org/fingerprintjs/-/fingerprintjs-0.5.3.tgz", |
||||
|
"integrity": "sha1-ACZCL64asQA2keE2wUPhm3UnslU=" |
||||
|
}, |
||||
|
"fingerprintjs2": { |
||||
|
"version": "2.1.2", |
||||
|
"resolved": "https://registry.npmjs.org/fingerprintjs2/-/fingerprintjs2-2.1.2.tgz", |
||||
|
"integrity": "sha512-ZPsLgjziFRbUb5tXWpEMtWp4XFnzSah8SiNfl3aoURDZ+2zi2tuIOYUULqDBV+Cb6paN+raWT+Q2qpOaCbX/Yw==" |
||||
|
}, |
||||
|
"follow-redirects": { |
||||
|
"version": "1.13.0", |
||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", |
||||
|
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" |
||||
|
}, |
||||
|
"moment": { |
||||
|
"version": "2.29.1", |
||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", |
||||
|
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
{ |
||||
|
"name": "uniTemplete", |
||||
|
"version": "1.0.0", |
||||
|
"description": "", |
||||
|
"main": "main.js", |
||||
|
"scripts": { |
||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||
|
}, |
||||
|
"uni-app": { |
||||
|
"scripts": { |
||||
|
"mp-dingtalk": { |
||||
|
"title": "钉钉小程序", |
||||
|
"env": { |
||||
|
"UNI_PLATFORM": "mp-alipay" |
||||
|
}, |
||||
|
"define": { |
||||
|
"MP-DINGTALK": true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "" |
||||
|
}, |
||||
|
"author": "wally", |
||||
|
"license": "ISC", |
||||
|
"dependencies": { |
||||
|
"axios": "^0.21.0", |
||||
|
"fingerprintjs": "^0.5.3", |
||||
|
"fingerprintjs2": "^2.1.2", |
||||
|
"moment": "^2.29.1" |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
{ |
||||
|
"pages": [ |
||||
|
{ |
||||
|
"path" : "pages/Grouping/Grouping", |
||||
|
"style" : {} |
||||
|
} |
||||
|
,{ |
||||
|
"path" : "pages/Details/Details", |
||||
|
"style" : { |
||||
|
"navigationBarTextStyle": "white", |
||||
|
"navigationBarTitleText": "2020年山西省学生跳绳比赛成绩查询", |
||||
|
"navigationBarBackgroundColor": "#FE5A38", |
||||
|
"backgroundColor": "#0a97c6" |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"globalStyle": { |
||||
|
"navigationBarTextStyle": "white", |
||||
|
"navigationBarTitleText": "2020年山西省学生跳绳比赛成绩查询", |
||||
|
"navigationBarBackgroundColor": "#4F89F4", |
||||
|
"backgroundColor": "#0a97c6" |
||||
|
}, |
||||
|
"easycom": { |
||||
|
"autoscan": true |
||||
|
} |
||||
|
} |
@ -0,0 +1,329 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="img-box"> |
||||
|
<img src="static/red.png"></img> |
||||
|
<view class="project-title"> |
||||
|
30秒单摇跳 |
||||
|
<!-- <icon class="cuIcon-right" type="icon"></icon> |
||||
|
<icon class="cuIcon-right" type="icon"></icon> --> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="project-box"> |
||||
|
<view class="project-team-name"> |
||||
|
<v-tabs v-model="current" :tabs="tabs" :pills="true" activeColor="white" pillsBorderRadius="25px" pillsColor="#FCBC3B" @change="changeTab"></v-tabs> |
||||
|
</view> |
||||
|
<view class="project-nav"> |
||||
|
<view class="ranking"> |
||||
|
<view class="ranking-title"> |
||||
|
排名 |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="team"> |
||||
|
<view class="team-title"> |
||||
|
参赛队伍 |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="player-name"> |
||||
|
<view class="name-title"> |
||||
|
姓名 |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="result"> |
||||
|
<view class="result-title"> |
||||
|
成绩 |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="project-con"> |
||||
|
<view class="ranking"> |
||||
|
<view class="ranking-con" v-for="(item,index) in list" :key="index"> |
||||
|
<text v-if="item.ranking - 0 === 1" class="top1">TOP{{ item.ranking }}</text> |
||||
|
<text v-else-if="item.ranking - 0 === 2" class="top2">TOP{{ item.ranking }}</text> |
||||
|
<text v-else-if="item.ranking - 0 === 3">TOP{{ item.ranking }}</text> |
||||
|
<text v-else>{{ item.ranking }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="team"> |
||||
|
<view class="team-con-box"> |
||||
|
<view class="team-con" :class="item.team.length - 0 > 8 ? 'team-con-act' : ''" v-for="(item,index) in list" :key="index"> |
||||
|
{{ item.team }} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="player-name"> |
||||
|
<view class="name-con" v-for="(item,index) in list" :key="index"> |
||||
|
{{ item.name }} |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="result"> |
||||
|
<view class="result-con" v-for="(item,index) in list" :key="index"> |
||||
|
{{ item.result }} |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
list: [{ |
||||
|
ranking: 1, |
||||
|
team: '山西太原实验小学十中', |
||||
|
name: '某某某', |
||||
|
result: 100 |
||||
|
},{ |
||||
|
ranking: 2, |
||||
|
team: 'XXXXXX学校XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 99 |
||||
|
},{ |
||||
|
ranking: 3, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 95 |
||||
|
},{ |
||||
|
ranking: 4, |
||||
|
team: 'XXXXXX学校X', |
||||
|
name: '某某某', |
||||
|
result: 93 |
||||
|
},{ |
||||
|
ranking: 5, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 92 |
||||
|
},{ |
||||
|
ranking: 6, |
||||
|
team: 'XXXXXX学校XXXXX', |
||||
|
name: '某某某', |
||||
|
result: 90 |
||||
|
},{ |
||||
|
ranking: 5, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 92 |
||||
|
},{ |
||||
|
ranking: 7, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 89 |
||||
|
},{ |
||||
|
ranking: 8, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 88 |
||||
|
},{ |
||||
|
ranking: 9, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 87 |
||||
|
},{ |
||||
|
ranking: 8, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 88 |
||||
|
},{ |
||||
|
ranking: 9, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 87 |
||||
|
},{ |
||||
|
ranking: 10, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 86 |
||||
|
},{ |
||||
|
ranking: 11, |
||||
|
team: 'XXXXXX学校', |
||||
|
name: '某某某', |
||||
|
result: 86 |
||||
|
}], |
||||
|
ActIndex: 0, |
||||
|
current: 0, |
||||
|
tabs: ['XXXXXX组', 'XXXXXX组XXXXXX组XXXXXX组', 'XXXXXX组', 'XXXXXX组', 'XXXXXX组', 'XXXXXX组', 'XXXXXX组', 'XXXXXX学校'] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
changeTab(index) { |
||||
|
console.log('当前选中的项:' + index) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
|
||||
|
.img-box { |
||||
|
position: relative; |
||||
|
width: 750rpx; |
||||
|
height: 500rpx; |
||||
|
img { |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
.project-box { |
||||
|
position: absolute; |
||||
|
width: 630rpx; |
||||
|
left: 60rpx; |
||||
|
background: $white; |
||||
|
box-shadow: 0 0 30px $redShadow; |
||||
|
height: 62%; |
||||
|
top: 31%; |
||||
|
padding: 35rpx 35rpx; |
||||
|
border-radius: 20rpx; |
||||
|
} |
||||
|
.project-title { |
||||
|
height: 26px; |
||||
|
line-height: 26px; |
||||
|
min-width: 130px; |
||||
|
position: absolute; |
||||
|
top: 58%; |
||||
|
left: 8%; |
||||
|
background: $white; |
||||
|
text-align: center; |
||||
|
padding-left: 20px; |
||||
|
padding-right: 20px; |
||||
|
border-radius: 50px; |
||||
|
color: #FE5835; |
||||
|
font-size: 16px; |
||||
|
font-weight: 1000; |
||||
|
} |
||||
|
.project-nav { |
||||
|
margin-top: 14px; |
||||
|
// box-shadow: 0 2px 2px $redShadow; |
||||
|
display: flex; |
||||
|
} |
||||
|
.project-con { |
||||
|
display: flex; |
||||
|
height: 80%; |
||||
|
overflow-y: auto; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
.ranking { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
} |
||||
|
.ranking-title { |
||||
|
height: 30px; |
||||
|
line-height: 30px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
.ranking-con { |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
color: #B87333; |
||||
|
font-size: 14px; |
||||
|
font-weight: 1000; |
||||
|
} |
||||
|
.team { |
||||
|
flex: 2; |
||||
|
text-align: center; |
||||
|
// overflow-y: visible !important; |
||||
|
width: 40%; |
||||
|
} |
||||
|
.team-con-box { |
||||
|
overflow-x: hidden; |
||||
|
} |
||||
|
.team-title { |
||||
|
height: 30px; |
||||
|
line-height: 30px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
.team-con { |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
// width: 200px; |
||||
|
white-space:nowrap; |
||||
|
} |
||||
|
.team-con-act { |
||||
|
animation: 15s wordsLoop linear infinite normal; |
||||
|
} |
||||
|
|
||||
|
@keyframes wordsLoop { |
||||
|
0% { |
||||
|
transform: translateX(0px); |
||||
|
-webkit-transform: translateX(0px); |
||||
|
} |
||||
|
99% { |
||||
|
transform: translateX(-100%); |
||||
|
-webkit-transform: translateX(-100%); |
||||
|
} |
||||
|
100% { |
||||
|
transform: translateX(0%); |
||||
|
-webkit-transform: translateX(0%); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@-webkit-keyframes wordsLoop { |
||||
|
0% { |
||||
|
transform: translateX(0px); |
||||
|
-webkit-transform: translateX(0px); |
||||
|
} |
||||
|
100% { |
||||
|
transform: translateX(-100%); |
||||
|
-webkit-transform: translateX(-100%); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
.player-name { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
} |
||||
|
.name-title { |
||||
|
height: 30px; |
||||
|
line-height: 30px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
.name-con { |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
.result { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
} |
||||
|
.result-title { |
||||
|
height: 30px; |
||||
|
line-height: 30px; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
.result-con { |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
} |
||||
|
.top1 { |
||||
|
font-size: 18px; |
||||
|
color: #FFD700; |
||||
|
} |
||||
|
.top2 { |
||||
|
font-size: 16px; |
||||
|
color: #c0c0c0; |
||||
|
} |
||||
|
.project-team-name { |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
width: 570rpx; |
||||
|
// background: $yellowLight; |
||||
|
display: flex; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
.team-det { |
||||
|
flex: 0 0 120px; |
||||
|
color: #ccc; |
||||
|
text-align: center; |
||||
|
font-size: 14px; |
||||
|
// flex-shrink:0; |
||||
|
// width: 100px; |
||||
|
} |
||||
|
.active { |
||||
|
color: $black; |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,165 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="img-box"> |
||||
|
<img src="static/blue.png"></img> |
||||
|
</view> |
||||
|
<view class="project-box"> |
||||
|
<view class="project-info" v-for="(item,index) in list" :key="index" @click="jump(index)"> |
||||
|
<view class="info-left"> |
||||
|
{{ index + 1 }} |
||||
|
</view> |
||||
|
<view class="info-con"> |
||||
|
<view class="info-con-left">{{ item.project }}</view> |
||||
|
<view>{{ item.time }}</view> |
||||
|
</view> |
||||
|
<view class="info-right"> |
||||
|
<view class="right-con"> |
||||
|
<text class="started" v-if="item.result - 0 === 0">未开始</text> |
||||
|
<text class="ongoing" v-if="item.result - 0 === 2">进行中</text> |
||||
|
<text class="success" v-if="item.result - 0 === 1">出成绩</text> |
||||
|
<icon class="cuIcon-right" type="icon"></icon> |
||||
|
<icon class="cuIcon-right" type="icon"></icon> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
list:[{ |
||||
|
project:'30秒单摇跳', |
||||
|
time:'11月28日 09:00', |
||||
|
result:1 |
||||
|
}, { |
||||
|
project:'3分钟单摇跳', |
||||
|
time:'11月28日 10:00', |
||||
|
result:1 |
||||
|
}, { |
||||
|
project:'30秒双摇跳', |
||||
|
time:'11月28日 11:00', |
||||
|
result:2 |
||||
|
}, { |
||||
|
project:'连续三摇跳', |
||||
|
time:'11月28日 14:00', |
||||
|
result:0 |
||||
|
}, { |
||||
|
project:'30秒交互绳', |
||||
|
time:'11月28日 11:00', |
||||
|
result:2 |
||||
|
}, { |
||||
|
project:'30秒交互绳', |
||||
|
time:'11月28日 11:00', |
||||
|
result:0 |
||||
|
}, { |
||||
|
project:'30秒交互绳', |
||||
|
time:'11月28日 11:00', |
||||
|
result:0 |
||||
|
}, { |
||||
|
project:'30秒交互绳', |
||||
|
time:'11月28日 11:00', |
||||
|
result:0 |
||||
|
}, { |
||||
|
project:'30秒交互绳', |
||||
|
time:'11月28日 11:00', |
||||
|
result:0 |
||||
|
}] |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
jump(num) { |
||||
|
// console.log(num) |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/Details/Details?num=${num}` |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.img-box { |
||||
|
position: relative; |
||||
|
width: 750rpx; |
||||
|
height: 500rpx; |
||||
|
img { |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
.project-box { |
||||
|
position: absolute; |
||||
|
width: 630rpx; |
||||
|
left: 60rpx; |
||||
|
background: $white; |
||||
|
box-shadow: 0 0 30px $blue; |
||||
|
height: 62%; |
||||
|
top: 31%; |
||||
|
padding: 35rpx 35rpx; |
||||
|
border-radius: 20rpx; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
.project-info { |
||||
|
height: 60px; |
||||
|
position: relative; |
||||
|
box-shadow: 0 0 10px $blueLight; |
||||
|
margin-bottom: 10px; |
||||
|
border-radius: 50px; |
||||
|
overflow-x: auto; |
||||
|
} |
||||
|
.info-left { |
||||
|
height: 60px; |
||||
|
width: 20%; |
||||
|
line-height: 60px; |
||||
|
text-align: center; |
||||
|
font-size: 30px; |
||||
|
color: $white; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
background: $blue; |
||||
|
} |
||||
|
.info-con { |
||||
|
position: absolute; |
||||
|
left: 20%; |
||||
|
width: 50%; |
||||
|
line-height: 30px; |
||||
|
padding-left: 20px; |
||||
|
font-size: 14px; |
||||
|
font-weight: 1000; |
||||
|
height: 100%; |
||||
|
} |
||||
|
.info-con-left { |
||||
|
letter-spacing:4px |
||||
|
} |
||||
|
.info-right { |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
height: 100%; |
||||
|
width: 30%; |
||||
|
font-size: 14px; |
||||
|
|
||||
|
icon { |
||||
|
font-size: 10px; |
||||
|
} |
||||
|
} |
||||
|
.right-con { |
||||
|
position: absolute; |
||||
|
bottom: 5px; |
||||
|
width: 85%; |
||||
|
text-align: justify !important; |
||||
|
text-align-last: justify; |
||||
|
font-size: 14px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
.started { |
||||
|
color: #ADADAD; |
||||
|
} |
||||
|
.ongoing { |
||||
|
color: #F3CB77; |
||||
|
} |
||||
|
.success { |
||||
|
color: $blue; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,112 @@ |
|||||
|
import { BASE_URL, ERR_CODE } from 'api/base'; |
||||
|
import Request from './request'; |
||||
|
import Fingerprint from 'fingerprintjs' |
||||
|
|
||||
|
const test = new Request(); |
||||
|
|
||||
|
/** |
||||
|
* 获取token |
||||
|
* @return {string} 本地保存的token |
||||
|
*/ |
||||
|
export const getToken = () => uni.getStorageSync('token'); |
||||
|
|
||||
|
test.setConfig(config => { |
||||
|
/* 设置全局配置 */ |
||||
|
config.baseUrl = BASE_URL; |
||||
|
config.header = { |
||||
|
...config.header, |
||||
|
}; |
||||
|
return config; |
||||
|
}); |
||||
|
|
||||
|
test.interceptor.request((config, cancel) => { |
||||
|
/* 请求之前拦截器 */ |
||||
|
config.header = { |
||||
|
...config.header, |
||||
|
|
||||
|
}; |
||||
|
/* |
||||
|
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
|
||||
|
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
|
||||
|
} |
||||
|
*/ |
||||
|
return config; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject) |
||||
|
* @param { Number } statusCode - 请求响应体statusCode(只读) |
||||
|
* @return { Boolean } 如果为true,则 resolve, 否则 reject |
||||
|
*/ |
||||
|
test.validateStatus = statusCode => { |
||||
|
return statusCode === ERR_CODE; |
||||
|
}; |
||||
|
|
||||
|
test.interceptor.response( |
||||
|
response => { |
||||
|
/* 请求之后拦截器 */ |
||||
|
return response; |
||||
|
}, |
||||
|
response => { |
||||
|
// 请求错误做点什么
|
||||
|
return response; |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
const http = new Request(); |
||||
|
|
||||
|
http.setConfig(config => { |
||||
|
// 设置全局配置
|
||||
|
config.baseUrl = BASE_URL; // 根域名不同
|
||||
|
|
||||
|
config.header = { |
||||
|
...config.header, |
||||
|
}; |
||||
|
return config; |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject) |
||||
|
* @param { Number } statusCode - 请求响应体statusCode(只读) |
||||
|
* @return { Boolean } 如果为true,则 resolve, 否则 reject |
||||
|
*/ |
||||
|
http.validateStatus = statusCode => { |
||||
|
return statusCode === ERR_CODE; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
// console.log(fingerprint)
|
||||
|
|
||||
|
http.interceptor.request((config, cancel) => { |
||||
|
/* 请求之前拦截器 */ |
||||
|
const token = getToken() ? { Authorization: `Bearer ${getToken()}` } : {}; |
||||
|
// const fingerprint = { fingerprint: new Fingerprint().get() };
|
||||
|
config.header = { |
||||
|
...config.header, |
||||
|
...token, |
||||
|
// ...fingerprint,
|
||||
|
}; |
||||
|
/* |
||||
|
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
|
||||
|
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
|
||||
|
} |
||||
|
*/ |
||||
|
return config; |
||||
|
}); |
||||
|
|
||||
|
http.interceptor.response( |
||||
|
response => { |
||||
|
/* 请求之后拦截器 */ |
||||
|
if (response.data.code !== ERR_CODE) { |
||||
|
// 服务端返回的状态码不等于200,则reject()
|
||||
|
return Promise.reject(response.data.msg); |
||||
|
} |
||||
|
return response.data.data; |
||||
|
}, |
||||
|
response => { |
||||
|
// 请求错误做点什么
|
||||
|
return response; |
||||
|
}, |
||||
|
); |
||||
|
|
||||
|
export { http, test }; |
@ -0,0 +1,273 @@ |
|||||
|
**插件使用说明** |
||||
|
|
||||
|
- 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截 |
||||
|
- 支持全局挂载 |
||||
|
- 支持多个全局配置实例 |
||||
|
- 支持自定义验证器 |
||||
|
- 支持文件上传(如不使用可以删除class里upload 方法) |
||||
|
- 支持` typescript `、` javascript ` 版本(如果不使用ts版本,则可以把luch-request-ts 文件夹删除) |
||||
|
- 下载后把 http-request 文件夹放到项目 utils/ 目录下 |
||||
|
|
||||
|
|
||||
|
**Example** |
||||
|
--- |
||||
|
创建实例 |
||||
|
|
||||
|
``` javascript |
||||
|
const http = new Request(); |
||||
|
``` |
||||
|
|
||||
|
执行` GET `请求 |
||||
|
|
||||
|
``` javascript |
||||
|
http.get('/user/login', {params: {userName: 'name', password: '123456'}}).then(res => { |
||||
|
|
||||
|
}).catch(err => { |
||||
|
|
||||
|
}) |
||||
|
// 局部修改配置,局部配置优先级高于全局配置 |
||||
|
http.get('/user/login', { |
||||
|
params: {userName: 'name', password: '123456'}, /* 会加在url上 */ |
||||
|
header: {}, /* 会覆盖全局header */ |
||||
|
dataType: 'json', |
||||
|
responseType: 'text' |
||||
|
}).then(res => { |
||||
|
|
||||
|
}).catch(err => { |
||||
|
|
||||
|
}) |
||||
|
``` |
||||
|
执行` POST `请求 |
||||
|
|
||||
|
``` javascript |
||||
|
http.post('/user/login', {userName: 'name', password: '123456'} ).then(res => { |
||||
|
|
||||
|
}).catch(err => { |
||||
|
|
||||
|
}) |
||||
|
// 局部修改配置,局部配置优先级高于全局配置 |
||||
|
http.post('/user/login', {userName: 'name', password: '123456'}, { |
||||
|
params: {}, /* 会加在url上 */ |
||||
|
header: {}, /* 会覆盖全局header */ |
||||
|
dataType: 'json', |
||||
|
responseType: 'text' |
||||
|
}).then(res => { |
||||
|
|
||||
|
}).catch(err => { |
||||
|
|
||||
|
}) |
||||
|
``` |
||||
|
执行` upload `请求 |
||||
|
|
||||
|
``` javascript |
||||
|
http.upload('api/upload/img', { |
||||
|
files: [], // 仅5+App支持 |
||||
|
fileType:'image/video/audio', // 仅支付宝小程序,且必填。 |
||||
|
filePath: '', // 要上传文件资源的路径。 |
||||
|
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容 |
||||
|
header: {}, |
||||
|
formData: {}, // HTTP 请求中其他额外的 form data |
||||
|
}).then(res => { |
||||
|
|
||||
|
}).catch(err => { |
||||
|
|
||||
|
}) |
||||
|
``` |
||||
|
**luch-request API** |
||||
|
-- |
||||
|
``` javascript |
||||
|
http.request({ |
||||
|
method: 'POST', // 请求方法必须大写 |
||||
|
url: '/user/12345', |
||||
|
data: { |
||||
|
firstName: 'Fred', |
||||
|
lastName: 'Flintstone' |
||||
|
}, |
||||
|
params: { // 会拼接到url上 |
||||
|
token: '1111' |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
具体参数说明:[uni.uploadFile](https://uniapp.dcloud.io/api/request/network-file) |
||||
|
http.upload('api/upload/img', { |
||||
|
files: [], // 仅5+App支持 |
||||
|
fileType:'image/video/audio', // 仅支付宝小程序,且必填。 |
||||
|
filePath: '', // 要上传文件资源的路径。 |
||||
|
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容 |
||||
|
header: {}, // 如填写,会覆盖全局header |
||||
|
formData: {}, // HTTP 请求中其他额外的 form data |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
请求方法别名 / 实例方法 |
||||
|
|
||||
|
``` javascript |
||||
|
http.request(config) |
||||
|
http.get(url[, config]) |
||||
|
http.upload(url[, config]) |
||||
|
http.delete(url[, data[, config]]) |
||||
|
http.head(url[, data[, config]]) |
||||
|
http.post(url[, data[, config]]) |
||||
|
http.put(url[, data[, config]]) |
||||
|
http.connect(url[, data[, config]]) |
||||
|
http.options(url[, data[, config]]) |
||||
|
http.trace(url[, data[, config]]) |
||||
|
``` |
||||
|
|
||||
|
**全局请求配置** |
||||
|
-- |
||||
|
``` javascript |
||||
|
{ |
||||
|
baseUrl: '', /* 全局根路径,需要注意,如果请求的路径为绝对路径,则不会应用baseUrl */ |
||||
|
header: { |
||||
|
'Content-Type': 'application/json;charset=UTF-8' |
||||
|
}, |
||||
|
method: 'GET', |
||||
|
dataType: 'json', |
||||
|
responseType: 'text' |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
全局配置修改` setConfig ` |
||||
|
|
||||
|
``` javascript |
||||
|
/** |
||||
|
* @description 修改全局默认配置 |
||||
|
* @param {Function} |
||||
|
*/ |
||||
|
http.setConfig((config) => { /* config 为默认全局配置*/ |
||||
|
config.baseUrl = 'http://www.bbb.cn'; /* 根域名 */ |
||||
|
config.header = { |
||||
|
a: 1, |
||||
|
b: 2 |
||||
|
} |
||||
|
return config |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
自定义验证器` validateStatus ` |
||||
|
|
||||
|
``` javascript |
||||
|
/** |
||||
|
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject) |
||||
|
* @param { Number } statusCode - 请求响应体statusCode(只读) |
||||
|
* @return { Boolean } 如果为true,则 resolve, 否则 reject |
||||
|
*/ |
||||
|
http.validateStatus = (statusCode) => { // 默认 |
||||
|
return statusCode === 200 |
||||
|
} |
||||
|
|
||||
|
// 举个栗子 |
||||
|
http.validateStatus = (statusCode) => { |
||||
|
return statusCode >= 200 && statusCode < 300 |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**拦截器** |
||||
|
-- |
||||
|
|
||||
|
在请求之前拦截 |
||||
|
|
||||
|
``` javascript |
||||
|
/** |
||||
|
* @param { Function} cancel - 取消请求,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行; 不会进入响应拦截器 |
||||
|
* |
||||
|
* @param {String} text ['handle cancel'| any] - catch((err) => {}) err.errMsg === 'handle cancel'。非必传,默认'handle cancel' |
||||
|
* @cancel {Object} config - catch((err) => {}) err.config === config; 非必传,默认为request拦截器修改之前的config |
||||
|
* function cancel(text, config) {} |
||||
|
*/ |
||||
|
http.interceptor.request((config, cancel) => { /* cancel 为函数,如果调用会取消本次请求。需要注意:调用cancel,本次请求的catch仍会执行。必须return config */ |
||||
|
config.header = { |
||||
|
...config.header, |
||||
|
a: 1 |
||||
|
} |
||||
|
/* |
||||
|
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行 |
||||
|
cancel('token 不存在', config) // 把修改后的config传入,之后响应就可以拿到修改后的config。 如果调用了cancel但是不传修改后的config,则catch((err) => {}) err.config 为request拦截器修改之前的config |
||||
|
} |
||||
|
*/ |
||||
|
return config; |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
在请求之后拦截 |
||||
|
|
||||
|
``` javascript |
||||
|
http.interceptor.response((response) => { /* 对响应成功做点什么 (statusCode === 200),必须return response*/ |
||||
|
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject() |
||||
|
// return Promise.reject(response) |
||||
|
// } |
||||
|
console.log(response) |
||||
|
return response |
||||
|
}, (response) => { /* 对响应错误做点什么 (statusCode !== 200),必须return response*/ |
||||
|
console.log(response) |
||||
|
return response |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
**typescript使用** |
||||
|
-- |
||||
|
在` request.ts `里还暴露了五个接口 |
||||
|
```javascript |
||||
|
{ |
||||
|
options, // request 方法配置参数 |
||||
|
handleOptions, // get/post 方法配置参数 |
||||
|
config, // init 全局config接口(setConfig 参数接口) |
||||
|
requestConfig, // 请求之前参数配置项 |
||||
|
response // 响应体 |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**常见问题** |
||||
|
-- |
||||
|
1. 为什么会请求两次? |
||||
|
- 总有些小白问这些很那啥的问题,有两种可能,一种是‘post三次握手’(不知道的请先给个五星好评,然后打自己一巴掌,并问自己,为什么这都不知道),还有一种可能是`本地访问接口时跨域请求,所以浏览器会先发一个option 去预测能否成功,然后再发一个真正的请求`(没有自己观察请求头,Request Method,就跑来问的,请再打自己一巴掌,并问自己,为什么这都不知道,不知道也行,为什么不百度)。 |
||||
|
2. 如何跨域? |
||||
|
- 问的人不少,可以先百度了解一下。<a href="https://ask.dcloud.net.cn/article/35267" target="_blank">如何跨域</a> |
||||
|
3. post 怎么传不了数组的参数啊? |
||||
|
- <a href="https://uniapp.dcloud.io/api/request/request">uni-request</a> <br> |
||||
|
可以点击看一下uni-request 的api 文档,data支持的文件类型只有<code>Object/String/ArrayBuffer</code>这个真跟我没啥关系 0.0 |
||||
|
4. 'Content-Type' 为什么要小写? |
||||
|
- hbuilderX 更新至‘2.3.0.20190919’ 后,uni.request post请求,如果 ‘Content-Type’ 大写,就会在后面自动拼接‘ application/json’,请求头变成 |
||||
|
`Content-Type: application/json;charset=UTF-8 application/json`,导致后端无法解析类型,`Status Code 415`,post 请求失败。但是小写就不会出现这个问题。至于为什么我也没有深究,我现在也不清楚这是他们的bug,还是以后就这样规范了。我能做的只有立马兼容,至于后边uni官方会不会继续变动也不清楚。 |
||||
|
|
||||
|
|
||||
|
**tip** |
||||
|
-- |
||||
|
- 不想使用upload 可把class 里的upload 删除 |
||||
|
|
||||
|
|
||||
|
**issue** |
||||
|
-- |
||||
|
有任何问题或者建议可以=> <a href="https://ask.dcloud.net.cn/question/74922" target="_blank">issue提交</a>,先给个五星好评QAQ!! |
||||
|
|
||||
|
|
||||
|
**作者想说** |
||||
|
-- |
||||
|
- 主体代码3kb |
||||
|
- 目前该插件已经上项目,遇到任何问题请先检查自己的代码(排除新版本发布的情况)。最近新上了` typescript ` 版本,因为本人没使用过ts,所以写的不好的地方,还请见谅~ |
||||
|
- 写代码很容易,为了让你们看懂写文档真的很lei 0.0 |
||||
|
- 最近发现有插件与我雷同,当初接触uni-app 就发现插件市场虽然有封装的不错的request库,但是都没有对多全局配置做处理,都是通过修改源码的方式配置。我首先推出通过class类,并仿照axios的api实现request请求库,并起名‘仿axios封装request网络请求库,支持拦截器全局配置’。他们虽然修改了部分代码,但是功能与性能并没有优化,反而使代码很冗余。希望能推出新的功能,和性能更加强悍的请求库。 |
||||
|
- 任何形式的‘参考’、‘借鉴’,请标明作者 |
||||
|
```javascript |
||||
|
<a href="https://ext.dcloud.net.cn/plugin?id=392">luch-request</a> |
||||
|
``` |
||||
|
- 关于问问题 |
||||
|
1. 首先请善于利用搜索引擎,不管百度,还是Google,遇到问题请先自己尝试解决。自己尝试过无法解决,再问。 |
||||
|
2. 不要问类似为什么我的xx无法使用这种问题。请仔细阅读文档,检查代码,或者说明运行环境,把相关代码贴至评论或者发送至我的邮箱,还可以点击上面的issue提交,在里面提问,可能我在里面已经回答了。 |
||||
|
3. 我的代码如果真的出现bug,或者你有好的建议、需求,可以提issue,我看到后会立即解决 |
||||
|
4. 不要问一些弱智问题!!! |
||||
|
- 如何问问题 |
||||
|
1. 仔细阅读文档,检查代码 |
||||
|
2. 说明运行环境,比如:app端 ios、android 版本号、手机机型、普遍现象还是个别现象(越详细越好) |
||||
|
3. 发出代码片段或者截图至邮箱(很重要) |
||||
|
4. 或者可以在上方的'issue提交' 里发出详细的问题描述 |
||||
|
5. 以上都觉得解决不了你的问题,可以加QQ:`370306150` |
||||
|
|
||||
|
**土豪赞赏** |
||||
|
-- |
||||
|
<img src="https://img-cdn-qiniu.dcloud.net.cn/uploads/answer/20191014/0d9fff1e6a57a83024787224593b39c1.png" width="150"> |
||||
|
|
||||
|
####创作不易,五星好评你懂得! |
@ -0,0 +1,316 @@ |
|||||
|
/** |
||||
|
* Request 1.0.2 |
||||
|
* @Class Request |
||||
|
* @description luch-request 1.0.2 http请求插件 |
||||
|
* @Author lu-ch |
||||
|
* @Date 2019-10-14 |
||||
|
* @Email webwork.s@qq.com |
||||
|
* http://ext.dcloud.net.cn/plugin?id=392
|
||||
|
*/ |
||||
|
export default class Request { |
||||
|
config = { |
||||
|
baseUrl: '', |
||||
|
header: { |
||||
|
'content-type': 'application/json;charset=UTF-8' |
||||
|
}, |
||||
|
method: 'GET', |
||||
|
dataType: 'json', |
||||
|
responseType: 'text' |
||||
|
} |
||||
|
|
||||
|
static posUrl (url) { /* 判断url是否为绝对路径 */ |
||||
|
return /(http|https):\/\/([\w.]+\/?)\S*/.test(url) |
||||
|
} |
||||
|
|
||||
|
static addQueryString (params) { |
||||
|
let paramsData = '' |
||||
|
Object.keys(params).forEach(function (key) { |
||||
|
paramsData += key + '=' + params[key] + '&' |
||||
|
}) |
||||
|
return paramsData.substring(0, paramsData.length - 1) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @property {Function} request 请求拦截器 |
||||
|
* @property {Function} response 响应拦截器 |
||||
|
* @type {{request: Request.interceptor.request, response: Request.interceptor.response}} |
||||
|
*/ |
||||
|
interceptor = { |
||||
|
/** |
||||
|
* @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。 |
||||
|
*/ |
||||
|
request: (cb) => { |
||||
|
if (cb) { |
||||
|
this.requestBeforeFun = cb |
||||
|
} |
||||
|
}, |
||||
|
/** |
||||
|
* @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么 |
||||
|
* @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么 |
||||
|
*/ |
||||
|
response: (cb, ecb) => { |
||||
|
if (cb && ecb) { |
||||
|
this.requestComFun = cb |
||||
|
this.requestComFail = ecb |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
requestBeforeFun (config) { |
||||
|
return config |
||||
|
} |
||||
|
|
||||
|
requestComFun (response) { |
||||
|
return response |
||||
|
} |
||||
|
|
||||
|
requestComFail (response) { |
||||
|
return response |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject) |
||||
|
* @param { Number } statusCode - 请求响应体statusCode(只读) |
||||
|
* @return { Boolean } 如果为true,则 resolve, 否则 reject |
||||
|
*/ |
||||
|
validateStatus (statusCode) { |
||||
|
return statusCode === 200 |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @Function |
||||
|
* @param {Request~setConfigCallback} f - 设置全局默认配置 |
||||
|
*/ |
||||
|
setConfig (f) { |
||||
|
this.config = f(this.config) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @Function |
||||
|
* @param {Object} options - 请求配置项 |
||||
|
* @prop {String} options.url - 请求路径 |
||||
|
* @prop {Object} options.data - 请求参数 |
||||
|
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型 |
||||
|
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse |
||||
|
* @prop {Object} [options.header = config.header] - 请求header |
||||
|
* @prop {Object} [options.method = config.method] - 请求方法 |
||||
|
* @returns {Promise<unknown>} |
||||
|
*/ |
||||
|
async request (options = {}) { |
||||
|
options.baseUrl = this.config.baseUrl |
||||
|
options.dataType = options.dataType || this.config.dataType |
||||
|
options.responseType = options.responseType || this.config.responseType |
||||
|
options.url = options.url || '' |
||||
|
options.data = options.data || {} |
||||
|
options.params = options.params || {} |
||||
|
options.header = options.header || this.config.header |
||||
|
options.method = options.method || this.config.method |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let next = true |
||||
|
|
||||
|
let handleRe = {} |
||||
|
options.complete = (response) => { |
||||
|
response.config = handleRe |
||||
|
if (this.validateStatus(response.statusCode)) { // 成功
|
||||
|
response = this.requestComFun(response) |
||||
|
resolve(response) |
||||
|
} else { |
||||
|
response = this.requestComFail(response) |
||||
|
reject(response) |
||||
|
} |
||||
|
} |
||||
|
const cancel = (t = 'handle cancel', config = options) => { |
||||
|
const err = { |
||||
|
errMsg: t, |
||||
|
config: config |
||||
|
} |
||||
|
reject(err) |
||||
|
next = false |
||||
|
} |
||||
|
|
||||
|
handleRe = { ...this.requestBeforeFun(options, cancel) } |
||||
|
const _config = { ...handleRe } |
||||
|
if (!next) return |
||||
|
|
||||
|
let mergeUrl = Request.posUrl(options.url) ? options.url : (options.baseUrl + options.url) |
||||
|
if (JSON.stringify(options.params) !== '{}') { |
||||
|
const paramsH = Request.addQueryString(options.params) |
||||
|
mergeUrl += mergeUrl.indexOf('?') === -1 ? `?${paramsH}` : `&${paramsH}` |
||||
|
} |
||||
|
_config.url = mergeUrl |
||||
|
uni.request(_config) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
get (url, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
method: 'GET', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
post (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'POST', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #ifndef MP-ALIPAY
|
||||
|
put (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'PUT', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #endif
|
||||
|
|
||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||
|
delete (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'DELETE', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #endif
|
||||
|
|
||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN
|
||||
|
connect (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'CONNECT', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #endif
|
||||
|
|
||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||
|
head (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'HEAD', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #endif
|
||||
|
|
||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
|
||||
|
options (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'OPTIONS', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #endif
|
||||
|
|
||||
|
// #ifdef APP-PLUS || H5 || MP-WEIXIN
|
||||
|
trace (url, data, options = {}) { |
||||
|
return this.request({ |
||||
|
url, |
||||
|
data, |
||||
|
method: 'TRACE', |
||||
|
...options |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// #endif
|
||||
|
|
||||
|
upload (url, { |
||||
|
// #ifdef APP-PLUS
|
||||
|
files, |
||||
|
// #endif
|
||||
|
// #ifdef MP-ALIPAY
|
||||
|
fileType, |
||||
|
// #endif
|
||||
|
filePath, |
||||
|
name, |
||||
|
header, |
||||
|
formData |
||||
|
}) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let next = true |
||||
|
let handleRe = {} |
||||
|
const pubConfig = { |
||||
|
baseUrl: this.config.baseUrl, |
||||
|
url, |
||||
|
// #ifdef APP-PLUS
|
||||
|
files, |
||||
|
// #endif
|
||||
|
// #ifdef MP-ALIPAY
|
||||
|
fileType, |
||||
|
// #endif
|
||||
|
filePath, |
||||
|
method: 'UPLOAD', |
||||
|
name, |
||||
|
header: header || this.config.header, |
||||
|
formData, |
||||
|
complete: (response) => { |
||||
|
response.config = handleRe |
||||
|
if (response.statusCode === 200) { // 成功
|
||||
|
response = this.requestComFun(response) |
||||
|
resolve(response) |
||||
|
} else { |
||||
|
response = this.requestComFail(response) |
||||
|
reject(response) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
const cancel = (t = 'handle cancel', config = pubConfig) => { |
||||
|
const err = { |
||||
|
errMsg: t, |
||||
|
config: config |
||||
|
} |
||||
|
reject(err) |
||||
|
next = false |
||||
|
} |
||||
|
|
||||
|
handleRe = { ...this.requestBeforeFun(pubConfig, cancel) } |
||||
|
const _config = { ...handleRe } |
||||
|
if (!next) return |
||||
|
_config.url = Request.posUrl(url) ? url : (this.config.baseUrl + url) |
||||
|
uni.uploadFile(_config) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* setConfig回调 |
||||
|
* @return {Object} - 返回操作后的config |
||||
|
* @callback Request~setConfigCallback |
||||
|
* @param {Object} config - 全局默认config |
||||
|
*/ |
||||
|
/** |
||||
|
* 请求拦截器回调 |
||||
|
* @return {Object} - 返回操作后的config |
||||
|
* @callback Request~requestCallback |
||||
|
* @param {Object} config - 全局config |
||||
|
* @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求 |
||||
|
*/ |
||||
|
/** |
||||
|
* 响应拦截器回调 |
||||
|
* @return {Object} - 返回操作后的response |
||||
|
* @callback Request~responseCallback |
||||
|
* @param {Object} response - 请求结果 response |
||||
|
*/ |
||||
|
/** |
||||
|
* 响应错误拦截器回调 |
||||
|
* @return {Object} - 返回操作后的response |
||||
|
* @callback Request~responseErrCallback |
||||
|
* @param {Object} response - 请求结果 response |
||||
|
*/ |
After Width: | Height: | Size: 275 KiB |
After Width: | Height: | Size: 220 KiB |
@ -0,0 +1,12 @@ |
|||||
|
import Vue from 'vue'; |
||||
|
import Vuex from 'vuex'; |
||||
|
import user from './modules/user/index'; |
||||
|
|
||||
|
|
||||
|
Vue.use(Vuex); |
||||
|
const store = new Vuex.Store({ |
||||
|
// modules:sessionStorage.getItem('state') ? JSON.parse(sessionStorage.getItem('state')): { user, project }
|
||||
|
modules: { user }, |
||||
|
}); |
||||
|
|
||||
|
export default store; |
@ -0,0 +1,9 @@ |
|||||
|
import { showLoading, hideLoading, showModal, showToast } from 'utils/ui'; |
||||
|
import { mpLogin, signIn, ddLogin } from 'utils/user'; |
||||
|
import { http } from 'plugins/request/index'; |
||||
|
|
||||
|
const actions = { |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
export default actions; |
@ -0,0 +1,5 @@ |
|||||
|
import state from './state'; |
||||
|
import mutations from './mutations'; |
||||
|
import actions from './actions.js'; |
||||
|
|
||||
|
export default { namespaced: true, state, actions, mutations }; |
@ -0,0 +1,26 @@ |
|||||
|
const mutations = { |
||||
|
/** |
||||
|
* 设置存储token |
||||
|
* @param {object} state |
||||
|
* @param {string} token |
||||
|
*/ |
||||
|
setToken(state, token) { |
||||
|
if (!token) return; |
||||
|
state.token = token; |
||||
|
uni.setStorageSync('token', token); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* 设置user数据 |
||||
|
* @param {object} state |
||||
|
* @param {object} user |
||||
|
*/ |
||||
|
setUser(state, user) { |
||||
|
if (!user) return; |
||||
|
state.user = { ...user }; |
||||
|
uni.setStorageSync('user', JSON.stringify(user)); |
||||
|
}, |
||||
|
|
||||
|
}; |
||||
|
|
||||
|
export default mutations; |
@ -0,0 +1,6 @@ |
|||||
|
const state = { |
||||
|
token: '', |
||||
|
user: null |
||||
|
}; |
||||
|
|
||||
|
export default state; |
@ -0,0 +1,133 @@ |
|||||
|
/* 颜色变量 */ |
||||
|
/* Color 可以自定义相关配色 */ |
||||
|
/* 标准色 */ |
||||
|
$red: #F26A1B; |
||||
|
$orange: #f37b1d; |
||||
|
$yellow: #fbbd08; |
||||
|
$olive: #76AC96; |
||||
|
$green: #11A20D; // 健康打卡 绿 |
||||
|
$cyan: #0897C7; // 按钮蓝 |
||||
|
$blue: #6D99F2; |
||||
|
$darkBlue: #0076FF; |
||||
|
$purple: #677DBF; // 校园打卡 青 |
||||
|
$mauve: #9c26b0; |
||||
|
$pink: #e03997; |
||||
|
$brown: #a5673f; |
||||
|
$grey: #CDCDCD; |
||||
|
$black: #333333; |
||||
|
$darkGray: #666666; |
||||
|
$gray: #808080; |
||||
|
$ghostWhite: #f1f1f1; |
||||
|
$white: #ffffff; |
||||
|
|
||||
|
/* 浅色 */ |
||||
|
$redLight: #fadbd9; |
||||
|
$orangeLight: #fde6d2; |
||||
|
$yellowLight: #fef2ce; |
||||
|
$oliveLight: #e8f4d9; |
||||
|
$greenLight: #d7f0db; |
||||
|
$cyanLight: #d2f1f0; |
||||
|
$blueLight: #cce6ff; |
||||
|
$purpleLight: #e1d7f0; |
||||
|
$mauveLight: #ebd4ef; |
||||
|
$pinkLight: #f9d7ea; |
||||
|
$brownLight: #ede1d9; |
||||
|
$greyLight: #F5f5f5; |
||||
|
|
||||
|
/* 渐变色 */ |
||||
|
$gradualRed: linear-gradient(45deg, #f43f3b, #ec008c); |
||||
|
$gradualOrange: linear-gradient(45deg, #ff9700, #ed1c24); |
||||
|
$gradualGreen: linear-gradient(45deg, #39b54a, #8dc63f); |
||||
|
$gradualPurple: linear-gradient(45deg, #ec008c, #5e00ff); |
||||
|
$gradualPink: linear-gradient(45deg, #ec008c, #6739b6); |
||||
|
$gradualBlue: linear-gradient(45deg, #0081ff, #1cbbb4); |
||||
|
|
||||
|
/* 阴影透明色 */ |
||||
|
$ShadowSize: 6rpx 6rpx 8rpx; |
||||
|
$redShadow: rgba(204, 69, 59, 0.2); |
||||
|
$orangeShadow: rgba(217, 109, 26, 0.2); |
||||
|
$yellowShadow: rgba(224, 170, 7, 0.2); |
||||
|
$oliveShadow: rgba(124, 173, 55, 0.2); |
||||
|
$greenShadow: rgba(48, 156, 63, 0.2); |
||||
|
$cyanShadow: rgba(28, 187, 180, 0.2); |
||||
|
$blueShadow: rgba(0, 102, 204, 0.2); |
||||
|
$purpleShadow: rgba(88, 48, 156, 0.2); |
||||
|
$mauveShadow: rgba(133, 33, 150, 0.2); |
||||
|
$pinkShadow: rgba(199, 50, 134, 0.2); |
||||
|
$brownShadow: rgba(140, 88, 53, 0.2); |
||||
|
$greyShadow: rgba(114, 130, 138, 0.2); |
||||
|
$grayShadow: rgba(114, 130, 138, 0.2); |
||||
|
$blackShadow: rgba(26, 26, 26, 0.2); |
||||
|
$whiteShadow: rgba(255, 255, 255, 0.2); |
||||
|
|
||||
|
/* 行为相关颜色 */ |
||||
|
$uni-color-primary: #007aff; |
||||
|
$uni-color-success: #4cd964; |
||||
|
$uni-color-warning: #f0ad4e; |
||||
|
$uni-color-error: #dd524d; |
||||
|
|
||||
|
/* 首页四个配色 */ |
||||
|
$iconGreen: #62D4A6; |
||||
|
$iconCyan: #9376F5; |
||||
|
$iconBlue: #58C9D1; |
||||
|
$iconPurple: #CF7FE6; |
||||
|
|
||||
|
/* 文字基本颜色 */ |
||||
|
$uni-text-color: #101010; //基本色 |
||||
|
$uni-text-color-inverse: #fff; //反色 |
||||
|
$uni-text-color-grey: #747474; //辅助灰色,如加载更多的提示信息 |
||||
|
$uni-text-color-placeholder: #808080; |
||||
|
$uni-text-color-disable: #c0c0c0; |
||||
|
|
||||
|
/* 背景颜色 */ |
||||
|
$uni-bg-color: #ffffff; |
||||
|
$uni-bg-color-grey: #f8f8f8; |
||||
|
$uni-bg-color-hover: #f1f1f1; //点击状态颜色 |
||||
|
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色 |
||||
|
|
||||
|
/* 边框颜色 */ |
||||
|
$uni-border-color: #c8c7cc; |
||||
|
|
||||
|
/* 尺寸变量 */ |
||||
|
|
||||
|
/* 文字尺寸 */ |
||||
|
$uni-font-size: 20rpx; |
||||
|
$uni-font-size-sm: 24rpx; |
||||
|
$uni-font-size-base: 28rpx; |
||||
|
$uni-font-size-lg: 32rpx; |
||||
|
$uni-font-size-xl: 36rpx; |
||||
|
$uni-font-size-xxl: 44rpx; |
||||
|
$uni-font-size-sl: 80rpx; |
||||
|
$uni-font-size-xsl: 120rpx; |
||||
|
|
||||
|
/* 图片尺寸 */ |
||||
|
$uni-img-size-sm: 40rpx; |
||||
|
$uni-img-size-base: 52rpx; |
||||
|
$uni-img-size-lg: 80rpx; |
||||
|
|
||||
|
/* Border Radius */ |
||||
|
$uni-border-radius-sm: 4rpx; |
||||
|
$uni-border-radius-base: 6rpx; |
||||
|
$uni-border-radius-lg: 12rpx; |
||||
|
$uni-border-radius-circle: 50%; |
||||
|
|
||||
|
/* 水平间距 */ |
||||
|
$uni-spacing-row-sm: 10px; |
||||
|
$uni-spacing-row-base: 20rpx; |
||||
|
$uni-spacing-row-lg: 30rpx; |
||||
|
|
||||
|
/* 垂直间距 */ |
||||
|
$uni-spacing-col-sm: 8rpx; |
||||
|
$uni-spacing-col-base: 16rpx; |
||||
|
$uni-spacing-col-lg: 24rpx; |
||||
|
|
||||
|
/* 透明度 */ |
||||
|
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 |
||||
|
|
||||
|
/* 文章场景相关 */ |
||||
|
$uni-color-title: #2c405a; // 文章标题颜色 |
||||
|
$uni-font-size-title: 40rpx; |
||||
|
$uni-color-subtitle: #555555; // 二级标题颜色 |
||||
|
$uni-font-size-subtitle: 36rpx; |
||||
|
$uni-color-paragraph: #3f536e; // 文章段落颜色 |
||||
|
$uni-font-size-paragraph: 30rpx; |
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* 显示模态框 |
||||
|
* @param {string} msg |
||||
|
* @param {boolean} showCancel |
||||
|
* @param {string} confirmText |
||||
|
*/ |
||||
|
export const showModal = (msg, showCancel = false, confirmText = '知道了') => |
||||
|
uni.showModal({ |
||||
|
title: '提示', |
||||
|
content: msg, |
||||
|
showCancel, |
||||
|
confirmText, |
||||
|
confirmColor: '#1890ff', |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 显示toast |
||||
|
* @param {string} msg |
||||
|
*/ |
||||
|
export const showToast = msg => |
||||
|
uni.showToast({ |
||||
|
title: msg, |
||||
|
icon: 'none', |
||||
|
duration: 3000, |
||||
|
}); |
||||
|
|
||||
|
// 显示加载动画
|
||||
|
export const showLoading = (msg = '努力加载中...') => uni.showLoading({ title: msg }); |
||||
|
|
||||
|
// 隐藏加载动画
|
||||
|
export const hideLoading = () => uni.hideLoading(); |
@ -0,0 +1,132 @@ |
|||||
|
import { SIGN_IN } from 'api/user'; |
||||
|
import { ERR_CODE } from 'config/config.default'; |
||||
|
import { http as axios } from 'plugins/request/index'; |
||||
|
import { SIGN_IN_CLIENTS, SIGN_IN_TYPES } from 'config/config.user'; |
||||
|
|
||||
|
/** |
||||
|
* 保存token |
||||
|
* @param {string} token 服务端返回的token数据 |
||||
|
*/ |
||||
|
export const setToken = token => { |
||||
|
uni.setStorageSync('token', token); |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 获取token |
||||
|
* @return {string} 本地保存的token |
||||
|
*/ |
||||
|
export const getToken = () => uni.getStorageSync('token'); |
||||
|
|
||||
|
/** |
||||
|
* 提交登录信息 |
||||
|
* @param {object} params 提交的参数 |
||||
|
*/ |
||||
|
export const signIn = params => { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
axios |
||||
|
.post(SIGN_IN, params) |
||||
|
.then(res => { |
||||
|
const { success, code, data, msg } = res.data; |
||||
|
if (success && code === ERR_CODE) { |
||||
|
if (!data || !data.token) { |
||||
|
reject('服务端返回数据不正确'); |
||||
|
} else { |
||||
|
resolve(data); |
||||
|
} |
||||
|
} else { |
||||
|
reject(`登录请求失败 ${msg}`); |
||||
|
} |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
reject(err); |
||||
|
}); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 微信登录
|
||||
|
export const wxLogin = () => { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
uni.login({ |
||||
|
provider: 'weixin', |
||||
|
success(response) { |
||||
|
if (response.code) { |
||||
|
const params = { |
||||
|
client: SIGN_IN_CLIENTS['mp'], |
||||
|
type: SIGN_IN_TYPES['mp'], |
||||
|
data: { identifier: response.code, credential: 'health' }, |
||||
|
// redirect: 'https://test.tall.wiki/gateway/health/initMsg',
|
||||
|
redirect: 'https://www.tall.wiki/gateway/health/initMsg', |
||||
|
}; |
||||
|
resolve(params); |
||||
|
} else { |
||||
|
reject(response.errMsg); |
||||
|
} |
||||
|
}, |
||||
|
fail() { |
||||
|
console.log('fail'); |
||||
|
reject('微信登录失败'); |
||||
|
}, |
||||
|
}); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 企业微信登录
|
||||
|
export const wxWorkLogin = () => { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
wx.qy.login({ |
||||
|
provider: 'weixin', |
||||
|
success(response) { |
||||
|
if (response.code) { |
||||
|
const params = { |
||||
|
client: SIGN_IN_CLIENTS['wx_work'], |
||||
|
type: SIGN_IN_TYPES['wx_work'], |
||||
|
data: { identifier: response.code, credential: 'health' }, |
||||
|
// redirect: 'https://test.tall.wiki/gateway/health/initMsg',
|
||||
|
redirect: 'https://www.tall.wiki/gateway/health/initMsg', |
||||
|
}; |
||||
|
resolve(params); |
||||
|
} else { |
||||
|
reject(response.errMsg); |
||||
|
} |
||||
|
}, |
||||
|
fail() { |
||||
|
console.log('fail'); |
||||
|
reject('微信登录失败'); |
||||
|
}, |
||||
|
}); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 小程序登录
|
||||
|
export const mpLogin = () => { |
||||
|
try { |
||||
|
const res = uni.getSystemInfoSync(); |
||||
|
if (res.environment === 'wxwork') { |
||||
|
return wxWorkLogin(); |
||||
|
} else { |
||||
|
return wxLogin(); |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.log('err: ', err); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 钉钉登录
|
||||
|
export const ddLogin = () => { |
||||
|
console.log('dingtalk login.........'); |
||||
|
try { |
||||
|
dd.getAuthCode({ |
||||
|
success(res) { |
||||
|
console.log('res: ', res); |
||||
|
/*{ |
||||
|
authCode: 'hYLK98jkf0m' // string authCode
|
||||
|
}*/ |
||||
|
}, |
||||
|
fail: function(err) { |
||||
|
console.log('err: ', err); |
||||
|
}, |
||||
|
}); |
||||
|
} catch (error) { |
||||
|
console.log('error: ', error); |
||||
|
} |
||||
|
}; |
@ -0,0 +1,20 @@ |
|||||
|
/** |
||||
|
* 格式化 querystring |
||||
|
* taskId=3&scene=1011 -> |
||||
|
* {taskId:3,scene:1011} |
||||
|
*/ |
||||
|
export const formatQuery = str => { |
||||
|
if (!str) return; |
||||
|
const result = {}; |
||||
|
if (str.includes('&')) { |
||||
|
const arr = str.split('&'); |
||||
|
arr.forEach(item => { |
||||
|
const arr1 = item.split('='); |
||||
|
result[arr1[0]] = arr1[1]; |
||||
|
}); |
||||
|
} else { |
||||
|
const arr = str.split('='); |
||||
|
result[arr[0]] = arr[1]; |
||||
|
} |
||||
|
return result; |
||||
|
}; |
@ -0,0 +1,21 @@ |
|||||
|
const path = require('path'); |
||||
|
const resolve = dir => path.join(__dirname, dir); |
||||
|
|
||||
|
module.exports = { |
||||
|
lintOnSave: true, |
||||
|
configureWebpack: { |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
'~': __dirname, |
||||
|
config: resolve('config'), |
||||
|
api: resolve('api'), |
||||
|
store: resolve('store'), |
||||
|
components: resolve('components'), |
||||
|
pages: resolve('pages'), |
||||
|
common: resolve('common'), |
||||
|
plugins: resolve('plugins'), |
||||
|
utils: resolve('utils'), |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,8 @@ |
|||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. |
||||
|
# yarn lockfile v1 |
||||
|
|
||||
|
|
||||
|
moment@^2.24.0: |
||||
|
version "2.24.0" |
||||
|
resolved "https://registry.npm.taobao.org/moment/download/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" |
||||
|
integrity sha1-DQVdU/UFKqZTyfbraLtdEr9cK1s= |
Loading…
Reference in new issue