wechat
lgyg 2023-08-24 22:33:07 +08:00
parent 33e6f8fed9
commit ecd9eca8d0
41 changed files with 3393 additions and 112 deletions

1152
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -70,8 +70,10 @@
"@vue/shared": "^3.0.0",
"core-js": "^3.6.5",
"flyio": "^0.6.2",
"uview-ui": "^1.8.8",
"vue": "^2.6.11",
"vuex": "^3.2.0"
"vuex": "^3.2.0",
"vuex-persistedstate": "^4.1.0"
},
"devDependencies": {
"@dcloudio/types": "^3.3.2",
@ -93,7 +95,11 @@
"jest": "^25.4.0",
"mini-types": "*",
"miniprogram-api-typings": "*",
"node-sass": "^4.14.1",
"postcss-comment": "^2.0.0",
"sass-loader": "^10.1.1",
"stylus": "^0.54.8",
"stylus-loader": "^3.0.2",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [

View File

@ -12,6 +12,139 @@
}
</script>
<style>
<style lang="scss">
@import "uview-ui/index.scss";
/*每个页面公共css */
.bg-page{
width: 100%;
min-height: 100%;
background-color: #eeeeef;
position: absolute;
left: 0;
top: 0;
}
.mt24{
margin-top: 24rpx;
}
.mt20{
margin-top: 20rpx;
}
.mr20{
margin-right: 20rpx;
}
.mr30{
margin-right: 30rpx;
}
.pdlr18{
padding-left: 18rpx;
padding-right: 18rpx;
}
.pdl40{
padding-left: 40rpx;
}
.pdlr12{
padding-left: 12rpx;
padding-right: 12rpx;
}
.flex-column{
display: flex;
flex-direction: column;
}
.flex-row{
display: flex;
flex-direction: row;
}
.clamp {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
.clamp-2 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.cu-btn{
height: 60rpx;
flex-shrink: 0;
font-size: 24rpx;
position: relative;
border: 0rpx;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: 0 30rpx;
line-height: 1;
text-align: center;
text-decoration: none;
overflow: visible;
margin-left: initial;
transform: translate(0rpx, 0rpx);
margin-right: initial;
}
.cu-btn.round{
border-radius: 1000rpx;
border: none;
}
.cu-btn::after {
display: none;
}
.cu-btn[class*="bg-"]::after {
display: none;
}
.cu-btn.round[class*="line"]::after {
border-radius: 1000rpx;
}
.cu-btn.button-hover {
transform: translate(1rpx, 1rpx);
}
.cu-btn[class*="line"] {
background-color: transparent;
}
.handle-btn .u-btn.u-btn--warning--disabled{
color: #ffffff !important;
border-color: #C0C0C0 !important;
background-color: #C0C0C0 !important;
}
.cu-btn[class*="line"]::after {
content: " ";
display: block;
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
border: 1rpx solid currentColor;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
border-radius: 12rpx;
z-index: 1;
pointer-events: none;
}
.safe-area-inset-bottom{
padding-bottom: 0;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.input-placeholder,.textarea-placeholder{
color: #c3c3c3;
}
.list_arrowR::after{
content: '';
width: 15rpx;
height: 25rpx;
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
background: url('./static/img/my_arrow_right.png') no-repeat center;
background-size: 100%;
}
</style>

69
src/api/index.js 100644
View File

@ -0,0 +1,69 @@
import Request from './luch-request/index.js'
import jwt from './jwt.js'
import {toast} from '@/com/utils.js'
//测试地址
const baseApi = 'http://36.133.205.221:81';
const http = new Request();
http.setConfig((config) => { /* 设置全局配置 */
config.baseURL = baseApi
config.header = {
...config.header,
'Accept': 'application/json'
}
config.custom = {
auth: true, // 是否传token
// loading: false // 是否使用loading
}
config.imeout = 30000
return config
})
/* 请求之前拦截器 */
http.interceptors.request.use((config, cancel) => {
if (config.custom.auth) {
// 需要权限认证的路由 需携带自定义参数 {custom: {auth: true}}
config.header.Authorization = 'Bearer '+jwt.getAccessToken();
}
return config
})
/* 请求之后拦截器 */
let isRefreshing = false;//多次锁
http.interceptors.response.use((response) => { /* 请求之后拦截器*/
const {code} = response.data
console.log(response)
if(code ==4024){
toast(response.data.msg)
}else if(code ==401){
toast('请关闭小程序,重新进入')
} else{
}
return response
}, (err) => { // 请求错误
const {code} = err.data
console.log(code)
if(code == 401){//过期未登录
const config = err.config
if(!isRefreshing){
isRefreshing = true
}else{
}
}else {
}
console.log(err,'======')
return Promise.reject(err)
})
const getFullUrl = (url,params,header) => {
return http.post(url, params,{...header});
};
export {
http,
getFullUrl
}

57
src/api/jwt.js 100644
View File

@ -0,0 +1,57 @@
import store from '@/store/index.js'
const tokenKey = 'user_access_token'; //键值
// token
const getAccessToken = function() {
let token = store.state.user_access_token ? store.state.user_access_token : uni.getStorageSync(tokenKey);
try {
token = token?token:''
} catch (e) {}
return token;
}
const setAccessToken = (access_token) => {
try {
uni.setStorageSync(tokenKey, access_token);
store.commit('USERACCESSTOKEN',access_token);
return true;
} catch (e) {
return false;
}
}
const clearAccessToken = function() {
try {
uni.removeStorageSync(tokenKey);
store.commit(tokenKey,'');
} catch (e) {}
}
//用户key
const getUserKey = function() {
let userKey = store.state.user_key ? store.state.user_key : uni.getStorageSync('user_key');
try {
userKey = userKey?userKey:''
} catch (e) {}
return userKey;
}
const setUserKey = (user_key) => {
try {
uni.setStorageSync('user_key', user_key);
store.commit('USERKEY',user_key);
return true;
} catch (e) {
return false;
}
}
const clearUserKey = function() {
try {
uni.removeStorageSync('user_key');
store.commit('USERKEY','');
} catch (e) {}
}
export default {
getAccessToken,
setAccessToken,
clearAccessToken,
getUserKey,
setUserKey,
clearUserKey
}

View File

@ -0,0 +1,99 @@
import buildURL from '../helpers/buildURL'
import buildFullPath from '../core/buildFullPath'
import settle from '../core/settle'
import { isUndefined } from "../utils"
/**
* 返回可选值存在的配置
* @param {Array} keys - 可选值数组
* @param {Object} config2 - 配置
* @return {{}} - 存在的配置项
*/
const mergeKeys = (keys, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
return config
}
export default (config) => {
return new Promise((resolve, reject) => {
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
const _config = {
url: fullPath,
header: config.header,
complete: (response) => {
config.fullPath = fullPath
response.config = config
try {
// 对可能字符串不是json 的情况容错
if (typeof response.data === 'string') {
response.data = JSON.parse(response.data)
}
// eslint-disable-next-line no-empty
} catch (e) {
}
settle(resolve, reject, response)
}
}
let requestTask
if (config.method === 'UPLOAD') {
delete _config.header['content-type']
delete _config.header['Content-Type']
let otherConfig = {
// #ifdef MP-ALIPAY
fileType: config.fileType,
// #endif
filePath: config.filePath,
name: config.name
}
const optionalKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef H5
'file',
// #endif
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData'
]
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
} else if (config.method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config['timeout'])) {
_config['timeout'] = config['timeout']
}
// #endif
requestTask = uni.downloadFile(_config)
} else {
const optionalKeys = [
'data',
'method',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY || APP-PLUS
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
]
requestTask = uni.request({..._config,...mergeKeys(optionalKeys, config)})
}
if (config.getTask) {
config.getTask(requestTask, config)
}
})
}

