Browse Source

login init

devlop
邓雄飞 4 years ago
parent
commit
ce66710ffe
15 changed files with 1374 additions and 148 deletions
  1. 32
      App.vue
  2. 42
      apis/loginApi.js
  3. 29
      enums/index.js
  4. 6
      pages.json
  5. 35
      pages/agreement/index.vue
  6. 271
      pages/login/index.vue
  7. 126
      store/index.js
  8. 31
      uni_modules/uni-easyinput/changelog.md
  9. 56
      uni_modules/uni-easyinput/components/uni-easyinput/common.js
  10. 461
      uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue
  11. 90
      uni_modules/uni-easyinput/package.json
  12. 11
      uni_modules/uni-easyinput/readme.md
  13. 84
      utils/hook.js
  14. 215
      utils/http/http.js
  15. 33
      utils/http/index.js

32
App.vue

@ -1,18 +1,24 @@
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
import { go2 } from '@/utils/hook.js'
import store from '@/store/index.js'
export default {
onLaunch: function () {
const token = store.state.qnToken
if (!token) {
go2('login')
}
console.log('App launched')
},
onShow: function () {
console.log('App Show')
},
onHide: function () {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
@import url("./common/css/reset.scss");
/*每个页面公共css */
@import url('./common/css/reset.scss');
</style>

42
apis/loginApi.js

@ -0,0 +1,42 @@
import http from '../utils/http/index.js'
/**
* 手机登录获取验证码接口
* @param {object} data 获取验证码参数
* @returns 获取验证码结果
* swagger:http://api-ops-uec-test.qniao.cn/uec/swagger-ui/index.html?urls.primaryName=CustomerApi#/%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81/authorizeByCaptchaUsingPOST
*/
export const getAuthCaptcha = (data) => {
return http.post({
url: '/uec/get/auth-captcha',
data
})
}
/**
* 手机验证码登录,返回临时token
* @param {object} data 登录参数
* @returns {object} 返回参数 {token: '', userId: ''}
*
* swagger:http://api-ops-uec-test.qniao.cn/uec/swagger-ui/index.html?urls.primaryName=CustomerApi#/%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81/authorizeByCaptchaUsingPOST
*/
export const loginByPhone = (data) => {
return http.post({
url: '/uec/authorize/by-captcha',
data
})
}
/**
* 手机验证码登录,返回临时token
* @param {object} data 登录参数
* @returns {string} 返回参数 token:string
*
* swagger:http://api-ops-yyt-test.qniao.cn/yyt-uec/swagger-ui/index.html?urls.primaryName=CustomerApi#/%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81/getProductLineTokenByLoginTokenUsingPOST
*/
export const getQnToken = (data) => {
return http.post({
url: '/yyt-uec/authorize/get/product-line-token/by/login-token',
data
})
}

29
enums/index.js

@ -0,0 +1,29 @@
/**
* 账号类型
*/
export const accountType = {
WX: 1,
PHONE: 2,
EMAIL: 3,
APPLEID: 4,
CUSTOM: 5
}
/**
* 验证码用途
*/
export const codePurpose = {
CERTIFICATION: 1,
RESET_LOGIN_PASSWORD: 2,
RESET_PHONE: 3,
BIND_BANK_CARD: 4,
RESET_CREDIT_PASSWORD: 5
}
/**
* 可验证账号类型
*/
export const verificationType = {
PHONE: 1,
EMAIL: 2
}

6
pages.json

@ -24,6 +24,12 @@
"navigationBarTitleText": "登录",
"enablePullDownRefresh": false
}
},
{
"path": "pages/agreement/index",
"style": {
"navigationBarTitleText": "用户与隐私协议"
}
}
],
"globalStyle": {

35
pages/agreement/index.vue

@ -0,0 +1,35 @@
<template>
<view class="content">agreement</view>
</template>
<script>
export default {}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>

271
pages/login/index.vue

@ -1,15 +1,270 @@
<template>
<view>
login
</view>
<view>
<uni-nav-bar
:fixed="true"
color="#ffffff"
background-color="#ffffff"
:status-bar="true"
:border="false"
left-icon="closeempty"
@clickLeft="close"
/>
<view class="logo_area">
<image class="logo" src="/static/logo.png"></image>
<text class="title">纸掌柜</text>
</view>
<view class="form">
<view class="phone">
<uni-easyinput
v-model="phoneNumber"
:inputBorder="false"
:placeholderStyle="'font-size:32rpx;'"
placeholder="请输入手机号码"
:maxlength="11"
/>
</view>
<view class="captcha">
<uni-easyinput
v-model="captcha"
:inputBorder="false"
:placeholderStyle="'font-size:32rpx;'"
placeholder="请输入验证码"
:maxlength="6"
/>
<view v-show="timer > 0" class="timer_area">
<text class="timer">{{ `(${timer}S后) ` }}</text>
<text class="timer_text">重新获取</text>
</view>
<text v-show="timer <= 0" class="code" @click="getCode">
获取验证码
</text>
</view>
<view
class="login-button"
@click="login"
:style="{ opacity: disabled ? '0.5' : '' }"
>
登录
</view>
</view>
<view class="agreement_area">
<checkbox-group @change="onCheck">
<checkbox
value="cb"
:checked="checked"
color="#000000"
style="transform: scale(0.7)"
/>
</checkbox-group>
<view class="agreement">
已阅读并同意纸掌柜
<text class="agreement_text" @click="jumpAgreement">
用户与隐私协议
</text>
</view>
</view>
<!--
<uni-easyinput v-model="captcha" placeholder="验证码" />
<button @click="getCode">获取验证码</button>
<button @click="login">登录</button> -->
</view>
</template>
<script>
export default {
}
import { getAuthCaptcha, loginByPhone, getQnToken } from '@/apis/loginApi'
import { accountType, verificationType, codePurpose } from '@/enums/index.js'
import store from '@/store/index.js'
import { go2, back } from '@/utils/hook.js'
export default {
data() {
return {
phoneNumber: '',
captcha: '',
timer: 0,
checked: false
}
},
methods: {
// 退
close() {
back()
},
onCheck(e) {
if (e.detail.value.length > 0) {
this.checked = true
} else {
this.checked = false
}
},
jumpAgreement() {
go2('agreement')
},
//
getCode() {
if (this.phoneNumber.trim() === '') {
uni.showToast({
title: '请输入手机号码',
icon: 'none'
})
return
}
if (this.phoneNumber.trim().length !== 11) {
uni.showToast({
title: '请输入正确的手机号码',
icon: 'none'
})
return
}
this.countdown()
getAuthCaptcha({
purpose: codePurpose.CERTIFICATION,
verifiableAccount: this.phoneNumber,
verifiableAccountType: verificationType.PHONE
}).then((res) => {
console.log(res)
})
},
//
countdown() {
this.timer = 60
let timer = setInterval(() => {
this.timer--
if (this.timer <= 0) {
clearInterval(timer)
}
}, 1000)
},
//
login() {
if (this.disabled) {
return
}
if (!this.checked) {
uni.showToast({
title: '请同意用户与隐私协议',
icon: 'none'
})
return
}
loginByPhone({
accountType: accountType.PHONE,
account: this.phoneNumber,
captcha: this.captcha
}).then((res) => {
if (res) {
store.commit('setUecToken', res.token)
getQnToken({ loginToken: res.token }).then((token) => {
if (token) {
store.commit('setToken', token)
//
const nextPage = store.state.nextPage
if (nextPage.name) {
go2(nextPage.name, nextPage.data)
} else {
go2('mine')
}
store.commit('removeNextPage')
}
})
}
})
}
},
computed: {
disabled() {
return (
this.phoneNumber.trim() === '' ||
this.phoneNumber.trim().length != 11 ||
this.captcha.trim() === ''
)
}
}
}
</script>
<style>
<style lang="scss">
.logo_area {
margin-top: 187rpx;
width: 750rpx;
height: 100rpx;
display: flex;
justify-content: center;
align-items: center;
.logo {
width: 100rpx;
height: 100rpx;
margin-right: 20rpx;
}
.title {
opacity: 0.8;
font-size: 60rpx;
color: #000000;
letter-spacing: 9rpx;
text-align: center;
font-weight: 400;
}
}
.form {
width: 750rpx;
margin-top: 280rpx;
padding: 0 64rpx;
.phone {
width: 622rpx;
border-bottom: 2rpx solid #e0e0e0;
}
.captcha {
margin-top: 44rpx;
width: 622rpx;
border-bottom: 2rpx solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
.timer_area {
width: auto;
flex: 0 0 auto;
.timer {
font-size: 32rpx;
color: #007aff;
}
.timer_area {
font-size: 32rpx;
color: #999999;
}
}
.code {
width: 160rpx;
font-size: 32rpx;
color: #007aff;
flex: 0 0 auto;
}
}
.login-button {
font-size: 32rpx;
margin-top: 92rpx;
width: 622rpx;
height: 86rpx;
border-radius: 42px;
display: flex;
justify-content: center;
align-items: center;
background-color: #007aff;
color: #ffffff;
}
}
.agreement_area {
position: fixed;
bottom: 108rpx;
width: 750rpx;
display: flex;
justify-content: center;
align-items: center;
.agreement {
font-size: 24rpx;
color: #555555;
.agreement_text {
font-size: 24rpx;
color: #007aff;
}
}
}
</style>

126
store/index.js

@ -1,7 +1,131 @@
import Vue from 'vue'
import Vuex from 'vuex'
import { isObject } from '@/utils/is'
let qnToken = null,
userInfo = null,
supplierInfo = null,
uecToken = null
try {
uecToken = uni.getStorageSync('uecToken')
qnToken = uni.getStorageSync('qnToken')
userInfo = uni.getStorageSync('userInfo')
if (userInfo) {
userInfo = JSON.parse(userInfo)
}
supplierInfo = uni.getStorageSync('supplierInfo')
if (supplierInfo) {
supplierInfo = JSON.parse(supplierInfo)
}
} catch (e) {
console.error('初始化错误:', e)
}
Vue.use(Vuex)
const store = new Vuex.Store({
state: {}
state: {
uecToken: uecToken || '',
qnToken: qnToken || '', // token
userInfo: userInfo || {}, // 用户信息
supplierInfo: supplierInfo || {}, // 纸盘商信息
nextPage: {
name: '',
data: {}
}
},
mutations: {
setUecToken(state, token) {
try {
uni.setStorageSync('uecToken', token)
state.uecToken = token
} catch (e) {
console.error('更改uecToken失败:', e)
}
},
removeUecToken(state) {
try {
uni.removeStorageSync('uecToken')
state.uecToken = ''
} catch (e) {
console.error('删除uecToken失败:', e)
}
},
setToken(state, token) {
try {
uni.setStorageSync('qnToken', token)
state.qnToken = token
} catch (e) {
console.error('更改token失败:', e)
}
},
removeToken(state) {
try {
uni.removeStorageSync('qnToken')
state.qnToken = ''
} catch (e) {
console.error('删除token失败:', e)
}
},
setUserInfo(state, userInfo) {
if (!isObject(userInfo)) {
console.error('userInfo必须是对象')
return
}
try {
uni.setStorageSync('userInfo', JSON.stringify(userInfo))
state.userInfo = userInfo
} catch (e) {
console.error('更改userInfo失败:', e)
}
},
removeUserInfo(state) {
try {
uni.removeStorageSync('userInfo')
state.userInfo = {}
} catch (e) {
console.error('删除userInfo失败:', e)
}
},
setSupplierInfo(state, supplierInfo) {
if (!isObject(supplierInfo)) {
console.error('supplierInfo必须是对象')
return
}
try {
uni.setStorageSync('supplierInfo', JSON.stringify(supplierInfo))
state.supplierInfo = supplierInfo
} catch (e) {
console.error('更改supplierInfo失败:', e)
}
},
removeSupplierInfo(state) {
try {
uni.removeStorageSync('supplierInfo')
state.supplierInfo = {}
} catch (e) {
console.error('删除supplierInfo失败:', e)
}
},
setNextPage(state, nextPage) {
if (!isObject(nextPage)) {
console.error('nextPage必须是对象')
return
}
state.nextPage.name = nextPage.name || ''
state.nextPage.data = nextPage.data || {}
},
removeNextPage(state) {
state.nextPage.name = ''
state.nextPage.data = {}
}
},
actions: {
logout({ commit }) {
commit('removeUecToken')
commit('removeToken')
commit('removeUserInfo')
commit('removeSupplierInfo')
}
}
})
export default store

31
uni_modules/uni-easyinput/changelog.md

@ -0,0 +1,31 @@
## 1.0.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-easyinput](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
## 0.1.4(2021-08-20)
- 修复 在 uni-forms 的动态表单中默认值校验不通过的 bug
## 0.1.3(2021-08-11)
- 修复 在 uni-forms 中重置表单,错误信息无法清除的问题
## 0.1.2(2021-07-30)
- 优化 vue3下事件警告的问题
## 0.1.1
- 优化 errorMessage 属性支持 Boolean 类型
## 0.1.0(2021-07-13)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 0.0.16(2021-06-29)
- 修复 confirmType 属性(仅 type="text" 生效)导致多行文本框无法换行的 bug
## 0.0.15(2021-06-21)
- 修复 passwordIcon 属性拼写错误的 bug
## 0.0.14(2021-06-18)
- 新增 passwordIcon 属性,当type=password时是否显示小眼睛图标
- 修复 confirmType 属性不生效的问题
## 0.0.13(2021-06-04)
- 修复 disabled 状态可清出内容的 bug
## 0.0.12(2021-05-12)
- 新增 组件示例地址
## 0.0.11(2021-05-07)
- 修复 input-border 属性不生效的问题
## 0.0.10(2021-04-30)
- 修复 ios 遮挡文字、显示一半的问题
## 0.0.9(2021-02-05)
- 调整为uni_modules目录规范
- 优化 兼容 nvue 页面

56
uni_modules/uni-easyinput/components/uni-easyinput/common.js

@ -0,0 +1,56 @@
/**
* @desc 函数防抖
* @param func 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行 false - 延迟执行
*/
export const debounce = function(func, wait = 1000, immediate = true) {
let timer;
console.log(1);
return function() {
console.log(123);
let context = this,
args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) func.apply(context, args);
} else {
timer = setTimeout(() => {
func.apply(context, args);
}, wait)
}
}
}
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 使用表时间戳在时间段开始的时候触发 2 使用表定时器在时间段结束的时候触发
*/
export const throttle = (func, wait = 1000, type = 1) => {
let previous = 0;
let timeout;
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}