View File

@ -0,0 +1,51 @@
'use strict'
function InterceptorManager() {
this.handlers = []
}
/**
* Add a new interceptor to the stack
*
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
})
return this.handlers.length - 1
}
/**
* Remove an interceptor from the stack
*
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null
}
}
/**
* Iterate over all the registered interceptors
*
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
this.handlers.forEach(h => {
if (h !== null) {
fn(h)
}
})
}
export default InterceptorManager

View File

@ -0,0 +1,199 @@
/**
* @Class Request
* @description luch-request http请求插件
* @version 3.0.5
* @Author lu-ch
* @Date 2021-01-06
* @Email webwork.s@qq.com
* 文档: https://www.quanzhan.co/luch-request/
* github: https://github.com/lei-mu/luch-request
* DCloud: http://ext.dcloud.net.cn/plugin?id=392
* HBuilderX: beat-3.0.4 alpha-3.0.4
*/
import dispatchRequest from './dispatchRequest'
import InterceptorManager from './InterceptorManager'
import mergeConfig from './mergeConfig'
import defaults from './defaults'
import { isPlainObject } from '../utils'
export default class Request {
/**
* @param {Object} arg - 全局配置
* @param {String} arg.baseURL - 全局根路径
* @param {Object} arg.header - 全局header
* @param {String} arg.method = [GET|POST|PUT|DELETE|CONNECT|HEAD|OPTIONS|TRACE] - 全局默认请求方式
* @param {String} arg.dataType = [json] - 全局默认的dataType
* @param {String} arg.responseType = [text|arraybuffer] - 全局默认的responseTypeApp和支付宝小程序不支持
* @param {Object} arg.custom - 全局默认的自定义参数
* @param {Number} arg.timeout - 全局默认的超时时间单位 ms默认60000H5(HBuilderX 2.9.9+)APP(HBuilderX 2.9.9+)微信小程序2.10.0支付宝小程序
* @param {Boolean} arg.sslVerify - 全局默认的是否验证 ssl 证书默认true.仅App安卓端支持HBuilderX 2.3.3+
* @param {Boolean} arg.withCredentials - 全局默认的跨域请求时是否携带凭证cookies默认false仅H5支持HBuilderX 2.6.15+
* @param {Boolean} arg.firstIpv4 - 全DNS解析时优先使用ipv4默认false App-Android 支持 (HBuilderX 2.8.0+)
* @param {Function(statusCode):Boolean} arg.validateStatus - 全局默认的自定义验证器默认statusCode >= 200 && statusCode < 300
*/
constructor(arg = {}) {
if (!isPlainObject(arg)) {
arg = {}
console.warn('设置全局参数必须接收一个Object')
}
this.config = {...defaults, ...arg}
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
}
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig(f) {
this.config = f(this.config)
}
middleware(config) {
config = mergeConfig(this.config, config)
let chain = [dispatchRequest, undefined]
let promise = Promise.resolve(config)
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected)
})
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected)
})
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise
}
/**
* @Function
* @param {Object} config - 请求配置项
* @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>}
*/
request(config = {}) {
return this.middleware(config)
}
get(url, options = {}) {
return this.middleware({
url,
method: 'GET',
...options
})
}
post(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY
put(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
connect(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN || MP-BAIDU
head(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef H5 || MP-WEIXIN
trace(url, data, options = {}) {
return this.middleware({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload(url, config = {}) {
config.url = url
config.method = 'UPLOAD'
return this.middleware(config)
}
download(url, config = {}) {
config.url = url
config.method = 'DOWNLOAD'
return this.middleware(config)
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/

View File

@ -0,0 +1,20 @@
'use strict'
import isAbsoluteURL from '../helpers/isAbsoluteURL'
import combineURLs from '../helpers/combineURLs'
/**
* Creates a new URL by combining the baseURL with the requestedURL,
* only when the requestedURL is not already an absolute URL.
* If the requestURL is absolute, this function returns the requestedURL untouched.
*
* @param {string} baseURL The base URL
* @param {string} requestedURL Absolute or relative URL to combine
* @returns {string} The combined full path
*/
export default function buildFullPath(baseURL, requestedURL) {
if (baseURL && !isAbsoluteURL(requestedURL)) {
return combineURLs(baseURL, requestedURL)
}
return requestedURL
}

View File

@ -0,0 +1,30 @@
/**
* 默认的全局配置
*/
export default {
baseURL: '',
header: {},
method: 'GET',
dataType: 'json',
// #ifndef MP-ALIPAY || APP-PLUS
responseType: 'text',
// #endif
custom: {},
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
timeout: 60000,
// #endif
// #ifdef APP-PLUS
sslVerify: true,
// #endif
// #ifdef H5
withCredentials: false,
// #endif
// #ifdef APP-PLUS
firstIpv4: false,
// #endif
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300
}
}

View File

@ -0,0 +1,6 @@
import adapter from '../adapters/index'
export default (config) => {
return adapter(config)
}

View File

@ -0,0 +1,103 @@
import {deepMerge, isUndefined} from '../utils'
/**
* 合并局部配置优先的配置如果局部有该配置项则用局部如果全局有该配置项则用全局
* @param {Array} keys - 配置项
* @param {Object} globalsConfig - 当前的全局配置
* @param {Object} config2 - 局部配置
* @return {{}}
*/
const mergeKeys = (keys, globalsConfig, config2) => {
let config = {}
keys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
} else if (!isUndefined(globalsConfig[prop])) {
config[prop] = globalsConfig[prop]
}
})
return config
}
/**
*
* @param globalsConfig - 当前实例的全局配置
* @param config2 - 当前的局部配置
* @return - 合并后的配置
*/
export default (globalsConfig, config2 = {}) => {
const method = config2.method || globalsConfig.method || 'GET'
let config = {
baseURL: globalsConfig.baseURL || '',
method: method,
url: config2.url || '',
params: config2.params || {},
custom: {...(globalsConfig.custom || {}), ...(config2.custom || {})},
header: deepMerge(globalsConfig.header || {}, config2.header || {})
}
const defaultToConfig2Keys = ['getTask', 'validateStatus']
config = {...config, ...mergeKeys(defaultToConfig2Keys, globalsConfig, config2)}
// eslint-disable-next-line no-empty
if (method === 'DOWNLOAD') {
// #ifdef H5 || APP-PLUS
if (!isUndefined(config2.timeout)) {
config['timeout'] = config2['timeout']
} else if (!isUndefined(globalsConfig.timeout)) {
config['timeout'] = globalsConfig['timeout']
}
// #endif
} else if (method === 'UPLOAD') {
delete config.header['content-type']
delete config.header['Content-Type']
const uploadKeys = [
// #ifdef APP-PLUS || H5
'files',
// #endif
// #ifdef MP-ALIPAY
'fileType',
// #endif
// #ifdef H5
'file',
// #endif
'filePath',
'name',
// #ifdef H5 || APP-PLUS
'timeout',
// #endif
'formData',
]
uploadKeys.forEach(prop => {
if (!isUndefined(config2[prop])) {
config[prop] = config2[prop]
}
})
// #ifdef H5 || APP-PLUS
if (isUndefined(config.timeout) && !isUndefined(globalsConfig.timeout)) {
config['timeout'] = globalsConfig['timeout']
}
// #endif
} else {
const defaultsKeys = [
'data',
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
'timeout',
// #endif
'dataType',
// #ifndef MP-ALIPAY || APP-PLUS
'responseType',
// #endif
// #ifdef APP-PLUS
'sslVerify',
// #endif
// #ifdef H5
'withCredentials',
// #endif
// #ifdef APP-PLUS
'firstIpv4',
// #endif
]
config = {...config, ...mergeKeys(defaultsKeys, globalsConfig, config2)}
}
return config
}

View File

@ -0,0 +1,16 @@
/**
* Resolve or reject a Promise based on response status.
*
* @param {Function} resolve A function that resolves the promise.
* @param {Function} reject A function that rejects the promise.
* @param {object} response The response.
*/
export default function settle(resolve, reject, response) {
const validateStatus = response.config.validateStatus
const status = response.statusCode
if (status && (!validateStatus || validateStatus(status))) {
resolve(response)
} else {
reject(response)
}
}

View File

@ -0,0 +1,69 @@
'use strict'
import * as utils from './../utils'
function encode(val) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, '+').
replace(/%5B/gi, '[').
replace(/%5D/gi, ']')
}
/**
* Build a URL by appending params to the end
*
* @param {string} url The base of the url (e.g., http://www.google.com)
* @param {object} [params] The params to be appended
* @returns {string} The formatted url
*/
export default function buildURL(url, params) {
/*eslint no-param-reassign:0*/
if (!params) {
return url
}
var serializedParams
if (utils.isURLSearchParams(params)) {
serializedParams = params.toString()
} else {
var parts = []
utils.forEach(params, function serialize(val, key) {
if (val === null || typeof val === 'undefined') {
return
}
if (utils.isArray(val)) {
key = key + '[]'
} else {
val = [val]
}
utils.forEach(val, function parseValue(v) {
if (utils.isDate(v)) {
v = v.toISOString()
} else if (utils.isObject(v)) {
v = JSON.stringify(v)
}
parts.push(encode(key) + '=' + encode(v))
})
})
serializedParams = parts.join('&')
}
if (serializedParams) {
var hashmarkIndex = url.indexOf('#')
if (hashmarkIndex !== -1) {
url = url.slice(0, hashmarkIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams
}
return url
}

View File

@ -0,0 +1,14 @@
'use strict'
/**
* Creates a new URL by combining the specified URLs
*
* @param {string} baseURL The base URL
* @param {string} relativeURL The relative URL
* @returns {string} The combined URL
*/
export default function combineURLs(baseURL, relativeURL) {
return relativeURL
? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '')
: baseURL
}

View File

@ -0,0 +1,14 @@
'use strict'
/**
* Determines whether the specified URL is absolute
*
* @param {string} url The URL to test
* @returns {boolean} True if the specified URL is absolute, otherwise false
*/
export default function isAbsoluteURL(url) {
// A URL is considered absolute if it begins with "<scheme>://" or "//" (protocol-relative URL).
// RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed
// by any combination of letters, digits, plus, period, or hyphen.
return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url)
}

116
src/api/luch-request/index.d.ts vendored 100644
View File

@ -0,0 +1,116 @@
type AnyObject = Record<string | number | symbol, any>
type HttpPromise<T> = Promise<HttpResponse<T>>;
type Tasks = UniApp.RequestTask | UniApp.UploadTask | UniApp.DownloadTask
export interface RequestTask {
abort: () => void;
offHeadersReceived: () => void;
onHeadersReceived: () => void;
}
export interface HttpRequestConfig<T = Tasks> {
/** 请求基地址 */
baseURL?: string;
/** 请求服务器接口地址 */
url?: string;
/** 请求查询参数,自动拼接为查询字符串 */
params?: AnyObject;
/** 请求体参数 */
data?: AnyObject;
/** 文件对应的 key */
name?: string;
/** HTTP 请求中其他额外的 form data */
formData?: AnyObject;
/** 要上传文件资源的路径。 */
filePath?: string;
/** 需要上传的文件列表。使用 files 时filePath 和 name 不生效App、H5 2.6.15+ */
files?: Array<{
name?: string;
file?: File;
uri: string;
}>;
/** 要上传的文件对象仅H52.6.15+)支持 */
file?: File;
/** 请求头信息 */
header?: AnyObject;
/** 请求方式 */
method?: "GET" | "POST" | "PUT" | "DELETE" | "CONNECT" | "HEAD" | "OPTIONS" | "TRACE" | "UPLOAD" | "DOWNLOAD";
/** 如果设为 json会尝试对返回的数据做一次 JSON.parse */
dataType?: string;
/** 设置响应的数据类型App和支付宝小程序不支持 */
responseType?: "text" | "arraybuffer";
/** 自定义参数 */
custom?: AnyObject;
/** 超时时间仅微信小程序2.10.0)、支付宝小程序支持 */
timeout?: number;
/** DNS解析时优先使用ipv4仅 App-Android 支持 (HBuilderX 2.8.0+) */
firstIpv4?: boolean;
/** 验证 ssl 证书 仅5+App安卓端支持HBuilderX 2.3.3+ */
sslVerify?: boolean;
/** 跨域请求时是否携带凭证cookies仅H5支持HBuilderX 2.6.15+ */
withCredentials?: boolean;
/** 返回当前请求的task, options。请勿在此处修改options。 */
getTask?: (task: T, options: HttpRequestConfig<T>) => void;
/** 全局自定义验证器 */
validateStatus?: (statusCode: number) => boolean | void;
}
export interface HttpResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
cookies: Array<string>;
data: T;
errMsg: string;
header: AnyObject;
}
export interface HttpUploadResponse<T = any> {
config: HttpRequestConfig;
statusCode: number;
data: T;
errMsg: string;
}
export interface HttpDownloadResponse extends HttpResponse {
tempFilePath: string;
}
export interface HttpError {
config: HttpRequestConfig;
statusCode?: number;
cookies?: Array<string>;
data?: any;
errMsg: string;
header?: AnyObject;
}
export interface HttpInterceptorManager<V, E = V> {
use(
onFulfilled?: (config: V) => Promise<V> | V,
onRejected?: (config: E) => Promise<E> | E
): void;
eject(id: number): void;
}
export abstract class HttpRequestAbstract {
constructor(config?: HttpRequestConfig);
config: HttpRequestConfig;
interceptors: {
request: HttpInterceptorManager<HttpRequestConfig, HttpRequestConfig>;
response: HttpInterceptorManager<HttpResponse, HttpError>;
}
middleware<T = any>(config: HttpRequestConfig): HttpPromise<T>;
request<T = any>(config: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
get<T = any>(url: string, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
upload<T = any>(url: string, config?: HttpRequestConfig<UniApp.UploadTask>): HttpPromise<T>;
delete<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
head<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
post<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
put<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
connect<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
options<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
trace<T = any>(url: string, data?: AnyObject, config?: HttpRequestConfig<UniApp.RequestTask>): HttpPromise<T>;
download(url: string, config?: HttpRequestConfig<UniApp.DownloadTask>): Promise<HttpDownloadResponse>;
setConfig(onSend: (config: HttpRequestConfig) => HttpRequestConfig): void;
}
declare class HttpRequest extends HttpRequestAbstract { }
export default HttpRequest;

View File

@ -0,0 +1,2 @@
import Request from './core/Request'
export default Request

View File

@ -0,0 +1,135 @@
'use strict'
// utils is a library of generic helper functions non-specific to axios
var toString = Object.prototype.toString
/**
* Determine if a value is an Array
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Array, otherwise false
*/
export function isArray (val) {
return toString.call(val) === '[object Array]'
}
/**
* Determine if a value is an Object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is an Object, otherwise false
*/
export function isObject (val) {
return val !== null && typeof val === 'object'
}
/**
* Determine if a value is a Date
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a Date, otherwise false
*/
export function isDate (val) {
return toString.call(val) === '[object Date]'
}
/**
* Determine if a value is a URLSearchParams object
*
* @param {Object} val The value to test
* @returns {boolean} True if value is a URLSearchParams object, otherwise false
*/
export function isURLSearchParams (val) {
return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams
}
/**
* Iterate over an Array or an Object invoking a function for each item.
*
* If `obj` is an Array callback will be called passing
* the value, index, and complete array for each item.
*
* If 'obj' is an Object callback will be called passing
* the value, key, and complete object for each property.
*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
export function forEach (obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
/*eslint no-param-reassign:0*/
obj = [obj]
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj)
}
} else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj)
}
}
}
}
/**
* 是否为boolean
* @param val
* @returns {boolean}
*/
export function isBoolean(val) {
return typeof val === 'boolean'
}
/**
* 是否为真正的对象{} new Object
* @param {any} obj - 检测的对象
* @returns {boolean}
*/
export function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
/**
* Function equal to merge with the difference being that no reference
* to original objects is kept.
*
* @see merge
* @param {Object} obj1 Object to merge
* @returns {Object} Result of all merge properties
*/
export function deepMerge(/* obj1, obj2, obj3, ... */) {
let result = {}
function assignValue(val, key) {
if (typeof result[key] === 'object' && typeof val === 'object') {
result[key] = deepMerge(result[key], val)
} else if (typeof val === 'object') {
result[key] = deepMerge({}, val)
} else {
result[key] = val
}
}
for (let i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue)
}
return result
}
export function isUndefined (val) {
return typeof val === 'undefined'
}

View File

@ -0,0 +1,3 @@
export const SHJGTMPLID = 'Xejag3ttwOw_xLvXsrMULjnoNcm5bJIyu-ACnshUnKY';//审核结果
export const SFTZTMPLID = 'slzGT4a8GDQUjmd4uHrZmHYD8ta55KrXe9RTOrL0qRc';//收费通知

462
src/com/utils.js 100644
View File

@ -0,0 +1,462 @@
/**
* 显示加载动画
* @export
* @param {string} [title="加载中..."]
* @param {boolean} [mask=true]
*/
export function showLoading(title = "加载中...", mask = true) {
uni.showLoading({
title,
mask
})
}
/**
* 关闭加载动画
* @export
*/
export function hideLoading() {
uni.hideLoading()
}
/**
* 轻提示框
* @export
* @param {*} title
* @param {*} duration
*/
export function toast(title = "", icon = "none", duration = 2000, cb) {
uni.showToast({
title,
icon: icon,
mask: false,
duration,
success: function() {
cb && cb()
}
})
}
/**
* 动态设置当前页面的标题
* @export
*/
export function setNavigationBarTitle(title = '') {
uni.setNavigationBarTitle({
title
});
}
/**
* 图片预览
* @export
* @param {*} current
* @param {*} [urls=[]]
*/
export function previewImage(current, urls = [],callback= () => {}) {
uni.previewImage({
current, // 当前显示图片的http链接
urls ,// 需要预览的图片http链接列表,
complete: function(e){
callback(e)
}
})
}
/**
* 从本地相册选择图片或使用相机拍照
* @export
* @param {string} [sourceType=['album', 'camera']]
* @param {*} cb
*/
export function chooseImage(count = 9, success = () => {}, err = () => {}) {
uni.chooseImage({
count,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success(res) {
// tempFilePath可以作为img标签的src属性显示图片
const tempFilePaths = res.tempFilePaths;
success(tempFilePaths)
},
fail() {
err()
}
})
}
/**
* 从本地相册选择图片或使用相机拍照
* @export
* @param {string} [sourceType=['album', 'camera']]
* @param {*} cb
*/
export function chooseVideo(success = () => {},err = () => {},sourceType = ['album', 'camera']) {
uni.chooseVideo({
sourceType: sourceType,
success(res) {
// tempFilePath可以作为img标签的src属性显示图片
console.log(res);
const tempFilePath = res.tempFilePath;
success(tempFilePath,res)
},
fail() {
err()
}
})
}
/**
* 图片预览跟视频
* @export
* @param {*} current
* @param {*} [urls=[]]
*/
export function previewMedia(current, sources = [],callback= () => {}) {
uni.previewMedia({
current, // 当前显示index
sources ,// 需要预览的http链接列表,
complete: function(e){
callback(e)
}
})
}
/**
* 压缩图片接口可选压缩质量
* @export
* @param {*} src
* @param {*} [success=()=> {}]
* @param {*} [err=()=> {}]
*/
export function compressImage(src, quality = 80, cb = () => {}, err = () => {}) {
console.log(src, '|');
uni.compressImage({
src: src,
quality: quality,
success: res => {
console.log(res.tempFilePath)
cb(res.tempFilePath)
},
fail(e) {
console.log("失败")
err(e)
}
})
}
/**
* setStorage 的同步版本
* @export
* @param {*} key
* @param {*} val
*/
export function setStorageSync(key, val) {
try {
uni.setStorageSync(key, val)
} catch (e) {
}
}
export function setStorage(key, val) {
try {
uni.setStorage({
key:key,
data:val
})
} catch (e) {
}
}
/**
* getStorage 的同步版本
* @export
* @param {*} key
* @returns
*/
export function getStorageSync(key) {
try {
return uni.getStorageSync(key)
} catch (e) {
}
}
/**
* removeStorage 的同步版本
* @export
* @param {*} key
*/
export function removeStorageSync(key) {
try {
uni.removeStorageSync(key)
} catch (e) {
}
}
/**
* clearStorage 的同步版本
* @export
*/
export function clearStorageSync() {
try {
uni.clearStorageSync()
} catch (e) {
}
}
/**
* 显示小红点
* @export
* @param {*} index
*/
export function showTabBarRedDot(index) {
uni.showTabBarRedDot({
index
})
}
/**
* 隐藏小红点
* @export
* @param {*} index
*/
export function hideTabBarRedDot(index) {
uni.hideTabBarRedDot({
index
})
}
/**
* 返回上一页
* @export
* @param
*/
export function navigateBack() {
uni.navigateBack({
delta: 1,
success() {},
fail() {
var pages = getCurrentPages();
var page = pages[pages.length - 2];
if(page){
uni.switchTab({
url: page.route
});
}else{
uni.switchTab({
url: '/pages/index/index'
});
}
}
});
}
export function formatDate(time, fmt) {
if (!time) {
return ''
}
let _date = new Date(time);
let o = {
'M+': _date.getMonth() + 1, // 月份
'd+': _date.getDate(), // 日
'h+': _date.getHours(), // 小时
'm+': _date.getMinutes(), // 分
's+': _date.getSeconds(), // 秒
'q+': Math.floor((_date.getMonth() + 3) / 3), // 季度
'S': _date.getMilliseconds() // 毫秒
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (_date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
}
}
return fmt;
}
//验证手机
export function isPhoneNumber(val){
let rt = /^1\d{10}$/
if(rt.test(val)){
return true
}else{
return false
}
}
export function arrayKeyGroup(array,key='id'){
let newArray = [];
console.log(key)
array.map(item=>{
return [item]
}).forEach(([{...item}])=>{
const flag = newArray.find(([{...o}])=>o[key] === item[key]);
if(!flag) {
newArray.push([{...item}])
} else {
newArray.forEach(([{...y}], index)=>{
if(y[key] === item[key]) {
newArray[index].push(item)
}
})
}
})
return newArray
}
export function stringHide(string,start,end){
let th = ''
let xlen = end - start;
for(let i =0;i < xlen;i++) {
th += '*'
}
return string.substring(0,start) + th + string.substring(end,string.length)
}
export function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
/**
* 路由跳转
*
* @param Object target {type, way, url}
*/
export function Target(target) {
if (target.type === 'mini-program') {
const way = target.way || 'navigateTo'
const params = { url: target.url }
switch (way) {
case 'navigateTo':
uni.navigateTo(params)
break;
case 'redirectTo':
uni.redirectTo(params)
break;
case 'reLaunch':
uni.reLaunch(params)
break;
case 'switchTab':
uni.switchTab(params)
break;
case 'navigateBack':
navigateBack()
break;
}
}
}
// 判断是否为空
export function isEmpty(obj){
if(typeof obj == "undefined" || obj == null || obj == ''){
return true;
}else{
return false;
}
}
export function getQueryName(str,name){
let query = str.split('?')[1];
let vars = query.split("&");
for (let i=0;i<vars.length;i++) {
let pair = vars[i].split("=");
if(pair[0] == name){return pair[1];}
}
return(false);
}
//指定位置插入字符
export function insertStr(soure, start, newStr){
return soure.slice(0, start) + newStr + soure.slice(start);
}
export function getSubscribeMessage(tmplIds,callback= () => {}){
let _tmplIds = tmplIds;
wx.requestSubscribeMessage({
tmplIds: _tmplIds,
success: (res)=> {
console.log(res,'成功')
let acceptnum = 0;
for(let vk of _tmplIds){
if(res[vk]==='accept'){//同意
acceptnum += 1
}
}
if(acceptnum==0){
}else{
callback(res)
}
},
fail:(err)=>{
if (err.errCode == 20004) {
console.log('用户拒绝了',err)
}else{
callback(res)
}
}
})
}
export function testIdCard(id) {
// 1 "验证通过!", 0 //校验不通过 // id为身份证号码
var format = /^(([1][1-5])|([2][1-3])|([3][1-7])|([4][1-6])|([5][0-4])|([6][1-5])|([7][1])|([8][1-2]))\d{4}(([1][9]\d{2})|([2]\d{3}))(([0][1-9])|([1][0-2]))(([0][1-9])|([1-2][0-9])|([3][0-1]))\d{3}[0-9xX]$/;
//号码规则校验
if(!format.test(id)){
return {'status':0,'msg':'身份证号码不合规'};
}
//区位码校验
//出生年月日校验 前正则限制起始年份为1900;
var year = id.substr(6,4),//身份证年
month = id.substr(10,2),//身份证月
date = id.substr(12,2),//身份证日
time = Date.parse(month+'-'+date+'-'+year),//身份证日期时间戳date
now_time = Date.parse(new Date()),//当前时间戳
dates = (new Date(year,month,0)).getDate();//身份证当月天数
if(time>now_time||date>dates){
return {'status':0,'msg':'出生日期不合规'}
}
//校验码判断
var c = new Array(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2); //系数
var b = new Array('1','0','X','9','8','7','6','5','4','3','2'); //校验码对照表
var id_array = id.split("");
var sum = 0;
for(var k=0;k<17;k++){
sum+=parseInt(id_array[k])*parseInt(c[k]);
}
if(id_array[17].toUpperCase() != b[sum%11].toUpperCase()){
return {'status':0,'msg':'身份证校验码不合规'}
}
return {'status':1,'msg':'校验通过'}
}
export function testEmail(email){
let regEmail = new RegExp("^[a-z0-9A-Z]+[- | a-z0-9A-Z . _]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-z]{2,}$");
return regEmail.test(email)
}
export function testBank(bank){
let reg = /[1-9]\d{12,18}/;
return reg.test(bank)
}
export function testPhone(phone){
let reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
return reg.test(phone)
}