461
uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue

@ -0,0 +1,461 @@
<template>
<view class="uni-easyinput" :class="{'uni-easyinput-error':msg}" :style="{color:inputBorder && msg?'#e43d33':styles.color}">
<view class="uni-easyinput__content" :class="{'is-input-border':inputBorder ,'is-input-error-border':inputBorder && msg,'is-textarea':type==='textarea','is-disabled':disabled}"
:style="{'border-color':inputBorder && msg?'#dd524d':styles.borderColor,'background-color':disabled?styles.disableColor:''}">
<uni-icons v-if="prefixIcon" class="content-clear-icon" :type="prefixIcon" color="#c0c4cc" @click="onClickIcon('prefix')"></uni-icons>
<textarea v-if="type === 'textarea'" class="uni-easyinput__content-textarea" :class="{'input-padding':inputBorder}"
:name="name" :value="val" :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" placeholder-class="uni-easyinput__placeholder-class"
:maxlength="inputMaxlength" :focus="focused" :autoHeight="autoHeight" @input="onInput" @blur="onBlur" @focus="onFocus"
@confirm="onConfirm"></textarea>
<input v-else :type="type === 'password'?'text':type" class="uni-easyinput__content-input" :style="{
'padding-right':type === 'password' ||clearable || prefixIcon?'':'10px',
'padding-left':prefixIcon?'':'10px'
}"
:name="name" :value="val" :password="!showPassword && type === 'password'" :placeholder="placeholder"
:placeholderStyle="placeholderStyle" placeholder-class="uni-easyinput__placeholder-class" :disabled="disabled" :maxlength="inputMaxlength" :focus="focused" :confirmType="confirmType" @focus="onFocus"
@blur="onBlur" @input="onInput" @confirm="onConfirm" />
<template v-if="type === 'password' && passwordIcon" >
<uni-icons v-if="val != '' " class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" :type="showPassword?'eye-slash-filled':'eye-filled'"
:size="18" color="#c0c4cc" @click="onEyes"></uni-icons>
</template>
<template v-else-if="suffixIcon">
<uni-icons v-if="suffixIcon" class="content-clear-icon" :type="suffixIcon" color="#c0c4cc" @click="onClickIcon('suffix')"></uni-icons>
</template>
<template v-else>
<uni-icons class="content-clear-icon" :class="{'is-textarea-icon':type==='textarea'}" type="clear" :size="clearSize"
v-if="clearable && val && !disabled" color="#c0c4cc" @click="onClear"></uni-icons>
</template>
<slot name="right"></slot>
</view>
</view>
</template>
<script>
// import {
// debounce,
// throttle
// } from './common.js'
/**
* Easyinput 输入框
* @description 此组件可以实现表单的输入与校验包括 "text" "textarea" 类型
* @tutorial https://ext.dcloud.net.cn/plugin?id=3455
* @property {String} value 输入内容
* @property {String } type 输入框的类型默认text password/text/textarea/..
* @value text 文本输入键盘
* @value textarea 多行文本输入键盘
* @value password 密码输入键盘
* @value number 数字输入键盘注意iOS上app-vue弹出的数字键盘并非9宫格方式
* @value idcard 身份证输入键盘支付宝百度QQ小程序
* @value digit 带小数点的数字键盘 App的nvue页面微信支付宝百度头条QQ小程序支持
* @property {Boolean} clearable 是否显示右侧清空内容的图标控件点击可清空输入框内容默认true
* @property {Boolean} autoHeight 是否自动增高输入区域type为textarea时有效默认true
* @property {String } placeholder 输入框的提示文字
* @property {String } placeholderStyle placeholder的样式(内联样式字符串)"color: #ddd"
* @property {Boolean} focus 是否自动获得焦点默认false
* @property {Boolean} disabled 是否禁用默认false
* @property {Number } maxlength 最大输入长度设置为 -1 的时候不限制最大长度默认140
* @property {String } confirmType 设置键盘右下角按钮的文字仅在type="text"时生效默认done
* @property {Number } clearSize 清除图标的大小单位px默认15
* @property {String} prefixIcon 输入框头部图标
* @property {String} suffixIcon 输入框尾部图标
* @property {Boolean} trim 是否自动去除两端的空格
* @value both 去除两端空格
* @value left 去除左侧空格
* @value right 去除右侧空格
* @value start 去除左侧空格
* @value end 去除右侧空格
* @value all 去除全部空格
* @value none 不去除空格
* @property {Boolean} inputBorder 是否显示input输入框的边框默认true
* @property {Boolean} passwordIcon type=password时是否显示小眼睛图标
* @property {Object} styles 自定义颜色
* @event {Function} input 输入框内容发生变化时触发
* @event {Function} focus 输入框获得焦点时触发
* @event {Function} blur 输入框失去焦点时触发
* @event {Function} confirm 点击完成按钮时触发
* @event {Function} iconClick 点击图标时触发
* @example <uni-easyinput v-model="mobile"></uni-easyinput>
*/
export default {
name: 'uni-easyinput',
emits:['click','iconClick','update:modelValue','input','focus','blur','confirm'],
model:{
prop:'modelValue',
event:'update:modelValue'
},
props: {
name: String,
value: [Number, String],
modelValue: [Number, String],
type: {
type: String,
default: 'text'
},
clearable: {
type: Boolean,
default: true
},
autoHeight: {
type: Boolean,
default: false
},
placeholder: String,
placeholderStyle: String,
focus: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
maxlength: {
type: [Number, String],
default: 140
},
confirmType: {
type: String,
default: 'done'
},
clearSize: {
type: [Number, String],
default: 15
},
inputBorder: {
type: Boolean,
default: true
},
prefixIcon: {
type: String,
default: ''
},
suffixIcon: {
type: String,
default: ''
},
trim: {
type: [Boolean, String],
default: true
},
passwordIcon:{
type: Boolean,
default: true
},
styles: {
type: Object,
default () {
return {
color: '#333',
disableColor: '#F7F6F6',
borderColor: '#e5e5e5'
}
}
},
errorMessage:{
type:[String,Boolean],
default:''
}
},
data() {
return {
focused: false,
errMsg: '',
val: '',
showMsg: '',
border: false,
isFirstBorder: false,
showClearIcon: false,
showPassword: false
};
},
computed: {
msg() {
return this.errorMessage || this.errMsg;
},
// uniappinputmaxlength
inputMaxlength() {
return Number(this.maxlength);
},
},
watch: {
value(newVal) {
if (this.errMsg) this.errMsg = ''
this.val = newVal
// fix by mehaotian is_reset uni-forms
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(newVal)
}
},
modelValue(newVal) {
if (this.errMsg) this.errMsg = ''
this.val = newVal
if (this.form && this.formItem &&!this.is_reset) {
this.is_reset = false
this.formItem.setValue(newVal)
}
},
focus(newVal) {
this.$nextTick(() => {
this.focused = this.focus
})
}
},
created() {
if(!this.value){
this.val = this.modelValue
}
if(!this.modelValue){
this.val = this.value
}
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
if (this.form && this.formItem) {
if (this.formItem.name) {
if(!this.is_reset){
this.is_reset = false
this.formItem.setValue(this.val)
}
this.rename = this.formItem.name
this.form.inputChildrens.push(this)
}
}
},
mounted() {
this.$nextTick(() => {
this.focused = this.focus
})
},
methods: {
/**
* 初始化变量值
*/
init() {
},
onClickIcon(type) {
this.$emit('iconClick', type)
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
onEyes() {
this.showPassword = !this.showPassword
},
onInput(event) {
let value = event.detail.value;
//
if (this.trim) {
if (typeof(this.trim) === 'boolean' && this.trim) {
value = this.trimStr(value)
}
if (typeof(this.trim) === 'string') {
value = this.trimStr(value, this.trim)
}
};
if (this.errMsg) this.errMsg = ''
this.val = value
// TODO vue2
this.$emit('input', value);
// TODO  vue3
this.$emit('update:modelValue',value)
},
onFocus(event) {
this.$emit('focus', event);
},
onBlur(event) {
let value = event.detail.value;
this.$emit('blur', event);
},
onConfirm(e) {
this.$emit('confirm', e.detail.value);
},
onClear(event) {
this.val = '';
// TODO vue2
this.$emit('input', '');
// TODO vue2
// TODO  vue3
this.$emit('update:modelValue','')
},
fieldClick() {
this.$emit('click');
},
trimStr(str, pos = 'both') {
if (pos === 'both') {
return str.trim();
} else if (pos === 'left') {
return str.trimLeft();
} else if (pos === 'right') {
return str.trimRight();
} else if (pos === 'start') {
return str.trimStart()
} else if (pos === 'end') {
return str.trimEnd()
} else if (pos === 'all') {
return str.replace(/\s+/g, '');
} else if (pos === 'none') {
return str;
}
return str;
}
}
};
</script>
<style lang="scss" scoped>
$uni-error: #e43d33;
$uni-border-1: #DCDFE6 !default;
.uni-easyinput {
/* #ifndef APP-NVUE */
width: 100%;
/* #endif */
flex: 1;
position: relative;
text-align: left;
color: #333;
font-size: 14px;
}
.uni-easyinput__content {
flex: 1;
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
box-sizing: border-box;
min-height: 36px;
/* #endif */
flex-direction: row;
align-items: center;
}
.uni-easyinput__content-input {
/* #ifndef APP-NVUE */
width: auto;
/* #endif */
position: relative;
overflow: hidden;
flex: 1;
line-height: 1;
font-size: 14px;
}
.uni-easyinput__placeholder-class {
color: #999;
font-size: 12px;
font-weight: 200;
}
.is-textarea {
align-items: flex-start;
}
.is-textarea-icon {
margin-top: 5px;
}
.uni-easyinput__content-textarea {
position: relative;
overflow: hidden;
flex: 1;
line-height: 1.5;
font-size: 14px;
padding-top: 6px;
padding-bottom: 10px;
height: 80px;
/* #ifndef APP-NVUE */
min-height: 80px;
width: auto;
/* #endif */
}
.input-padding {
padding-left: 10px;
}
.content-clear-icon {
padding: 0 5px;
}
.label-icon {
margin-right: 5px;
margin-top: -1px;
}
//
.is-input-border {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
align-items: center;
border: 1px solid $uni-border-1;
border-radius: 4px;
}
.uni-error-message {
position: absolute;
bottom: -17px;
left: 0;
line-height: 12px;
color: $uni-error;
font-size: 12px;
text-align: left;
}
.uni-error-msg--boeder {
position: relative;
bottom: 0;
line-height: 22px;
}
.is-input-error-border {
border-color: $uni-error;
.uni-easyinput__placeholder-class {
color: mix(#fff, $uni-error, 50%);;
}
}
.uni-easyinput--border {
margin-bottom: 0;
padding: 10px 15px;
// padding-bottom: 0;
border-top: 1px #eee solid;
}
.uni-easyinput-error {
padding-bottom: 0;
}
.is-first-border {
/* #ifndef APP-NVUE */
border: none;
/* #endif */
/* #ifdef APP-NVUE */
border-width: 0;
/* #endif */
}
.is-disabled {
border-color: red;
background-color: #F7F6F6;
color: #D5D5D5;
.uni-easyinput__placeholder-class {
color: #D5D5D5;
font-size: 12px;
}
}
</style>

90
uni_modules/uni-easyinput/package.json

@ -0,0 +1,90 @@
{
"id": "uni-easyinput",
"displayName": "uni-easyinput 增强输入框",
"version": "1.0.0",
"description": "Easyinput 组件是对原生input组件的增强",
"keywords": [
"uni-ui",
"uniui",
"input",
"uni-easyinput",
"输入框"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

11
uni_modules/uni-easyinput/readme.md

@ -0,0 +1,11 @@
### Easyinput 增强输入框
> **组件名:uni-easyinput**
> 代码块: `uEasyinput`
easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

84
utils/hook.js

@ -1,14 +1,82 @@
import store from '@/store/index'
// 框架方法封装
const tabList = ['client','trade','mall','mine']
const tabList = ['store', 'cart', 'mine']
/**
* @param {string} 跳转的tabBar页面
* @param {string} 跳转的tabBar页面
* @return {null}
*/
export function tab2(tabPage) {
if(tabList.includes(tabPage)) {
uni.switchTab({
url:`/pages/${tabPage}/index`
})
}
}
if (tabList.includes(tabPage)) {
uni.switchTab({
url: `/pages/${tabPage}/index`
})
}
}
/**
* @param {string} 返回上一级
* @return {null}
*/
export function back() {
if (getCurrentPages().length > 1) {
uni.navigateBack({
delta: 1
})
} else {
go2('client')
}
}
/**
* 跳转到指定页面包括tabBar页面
* @param {string} url 页面名称
* @param {object} data 页面参数
* @param {string} isRedirect 是否重定向 默认false
* @return {null}
*/
export function go2(url, data = {}, isRedirect = false) {
if (!url) {
console.error('请选择页面')
return
}
let param = ''
Object.keys(data).forEach((key) => {
if (param === '') {
param = `${key}=${data[key]}`
} else {
param += `&${key}=${data[key]}`
}
})
if (tabList.includes(url)) {
uni.switchTab({
url: `/pages/${url}/index${param ? '?' + param : ''}`
})
} else {
if (isRedirect) {
uni.redirectTo({
url: `/pages/${url}/index${param ? '?' + param : ''}`
})
} else {
uni.navigateTo({
url: `/pages/${url}/index${param ? '?' + param : ''}`
})
}
}
}
/**
* 跳转到指定页面包括tabBar页面校验是否已登录未登录则跳转到登录页面
* @param {string} url 页面名称
* @param {object} data 页面参数
* @param {string} isRedirect 是否重定向 默认false
* @return {null}
*/
export function loginGo2(url, data = {}, isRedirect) {
const token = store.state.qnToken
if (token) {
go2(url, data, isRedirect)
} else {
store.commit('setNextPage', { name: url, data })
go2('login')
}
}

215
utils/http/http.js

@ -1,112 +1,109 @@
import {
isFunction
} from '../is.js'
const urlEnv = process.env.NODE_ENV === 'production' ? '-test' : ''
import { isFunction } from '../is.js'
const urlEnv = process.env.NODE_ENV === 'production' ? '' : '-test'
const uplaodUrl = `https://api-ops-yyt${urlEnv}.qniao.cn/cloud-print-user-center/utils/uploadImage`
export default class Http {
constructor(config = {}, {
reqInterceptor,
resInterceptor,
tranformConfig
} = {}) {
this.baseUrl = config.baseUrl
this.header = config.header || {
'content-type': 'application/json;charset=UTF-8'
}
this.requestOption = config.requestOption || {}
this.reqInterceptor = reqInterceptor
this.resInterceptor = resInterceptor
this.tranformConfig = tranformConfig
}
post(config, options) {
return this.request({
method: 'POST',
...config
}, options)
}
get(config, options) {
return this.request({
method: 'GET',
...config
}, options)
}
/**
* @param {Object} config 和请求相关的参数
* @param {Object} options 对请求数据进行额外处理的参数
*/
request(config, options) {
let conf = Object.assign({}, config)
const {
tranformConfig,
reqInterceptor,
resInterceptor
} = this
if (tranformConfig && isFunction(tranformConfig)) {
conf = tranformConfig(this, config)
}
let opt = Object.assign({}, this.requestOption, options)
if (reqInterceptor && isFunction(reqInterceptor)) {
conf = reqInterceptor(conf, opt)
}
return new Promise((resolve, reject) => {
uni.request({
url: conf.url,
data: conf.data,
header: conf.header,
success: (res) => {
console.log('native response', res)
let data = resInterceptor(res, opt)
// 服务器错误也会用then抛出,需要自己判断data==null
resolve(data)
},
fail: (err) => {
uni.showToast({
title: '发生未知错误,请联系客服'
})
reject(err)
}
})
}).catch(err => {
// 吃掉请求产生的异常
// 后期可以记录
console.log('native response error', err)
})
}
// config:{}
uploadFile(config, options) {
return new Promise((resolve, rejetc) => {
let conf = Object.assign({}, config)
conf.url = uplaodUrl
const {
reqInterceptor
} = this
let opt = Object.assign({}, this.requestOption, options)
if (reqInterceptor && isFunction(reqInterceptor)) {
conf = reqInterceptor(conf, opt)
}
delete conf.header['Content-Type']
uni.uploadFile({
url: conf.url,
header: {
...conf.header,
image: config.data.filePath,
// 'Content-Type': 'image/png'
},
filePath: config.data.filePath,
name: config.data.fileName || 'image',
formData: {
image: config.data.filePath
},
success: (res) => {
uni.hideLoading()
resolve(res.data)
},
fail: (err) => {
reject(err)
}
})
}).catch(err => {
console.log('upload native err', err)
})
}
constructor(
config = {},
{ reqInterceptor, resInterceptor, tranformConfig } = {}
) {
this.baseUrl = config.baseUrl
this.header = config.header || {
'content-type': 'application/json;charset=UTF-8'
}
this.requestOption = config.requestOption || {}
this.reqInterceptor = reqInterceptor
this.resInterceptor = resInterceptor
this.tranformConfig = tranformConfig
}
post(config, options) {
return this.request(
{
method: 'POST',
...config
},
options
)
}
get(config, options) {
return this.request(
{
method: 'GET',
...config
},
options
)
}
/**
* @param {Object} config 和请求相关的参数
* @param {Object} options 对请求数据进行额外处理的参数
*/
request(config, options) {
let conf = Object.assign({}, config)
const { tranformConfig, reqInterceptor, resInterceptor } = this
if (tranformConfig && isFunction(tranformConfig)) {
conf = tranformConfig(this, config)
}
let opt = Object.assign({}, this.requestOption, options)
if (reqInterceptor && isFunction(reqInterceptor)) {
conf = reqInterceptor(conf, opt)
}
return new Promise((resolve, reject) => {
uni.request({
url: conf.url,
data: conf.data,
header: conf.header,
method: conf.method,
success: (res) => {
let data = resInterceptor(res, opt)
// 服务器错误也会用then抛出,需要自己判断data==null
resolve(data)
},
fail: (err) => {
uni.showToast({
title: '发生未知错误,请联系客服'
})
reject(err)
}
})
}).catch((err) => {
// 吃掉请求产生的异常
// 后期可以记录
console.log('native response error', err)
})
}
// config:{}
uploadFile(config, options) {
return new Promise((resolve, rejetc) => {
let conf = Object.assign({}, config)
conf.url = uplaodUrl
const { reqInterceptor } = this
let opt = Object.assign({}, this.requestOption, options)
if (reqInterceptor && isFunction(reqInterceptor)) {
conf = reqInterceptor(conf, opt)
}
delete conf.header['Content-Type']
uni.uploadFile({
url: conf.url,
header: {
...conf.header,
image: config.data.filePath
// 'Content-Type': 'image/png'
},
filePath: config.data.filePath,
name: config.data.fileName || 'image',
formData: {
image: config.data.filePath
},
success: (res) => {
uni.hideLoading()
resolve(res.data)
},
fail: (err) => {
reject(err)
}
})
}).catch((err) => {
console.log('upload native err', err)
})
}
}

33
utils/http/index.js

@ -1,8 +1,8 @@
import Http from './http.js'
// 请求封装文件
const urlEnv = process.env.NODE_ENV === 'production' ? '-test' : ''
const xappid = '503258978847966404'
const urlEnv = process.env.NODE_ENV === 'production' ? '' : '-test'
const xappid = '503258978847966403'
// 请求url列表
const prefixList = {
'/yyt-uec': `https://api-client-yyt${urlEnv}.qniao.cn`,
@ -27,18 +27,25 @@ const config = {
// 请求拦截
const reqInterceptor = (config, options) => {
const { url = '' } = config
// 添加token
const token = uni.getStorageSync('Qn_token')
let token = ''
try {
if (url.startsWith('uec')) {
token = uni.getStorageSync('uecToken')
} else {
token = uni.getStorageSync('qnToken')
}
} catch (e) {
console.error('获取缓存失败:', e)
}
config.header = {
...config.header,
Authorization:
token ||
'iHP4V/g6O5DXHixyNrf7tm/UsBwShEYjzGx1kBBPitXOsbrMnv5z4DHjhbgWwrgz/eyAbzsk0APv+gBprZdnNQ==',
Authorization: token || '',
'X-APP-ID': xappid
}
// 改变url
let newUrl = ''
const { url = '' } = config
if (url.startsWith('http')) {
newUrl = url
} else {
@ -60,6 +67,7 @@ const reqInterceptor = (config, options) => {
// 响应拦截
const resInterceptor = (response, options) => {
uni.hideLoading()
if (options.isReturnNativeResponse) {
return response
}
@ -70,8 +78,15 @@ const resInterceptor = (response, options) => {
const { statusCode } = response
const res = response.data
if (statusCode >= 200 && statusCode < 300) {
uni.hideLoading()
return res
if (res.code == 0) {
return res.data
} else {
uni.showToast({
title: res.message,
icon: 'none'
})
return null
}
} else {
uni.showToast({
title: '服务器错误,请联系客服'

Loading…
Cancel
Save