View File

@ -1,12 +1,18 @@
import Vue from 'vue'
import App from './App'
import './uni.promisify.adaptor'
import uView from "uview-ui";
Vue.use(uView);
import {http,getFullUrl} from '@/api/index.js'
import store from './store/index.js'
Vue.config.productionTip = false
Vue.prototype.$http = http
Vue.prototype.$getFullUrl = getFullUrl
App.mpType = 'app'
const app = new Vue({
store,
...App
})
app.$mount()

View File

@ -1,16 +1,66 @@
{
"easycom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
},
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
"navigationBarTitleText": "隆昌农业大数据监控平台"
}
}
,{
"path" : "pages/login/login",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
,{
"path" : "pages/user/user",
"style" :
{
"navigationBarTitleText": "个人中心",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
}
,{
"path" : "pages/user/password-edit",
"style" :
{
"navigationBarTitleText": "修改密码",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
"navigationBarTextStyle": "white",
"navigationBarTitleText": "隆昌农业大数据监控平台",
"navigationBarBackgroundColor": "#2a7dc9",
"backgroundColor": "#2a7dc9"
},
"tabBar": {
"color": "#6c6b6b",
"selectedColor": "#1296db",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/tab/tab_home.png",
"selectedIconPath": "static/tab/tab_home_h.png",
"text": "工作台"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/tab/tab_user.png",
"selectedIconPath": "static/tab/tab_user_h.png",
"text": "我的"
}
]
}
}

View File

@ -1,9 +1,6 @@
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<view>
<text class="title">{{title}}</text>
</view>
<view class="index-page">
</view>
</template>

View File

@ -0,0 +1,218 @@
<template>
<view class="login-page">
<view class="img-a">
<view class="t-b">
隆昌农业大数据监控平台
</view>
</view>
<view class="login-view" style="">
<view class="t-login">
<form class="cl">
<view class="t-a">
<text class="txt">账号</text>
<input type="text" name="username" placeholder="请输入您的账号"
v-model="username" />
</view>
<view class="t-a">
<text class="txt">密码</text>
<input type="password" name="password" maxlength="18"
placeholder="请输入您的密码" v-model="password" />
</view>
<button @tap="login()" type="button"> </button>
<!-- <view class="reg" @tap="reg()"> </view> -->
</form>
</view>
</view>
</view>
</template>
<script>
import {getStorageSync,setStorageSync,setStorage, toast} from '@/com/utils.js'
import jwt from '@/api/jwt.js'
export default {
data() {
return {
username: '', //
password: '' //
};
},
onLoad() {},
methods: {
//
login() {
if (!this.username) {
uni.showToast({ title: '请输入您的账号', icon: 'none' });
return;
}
if (!this.password) {
uni.showToast({ title: '请输入您的密码', icon: 'none' });
return;
}
let params = {
username:this.username,
password:this.password
};
this.$http.post('/api/auth/login',params,{
custom:{
auth:false
}
}).then(({data})=>{
console.log(data);
if(data.code==200){
let _data = data.data;
let _info = _data.info;
console.log(_data)
jwt.setAccessToken(_data.token)
this.$store.dispatch('USER_INFO',_info);
uni.switchTab({
url:'/pages/index/index'
})
}
}).catch(()=>{
})
uni.showToast({ title: '登录成功!', icon: 'none' });
},
}
};
</script>
<style lang="scss" scoped>
.login-page{
.txt {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.img-a {
width: 100%;
height: 450rpx;
background-image: url(../../static/head.png);
background-size: 100%;
}
.reg {
font-size: 28rpx;
color: #fff;
height: 90rpx;
line-height: 90rpx;
border-radius: 50rpx;
font-weight: bold;
background: #f5f6fa;
color: #000000;
text-align: center;
margin-top: 30rpx;
}
.login-view {
width: 100%;
position: relative;
margin-top: -120rpx;
background-color: #ffffff;
border-radius: 8% 8% 0% 0;
}
.t-login {
width: 600rpx;
margin: 0 auto;
font-size: 28rpx;
padding-top: 80rpx;
}
.t-login button {
font-size: 28rpx;
background: #2796f2;
color: #fff;
height: 90rpx;
line-height: 90rpx;
border-radius: 50rpx;
font-weight: bold;
}
.t-login input {
height: 90rpx;
line-height: 90rpx;
margin-bottom: 50rpx;
border-bottom: 1px solid #e9e9e9;
font-size: 28rpx;
}
.t-login .t-a {
position: relative;
}
.t-b {
text-align: left;
font-size: 42rpx;
color: #ffffff;
padding: 130rpx 0 0 70rpx;
font-weight: bold;
line-height: 70rpx;
}
.t-login .t-c {
position: absolute;
right: 22rpx;
top: 22rpx;
background: #5677fc;
color: #fff;
font-size: 24rpx;
border-radius: 50rpx;
height: 50rpx;
line-height: 50rpx;
padding: 0 25rpx;
}
.t-login .t-d {
text-align: center;
color: #999;
margin: 80rpx 0;
}
.t-login .t-e {
text-align: center;
width: 250rpx;
margin: 80rpx auto 0;
}
.t-login .t-g {
float: left;
width: 50%;
}
.t-login .t-e image {
width: 50rpx;
height: 50rpx;
}
.t-login .t-f {
text-align: center;
margin: 150rpx 0 0 0;
color: #666;
}
.t-login .t-f text {
margin-left: 20rpx;
color: #aaaaaa;
font-size: 27rpx;
}
.t-login .uni-input-placeholder {
color: #aeaeae;
}
.cl {
zoom: 1;
}
.cl:after {
clear: both;
display: block;
visibility: hidden;
height: 0;
content: '\20';
}
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<view class="password-page">
<u-form :model="form" ref="uForm">
<u-form-item label="密码:" label-width="100">
<u-input v-model="form.password" :type="type" :password-icon="passwordIcon" :clearable="true"/>
</u-form-item>
<u-form-item label="确认密码:" label-width="150">
<u-input v-model="form.password_confirmation" :type="type" :password-icon="passwordIcon" :clearable="true"/>
</u-form-item>
<view class="btns" style="margin-top: 68rpx;">
<u-button type="primary" @click="passwordEditFn()"></u-button>
</view>
</u-form>
</view>
</template>
<script>
export default {
data() {
return {
form: {
password: '',
password_confirmation: '',
},
type: 'password',
passwordIcon: true,
};
},
onLoad() {
},
methods:{
passwordEditFn(){
console.log(this.form);
if (!this.form.password) {
uni.showToast({ title: '请输入新密码', icon: 'none' });
return;
}
if (!this.form.password_confirmation) {
uni.showToast({ title: '请输入确认密码', icon: 'none' });
return;
}
if (this.form.password != this.form.password_confirmation) {
uni.showToast({ title: '两次密码不一致', icon: 'none' });
return;
}
this.$http.put('/api/users/reset-password',this.form).then(({data})=>{
console.log(data)
if(data.code==200){
}else{
uni.showToast({ title: data.message, icon: 'none' });
}
}).catch(()=>{
})
}
}
}
</script>
<style lang="scss" scoped>
.password-page{
padding: 32rpx;
}
</style>

View File

@ -0,0 +1,305 @@
<template>
<view class="user-page ">
<u-navbar :is-back="false" :title="title" :title-color="titleColor"
:background="background" :border-bottom="false" :back-icon-color="backIconColor"></u-navbar>
<view class="top-section-box">
<view class="top-box">
<view class="user-box">
<view class="user-pic" @click="linkLoginF">
<image class="upic" :src="userInfo.avatar?userInfo.avatar:''"></image>
</view>
<view class="desc-box">
<view class="name">{{userInfo.name}}</view>
<view class="tel">{{userInfo.phone|hidePhone}}</view>
</view>
</view>
</view>
<view class="bg_linec"></view>
</view>
<view class="content-box">
<view class="pdlr18">
<view class="card-box flex-row">
<view class="p-list">
<view class="txt clamp">{{userInfo.username?userInfo.username:'——'}}</view>
<view class="tit">登录名</view>
</view>
<view class="p-list clamp">
<view class="txt">{{userInfo.department?userInfo.department:'——'}}</view>
<view class="tit">角色</view>
</view>
</view>
</view>
<view class="nav_title">系统管理</view>
<view class="nav-box">
<view class="nav-list" @click="linnavF('/pages/user/password-edit')">
<view class="icon_img">
<u-icon name="lock-open" size="46"></u-icon>
</view>
<view class="item-c">
<view class="tname">修改密码</view>
<view class="arrow_R"></view>
</view>
</view>
<view class="nav-list" @click="linnavF('/pages/user/password-edit')">
<view class="icon_img">
<u-icon name="man-add" size="46"></u-icon>
</view>
<view class="item-c">
<view class="tname">角色管理</view>
<view class="arrow_R"></view>
</view>
</view>
<view class="nav-list" @click="linnavF('/pages/user/password-edit')">
<view class="icon_img">
<u-icon name="account" size="46"></u-icon>
</view>
<view class="item-c">
<view class="tname">账号管理</view>
<view class="arrow_R"></view>
</view>
</view>
<view class="nav-list" @click="linnavF('/pages/user/password-edit')">
<view class="icon_img">
<u-icon name="clock" size="46"></u-icon>
</view>
<view class="item-c">
<view class="tname">系统日志</view>
<view class="arrow_R"></view>
</view>
</view>
<view class="nav-list" @click="linnavF('/pages/user/password-edit')">
<view class="icon_img">
<u-icon name="attach" size="46"></u-icon>
</view>
<view class="item-c">
<view class="tname">友情链接</view>
<view class="arrow_R"></view>
</view>
</view>
</view>
<!-- 退出 -->
<view class="yr-loginout">
<u-button type="error" :plain="true" @click="loginOut()">退</u-button>
</view>
</view>
<u-modal v-model="show" :content="content" @confirm="handleLoginOut()" :show-cancel-button="true"></u-modal>
</view>
</template>
<script>
import {mapGetters,mapActions} from 'vuex'
import {USER_INFO} from '@/store/mutation-types.js'
import {showLoading,toast,setStorage,stringHide} from '@/com/utils.js'
export default {
data() {
return {
title:"个人中心",
titleColor:"#ffffff",
backIconColor:"#ffffff",
background:{
backgroundColor:"#2a7dc9"
},
show:false,
content:'是否确认退出系统?'
};
},
filters:{
hidePhone(val){
if(val){
return stringHide(val,3,8)
}
}
},
computed:{
...mapGetters(['userInfo'])
},
onLoad() {
},
onShow() {
console.log(this.userInfo)
this.getUserInfo()
},
methods:{
getUserInfo(){
this.$http.get('/api/users/info').then(({data})=>{
if(data.code==200){
let user = data.data.info;
let userInfo = {...this.userInfo,...user};
console.log(userInfo,'=====bbb====')
this.$store.dispatch('USER_INFO',userInfo);
}
}).catch(err=>{
})
},
linnavF(url){
uni.navigateTo({
url:url
})
},
loginOut(){
this.show = true;
},
//退
handleLoginOut(){
this.$http.delete('/api/users/logout').then(({data})=>{
if(data.code==200){
uni.reLaunch({
url:'/pages/login/login'
})
}
}).catch(()=>{
uni.reLaunch({
url:'/pages/login/login'
})
})
}
}
}
</script>
<style lang="scss" scoped>
.top-section-box{
.top-box{
background-color: #2a7dc9;
}
.user-box{
display: flex;
align-items: center;
padding: 30rpx;
.user-pic{
flex: 0 0 134rpx;
width: 134rpx;
height: 134rpx;
border-radius: 50%;
border: 4rpx solid #FFFFFF;
.upic{
display: block;
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.desc-box{
flex: 1;
width: 0;
color: #FFFFFF;
padding-left: 24rpx;
.name{
font-size: 34rpx;
}
.tel{
font-size: 34rpx;
margin-top: 10rpx;
}
}
}
}
.bg_linec{
width: 100%;
height: 110rpx;
background-color: #2a7dc9;
border-radius: 0 0 100% 100%;
margin-top: -60rpx;
}
.content-box{
padding: 0 20rpx;
.card-box{
min-height: 170rpx;
padding: 30rpx 20rpx;
background-color: #FFFFFF;
margin-bottom: 20rpx;
margin-top: -50rpx;
border-radius: 12rpx;
box-shadow:0 0 20rpx rgba(0,0,0,0.15);
justify-content: center;
.p-list{
margin: 10rpx 0;
font-size: 28rpx;
color: #333333;
flex: 1;
width: 33%;
text-align: center;
.tit{
color: #333333;
font-size: 32rpx;
margin-top: 10rpx;
}
.txt{
color: #888;
}
}
}
.nav_title{
padding: 20rpx 20rpx 0 20rpx;
// font-weight: bold;
font-size: 24rpx;
}
.nav-box{
padding-top: 24rpx;
background-color: #FFFFFF;
padding-left: 20rpx;
padding-right: 20rpx;
padding-bottom: 24rpx;
border-radius: 12rpx;
}
.nav-list{
height: 96rpx;
display: flex;
align-items: center;
background-color: #FFFFFF;
padding-left: 10rpx;
position: relative;
&::after{
content:'';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border-bottom: 1px solid #e5e5e5;
transform: scaleY(.5);
}
.icon_img{
width: 46rpx;
height: 46rpx;
margin-right: 20rpx;
color: #666;
.img{
width: 100%;
height: 100%;
}
}
.item-c{
flex: 1;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 36rpx;
box-sizing: border-box;
position: relative;
.arrow_R{
width: 15rpx;
height: 25rpx;
background: url(../../static/img/my_arrow_right.png) no-repeat center;
background-size: 100%;
}
}
&.noBorder{
.item-c{
&::after{
border: none;
}
}
}
}
}
.yr-loginout{
margin-top: 140rpx;
}
</style>

BIN
src/static/head.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,16 @@
import * as types from "./mutation-types";
import {getStorageSync,setStorageSync,setStorage} from '@/com/utils.js'
const actions = {
[types.USER_INFO]({commit},userInfo){
let _data = {};
if(userInfo&&userInfo.id){
_data = userInfo;
// setStorage('userInfo',userInfo)
}else{
_data = getStorageSync('userInfo');
}
commit(types.USER_INFO, _data);
},
};
export default actions;

View File

@ -0,0 +1,4 @@
//用户信息
export const userInfo = (state)=> state.userInfo
//token
export const token = (state)=> state.user_access_token

30
src/store/index.js 100644
View File

@ -0,0 +1,30 @@
import Vue from "vue";
import Vuex from "vuex";
import actions from "./actions";
import * as getters from "./getters";
import state from "./state";
import mutations from "./mutations";
import createPersistedState from 'vuex-persistedstate';
Vue.use(Vuex);
// store
const store = new Vuex.Store({
getters,
state,
actions,
mutations,
plugins: [
createPersistedState({
// 当state中的值发生变化的时候出发reduce函数
reducer(val) {
console.log(val,'createPersistedState') // value值为当前state中的所有值对象
// return什么localstorage中的key值为vuex的value值就是什么而且是实时与state中的值保持同步
return {
userInfo: val.userInfo
}
}
})
]
});
export default store;

View File

@ -0,0 +1,5 @@
// ------------------------------------mustions types------------------------------------//
//用户信息
export const USER_INFO = 'USER_INFO'
export const USERACCESSTOKEN = 'USERACCESSTOKEN'
export const USERKEY = 'USERKEY'

View File

@ -0,0 +1,14 @@
import * as types from "./mutation-types";
const mutations = {
// 设置用户信息
[types.USER_INFO](state, userInfo) {
state.userInfo = userInfo
},
[types.USERACCESSTOKEN](state, token) {
state.user_access_token = token
},
[types.USERKEY](state, userkey) {
state.user_key = userkey
},
};
export default mutations;

View File

@ -0,0 +1,7 @@
const state = {
userInfo: {},
user_access_token: '',
user_key:'',
};
export default state;

View File

@ -11,7 +11,7 @@
*
* 使scss scss 使 import
*/
@import 'uview-ui/theme.scss';
/* 颜色变量 */
/* 行为相关颜色 */