@ -0,0 +1,21 @@ |
|||
<script> |
|||
export default { |
|||
onLaunch: function() { |
|||
console.log('App Launch') |
|||
}, |
|||
onShow: function() { |
|||
console.log('App Show') |
|||
}, |
|||
onHide: function() { |
|||
console.log('App Hide') |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
@import './common/qiun.css'; |
|||
@import './common/uni.css'; |
|||
@import "./common/main.css"; |
|||
@import "./common/icon.css"; |
|||
@import "./common/iconfont.css"; |
|||
</style> |
|||
@ -0,0 +1,84 @@ |
|||
import http from '@/common/request' |
|||
import deviceData from '@/common/deviceData' |
|||
|
|||
export function getDeviceList(productId) { |
|||
let data = {} |
|||
if(productId){ |
|||
data.productId = productId |
|||
} |
|||
return http.get('/device/list',data) |
|||
} |
|||
|
|||
export function getDeviceInfo(productId, deviceName) { |
|||
return http.get(`/device/find/${productId}/${deviceName}`) |
|||
} |
|||
export function controlDeviceCmd(productId, deviceName, cmd) { |
|||
return deviceCmd({ |
|||
url: '/device/cmd/simple', |
|||
method: 'post', |
|||
params: { |
|||
productId: productId, |
|||
deviceName: deviceName |
|||
}, |
|||
data: cmd |
|||
}) |
|||
} |
|||
|
|||
export function getDeviceLatestUpMessage(productId, deviceName) { |
|||
return deviceData.get(`/device/message/find/one/up/${productId}/${deviceName}`) |
|||
} |
|||
|
|||
export function getDeviceAllUpMessage(productId, deviceName, pageNum, pageSize) { |
|||
let data = {}; |
|||
if(deviceName){ |
|||
data.deviceName = deviceName |
|||
} |
|||
if(pageNum){ |
|||
data.pageNum = pageNum |
|||
} |
|||
if(pageSize){ |
|||
data.pageSize = pageSize |
|||
} |
|||
return deviceData.get(`/device/message/find/all/up/${productId}`,data) |
|||
} |
|||
|
|||
export function getDeviceAllDownMessage(productId, deviceName, pageNum, pageSize) { |
|||
return deviceData({ |
|||
url: `/device/message/find/all/down/${productId}`, |
|||
params: { |
|||
deviceName: deviceName, |
|||
pageNum: pageNum, |
|||
pageSize: pageSize |
|||
}, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
export function statisticsDevice(productId) { |
|||
let data = {} |
|||
if(productId){ |
|||
data.productId = productId |
|||
} |
|||
return http.get('/device/statistics',data) |
|||
} |
|||
|
|||
export function getSubDeviceList(devicePid) { |
|||
return request({ |
|||
url: `/device/list/sub/${devicePid}`, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
export function addSubDevice(deviceId, devicePid) { |
|||
return request({ |
|||
url: `/device/add/sub/${deviceId}/${devicePid}`, |
|||
method: 'post' |
|||
}) |
|||
} |
|||
|
|||
export function deleteSubDevice(deviceId, devicePid) { |
|||
return request({ |
|||
url: `/device/delete/sub/${deviceId}/${devicePid}`, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
import http from '@/common/request' |
|||
|
|||
export function login(username, password) { |
|||
return http.post('/user/login', {name: username, password: password} ) |
|||
} |
|||
|
|||
export function getInfo(token) { |
|||
return request({ |
|||
url: '/user/info', |
|||
method: 'get', |
|||
params: { token } |
|||
}) |
|||
} |
|||
|
|||
export function logout() { |
|||
return request({ |
|||
url: '/user/logout', |
|||
method: 'post' |
|||
}) |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
const TokenKey = 'iotx_token' |
|||
|
|||
export function getToken() { |
|||
try { |
|||
return uni.getStorageSync(TokenKey); |
|||
} catch (e) { |
|||
console.error('getToken failed '); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
export function setToken(token) { |
|||
try { |
|||
uni.setStorageSync(TokenKey, token); |
|||
} catch (e) { |
|||
console.error('setToken failed '+token); |
|||
} |
|||
} |
|||
|
|||
export function removeToken() { |
|||
try { |
|||
uni.removeStorageSync(TokenKey); |
|||
} catch (e) { |
|||
console.error('removeToken failed '); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
import Request from '@/js_sdk/luch-request/request' |
|||
const http = new Request(); |
|||
|
|||
http.setConfig((config) => { /* 设置全局配置 */ |
|||
config.baseUrl = 'http://39.98.253.192/data'; /* 根域名不同 */ |
|||
return config; |
|||
}) |
|||
|
|||
http.interceptor.request((config, cancel) => { |
|||
return config; |
|||
}) |
|||
|
|||
http.interceptor.response((response) => { /* 请求之后拦截器 */ |
|||
const res = response.data |
|||
if (res.code !== 0) { |
|||
uni.showToast({title: res.msg, duration:2000}) |
|||
return Promise.reject('error') |
|||
} else { |
|||
return res |
|||
} |
|||
}) |
|||
export default http; |
|||
@ -0,0 +1,74 @@ |
|||
@font-face { |
|||
font-family: "iconfont"; |
|||
src: url('//at.alicdn.com/t/font_1339011_fnx8kfgj0pj.eot?t=1565167418581'); /* IE9 */ |
|||
src: url('//at.alicdn.com/t/font_1339011_fnx8kfgj0pj.eot?t=1565167418581#iefix') format('embedded-opentype'), /* IE6-IE8 */ |
|||
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAwMAAsAAAAAFfAAAAu8AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEbgqbYJZBATYCJAM8CyAABCAFhG0HgX4bZxIjkjBWFtlfHRh619RhnXa6spLe6UhyxCeuqPiLkn5hO5Iv6kd6n4Mnfq29+XYq8dC4iWqWRKOINbInSJEkljJtDgmdDYBLnYZgbrtqLZnGbOUNcH9LLzDoffLLTvLz/Db/3FfAeyCohIpidGBsbeMacxH12Br+b0LnyuhVBLGKQNgGMq3sQjHw09qvKhpFow7eCIWUKWne/4uMILPoYL4fca+ERCak3Y/ZgJ9oEg3R6sUL6UK6Eq/EC7mfhW3GbGqCBfOg1tdxN0OANA0iZGRsfJ4kDpkKeh3sbq+S9OXiUvyEZFaIOdUhD3iS5s6cAe7NHy9/dMkGDN4i32p6a3SDAeVv2bIxu2D/4KY6CVzOAgtEgAN5ii15tDaJTJaOG+i4hDpFVhnUqfZrqGmLDuucbjY//+b+luNYoamUlIU6jf/Ag4AnTZYcQh5HggwWQ5IUSEBmWfJ8ac6DoiUigIrAgxsI0uAWgiy4gyAHHkApwmuwV8dbQBHjwxAkwOcgyIBvIhEWmp+JMPA3J0iafBnFpJBZgZ/SAIbAXYD/ApkbZokjLLa0eEUGkiQtAhXfebY8mkZ8Pz8Rj89PcHDgohJUIlGElA8Q27vwCb49IQ915/H4Mhniu5I9rp+DvZ23c4gdn768maNb9joX02hZp19F6w4nom6nf2VblxxnymrX+Izasg9xNLbRug62Xdup6So9avGkNSs7t9HIYimhNdZLVnqoneVhie2xyvqo1PxgtNXKjzYXs5ZEg0lmMkp3WVGTLa/AEaw5Qa+XGAyMQXba1j8ZimoAYWmzqbzlrLkXwftSVmuZZp92D7BrfcGgk6G29cXGxm2dqJ9pflDcHJl2Bwu8r9L8YAiyWUut5uJWessF1xNmiyUHHbNabckyWf1+f53EV77ZlnPUAsB05TWsNmfTwBy3eTdac8vqbXkmA4BebDwToadprTGvvsv/G8WK9e3iY3R1+lapxsSokEZK6zMKWssp7ZawDEM3orViMEgYkwwZadooRSYZo4MvQppBBgmwEkYnKUb04kRXiU2gmGGKg5pDdKUDlwBsnQ40ljWb2z8Qr9+90xnbaimZNGLuobSp8OkxtVlKGF1drZgGxmAko7UIudcC8yOTpPjwjjhi9sDEF8wlFovKZpsJh+VhYNfjCS49ZvXCU6829zNA7QntKReN7MgJpzqLziZfdkqFa/br9rph3ckKwgjVDjOQEkC1VSEdB93GEYrXrDocPH7OdKl+Zp3OIBhyIFvXomurR0v4iDNRf7uyWoAVe2OF2/e7I80YQYndeDskHMUArdKKZ/K1E3SJZVJjOwCrp/nadieo6/AVrkRYmflq7cxnMxalzzCh8CaDe3OkvkNiapOtsuksboYWialrVRF1QPsWG1p0YKUiG4byN+swWitFGmOrfiZtNRsdjEDX1Q2ocYxRY9IZWD1jUfoMSwNv0n8irPR1jM4kW3Ved9Zt1XbQ1UsbrepAao3BBp3p7zBMYQ4CVW9/8OtQRkQ+Go6qJnQdw5HtsT+glofqCh9hl1VstaYtn9mpDajS13a0yGQYu9eXQfVdbT+9A4Bp249wQ1vXMunQc1ZVZ6XF3It3ZNtpkbYTbRHIZC0pwdg1tKi1E+EqU74jjAjX1LfpWuoAY8UCH6lfCCtnjB2lFUfoDYywvz9NlJEubm5nO3yZti5Npx/tvgdu05TcI6oIEl7WHxEni46pyvdLVkr2qY+FFJwm4qSQTB5STlny9ct9UVxu9PPddl6uysuhS+8WFgqCJR/ODpYsqEQLF2CVEX6uypxsuR/8ngtx1cPjKiFhvo64Sv023UF9aob2AcKkhvrERKseZx4uFqdmVwXlBiqKS+7c0V62IiQopyIrReYzCc/H6Zji7YUzsKYmbAYUFi7u5pN1uESckp3hlTsFemAj//1nhJ6BkQUa2T1v2rvMVKlvjt8okedXtBjyyRnNTXEy15qkGlenLUMPCT2Ex6o2K+k3jnKli1LuCB442zHN7SdounaaEAgIPeP0GZDO3myYAwXdS8fmifKEc1Lds/o7j2OnVCapRZOKeja54so0gTq8z8UH4b2bLjh+mblj5+jYF697DMn0lDtkKO3ttSoHrpw7XjkKBV7BXYsoLqLndo9onEtmvGnwFxMcImZC8fUcDyXBJTMvZw0KvQw8tp0ed1bKpaZRsoMsld+QT6WS+YsoqidJBeRzFlNUitTOjsoPoEjFkf9/PsmJ60gKyPwUikxxmeASlzEBGXIuwikakC/P51PTKb5pEwuNPZ5cVrQwn2QdqYVUeEEBlSCpAg7+EABsV3RGt2q6T7ykKrzobQU1dTFVMTsia0kQDBr71wDZAMemniFFAcU+G3JTkyBvYHkekQeV/cm8WP7RIxuinaI3bNnKPyvyt27ZEN33aMSwlOiY4LI5poLI7Ax7e+0lRRaYDnj+rcj1ylmQ2VPY037gRHC/P2I+tn0NPs9xsXimZ6ZiqqPO7b97zezjF/L7usrhc+GAj9xVIWCxmCDPDjhLko7O67HQaf7bVaVQh1VwW795zsjUwjetRNa9u3ocDuWI3JSnIstiUNHo0UVYIcYPQ0Uxkard596nE/K+fTc2QEFOpL+XpubAgIEoh9f77zO1tRI23yTkDBzo4hg+KH6Qapbo+36P/d9FLvYx9pA8vgFWesfrnQ47fUA3RTc8bqIPDl13raqGBetzbny4sTS/QKYN+pEQ87bCi5xKjhe8O3gnSjjB2cFLeob8RZ7JX5nPmhbct7N3VUShgBE50/WZQXkh88JV4TWeBzUldkqF9M0GQdByZYJabb88SLDhjVQB85qSoQcOCOX8ij/nViEFc2ybyYWuPgrhjA2r5mCECZNQ+OzEdv/kFYXiip5BsnXkFVfp2ctLPG4hb23kS7qIy7v0Jlu0Q6TlPhKZ1s7N9E3xDfEZ8dkU+uwJLH1k+Pll0uG8G+KbayZH9Ej9x+UAwzvJGcfNcVLLTnL5B1xWd08ltLzTXF5u9cDuSMEckxn3tPgczZTNnTgAEaZ/nimjzy2Qkvt9NQCHA0e6F/B3EMYTJ4w4plaePIEbvThoyksEfFZL8ywMzcQM7HyxQC/nFKQilMot+LgBDZw+YwDqhcYOccjrnIWpe8K0WE0DeEeHF8ZlLZ7YwY870F69QFF34dI14qykUnJTcu3KgQ4Fe+pArKADNEufRS4Vd5d2m6zqBQrm2C4Sd4+MsFgQ1HwN+5cPfFcNW9jzqiCmvClStHpMkqa77I1siPqXU7ekeWNW20cQZIPgSE/nAVsEDSRx/fyoeJ1/Hd5ArionfbaJy8iHZB6R60vV34QpPlOgRsSHiL6JanxqRA4v1hbHHEPtoO8v+C0bJ8fB/7CN//h4ORl6JT7y0qFtOV4NrArfhx2D8XM+jptb5v3C56TF+d1RhavD9x3GleGWLjy4hX2Izz4a7gb95v9OWRamfOQy/yDrr55sSGLjmncAFzCQSxHAsU8S1G8ULuqdBiL8UEixm7Op2SuKFMJzGO2oAdd5cOSrcIBbzWVKhLXDbabERRhwwA3hwAUfV8IzBJHAQAyigAvpiIZgyDubAUeYADggggcAQbBQQghEsB5hYAf7EQ4iOOtKeN5EJEjhJaJAhHBEQylyVGTAL0vc7ikgMCpRnoST5LVawDur9zea2EDwpplh/xh6Vy/yJAu2vqHHUMUR/WAKZi10oFa8SrfDpiHRBapQcmKZu0Oa6rgjE0ntZMdwQGAeVwnKU1InyevNeRf6/t9oYgMhZdJX0n8Mvdu+ICchE3B9Y15o0q4M7AdTwCS10IxArZ35yiQ2ShMJuvidKpScYAsY3YGUFtOiLGk+s13vjNU3pfwIBQmp6YYyLdtxPX/MX5W/OrdYols0FGD//vA7gjcmgl+O6FVcKMdx43TrrVNxLWZRXNYRnLSoRMqPzmdfxlVUQb4tmpsvja+6OGHrwN9L6k66s2uDAf3Zkjc5dVJe52MsHdDZRs8Uc0C0M7M2wDjCaTIBAAAA') format('woff2'), |
|||
url('//at.alicdn.com/t/font_1339011_fnx8kfgj0pj.woff?t=1565167418581') format('woff'), |
|||
url('//at.alicdn.com/t/font_1339011_fnx8kfgj0pj.ttf?t=1565167418581') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ |
|||
url('//at.alicdn.com/t/font_1339011_fnx8kfgj0pj.svg?t=1565167418581#iconfont') format('svg'); /* iOS 4.1- */ |
|||
} |
|||
|
|||
.iconfont { |
|||
font-family: "iconfont"; |
|||
font-size: 16px; |
|||
font-style: normal; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
|||
|
|||
.icon-shebei:before { |
|||
content: "\e606"; |
|||
} |
|||
|
|||
.icon-lora:before { |
|||
content: "\e7bd"; |
|||
} |
|||
|
|||
.icon-NATwangguan:before { |
|||
content: "\e665"; |
|||
} |
|||
|
|||
.icon-wendu:before { |
|||
content: "\e61f"; |
|||
} |
|||
|
|||
.icon-ditu:before { |
|||
content: "\e601"; |
|||
} |
|||
|
|||
.icon-wenshidu:before { |
|||
content: "\e600"; |
|||
} |
|||
|
|||
.icon-shebei2:before { |
|||
content: "\e643"; |
|||
} |
|||
|
|||
.icon-kuaicheduanxinwangguan:before { |
|||
content: "\eb53"; |
|||
} |
|||
|
|||
.icon-kuaicheduanxinwangguan1:before { |
|||
content: "\eb5f"; |
|||
} |
|||
|
|||
.icon-wenshidu1:before { |
|||
content: "\e602"; |
|||
} |
|||
|
|||
.icon-shidu:before { |
|||
content: "\e653"; |
|||
} |
|||
|
|||
.icon-yemian-copy-copy-copy:before { |
|||
content: "\e603"; |
|||
} |
|||
|
|||
.icon-gerenzhongxinwoderenwubiaozhuntoumianxing:before { |
|||
content: "\e604"; |
|||
} |
|||
|
|||
.icon-gateway:before { |
|||
content: "\e615"; |
|||
} |
|||
|
|||
@ -0,0 +1,40 @@ |
|||
import crypto from 'crypto' |
|||
import mqtt from './mqtt' |
|||
|
|||
const host = '39.98.253.192' |
|||
const url = `wx://${host}/mqtt` |
|||
|
|||
export function getMQTTClient(productId, deviceName, deviceSecret) { |
|||
const clientId = 'iotx_admin_' + Math.random().toString(16).substr(2, 8) |
|||
const content = productId + deviceName + clientId |
|||
const hmac = crypto.createHmac('md5', deviceSecret) |
|||
const up = hmac.update(content) |
|||
const sign = up.digest('hex') |
|||
const options = { |
|||
keepalive: 200, |
|||
clean: true, |
|||
reconnectPeriod: 5000, |
|||
connectTimeout: 5000, |
|||
clientId: clientId, |
|||
username: `${deviceName}&${productId}`, |
|||
password: sign |
|||
} |
|||
const client = mqtt.connect(url, options) |
|||
client.on('error', (e) => { |
|||
console.log(e) |
|||
client.end() |
|||
}) |
|||
let reconnectCount = 1 |
|||
client.on('reconnect', () => { |
|||
console.log(clientId + ' reconnect ' + reconnectCount + ' times') |
|||
reconnectCount++ |
|||
if (reconnectCount > 3) { |
|||
client.end() |
|||
console.log(clientId + ' reconnect failed') |
|||
} |
|||
}) |
|||
client.on('close', () => { |
|||
console.log(clientId + ' disconnected') |
|||
}) |
|||
return client |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
page { |
|||
background: #F4F5F6; |
|||
width: 750upx; |
|||
overflow-x: hidden; |
|||
} |
|||
|
|||
.qiun-padding { |
|||
padding: 2%; |
|||
width: 96%; |
|||
} |
|||
|
|||
.qiun-wrap { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.qiun-rows { |
|||
display: flex; |
|||
flex-direction: row !important; |
|||
} |
|||
|
|||
.qiun-columns { |
|||
display: flex; |
|||
flex-direction: column !important; |
|||
} |
|||
|
|||
.qiun-common-mt { |
|||
margin-top: 10upx; |
|||
} |
|||
|
|||
.qiun-common-border-bottom { |
|||
border-bottom: 1px solid #E9E9E9; |
|||
} |
|||
|
|||
.qiun-bg-white { |
|||
background: #FFFFFF; |
|||
} |
|||
|
|||
.qiun-title-bar { |
|||
width: 96%; |
|||
padding: 10upx 2%; |
|||
flex-wrap: nowrap; |
|||
} |
|||
|
|||
.qiun-title-dot-light { |
|||
border-left: 10upx solid #0ea391; |
|||
padding-left: 10upx; |
|||
font-size: 32upx; |
|||
color: #000000 |
|||
} |
|||
|
|||
.qiun-textarea { |
|||
height: 400upx; |
|||
font-size: 34upx; |
|||
box-sizing: border-box; |
|||
line-height: 50upx; |
|||
width: 100%; |
|||
background-color: #FFFFFF; |
|||
} |
|||
|
|||
.qiun-text-tips { |
|||
font-size: 28upx; |
|||
color: #dc2626; |
|||
line-height: 40upx; |
|||
padding: 6upx; |
|||
} |
|||
|
|||
.qiun-button { |
|||
background: #2fc25b; |
|||
color: #FFFFFF; |
|||
margin: 20upx; |
|||
} |
|||
|
|||
/* 通用样式 */ |
|||
.qiun-charts { |
|||
width: 750upx; |
|||
height: 500upx; |
|||
background-color: #FFFFFF; |
|||
} |
|||
|
|||
.charts { |
|||
width: 750upx; |
|||
height: 500upx; |
|||
background-color: #FFFFFF; |
|||
} |
|||
|
|||
/* 横屏样式 */ |
|||
.qiun-charts-rotate { |
|||
width: 700upx; |
|||
height: 1100upx; |
|||
background-color: #FFFFFF; |
|||
padding: 25upx; |
|||
} |
|||
|
|||
.charts-rotate { |
|||
width: 700upx; |
|||
height: 1100upx; |
|||
background-color: #FFFFFF; |
|||
} |
|||
|
|||
/* 圆弧进度样式 */ |
|||
.qiun-charts3 { |
|||
width: 750upx; |
|||
height: 250upx; |
|||
background-color: #FFFFFF; |
|||
position: relative; |
|||
} |
|||
|
|||
.charts3 { |
|||
position: absolute; |
|||
width: 250upx; |
|||
height: 250upx; |
|||
background-color: #FFFFFF; |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
import Request from '@/js_sdk/luch-request/request' |
|||
import store from '@/store' |
|||
import { getToken } from '@/common/auth' |
|||
|
|||
const http = new Request(); |
|||
|
|||
http.setConfig((config) => { /* 设置全局配置 */ |
|||
config.baseUrl = 'http://39.98.253.192/api'; /* 根域名不同 */ |
|||
return config; |
|||
}) |
|||
|
|||
http.interceptor.request((config, cancel) => { /* 请求之前拦截器 */ |
|||
if (store.getters.token) { |
|||
config.header['token'] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
|||
} |
|||
return config; |
|||
}) |
|||
|
|||
http.interceptor.response((response) => { /* 请求之后拦截器 */ |
|||
const res = response.data |
|||
if (res.code !== 0) { |
|||
uni.showToast({title: res.msg, duration:2000}); |
|||
|
|||
// 10001 无效的token或者token已过期
|
|||
if (res.code === 10001) { |
|||
uni.showModal({ |
|||
title: '重新登录', |
|||
content: 'token已经过期,需重新登录', |
|||
success: function (res) { |
|||
store.dispatch('FedLogOut').then(() => { |
|||
location.reload() // 为了重新实例化vue-router对象 避免bug
|
|||
}) |
|||
} |
|||
}); |
|||
} |
|||
return Promise.reject('error') |
|||
} else { |
|||
return response.data |
|||
} |
|||
}) |
|||
|
|||
export default http; |
|||
@ -0,0 +1,182 @@ |
|||
<template> |
|||
<view class="search" :style="{ backgroundColor: backgroundColor }"> |
|||
<view class="content" :style="{ 'border-radius': radius + 'px', border: border }"> |
|||
<view class="content-box" :class="{ center: mode === 2 }"> |
|||
<text class="icon icon-search"></text> |
|||
<input class="input" :class="{ center: !active && mode === 2 }" :focus="isFocus" :placeholder="placeholder" v-model="inputVal" @focus="focus" @blur="blur" /> |
|||
<!-- <view v-if="!active && mode === 2" class="input sub" @click="getFocus">请输入搜索内容</view> --> |
|||
<text v-if="isDelShow" class="icon icon-del" @click="clear"></text> |
|||
</view> |
|||
<view v-show="(active && show && button === 'inside') || (isDelShow && button === 'inside')" class="searchBtn" @click="search">搜索</view> |
|||
</view> |
|||
<view v-if="button === 'outside'" class="button" :class="{ active: show || active }" @click="search"> |
|||
<view class="button-item">{{ !show ? searchName : '搜索' }}</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
mode: { |
|||
type: Number, |
|||
default: 1 |
|||
}, |
|||
button: { |
|||
type: String, |
|||
default: 'outside' |
|||
}, |
|||
show: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
radius: { |
|||
type: String, |
|||
default: '60' |
|||
}, |
|||
placeholder: { |
|||
type: String, |
|||
default: '请输入搜索内容' |
|||
}, |
|||
backgroundColor: { |
|||
type: String, |
|||
default: '#fff' |
|||
}, |
|||
border: { type: String, default: '1px #f5f5f5 solid' } |
|||
|
|||
}, |
|||
data() { |
|||
return { |
|||
active: false, |
|||
inputVal: '', |
|||
searchName: '取消', |
|||
isDelShow: false, |
|||
isFocus: false |
|||
}; |
|||
}, |
|||
methods: { |
|||
focus() { |
|||
this.active = true; |
|||
}, |
|||
blur() { |
|||
this.isFocus = false; |
|||
if (!this.inputVal) { |
|||
this.active = false; |
|||
} |
|||
}, |
|||
clear() { |
|||
this.inputVal = ''; |
|||
this.active = false; |
|||
this.$emit('search', ''); |
|||
}, |
|||
getFocus() { |
|||
this.isFocus = true; |
|||
}, |
|||
search() { |
|||
if (!this.inputVal) return; |
|||
console.log(this.inputVal); |
|||
this.$emit('search', this.inputVal); |
|||
} |
|||
}, |
|||
watch: { |
|||
inputVal(newVal) { |
|||
if (newVal) { |
|||
this.searchName = '搜索'; |
|||
this.isDelShow = true; |
|||
} else { |
|||
this.searchName = '取消'; |
|||
this.isDelShow = false; |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.search { |
|||
display: flex; |
|||
width: 100%; |
|||
border-bottom: 1px #f5f5f5 solid; |
|||
box-sizing: border-box; |
|||
padding: 15upx; |
|||
font-size: $uni-font-size-base; |
|||
background: #fff; |
|||
.content { |
|||
display: flex; |
|||
align-items: center; |
|||
width: 100%; |
|||
height: 60upx; |
|||
border: 1px #ccc solid; |
|||
background: #fff; |
|||
overflow: hidden; |
|||
transition: all 0.2s linear; |
|||
border-radius: 30px; |
|||
|
|||
.content-box { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
&.center { |
|||
justify-content: center; |
|||
} |
|||
.icon { |
|||
padding: 0 15upx; |
|||
&.icon-del { |
|||
font-size: 38upx; |
|||
} |
|||
} |
|||
.input { |
|||
width: 100%; |
|||
max-width: 100%; |
|||
line-height: 60upx; |
|||
height: 60upx; |
|||
transition: all 0.2s linear; |
|||
&.center { |
|||
width: 200upx; |
|||
} |
|||
&.sub { |
|||
// position: absolute; |
|||
width: auto; |
|||
color: grey; |
|||
} |
|||
} |
|||
} |
|||
.searchBtn { |
|||
height: 100%; |
|||
flex-shrink: 0; |
|||
padding: 0 30upx; |
|||
background: $uni-color-success; |
|||
line-height: 60upx; |
|||
color: #fff; |
|||
border-left: 1px #ccc solid; |
|||
transition: all 0.3s; |
|||
} |
|||
} |
|||
|
|||
.button { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
position: relative; |
|||
flex-shrink: 0; |
|||
width: 0; |
|||
transition: all 0.2s linear; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
&.active { |
|||
padding-left: 15upx; |
|||
width: 100upx; |
|||
} |
|||
} |
|||
} |
|||
@font-face { |
|||
font-family: 'iconfont'; |
|||
src: url('https://at.alicdn.com/t/font_989023_efq0mtli526.ttf') format('truetype'); |
|||
} |
|||
.icon { |
|||
font-family: iconfont; |
|||
font-size: 32upx; |
|||
font-style: normal; |
|||
color: #999; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,174 @@ |
|||
<template> |
|||
<view class='refreshBox' :style="isTranform"> |
|||
<view class='refresh' :style="isZoom" :class="isEnd==2?'animationSmall':''"> |
|||
<view class='refreshWord' v-if="isEnd == 0">松开刷新</view> |
|||
<view class='refreshCirle animation' v-if="isEnd == 1"></view> |
|||
<image class='iconYes' src='../static/icon-yes.png' v-if="isEnd==2"></image> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'refresh', |
|||
props: { |
|||
isTop:{ |
|||
type:Number, |
|||
default:1 |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
isTranf: 0, |
|||
touchStart: '', |
|||
touchMove: '', |
|||
isEnd: 0 |
|||
}; |
|||
}, |
|||
|
|||
methods: { |
|||
refreshStart(e) { |
|||
if (this.isEnd == 0 && this.isTop == 1) { |
|||
this.touchStart = e.changedTouches[0].pageY |
|||
} |
|||
}, |
|||
refreshMove(e){ |
|||
if (this.isEnd == 0 && this.isTop == 1) { |
|||
var touchStart = this.touchStart, |
|||
oldMove = this.touchMove, |
|||
newMove = e.changedTouches[0].pageY |
|||
if (touchStart <= newMove) { |
|||
var isTranf = touchStart > newMove ? 0 : newMove - touchStart |
|||
this.isTranf = isTranf |
|||
this.touchMove = e.changedTouches[0].pageY |
|||
} |
|||
} else{ |
|||
this.isTranf = 0 |
|||
this.isEnd = 0 |
|||
this.touchStart = 9999 |
|||
} |
|||
}, |
|||
refreshEnd(e) { |
|||
var that = this |
|||
if (this.isEnd == 0 && this.isTop == 1) { |
|||
if (this.isTranf >= 90) { |
|||
this.isTranf = 125 |
|||
this.isEnd = 1 |
|||
this.$emit('isRefresh'); |
|||
} else { |
|||
this.isTranf = 0 |
|||
} |
|||
} |
|||
}, |
|||
endAfter(){ |
|||
this.isEnd = 2 |
|||
setTimeout(() => { |
|||
this.isTranf = 0 |
|||
this.isEnd = 0 |
|||
}, 600) |
|||
} |
|||
}, |
|||
computed: { |
|||
isTranform() { |
|||
var isTranf = this.isTranf > 150 ? 150 : this.isTranf |
|||
var isTemp = `transform: translateY(${isTranf-100}px);` |
|||
return isTemp |
|||
}, |
|||
isZoom() { |
|||
var isTranf = this.isTranf > 125 ? 125 : this.isTranf |
|||
var isTemp = `zoom:${isTranf/125};` |
|||
return isTemp |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
.refreshBox { |
|||
margin:0 auto; |
|||
width: 100%; |
|||
height: 100upx; |
|||
overflow: hidden; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
max-height: 300upx; |
|||
position: fixed; |
|||
z-index: 999; |
|||
top: 0; |
|||
left: 0; |
|||
transform: translateY(-100upx); |
|||
} |
|||
|
|||
.animationSmall { |
|||
animation: small 1.1s both; |
|||
} |
|||
|
|||
@keyframes small { |
|||
0% { |
|||
transform: scale(1) |
|||
} |
|||
20%{ |
|||
transform: scale(1.4) |
|||
} |
|||
100% { |
|||
transform: scale(0) |
|||
} |
|||
} |
|||
.refreshWord{ |
|||
width: 150upx; |
|||
height: 26upx; |
|||
font-size: 24upx; |
|||
line-height: 26upx; |
|||
text-align: center; |
|||
border-radius: 26upx; |
|||
} |
|||
|
|||
.refresh { |
|||
min-width: 50upx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
height: 50upx; |
|||
background: #FFFFFF; |
|||
box-shadow: 0 0 16upx 0 rgba(0, 0, 0, 0.10); |
|||
border-radius: 50upx; |
|||
} |
|||
|
|||
.refreshCirle { |
|||
width: 26upx; |
|||
height: 26upx; |
|||
border-radius: 50%; |
|||
display: inline-block; |
|||
position: relative; |
|||
border: 6upx solid black; |
|||
border-bottom-color: transparent; |
|||
border-top-color: transparent; |
|||
} |
|||
|
|||
.animation { |
|||
animation: rotate 1.1s infinite; |
|||
animation-timing-function: cubic-bezier(0.3, 1.65, 0.7, -0.65); |
|||
} |
|||
|
|||
@keyframes rotate { |
|||
0% { |
|||
transform: rotate(0deg); |
|||
} |
|||
|
|||
100% { |
|||
transform: rotate(360deg); |
|||
} |
|||
} |
|||
|
|||
.true { |
|||
color: black; |
|||
} |
|||
|
|||
.iconYes { |
|||
width: 34upx; |
|||
height: 34upx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,34 @@ |
|||
### Badge 数字角标 |
|||
|
|||
数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景,组件名:``uni-badge``,代码块: uBadge。 |
|||
|
|||
**使用方式:** |
|||
|
|||
在 ``script`` 中引用组件 |
|||
|
|||
```javascript |
|||
import uniBadge from "@/components/uni-badge/uni-badge.vue" |
|||
export default { |
|||
components: {uniBadge} |
|||
} |
|||
``` |
|||
|
|||
在 ``template`` 中使用组件 |
|||
|
|||
```html |
|||
<uni-badge text="1"></uni-badge> |
|||
<uni-badge text="2" type="success" @click="bindClick"></uni-badge> |
|||
<uni-badge text="3" type="primary" :inverted="true"></uni-badge> |
|||
``` |
|||
|
|||
实际效果参考:[https://github.com/dcloudio/uni-ui](https://github.com/dcloudio/uni-ui) |
|||
|
|||
**Badge 属性说明:** |
|||
|
|||
|属性名 |类型 |默认值 |说明 | |
|||
|--- |---- |--- |--- | |
|||
|text |String |- |角标内容 | |
|||
|type |String |default|颜色类型,可选值:default(灰色)、primary(蓝色)、success(绿色)、warning(黄色)、error(红色) | |
|||
|size |String |normal|Badge 大小,可取值:normal、small| |
|||
|inverted |Boolean |false |是否无需背景颜色,为 true 时,背景颜色将变为文字的字体颜色 | |
|||
|@click |EventHandle| - |点击 Badge 触发事件 | |
|||
@ -0,0 +1,109 @@ |
|||
<template> |
|||
<text class="uni-badge" v-if="text" :class="setClass" @click="onClick()">{{text}}</text> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'uni-badge', |
|||
props: { |
|||
type: { |
|||
type: String, |
|||
default: 'default' |
|||
}, |
|||
inverted: { |
|||
type: Boolean, |
|||
default: false |
|||
}, |
|||
text: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
size: { //small.normal |
|||
type: String, |
|||
default: 'normal' |
|||
} |
|||
}, |
|||
computed: { |
|||
setClass() { |
|||
let classList = ['uni-badge-' + this.type, 'uni-badge--' + this.size]; |
|||
if (this.inverted === true) { |
|||
classList.push('uni-badge-inverted') |
|||
} |
|||
return classList.join(" ") |
|||
} |
|||
}, |
|||
methods: { |
|||
onClick() { |
|||
this.$emit('click') |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
$bage-size:12px; |
|||
$bage-small:scale(0.8); |
|||
|
|||
.uni-badge { |
|||
font-family: 'Helvetica Neue', Helvetica, sans-serif; |
|||
box-sizing: border-box; |
|||
font-size: $bage-size; |
|||
line-height: 1; |
|||
display: inline-block; |
|||
padding: 3px 6px; |
|||
color: $uni-text-color; |
|||
border-radius: 100px; |
|||
background-color: $uni-bg-color-hover; |
|||
|
|||
&.uni-badge-inverted { |
|||
padding: 0 5px 0 0; |
|||
color: $uni-text-color-grey; |
|||
background-color: transparent; |
|||
} |
|||
|
|||
&-primary { |
|||
color: $uni-text-color-inverse; |
|||
background-color: $uni-color-primary; |
|||
|
|||
&.uni-badge-inverted { |
|||
color: $uni-color-primary; |
|||
background-color: transparent |
|||
} |
|||
} |
|||
|
|||
&-success { |
|||
color: $uni-text-color-inverse; |
|||
background-color: $uni-color-success; |
|||
|
|||
&.uni-badge-inverted { |
|||
color: $uni-color-success; |
|||
background-color: transparent |
|||
} |
|||
} |
|||
|
|||
&-warning { |
|||
color: $uni-text-color-inverse; |
|||
background-color: $uni-color-warning; |
|||
|
|||
&.uni-badge-inverted { |
|||
color: $uni-color-warning; |
|||
background-color: transparent |
|||
} |
|||
} |
|||
|
|||
&-error { |
|||
color: $uni-text-color-inverse; |
|||
background-color: $uni-color-error; |
|||
|
|||
&.uni-badge-inverted { |
|||
color: $uni-color-error; |
|||
background-color: transparent |
|||
} |
|||
} |
|||
|
|||
&--small { |
|||
transform: $bage-small; |
|||
transform-origin: center center; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,368 @@ |
|||
<template> |
|||
<view> |
|||
<view |
|||
class="fab-box fab" |
|||
:class="{ |
|||
leftBottom: leftBottom, |
|||
rightBottom: rightBottom, |
|||
leftTop: leftTop, |
|||
rightTop: rightTop |
|||
}" |
|||
> |
|||
<view |
|||
class="fab-circle" |
|||
:class="{ |
|||
left: horizontal === 'left' && direction === 'horizontal', |
|||
top: vertical === 'top' && direction === 'vertical', |
|||
bottom: vertical === 'bottom' && direction === 'vertical', |
|||
right: horizontal === 'right' && direction === 'horizontal' |
|||
}" |
|||
:style="{ 'background-color': styles.buttonColor }" |
|||
@click="open" |
|||
> |
|||
<text class="icon icon-jia" :class="{ active: showContent }"></text> |
|||
</view> |
|||
<view |
|||
class="fab-content" |
|||
:class="{ |
|||
left: horizontal === 'left', |
|||
right: horizontal === 'right', |
|||
flexDirection: direction === 'vertical', |
|||
flexDirectionStart: flexDirectionStart, |
|||
flexDirectionEnd: flexDirectionEnd |
|||
}" |
|||
:style="{ width: boxWidth, height: boxHeight, background: styles.backgroundColor }" |
|||
> |
|||
<view v-if="flexDirectionStart || horizontalLeft" class="fab-item first"></view> |
|||
<view |
|||
class="fab-item" |
|||
v-for="(item, index) in content" |
|||
:key="index" |
|||
:class="{ active: showContent }" |
|||
:style="{ |
|||
color: item.active ? styles.selectedColor : styles.color |
|||
}" |
|||
@click="taps(index, item)" |
|||
> |
|||
<image |
|||
class="content-image" |
|||
:src="item.active ? item.selectedIconPath : item.iconPath" |
|||
mode="" |
|||
></image> |
|||
<text class="text">{{ item.text }}</text> |
|||
</view> |
|||
<view v-if="flexDirectionEnd || horizontalRight" class="fab-item first"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
props: { |
|||
pattern: { |
|||
type: Object, |
|||
default: () => { |
|||
return {}; |
|||
} |
|||
}, |
|||
horizontal: { |
|||
type: String, |
|||
default: 'left' |
|||
}, |
|||
vertical: { |
|||
type: String, |
|||
default: 'bottom' |
|||
}, |
|||
direction: { |
|||
type: String, |
|||
default: 'horizontal' |
|||
}, |
|||
content: { |
|||
type: Array, |
|||
default: () => { |
|||
return []; |
|||
} |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
fabShow: false, |
|||
flug: true, |
|||
showContent: false, |
|||
styles: { |
|||
color: '#3c3e49', |
|||
selectedColor: '#007AFF', |
|||
backgroundColor: '#fff', |
|||
buttonColor: '#3c3e49' |
|||
} |
|||
}; |
|||
}, |
|||
created() { |
|||
if (this.top === 0) { |
|||
this.fabShow = true; |
|||
} |
|||
// 初始化样式 |
|||
this.styles = Object.assign({}, this.styles, this.pattern); |
|||
}, |
|||
methods: { |
|||
open() { |
|||
this.showContent = !this.showContent; |
|||
}, |
|||
/** |
|||
* 按钮点击事件 |
|||
*/ |
|||
taps(index, item) { |
|||
this.$emit('trigger', { |
|||
index, |
|||
item |
|||
}); |
|||
}, |
|||
/** |
|||
* 获取 位置信息 |
|||
*/ |
|||
getPosition(types, paramA, paramB) { |
|||
if (types === 0) { |
|||
return this.horizontal === paramA && this.vertical === paramB; |
|||
} else if (types === 1) { |
|||
return this.direction === paramA && this.vertical === paramB; |
|||
} else if (types === 2) { |
|||
return this.direction === paramA && this.horizontal === paramB; |
|||
} else { |
|||
return this.showContent && this.direction === paramA |
|||
? this.contentWidth |
|||
: this.contentWidthMin; |
|||
} |
|||
} |
|||
}, |
|||
watch: { |
|||
pattern(newValue, oldValue) { |
|||
console.log(JSON.stringify(newValue)); |
|||
this.styles = Object.assign({}, this.styles, newValue); |
|||
} |
|||
}, |
|||
computed: { |
|||
contentWidth(e) { |
|||
return uni.upx2px((this.content.length + 1) * 110 + 20) + 'px'; |
|||
}, |
|||
contentWidthMin() { |
|||
return uni.upx2px(110) + 'px'; |
|||
}, |
|||
// 动态计算宽度 |
|||
boxWidth() { |
|||
return this.getPosition(3, 'horizontal'); |
|||
}, |
|||
// 动态计算高度 |
|||
boxHeight() { |
|||
return this.getPosition(3, 'vertical'); |
|||
}, |
|||
// 计算左下位置 |
|||
leftBottom() { |
|||
return this.getPosition(0, 'left', 'bottom'); |
|||
}, |
|||
// 计算右下位置 |
|||
rightBottom() { |
|||
return this.getPosition(0, 'right', 'bottom'); |
|||
}, |
|||
// 计算左上位置 |
|||
leftTop() { |
|||
return this.getPosition(0, 'left', 'top'); |
|||
}, |
|||
rightTop() { |
|||
return this.getPosition(0, 'right', 'top'); |
|||
}, |
|||
flexDirectionStart() { |
|||
return this.getPosition(1, 'vertical', 'top'); |
|||
}, |
|||
flexDirectionEnd() { |
|||
return this.getPosition(1, 'vertical', 'bottom'); |
|||
}, |
|||
horizontalLeft() { |
|||
return this.getPosition(2, 'horizontal', 'left'); |
|||
}, |
|||
horizontalRight() { |
|||
return this.getPosition(2, 'horizontal', 'right'); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.fab-box { |
|||
position: fixed; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
z-index: 2; |
|||
} |
|||
|
|||
.fab-box.top { |
|||
width: 60upx; |
|||
height: 60upx; |
|||
right: 30upx; |
|||
bottom: 60upx; |
|||
border: 1px #5989b9 solid; |
|||
background: #6699cc; |
|||
border-radius: 10upx; |
|||
color: #fff; |
|||
transition: all 0.3; |
|||
opacity: 0; |
|||
} |
|||
|
|||
.fab-box.active { |
|||
opacity: 1; |
|||
} |
|||
|
|||
.fab-box.fab { |
|||
z-index: 10; |
|||
} |
|||
|
|||
.fab-box.fab.leftBottom { |
|||
left: 30upx; |
|||
bottom: 60upx; |
|||
} |
|||
|
|||
.fab-box.fab.leftTop { |
|||
left: 30upx; |
|||
top: 80upx; |
|||
/* #ifdef H5 */ |
|||
top: calc(80upx + var(--window-top)); |
|||
/* #endif */ |
|||
} |
|||
|
|||
.fab-box.fab.rightBottom { |
|||
right: 30upx; |
|||
bottom: 60upx; |
|||
} |
|||
|
|||
.fab-box.fab.rightTop { |
|||
right: 30upx; |
|||
top: 80upx; |
|||
/* #ifdef H5 */ |
|||
top: calc(80upx + var(--window-top)); |
|||
/* #endif */ |
|||
} |
|||
|
|||
.fab-circle { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
position: absolute; |
|||
width: 110upx; |
|||
height: 110upx; |
|||
background: #3c3e49; |
|||
/* background: #5989b9; */ |
|||
border-radius: 50%; |
|||
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.2); |
|||
z-index: 11; |
|||
} |
|||
|
|||
.fab-circle.left { |
|||
left: 0; |
|||
} |
|||
|
|||
.fab-circle.right { |
|||
right: 0; |
|||
} |
|||
|
|||
.fab-circle.top { |
|||
top: 0; |
|||
} |
|||
|
|||
.fab-circle.bottom { |
|||
bottom: 0; |
|||
} |
|||
|
|||
.fab-circle .icon-jia { |
|||
color: #ffffff; |
|||
font-size: 50upx; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
.fab-circle .icon-jia.active { |
|||
transform: rotate(135deg); |
|||
} |
|||
|
|||
.fab-content { |
|||
background: #6699cc; |
|||
box-sizing: border-box; |
|||
display: flex; |
|||
border-radius: 100upx; |
|||
overflow: hidden; |
|||
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1); |
|||
transition: all 0.2s; |
|||
width: 110upx; |
|||
} |
|||
|
|||
.fab-content.left { |
|||
justify-content: flex-start; |
|||
} |
|||
|
|||
.fab-content.right { |
|||
justify-content: flex-end; |
|||
} |
|||
|
|||
.fab-content.flexDirection { |
|||
flex-direction: column; |
|||
justify-content: flex-end; |
|||
} |
|||
|
|||
.fab-content.flexDirectionStart { |
|||
flex-direction: column; |
|||
justify-content: flex-start; |
|||
} |
|||
|
|||
.fab-content.flexDirectionEnd { |
|||
flex-direction: column; |
|||
justify-content: flex-end; |
|||
} |
|||
|
|||
.fab-content .fab-item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
align-items: center; |
|||
width: 110upx; |
|||
height: 110upx; |
|||
font-size: 24upx; |
|||
color: #fff; |
|||
opacity: 0; |
|||
transition: opacity 0.2s; |
|||
} |
|||
|
|||
.fab-content .fab-item.active { |
|||
opacity: 1; |
|||
} |
|||
|
|||
.fab-content .fab-item .content-image { |
|||
width: 50upx; |
|||
height: 50upx; |
|||
margin-bottom: 5upx; |
|||
} |
|||
|
|||
.fab-content .fab-item.first { |
|||
width: 110upx; |
|||
} |
|||
|
|||
@font-face { |
|||
font-family: 'iconfont'; |
|||
src: url('https://at.alicdn.com/t/font_1028200_xhbo4rn58rp.ttf?t=1548214263520') |
|||
format('truetype'); |
|||
} |
|||
|
|||
.icon { |
|||
font-family: 'iconfont' !important; |
|||
font-size: 16px; |
|||
font-style: normal; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-osx-font-smoothing: grayscale; |
|||
} |
|||
|
|||
.icon-jia:before { |
|||
content: '\e630'; |
|||
} |
|||
|
|||
.icon-arrow-up:before { |
|||
content: '\e603'; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,35 @@ |
|||
### Icon 图标 |
|||
|
|||
用于展示 icon,组件名:``uni-icon``,代码块: uIcon。 |
|||
|
|||
**使用方式:** |
|||
|
|||
在 ``script`` 中引用组件 |
|||
|
|||
```javascript |
|||
import uniIcon from "@/components/uni-icon/uni-icon.vue" |
|||
export default { |
|||
components: {uniIcon} |
|||
} |
|||
``` |
|||
|
|||
在 ``template`` 中使用组件 |
|||
|
|||
```html |
|||
<uni-icon type="contact" size="30"></uni-icon> |
|||
``` |
|||
|
|||
实际效果参考:[https://github.com/dcloudio/uni-ui](https://github.com/dcloudio/uni-ui) |
|||
|
|||
**Icon 属性说明:** |
|||
|
|||
|属性名 |类型|默认值 |说明| |
|||
|---|----|---|---| |
|||
|type |String |-|图标图案,参考下表| |
|||
|color |String |-|图标颜色 | |
|||
|size |Number |24|图标大小| |
|||
|@click |EventHandle|-|点击 Icon 触发事件| |
|||
|
|||
**type 类型:** |
|||
|
|||
 |
|||
@ -0,0 +1,196 @@ |
|||
<template> |
|||
<view class="uni-list-cell" :class="[disabled === true || disabled === 'true' ? 'uni-list-cell--disabled' : '']" |
|||
:hover-class="disabled === true || disabled === 'true' || showSwitch === true || showSwitch === 'true' ? '' : 'uni-list-cell--hover'" @click="onClick"> |
|||
<view class="uni-list-cell__container"> |
|||
<view class="uni-list-cell__icon" v-if="thumb"> |
|||
<image class="uni-list-cell__icon-img" :src="thumb"></image> |
|||
</view> |
|||
<view class="uni-list-cell__icon" v-else-if="showExtraIcon === true || showExtraIcon === 'true'"> |
|||
<uni-icon :color="extraIcon.color" :size="extraIcon.size" :type="extraIcon.type"></uni-icon> |
|||
</view> |
|||
<view class="uni-list-cell__content"> |
|||
<view class="uni-list-cell__content-title">{{title}}</view> |
|||
<view class="uni-list-cell__content-note" v-if="note">{{note}}</view> |
|||
</view> |
|||
<view class="uni-list-cell__extra" v-if="showBadge === true || showBadge === 'true' || showArrow === true || showArrow === 'true'||showSwitch === true || showSwitch === 'true'"> |
|||
<uni-badge v-if="showBadge === true || showBadge === 'true'" :type="badgeType" :text="badgeText"></uni-badge> |
|||
<switch v-if="showSwitch === true || showSwitch === 'true'" :disabled='disabled' :checked="switchChecked" @change="onSwitchChange" /> |
|||
<uni-icon v-if="showArrow === true || showArrow === 'true'" color="#bbb" size="20" type="arrowright"></uni-icon> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import uniIcon from '../uni-icon/uni-icon.vue' |
|||
import uniBadge from '../uni-badge/uni-badge.vue' |
|||
export default { |
|||
name: 'uni-list-item', |
|||
components: { |
|||
uniIcon, |
|||
uniBadge |
|||
}, |
|||
data() { |
|||
return { |
|||
|
|||
}; |
|||
}, |
|||
props: { |
|||
title: String, //列表标题 |
|||
note: String, //列表描述 |
|||
disabled: { //是否禁用 |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
showArrow: { //是否显示箭头 |
|||
type: [Boolean, String], |
|||
default: true |
|||
}, |
|||
showBadge: { //是否显示数字角标 |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
showSwitch: { //是否显示Switch |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
switchChecked: { //Switch是否被选中 |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
badgeText: String, //badge内容 |
|||
badgeType: { //badge类型 |
|||
type: String, |
|||
default: 'success' |
|||
}, |
|||
thumb: String, //缩略图 |
|||
showExtraIcon: { //是否显示扩展图标 |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
extraIcon: { |
|||
type: Object, |
|||
default () { |
|||
return { |
|||
type: "contact", |
|||
color: "#000000", |
|||
size: "20" |
|||
}; |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
onClick() { |
|||
this.$emit('click') |
|||
}, |
|||
onSwitchChange(e) { |
|||
this.$emit('switchChange', e.detail) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
@mixin list-hover { |
|||
background-color: $uni-bg-color-hover; |
|||
} |
|||
|
|||
@mixin list-disabled { |
|||
opacity: 0.3; |
|||
} |
|||
|
|||
$list-cell-pd:$uni-spacing-col-lg $uni-spacing-row-lg; |
|||
|
|||
.uni-list-cell { |
|||
font-size: $uni-font-size-lg; |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
&--disabled { |
|||
@include list-disabled; |
|||
} |
|||
|
|||
&--hover { |
|||
@include list-hover; |
|||
} |
|||
|
|||
&__container { |
|||
padding: $list-cell-pd; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
flex: 1; |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
|
|||
&:after { |
|||
position: absolute; |
|||
z-index: 3; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 30upx; |
|||
height: 1px; |
|||
content: ''; |
|||
-webkit-transform: scaleY(.5); |
|||
transform: scaleY(.5); |
|||
background-color: $uni-border-color; |
|||
} |
|||
} |
|||
|
|||
&__content { |
|||
flex: 1; |
|||
overflow: hidden; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
&-title { |
|||
font-size: $uni-font-size-lg; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
color: inherit; |
|||
line-height: 1.5; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
&-note { |
|||
color: $uni-text-color-grey; |
|||
font-size: $uni-font-size-base; |
|||
white-space: normal; |
|||
display: -webkit-box; |
|||
-webkit-box-orient: vertical; |
|||
-webkit-line-clamp: 2; |
|||
overflow: hidden; |
|||
} |
|||
} |
|||
|
|||
&__extra { |
|||
width: 25%; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: flex-end; |
|||
align-items: center; |
|||
} |
|||
|
|||
&__icon { |
|||
margin-right: 18upx; |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
align-items: center; |
|||
|
|||
&-img { |
|||
height: $uni-img-size-base; |
|||
width: $uni-img-size-base; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.uni-list>.uni-list-cell:last-child .uni-list-cell-container:after { |
|||
height: 0px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,88 @@ |
|||
### List 列表 |
|||
|
|||
列表组件,组件名:``uni-list``、``uni-list-item``,代码块: uList。 |
|||
|
|||
**使用方式:** |
|||
|
|||
在 ``script`` 中引用组件 |
|||
|
|||
```javascript |
|||
import uniList from '@/components/uni-list/uni-list.vue' |
|||
import uniListItem from '@/components/uni-list-item/uni-list-item.vue' |
|||
export default { |
|||
components: {uniList,uniListItem} |
|||
} |
|||
``` |
|||
|
|||
List 一般用法 |
|||
|
|||
```html |
|||
<uni-list> |
|||
<uni-list-item title="标题文字" show-arrow="false"></uni-list-item> |
|||
<uni-list-item title="标题文字"></uni-list-item> |
|||
<uni-list-item title="标题文字" show-badge="true" badge-text="12"></uni-list-item> |
|||
<uni-list-item title="禁用状态" disabled="true" show-badge="true" badge-text="12"></uni-list-item> |
|||
</uni-list> |
|||
``` |
|||
|
|||
带描述信息 |
|||
|
|||
```html |
|||
<uni-list> |
|||
<uni-list-item title="标题文字" note="描述信息"></uni-list-item> |
|||
<uni-list-item title="标题文字" note="描述信息" show-badge="true" badge-text="12"></uni-list-item> |
|||
</uni-list> |
|||
``` |
|||
|
|||
包含图片 |
|||
|
|||
```html |
|||
<uni-list> |
|||
<uni-list-item title="标题文字" thumb="http://img-cdn-qiniu.dcloud.net.cn/new-page/hx.png"></uni-list-item> |
|||
</uni-list> |
|||
``` |
|||
|
|||
包含图标 |
|||
|
|||
```html |
|||
<uni-list> |
|||
<uni-list-item title="标题文字" |
|||
show-extra-icon="true" |
|||
:extra-icon="{color: '#4cd964',size: '22',type: 'spinner'}"> |
|||
</uni-list-item> |
|||
</uni-list> |
|||
``` |
|||
|
|||
显示Switch |
|||
|
|||
```html |
|||
<uni-list> |
|||
<uni-list-item title="标题文字" show-switch="true" show-arrow="false"></uni-list-item> |
|||
</uni-list> |
|||
``` |
|||
|
|||
实际效果参考:[https://github.com/dcloudio/uni-ui](https://github.com/dcloudio/uni-ui) |
|||
|
|||
**uniListItem 属性说明:** |
|||
|
|||
|属性名|类型|默认值 |说明| |
|||
|---|----|---|---| |
|||
|title|String|-|标题| |
|||
|note|String|-|描述| |
|||
|disabled|Boolean|false|是否禁用| |
|||
|show-arrow|Boolean|true|是否显示箭头图标| |
|||
|show-badge|Boolean|false|是否显示数字角标| |
|||
|badge-text|String|-|数字角标内容| |
|||
|badge-type|String|-|数字角标类型,参考[Badge 数字角标](https://ext.dcloud.net.cn/plugin?id=21)| |
|||
|show-switch|Boolean|false|是否显示Switch| |
|||
|switch-checked|Boolean|false|Switch是否被选中| |
|||
|show-extra-icon|Boolean|false|左侧是否显示扩展图标| |
|||
|extra-icon|Object|-|扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [Iocn 图标](https://ext.dcloud.net.cn/plugin?id=28)| |
|||
|thumb|String|-|左侧缩略图,若thumb有值,则不会显示扩展图标| |
|||
|
|||
**uniListItem 事件说明:** |
|||
|
|||
|事件称名|说明|返回参数| |
|||
|---|----|---| |
|||
|click|点击 uniListItem 触发事件|-| |
|||
|switchChange|点击切换 Switch 时触发|{value:checked}| |
|||
@ -0,0 +1,45 @@ |
|||
<template> |
|||
<view class="uni-list"> |
|||
<slot></slot> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
name: 'uni-list' |
|||
} |
|||
</script> |
|||
<style lang="scss"> |
|||
.uni-list { |
|||
background-color: $uni-bg-color; |
|||
position: relative; |
|||
width: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
|
|||
&:after { |
|||
position: absolute; |
|||
z-index: 10; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
height: 1px; |
|||
content: ''; |
|||
-webkit-transform: scaleY(0.5); |
|||
transform: scaleY(0.5); |
|||
background-color: $uni-border-color; |
|||
} |
|||
|
|||
&:before { |
|||
position: absolute; |
|||
z-index: 10; |
|||
right: 0; |
|||
top: 0; |
|||
left: 0; |
|||
height: 1px; |
|||
content: ''; |
|||
-webkit-transform: scaleY(0.5); |
|||
transform: scaleY(0.5); |
|||
background-color: $uni-border-color; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,209 @@ |
|||
<template> |
|||
<view class="uni-navbar" :class="{'uni-navbar-fixed':isFixed,'uni-navbar-shadow':hasShadow}" :style="{'background-color':backgroundColor}"> |
|||
<uni-status-bar v-if="insertStatusBar"></uni-status-bar> |
|||
<view class="uni-navbar-header" :style="{color:color}"> |
|||
<view class="uni-navbar-header-btns" @tap="onClickLeft"> |
|||
<view v-if="leftIcon.length"> |
|||
<uni-icon :type="leftIcon" :color="color" size="24"></uni-icon> |
|||
</view> |
|||
<view v-if="leftText.length" class="uni-navbar-btn-text" :class="{'uni-navbar-btn-icon-left':!leftIcon.length}">{{leftText}}</view> |
|||
<slot name="left"></slot> |
|||
</view> |
|||
<view class="uni-navbar-container"> |
|||
<view v-if="title.length" class="uni-navbar-container-title">{{title}}</view> |
|||
<!-- 标题插槽 --> |
|||
<slot></slot> |
|||
</view> |
|||
<view class="uni-navbar-header-btns" @tap="onClickRight"> |
|||
<view v-if="rightIcon.length"> |
|||
<uni-icon :type="rightIcon" :color="color" size="24"></uni-icon> |
|||
</view> |
|||
<!-- 优先显示图标 --> |
|||
<view v-if="rightText.length&&!rightIcon.length" class="uni-navbar-btn-text">{{rightText}}</view> |
|||
<slot name="right"></slot> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import uniStatusBar from '../uni-status-bar/uni-status-bar.vue' |
|||
import uniIcon from '../uni-icon/uni-icon.vue' |
|||
|
|||
export default { |
|||
components: { |
|||
uniStatusBar, |
|||
uniIcon |
|||
}, |
|||
props: { |
|||
/** |
|||
* 标题文字 |
|||
*/ |
|||
title: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
/** |
|||
* 左侧按钮文本 |
|||
*/ |
|||
leftText: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
/** |
|||
* 右侧按钮文本 |
|||
*/ |
|||
rightText: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
/** |
|||
* 左侧按钮图标 |
|||
*/ |
|||
leftIcon: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
/** |
|||
* 右侧按钮图标 |
|||
*/ |
|||
rightIcon: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
/** |
|||
* 是否固定在顶部 |
|||
*/ |
|||
fixed: { |
|||
type: [Boolean, String], |
|||
default: false |
|||
}, |
|||
/** |
|||
* 按钮图标和文字颜色 |
|||
*/ |
|||
color: { |
|||
type: String, |
|||
default: '#000000' |
|||
}, |
|||
/** |
|||
* 背景颜色 |
|||
*/ |
|||
backgroundColor: { |
|||
type: String, |
|||
default: '#FFFFFF' |
|||
}, |
|||
/** |
|||
* 是否包含状态栏,默认固定在顶部时包含 |
|||
*/ |
|||
statusBar: { |
|||
type: [Boolean, String], |
|||
default: '' |
|||
}, |
|||
/** |
|||
* 是否使用阴影,默认根据背景色判断 |
|||
*/ |
|||
shadow: { |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
computed: { |
|||
isFixed() { |
|||
return String(this.fixed) === 'true' |
|||
}, |
|||
insertStatusBar() { |
|||
switch (String(this.statusBar)) { |
|||
case 'true': |
|||
return true |
|||
case 'false': |
|||
return false |
|||
default: |
|||
return this.isFixed |
|||
} |
|||
}, |
|||
hasShadow() { |
|||
var backgroundColor = this.backgroundColor |
|||
switch (String(this.shadow)) { |
|||
case 'true': |
|||
return true |
|||
case 'false': |
|||
return false |
|||
default: |
|||
return backgroundColor !== 'transparent' && backgroundColor.indexOf('rgba') < 0 |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
/** |
|||
* 左侧按钮点击事件 |
|||
*/ |
|||
onClickLeft() { |
|||
this.$emit('clickLeft') |
|||
this.$emit('click-left') |
|||
}, |
|||
/** |
|||
* 右侧按钮点击事件 |
|||
*/ |
|||
onClickRight() { |
|||
this.$emit('clickRight') |
|||
this.$emit('click-right') |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.uni-navbar { |
|||
display: block; |
|||
position: relative; |
|||
width: 100%; |
|||
background-color: #FFFFFF; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.uni-navbar view{ |
|||
line-height:44px; |
|||
} |
|||
|
|||
.uni-navbar-shadow { |
|||
box-shadow: 0 1px 6px #ccc; |
|||
} |
|||
|
|||
.uni-navbar.uni-navbar-fixed { |
|||
position: fixed; |
|||
z-index: 998; |
|||
} |
|||
|
|||
.uni-navbar-header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
width: 100%; |
|||
height:44px; |
|||
line-height:44px; |
|||
font-size: 16px; |
|||
} |
|||
|
|||
.uni-navbar-header .uni-navbar-header-btns{ |
|||
display:inline-flex; |
|||
flex-wrap:nowrap; |
|||
flex-shrink:0; |
|||
width: 120upx; |
|||
padding:0 12upx; |
|||
} |
|||
|
|||
.uni-navbar-header .uni-navbar-header-btns:first-child{ |
|||
padding-left:0; |
|||
} |
|||
.uni-navbar-header .uni-navbar-header-btns:last-child{ |
|||
width: 60upx; |
|||
} |
|||
.uni-navbar-container{ |
|||
width:100%; |
|||
margin:0 10upx; |
|||
} |
|||
.uni-navbar-container-title{ |
|||
font-size:30upx; |
|||
text-align:center; |
|||
padding-right: 60upx; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,134 @@ |
|||
<template> |
|||
<view class="segmented-control" :class="styleType" :style="wrapStyle"> |
|||
<view v-for="(item, index) in values" class="segmented-control-item" :class="styleType" :key="index" :style="index === currentIndex ? activeStyle : itemStyle" @click="onClick(index)"> |
|||
{{item}} |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'uni-segmented-control', |
|||
props: { |
|||
current: { |
|||
type: Number, |
|||
default: 0 |
|||
}, |
|||
values: { |
|||
type: Array, |
|||
default () { |
|||
return []; |
|||
} |
|||
}, |
|||
activeColor: { |
|||
type: String, |
|||
default: '#007aff' |
|||
}, |
|||
styleType: { |
|||
type: String, |
|||
default: 'button' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
currentIndex: this.current |
|||
} |
|||
}, |
|||
watch: { |
|||
current(val) { |
|||
if (val !== this.currentIndex) { |
|||
this.currentIndex = val; |
|||
} |
|||
} |
|||
}, |
|||
computed: { |
|||
wrapStyle() { |
|||
let styleString = ''; |
|||
switch (this.styleType) { |
|||
case 'text': |
|||
styleString = `border:0;`; |
|||
break; |
|||
default: |
|||
styleString = `border-color: ${this.activeColor}`; |
|||
break; |
|||
} |
|||
return styleString; |
|||
}, |
|||
itemStyle() { |
|||
let styleString = ''; |
|||
switch (this.styleType) { |
|||
case 'text': |
|||
styleString = `color:#000;border-left:0;`; |
|||
break; |
|||
default: |
|||
styleString = `color:${this.activeColor};border-color:${this.activeColor};`; |
|||
break; |
|||
} |
|||
return styleString; |
|||
}, |
|||
activeStyle() { |
|||
let styleString = ''; |
|||
switch (this.styleType) { |
|||
case 'text': |
|||
styleString = `color:${this.activeColor};border-left:0;border-bottom-style:solid;`; |
|||
break; |
|||
default: |
|||
styleString = `color:#fff;border-color:${this.activeColor};background-color:${this.activeColor}`; |
|||
break; |
|||
} |
|||
return styleString; |
|||
} |
|||
}, |
|||
methods: { |
|||
onClick(index) { |
|||
if (this.currentIndex !== index) { |
|||
this.currentIndex = index; |
|||
this.$emit('clickItem', index); |
|||
} |
|||
} |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.segmented-control { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
width: 75%; |
|||
font-size: 28upx; |
|||
border-radius: 10upx; |
|||
box-sizing: border-box; |
|||
margin: 0 auto; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.segmented-control.button { |
|||
border: 2upx solid; |
|||
} |
|||
|
|||
.segmented-control.text { |
|||
border: 0; |
|||
border-radius: 0upx; |
|||
} |
|||
|
|||
|
|||
.segmented-control-item { |
|||
flex: 1; |
|||
text-align: center; |
|||
line-height: 60upx; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.segmented-control-item.button { |
|||
border-left: 1upx solid; |
|||
} |
|||
|
|||
.segmented-control-item.text { |
|||
border-left: 0; |
|||
} |
|||
|
|||
.segmented-control-item:first-child { |
|||
border-left-width: 0; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,30 @@ |
|||
<template> |
|||
<view class="uni-status-bar" :style="style"> |
|||
<slot></slot> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
computed: { |
|||
style() { |
|||
//#ifdef MP-WEIXIN |
|||
var systemInfo = uni.getSystemInfoSync() |
|||
return `height:${systemInfo.statusBarHeight}px` |
|||
//#endif |
|||
//#ifdef APP-PLUS |
|||
return '' |
|||
//#endif |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
.uni-status-bar { |
|||
display: block; |
|||
width: 100%; |
|||
height: 20px; |
|||
height: var(--status-bar-height); |
|||
} |
|||
</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,57 @@ |
|||
import Request from './request' |
|||
const test = new Request(); |
|||
test.setConfig((config) => { /* 设置全局配置 */ |
|||
config.baseUrl = 'http://www.aaa.cn'; |
|||
config.header = { |
|||
a: 1, |
|||
b: 2 |
|||
} |
|||
return config |
|||
}) |
|||
test.interceptor.request((config, cancel) => { /* 请求之前拦截器 */ |
|||
config.header = { |
|||
...config.header, |
|||
a: 1 |
|||
} |
|||
/* |
|||
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
|
|||
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
|
|||
} |
|||
*/ |
|||
return config; |
|||
}) |
|||
test.interceptor.response((response) => { /* 请求之后拦截器 */ |
|||
return response; |
|||
}) |
|||
|
|||
const http = new Request(); |
|||
http.setConfig((config) => { /* 设置全局配置 */ |
|||
config.baseUrl = 'http://www.bbb.cn'; /* 根域名不同 */ |
|||
config.header = { |
|||
a: 1, |
|||
b: 2 |
|||
} |
|||
return config |
|||
}) |
|||
http.interceptor.request((config, cancel) => { /* 请求之前拦截器 */ |
|||
config.header = { |
|||
...config.header, |
|||
b: 1 |
|||
} |
|||
/* |
|||
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
|
|||
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
|
|||
} |
|||
*/ |
|||
return config; |
|||
}) |
|||
http.interceptor.response((response) => { /* 请求之后拦截器 */ |
|||
console.log(response); |
|||
return response; |
|||
}) |
|||
export { |
|||
http, |
|||
test |
|||
}; |
|||
|
|||
|
|||
@ -0,0 +1,148 @@ |
|||
**插件使用说明** |
|||
|
|||
- 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截 |
|||
- 支持全局挂载 |
|||
- 支持多个全局配置实例 |
|||
- 支持typescript、javascript 版本(如果不使用ts版本,则可以把luch-request-ts 文件夹删除) |
|||
- 下载后把 http-request 文件夹放到项目 utils/ 目录下 |
|||
|
|||
|
|||
**Example** |
|||
--- |
|||
创建实例 |
|||
|
|||
``` javascript |
|||
const http = new Request(); |
|||
``` |
|||
|
|||
执行` GET `请求 |
|||
|
|||
``` javascript |
|||
http.get('/user/login', {userName: 'name', password: '123456'}).then(res => { |
|||
|
|||
}).catch(err => { |
|||
|
|||
}) |
|||
// 局部修改配置,局部配置优先级高于全局配置 |
|||
http.get('/user/login', {userName: 'name', password: '123456'}, { |
|||
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'}, { |
|||
header: {}, /* 会覆盖全局header */ |
|||
dataType: 'json', |
|||
responseType: 'text' |
|||
}).then(res => { |
|||
|
|||
}).catch(err => { |
|||
|
|||
}) |
|||
``` |
|||
|
|||
**全局请求配置** |
|||
-- |
|||
``` 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 |
|||
}) |
|||
``` |
|||
|
|||
**拦截器** |
|||
-- |
|||
|
|||
在请求之前拦截 |
|||
|
|||
``` 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) => { /* 必须return response*/ |
|||
console.log(response); |
|||
return response; |
|||
}) |
|||
``` |
|||
|
|||
**typescript使用** |
|||
-- |
|||
在` request.ts `里还暴露了五个接口 |
|||
```javascript |
|||
{ |
|||
options, // request 方法配置参数 |
|||
handleOptions, // get/post 方法配置参数 |
|||
config, // init 全局config接口(setConfig 参数接口) |
|||
requestConfig, // 请求之前参数配置项 |
|||
response // 响应体 |
|||
} |
|||
``` |
|||
|
|||
|
|||
**issue** |
|||
-- |
|||
有任何问题或者建议可以=> <a href="https://ask.dcloud.net.cn/question/74922" target="_blank">issue提交</a>,先给个五星好评QAQ!! |
|||
|
|||
|
|||
**作者想说** |
|||
-- |
|||
- 主体代码3kb |
|||
- 目前该插件已经上项目,遇到任何问题请先检查自己的代码(排除新版本发布的情况)。最近新上了` typescript ` 版本,因为本人没使用过ts,所以写的不好的地方,还请见谅~ |
|||
####创作不易,五星好评你懂得! |
|||
@ -0,0 +1,111 @@ |
|||
/** |
|||
* Request 0.0.8 |
|||
* @Class uni-app request网络请求库 |
|||
* @Author lu-ch |
|||
* @Date 2019-07-25 |
|||
* @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) |
|||
} |
|||
|
|||
interceptor = { |
|||
request: (f) => { |
|||
if (f) { |
|||
this.requestBeforeFun = f |
|||
} |
|||
}, |
|||
response: (f) => { |
|||
if (f) { |
|||
this.requestComFun = f |
|||
} |
|||
} |
|||
} |
|||
|
|||
static requestBeforeFun (config) { |
|||
return config |
|||
} |
|||
|
|||
static requestComFun (response) { |
|||
return response |
|||
} |
|||
|
|||
setConfig (f) { |
|||
this.config = f(this.config) |
|||
} |
|||
|
|||
request (options = {}) { |
|||
options.baseUrl = this.config.baseUrl |
|||
options.dataType = options.dataType || this.config.dataType |
|||
options.responseType = options.responseType || this.config.responseType |
|||
options.url = Request.posUrl(options.url) ? options.url : (options.baseUrl + options.url) |
|||
options.data = options.data || {} |
|||
options.header = options.header || this.config.header |
|||
options.method = options.method || this.config.method |
|||
return new Promise((resolve, reject) => { |
|||
let next = true |
|||
let _config = null |
|||
options.complete = (response) => { |
|||
let statusCode = response.statusCode |
|||
response.config = _config |
|||
response = this.requestComFun(response) |
|||
if (statusCode === 200) { // 成功
|
|||
resolve(response) |
|||
} else { |
|||
reject(response) |
|||
} |
|||
} |
|||
let cancel = (t = 'handle cancel', config = options) => { |
|||
let err = { |
|||
errMsg: t, |
|||
config: config |
|||
} |
|||
reject(err) |
|||
next = false |
|||
} |
|||
|
|||
_config = { ...this.requestBeforeFun(options, cancel) } |
|||
if (!next) return |
|||
uni.request(_config) |
|||
}) |
|||
} |
|||
|
|||
get (url, data, options = {}) { |
|||
return this.request({ |
|||
url, |
|||
data, |
|||
method: 'GET', |
|||
...options |
|||
}) |
|||
} |
|||
|
|||
post (url, data, options = {}) { |
|||
return this.request({ |
|||
url, |
|||
data, |
|||
method: 'POST', |
|||
...options |
|||
}) |
|||
} |
|||
|
|||
del (url, data, options = {}) { |
|||
return this.request({ |
|||
url, |
|||
data, |
|||
method: 'DELETE', |
|||
...options |
|||
}) |
|||
} |
|||
} |
|||
@ -0,0 +1,165 @@ |
|||
<template> |
|||
<canvas v-if="canvasId" :id="canvasId" :canvasId="canvasId" :style="{'width':cWidth*pixelRatio+'px','height':cHeight*pixelRatio+'px', 'transform': 'scale('+(1/pixelRatio)+')','margin-left':-cWidth*(pixelRatio-1)/2+'px','margin-top':-cHeight*(pixelRatio-1)/2+'px'}" |
|||
@touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @error="error"> |
|||
</canvas> |
|||
</template> |
|||
|
|||
<script> |
|||
import uCharts from './u-charts.js'; |
|||
var canvases = {}; |
|||
|
|||
export default { |
|||
props: { |
|||
chartType: { |
|||
required: true, |
|||
type: String, |
|||
default: 'column' |
|||
}, |
|||
opts: { |
|||
required: true, |
|||
type: Object, |
|||
default () { |
|||
return null; |
|||
}, |
|||
}, |
|||
canvasId: { |
|||
type: String, |
|||
default: 'u-canvas', |
|||
}, |
|||
cWidth: { |
|||
default: 375, |
|||
}, |
|||
cHeight: { |
|||
default: 250, |
|||
}, |
|||
pixelRatio: { |
|||
type: Number, |
|||
default: 1, |
|||
}, |
|||
}, |
|||
mounted() { |
|||
this.init(); |
|||
}, |
|||
methods: { |
|||
init() { |
|||
switch (this.chartType) { |
|||
case 'column': |
|||
this.initColumnChart(); |
|||
break; |
|||
case 'line': |
|||
this.initLineChart(); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
}, |
|||
initColumnChart() { |
|||
canvases[this.canvasId] = new uCharts({ |
|||
$this: this, |
|||
canvasId: this.canvasId, |
|||
type: 'column', |
|||
legend: true, |
|||
fontSize: 11, |
|||
background: '#FFFFFF', |
|||
pixelRatio: this.pixelRatio, |
|||
animation: true, |
|||
categories: this.opts.categories, |
|||
series: this.opts.series, |
|||
enableScroll: true, |
|||
xAxis: { |
|||
disableGrid: true, |
|||
itemCount: 4, |
|||
scrollShow: true |
|||
}, |
|||
yAxis: { |
|||
//disabled:true |
|||
}, |
|||
dataLabel: true, |
|||
width: this.cWidth * this.pixelRatio, |
|||
height: this.cHeight * this.pixelRatio, |
|||
extra: { |
|||
column: { |
|||
type: 'group', |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
initLineChart() { |
|||
canvases[this.canvasId] = new uCharts({ |
|||
$this: this, |
|||
canvasId: this.canvasId, |
|||
type: 'line', |
|||
fontSize: 11, |
|||
legend: true, |
|||
dataLabel: false, |
|||
dataPointShape: true, |
|||
background: '#FFFFFF', |
|||
pixelRatio: this.pixelRatio, |
|||
categories: this.opts.categories, |
|||
series: this.opts.series, |
|||
animation: true, |
|||
enableScroll: true, |
|||
xAxis: { |
|||
type: 'grid', |
|||
gridColor: '#CCCCCC', |
|||
gridType: 'dash', |
|||
dashLength: 8, |
|||
itemCount: 4, |
|||
scrollShow: true |
|||
}, |
|||
yAxis: { |
|||
gridType: 'dash', |
|||
gridColor: '#CCCCCC', |
|||
dashLength: 8, |
|||
splitNumber: 5, |
|||
min: 10, |
|||
max: 180, |
|||
format: (val) => { |
|||
return val.toFixed(0) + '元' |
|||
} |
|||
}, |
|||
width: this.cWidth * this.pixelRatio, |
|||
height: this.cHeight * this.pixelRatio, |
|||
extra: { |
|||
line: { |
|||
type: 'straight' |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
// 这里仅作为示例传入两个参数,cid为canvas-id,newdata为更新的数据,需要更多参数请自行修改 |
|||
changeData(cid,newdata) { |
|||
canvases[cid].updateData({ |
|||
series: newdata.series, |
|||
categories: newdata.categories |
|||
}); |
|||
}, |
|||
touchStart(e) { |
|||
canvases[this.canvasId].showToolTip(e, { |
|||
format: function(item, category) { |
|||
return category + ' ' + item.name + ':' + item.data |
|||
} |
|||
}); |
|||
canvases[this.canvasId].scrollStart(e); |
|||
}, |
|||
touchMove(e) { |
|||
canvases[this.canvasId].scroll(e); |
|||
}, |
|||
touchEnd(e) { |
|||
canvases[this.canvasId].scrollEnd(e); |
|||
}, |
|||
error(e) { |
|||
console.log(e) |
|||
} |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.charts { |
|||
width: 100%; |
|||
height: 100%; |
|||
flex: 1; |
|||
background-color: #FFFFFF; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,14 @@ |
|||
import Vue from 'vue' |
|||
import App from './App' |
|||
import store from './store' |
|||
|
|||
Vue.config.productionTip = false |
|||
|
|||
Vue.prototype.$store = store |
|||
App.mpType = 'app' |
|||
|
|||
const app = new Vue({ |
|||
store, |
|||
...App |
|||
}) |
|||
app.$mount() |
|||
@ -0,0 +1,76 @@ |
|||
{ |
|||
"name" : "hello-uni-app", |
|||
"appid" : "__UNI__1E9AE51", |
|||
"description" : "", |
|||
"versionName" : "1.0.0", |
|||
"versionCode" : "100", |
|||
"transformPx" : false, |
|||
/* 5+App特有相关 */ |
|||
"app-plus" : { |
|||
"usingComponents" : true, |
|||
"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" : "wx7ee0db110b58b021", |
|||
"setting" : { |
|||
"urlCheck" : false, |
|||
"minified" : true, |
|||
"postcss" : true, |
|||
"es6" : true |
|||
}, |
|||
"usingComponents" : true |
|||
}, |
|||
"mp-alipay" : { |
|||
"usingComponents" : true |
|||
}, |
|||
"mp-baidu" : { |
|||
"usingComponents" : true |
|||
}, |
|||
"mp-toutiao" : { |
|||
"usingComponents" : true |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
{ |
|||
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages |
|||
{ |
|||
"path": "pages/login/login", |
|||
"style": { |
|||
"backgroundColor": "#f5f6f8", |
|||
"navigationBarTitleText": "登录", |
|||
"app-plus": {} |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/home/home", |
|||
"style": { |
|||
"navigationBarTitleText": "首页" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/device/device", |
|||
"style": { |
|||
"navigationBarTitleText": "设备" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/location/location", |
|||
"style": { |
|||
"navigationBarTitleText": "定位" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/user/user", |
|||
"style": { |
|||
"navigationBarTitleText": "我的" |
|||
} |
|||
}, |
|||
{ |
|||
"path": "pages/device/detail/index", |
|||
"style": { |
|||
"navigationBarTitleText": " 设备详情" |
|||
} |
|||
} |
|||
], |
|||
"globalStyle": { |
|||
"navigationBarTextStyle": "black", |
|||
"navigationBarTitleText": "芯域物联", |
|||
"navigationBarBackgroundColor": "#F8F8F8", |
|||
"backgroundColor": "#F8F8F8" |
|||
}, |
|||
"tabBar": { |
|||
"backgroundColor": "#ffffff", |
|||
"color": "#cdcdcd", |
|||
"selectedColor": "#50B7EA", |
|||
"borderStyle": "white", |
|||
"list": [{ |
|||
"pagePath": "pages/home/home", |
|||
"iconPath": "static/home.png", |
|||
"selectedIconPath": "static/home_active.png", |
|||
"text": "首页" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/device/device", |
|||
"iconPath": "static/device.png", |
|||
"selectedIconPath": "static/device_active.png", |
|||
"text": "设备" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/location/location", |
|||
"iconPath": "static/location.png", |
|||
"selectedIconPath": "static/location_active.png", |
|||
"text": "定位" |
|||
}, |
|||
{ |
|||
"pagePath": "pages/user/user", |
|||
"iconPath": "static/user.png", |
|||
"selectedIconPath": "static/user_active.png", |
|||
"text": "我的" |
|||
} |
|||
] |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
<template> |
|||
<view> |
|||
<image style="width: 100%;" :src="info.isGateway | bannerFilter" mode="aspectFit"></image> |
|||
<view class="qiun-title-dot-light">设备信息</view> |
|||
<uni-list> |
|||
<uni-list-item :show-arrow="false" title="设备ID" :note="info.deviceName"></uni-list-item> |
|||
<uni-list-item :show-arrow="false" title="设备名称" :note="info.deviceNickName"></uni-list-item> |
|||
<uni-list-item :show-arrow="false" title="当前状态" :note="info.deviceStatus | statusFilter "></uni-list-item> |
|||
<uni-list-item :show-arrow="false" title="创建时间" :note="info.deviceAddTime"></uni-list-item> |
|||
<uni-list-item :show-arrow="false" title="激活时间" :note="info.deviceActiveTime"></uni-list-item> |
|||
<uni-list-item :show-arrow="false" title="最后上线时间" :note="info.deviceLastTime"></uni-list-item> |
|||
</uni-list> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import uniList from '@/components/uni-list/uni-list.vue'; |
|||
import uniListItem from '@/components/uni-list-item/uni-list-item.vue'; |
|||
export default { |
|||
components: { |
|||
uniList, |
|||
uniListItem |
|||
}, |
|||
filters: { |
|||
bannerFilter(type){ |
|||
const typeMap = { |
|||
'1': '../../../static/banner1.png', |
|||
'0': '../../../static/banner2.png' |
|||
}; |
|||
return typeMap[type]; |
|||
}, |
|||
statusFilter(type) { |
|||
const typeMap = { |
|||
'-1': '未激活', |
|||
'0': '离线', |
|||
'1': '上线' |
|||
}; |
|||
return typeMap[type]; |
|||
} |
|||
}, |
|||
props: { |
|||
info: { |
|||
type: Object, |
|||
default: null |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style></style> |
|||
@ -0,0 +1,78 @@ |
|||
<template> |
|||
<view> |
|||
<view class="uni-padding-wrap uni-common-mt"> |
|||
<uni-segmented-control :current="current" :values="items" @clickItem="onClickItem" /> |
|||
</view> |
|||
<view class="content"> |
|||
<view v-show="current === 0"> |
|||
<device-info :info="info"></device-info> |
|||
</view> |
|||
<view v-show="current === 1"> |
|||
<real-data :msg="msg"></real-data> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import uniSegmentedControl from '@/components/uni-segmented-control/uni-segmented-control.vue' |
|||
import deviceInfo from './device-info.vue' |
|||
import realData from './real-data.vue' |
|||
import { getDeviceInfo,getDeviceLatestUpMessage } from '@/api/device.js' |
|||
import { getMQTTClient } from '@/common/mqttClient.js' |
|||
export default { |
|||
components: { |
|||
uniSegmentedControl, |
|||
deviceInfo, |
|||
realData |
|||
}, |
|||
data() { |
|||
return { |
|||
info:{}, |
|||
msg:{}, |
|||
productId: '', |
|||
deviceName: '', |
|||
items: [ |
|||
'设备信息', |
|||
'实时数据' |
|||
], |
|||
current: 0, |
|||
client: null |
|||
} |
|||
}, |
|||
onLoad(e){ |
|||
if(e){ |
|||
this.productId = e.productId; |
|||
this.deviceName = e.deviceName; |
|||
} |
|||
this.fetchDeviceInfo() |
|||
this.fetchDeviceData() |
|||
}, |
|||
methods: { |
|||
onClickItem(index) { |
|||
if (this.current !== index) { |
|||
this.current = index |
|||
} |
|||
}, |
|||
fetchDeviceInfo(){ |
|||
getDeviceInfo(this.productId,this.deviceName).then(response => { |
|||
this.info = response.data; |
|||
this.client = getMQTTClient(this.info.productId,this.info.deviceName,this.info.deviceSecret) |
|||
console.log(this.client) |
|||
}) |
|||
}, |
|||
fetchDeviceData(){ |
|||
getDeviceLatestUpMessage(this.productId,this.deviceName).then(response => { |
|||
if(response.data){ |
|||
this.msg = response; |
|||
} |
|||
console.log(this.msg) |
|||
}) |
|||
} |
|||
}, |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
@ -0,0 +1,35 @@ |
|||
<template> |
|||
<view> |
|||
<uni-list v-for="(item,index) in realData" :key="index"> |
|||
<uni-list-item :show-arrow="false" :title="item.name" :note="item.data"></uni-list-item> |
|||
</uni-list> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import uniList from '@/components/uni-list/uni-list.vue' |
|||
import uniListItem from '@/components/uni-list-item/uni-list-item.vue' |
|||
export default { |
|||
components: { |
|||
uniList, |
|||
uniListItem |
|||
}, |
|||
props:{ |
|||
msg:{ |
|||
type: Object, |
|||
default: null |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
realData: [{name:'温度',data:'21.1'},{name:'湿度',data:'11.1'},{name:'温度',data:'21.1'}] |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
</style> |
|||
@ -0,0 +1,180 @@ |
|||
<template> |
|||
<view @touchstart="refreshStart" @touchmove="refreshMove" @touchend="refreshEnd"> |
|||
<refresh ref="refresh" @isRefresh='isRefresh'></refresh> |
|||
|
|||
<mSearch :mode="2" button="inside" @search="search($event)" backgroundColor="#0081ff"></mSearch> |
|||
<nav-tab style="margin-top: -10upx;" textFlex :tab-list="tabList" :tabCur="tabCur" @change="tabChange" tab-class="text-center text-white bg-blue " select-class="text-white"></nav-tab> |
|||
|
|||
<view class="cu-list menu-avatar margin-top"> |
|||
<view class="cu-item arrow"> |
|||
<view class="cu-avatar round lg" style="background-image:url(/static/humiture.png);"></view> |
|||
<view class="content padding-tb-sm"> |
|||
<view> |
|||
<text class="text-xl margin-left-xs">设备1</text> |
|||
</view> |
|||
<view class="flex"> |
|||
<text class="flex-sub iconfont icon-wendu text-blue text-xs margin-left-xs">10℃</text> |
|||
<text class="flex-sub iconfont icon-shidu text-blue text-xs ">12%</text> |
|||
</view> |
|||
</view> |
|||
<view class="action"> |
|||
<view class="text-grey text-xs">2019-08-01 22:20:09</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="content"> |
|||
<uni-list v-for="(item,index) in deviceList" :key="index"> |
|||
<uni-list-item |
|||
:title="item.deviceNickName" |
|||
:note="item.deviceName" |
|||
show-extra-icon |
|||
:extra-icon="getExtraIcon(item.deviceStatus)" |
|||
@click="handleClickItem(item)" |
|||
></uni-list-item> |
|||
</uni-list> |
|||
</view> |
|||
|
|||
<uni-fab :pattern="pattern" :content="content" horizontal="right" vertical="bottom" direction="vertical" @trigger="trigger"></uni-fab> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import refresh from '@/components/refresh/refresh.vue'; |
|||
import uniNavBar from '@/components/uni-nav-bar/uni-nav-bar.vue'; |
|||
import mSearch from '@/components/mehaotian-search/mehaotian-search.vue'; |
|||
import navTab from '@/components/wuc-tab/wuc-tab.vue'; |
|||
import uniList from '@/components/uni-list/uni-list.vue'; |
|||
import uniListItem from '@/components/uni-list-item/uni-list-item.vue'; |
|||
import uniFab from '@/components/uni-fab/uni-fab.vue'; |
|||
import { getDeviceList } from '@/api/device.js'; |
|||
export default { |
|||
components: { |
|||
refresh, |
|||
uniNavBar, |
|||
mSearch, |
|||
navTab, |
|||
uniList, |
|||
uniListItem, |
|||
uniFab |
|||
}, |
|||
data() { |
|||
return { |
|||
tabCur: 0, |
|||
tabList: [{ name: '网关' }, { name: '终端' }], |
|||
deviceList: [], |
|||
pattern: { |
|||
color: '#7A7E83', |
|||
backgroundColor: '#fff', |
|||
selectedColor: '#007AFF', |
|||
buttonColor:"#007AFF" |
|||
}, |
|||
content: [ |
|||
{ |
|||
iconPath: '/static/gateway.png', |
|||
selectedIconPath: '/static/gateway.png', |
|||
text: '添加网关', |
|||
active: false |
|||
}, |
|||
{ |
|||
iconPath: '/static/dtu.png', |
|||
selectedIconPath: '/static/dtu.png', |
|||
text: '添加终端', |
|||
active: false |
|||
} |
|||
] |
|||
}; |
|||
}, |
|||
onLoad(e){ |
|||
if(e.data !== undefined){ |
|||
this.tabChange(e.data); |
|||
}else{ |
|||
this.fetchDeviceList(); |
|||
} |
|||
|
|||
}, |
|||
methods: { |
|||
getExtraIcon(active){ |
|||
let color = '#e6e6e6'; |
|||
if(active === 1){ |
|||
color = '#4cd964'; |
|||
} |
|||
return { color: color, size: '22', type: 'circle-filled'} |
|||
}, |
|||
fetchDeviceList(){ |
|||
getDeviceList().then(response => { |
|||
this.deviceList = [] |
|||
if(response.data.list && response.data.list.length > 0){ |
|||
response.data.list.forEach(item => { |
|||
if(this.tabCur === 0 && item.isGateway === 1){ |
|||
this.deviceList.push(item) |
|||
}else if(this.tabCur === 1 && item.isGateway === 0){ |
|||
|
|||
this.deviceList.push(item) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
}, |
|||
refreshStart(e) { |
|||
this.$refs.refresh.refreshStart(e); |
|||
}, |
|||
refreshMove(e){ |
|||
this.$refs.refresh.refreshMove(e); |
|||
}, |
|||
refreshEnd(e) { |
|||
this.$refs.refresh.refreshEnd(e); |
|||
}, |
|||
isRefresh(){ |
|||
setTimeout(() => { |
|||
this.fetchDeviceList() |
|||
uni.showToast({ |
|||
icon: 'success', |
|||
title: '刷新成功' |
|||
}) |
|||
this.$refs.refresh.endAfter() //刷新结束调用 |
|||
}, 1000) |
|||
}, |
|||
search(val) { |
|||
let tempList = []; |
|||
this.deviceList.forEach((item) => { |
|||
if(item.id.indexOf(val) !== -1){ |
|||
tempList.push(item); |
|||
} |
|||
}) |
|||
this.deviceList = tempList; |
|||
}, |
|||
tabChange(index) { |
|||
this.tabCur = index; |
|||
this.fetchDeviceList() |
|||
}, |
|||
handleClickItem(item){ |
|||
uni.navigateTo({ |
|||
url: `detail/index?productId=${item.productId}&deviceName=${item.deviceName}` |
|||
}) |
|||
}, |
|||
trigger(e) { |
|||
console.log(e); |
|||
this.content[e.index].active = !e.item.active; |
|||
uni.showModal({ |
|||
title: '提示', |
|||
content: `您${this.content[e.index].active?'选中了':'取消了'}${e.item.text}`, |
|||
success: function(res) { |
|||
if (res.confirm) { |
|||
console.log('用户点击确定'); |
|||
} else if (res.cancel) { |
|||
console.log('用户点击取消'); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style> |
|||
.content { |
|||
margin-top: 10upx; |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,229 @@ |
|||
<template> |
|||
<view class="container"> |
|||
<view class="carousel-section"> |
|||
<swiper class="carousel" indicator-dots circular autoplay interval="3000"> |
|||
<swiper-item v-for="(item, index) in carouselList" :key="index" class="carousel-item"><image :src="item.src" /></swiper-item> |
|||
</swiper> |
|||
</view> |
|||
<view class="cate-section"> |
|||
<view class="cate-item" hover-class="hover" @tap="tapGateway"> |
|||
<image src="/static/gateway.png"></image> |
|||
<text>网关</text> |
|||
</view> |
|||
<view class="cate-item" hover-class="hover" @tap="tapDtu"> |
|||
<image src="/static/dtu.png"></image> |
|||
<text>终端</text> |
|||
</view> |
|||
</view> |
|||
<view class="qiun-columns"> |
|||
<view class="qiun-bg-white qiun-title-bar qiun-common-mt"><view class="qiun-title-dot-light">设备概览</view></view> |
|||
<view class="qiun-charts"><canvas canvas-id="canvasRing" id="canvasRing" class="charts" @touchstart="touchRing"></canvas></view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { statisticsDevice } from '@/api/device.js'; |
|||
import uCharts from '@/js_sdk/u-charts/u-charts/u-charts.js'; |
|||
var canvaRing = null; |
|||
var _self; |
|||
export default { |
|||
data() { |
|||
return { |
|||
cWidth: '', |
|||
cHeight: '', |
|||
pixelRatio: 1, |
|||
carouselList: [ |
|||
{ |
|||
src: '/static/banner1.png' |
|||
}, |
|||
{ |
|||
src: '/static/banner2.png' |
|||
}, |
|||
{ |
|||
src: '/static/banner1.png' |
|||
} |
|||
], |
|||
chartData:{ |
|||
title: {name:'设备总量',value:0}, |
|||
series: [{ name: '在线', data: 0 }, { name: '离线', data: 0 }, { name: '未激活', data: 0 }] |
|||
} |
|||
}; |
|||
}, |
|||
onLoad() { |
|||
_self = this; |
|||
this.cWidth = uni.upx2px(750); |
|||
this.cHeight = uni.upx2px(500); |
|||
this.fetchStatisticsDevice() |
|||
this.showRing('canvasRing', this.chartData); |
|||
}, |
|||
methods: { |
|||
fetchStatisticsDevice() { |
|||
statisticsDevice().then(response => { |
|||
console.log(response.data); |
|||
this.chartData.series[0].data = response.data.online |
|||
this.chartData.series[1].data = response.data.offline |
|||
this.chartData.series[2].data = response.data.total - response.data.active |
|||
this.chartData.title.value = response.data.total |
|||
this.showRing('canvasRing', this.chartData); |
|||
}); |
|||
}, |
|||
tapGateway() { |
|||
uni.switchTab({ |
|||
url: '/pages/device/device', |
|||
success: function(e) { |
|||
var page = getCurrentPages().pop(); |
|||
if (page == undefined || page == null) return; |
|||
page.onLoad({ data: 0 }); |
|||
} |
|||
}); |
|||
}, |
|||
tapDtu() { |
|||
uni.switchTab({ |
|||
url: '/pages/device/device', |
|||
success: function(e) { |
|||
var page = getCurrentPages().pop(); |
|||
if (page == undefined || page == null) return; |
|||
page.onLoad({ data: 1 }); |
|||
} |
|||
}); |
|||
}, |
|||
showRing(canvasId, chartData) { |
|||
canvaRing = new uCharts({ |
|||
$this: _self, |
|||
canvasId: canvasId, |
|||
type: 'ring', |
|||
fontSize: 11, |
|||
padding: [5, 5, 5, 5], |
|||
legend: { |
|||
show: true, |
|||
position: 'right', |
|||
float: 'center', |
|||
itemGap: 10, |
|||
padding: 5, |
|||
lineHeight: 26, |
|||
margin: 5, |
|||
//backgroundColor:'rgba(41,198,90,0.2)', |
|||
//borderColor :'rgba(41,198,90,0.5)', |
|||
borderWidth: 1 |
|||
}, |
|||
background: '#FFFFFF', |
|||
pixelRatio: _self.pixelRatio, |
|||
series: chartData.series, |
|||
animation: false, |
|||
width: _self.cWidth * _self.pixelRatio, |
|||
height: _self.cHeight * _self.pixelRatio, |
|||
disablePieStroke: true, |
|||
dataLabel: false, |
|||
subtitle: { |
|||
name: chartData.title.value + '', |
|||
color: '#7cb5ec', |
|||
fontSize: 25 * _self.pixelRatio |
|||
}, |
|||
title: { |
|||
name: chartData.title.name, |
|||
color: '#666666', |
|||
fontSize: 15 * _self.pixelRatio |
|||
}, |
|||
extra: { |
|||
pie: { |
|||
offsetAngle: -105, |
|||
ringWidth: 40 * _self.pixelRatio, |
|||
labelWidth: 15 |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
touchRing(e) { |
|||
canvaRing.touchLegend(e, { |
|||
animation: false |
|||
}); |
|||
canvaRing.showToolTip(e, { |
|||
format: function(item) { |
|||
return item.name + ':' + item.data; |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss"> |
|||
page { |
|||
.cate-section { |
|||
position: relative; |
|||
z-index: 5; |
|||
margin-top: 10upx; |
|||
} |
|||
.carousel-section { |
|||
padding: 0; |
|||
.carousel { |
|||
.carousel-item { |
|||
padding: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.carousel-section { |
|||
position: relative; |
|||
padding-top: 10px; |
|||
} |
|||
.carousel { |
|||
width: 100%; |
|||
height: 350upx; |
|||
|
|||
.carousel-item { |
|||
width: 100%; |
|||
height: 100%; |
|||
padding: 0 28upx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
image { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 10upx; |
|||
} |
|||
} |
|||
.hover { |
|||
background-color: #eee; |
|||
} |
|||
.cate-section { |
|||
display: flex; |
|||
justify-content: space-around; |
|||
align-items: center; |
|||
flex-wrap: wrap; |
|||
padding: 30upx 22upx; |
|||
background: #fff; |
|||
.cate-item { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
font-size: $font-sm + 2upx; |
|||
color: $font-color-dark; |
|||
} |
|||
/* 原图标颜色太深,不想改图了,所以加了透明度 */ |
|||
image { |
|||
width: 88upx; |
|||
height: 88upx; |
|||
margin-bottom: 14upx; |
|||
border-radius: 50%; |
|||
opacity: 0.7; |
|||
box-shadow: 4upx 4upx 20upx rgba(250, 67, 106, 0.3); |
|||
} |
|||
} |
|||
</style> |
|||
<style> |
|||
/*样式的width和height一定要与定义的cWidth和cHeight相对应*/ |
|||
.qiun-charts { |
|||
width: 750upx; |
|||
height: 500upx; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.charts { |
|||
width: 750upx; |
|||
height: 500upx; |
|||
background-color: #ffffff; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,38 @@ |
|||
<template> |
|||
<view> |
|||
<view class="page-body"> |
|||
<view class="page-section page-section-gap"> |
|||
<map style="width: 100%; height: 400px;" :latitude="latitude" :longitude="longitude" :markers="covers"> |
|||
</map> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
title: 'map', |
|||
latitude: 39.909, |
|||
longitude: 116.39742, |
|||
covers: [{ |
|||
latitude: 41.909, |
|||
longitude: 117.39742, |
|||
iconPath: '/static/location_active.png' |
|||
}, { |
|||
latitude: 39.90, |
|||
longitude: 116.39, |
|||
iconPath: '/static/location_active.png' |
|||
}] |
|||
} |
|||
}, |
|||
methods: { |
|||
|
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
|
|||
</style> |
|||
@ -0,0 +1,177 @@ |
|||
<template> |
|||
<view class="page_login"> |
|||
<!-- 头部logo --> |
|||
<view class="head"> |
|||
<view class="head_bg"> |
|||
<view class="head_inner_bg"><image style="width: 55px;height: 65px;" :src="imgInfo.head" class="head_logo" /></view> |
|||
</view> |
|||
</view> |
|||
<!-- 登录form --> |
|||
<view class="login_form"> |
|||
<view class="input"> |
|||
<view class="img"><image style="width:27px;height: 27px;" :src="imgInfo.icon_user" /></view> |
|||
<input type="text" v-model="loginForm.username" placeholder="请输入用户账号" /> |
|||
<view class="img"><image @tap="delUser" class="img_del" :src="imgInfo.icon_del" /></view> |
|||
</view> |
|||
<view class="line" /> |
|||
<view class="input"> |
|||
<view class="img"><image style="width:20px;height: 25px;" :src="imgInfo.icon_pwd" /></view> |
|||
<input :type="pwdType" :value="loginForm.password" @input="inputPwd" placeholder="请输入密码" /> |
|||
<view class="img" @tap="switchPwd"><image class="img_pwd_switch" :src="imgInfo.icon_pwd_switch" /></view> |
|||
</view> |
|||
</view> |
|||
<!-- 登录提交 --> |
|||
<button class="submit" type="primary" @tap="login">登录</button> |
|||
</view> |
|||
</template> |
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
loginForm: { |
|||
username: 'admin', |
|||
password: 'admin' |
|||
}, |
|||
pwdType: 'password', |
|||
imgInfo: { |
|||
head: '/static/head.png', |
|||
icon_user: '/static/icon_user.png', |
|||
icon_del: '/static/icon_del.png', |
|||
icon_pwd: '/static/icon_pwd.png', |
|||
icon_pwd_switch: '/static/icon_pwd_switch.png' |
|||
} |
|||
}; |
|||
}, |
|||
methods: { |
|||
inputPwd(e) { |
|||
this.loginForm.password = e.target.value; |
|||
}, |
|||
delUser() { |
|||
this.loginForm.username = ''; |
|||
}, |
|||
switchPwd() { |
|||
this.pwdType = this.pwdType === 'text' ? 'password' : 'text'; |
|||
}, |
|||
login() { |
|||
this.$store.dispatch('Login', this.loginForm).then(() => { |
|||
uni.switchTab({url: '/pages/home/home'}) |
|||
}).catch(() => { |
|||
uni.showToast({title: '登录失败', duration:2000,icon:'none'}); |
|||
}) |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
<style> |
|||
page { |
|||
height: auto; |
|||
min-height: 100%; |
|||
background-color: #f5f6f8; |
|||
} |
|||
</style> |
|||
<style lang="scss" scoped> |
|||
$logo-padding: 60px; |
|||
$form-border-color: rgba(214, 214, 214, 1); |
|||
$text-color: #b6b6b6; |
|||
|
|||
.page_login { |
|||
padding: 10px; |
|||
} |
|||
|
|||
.head { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding-top: $logo-padding; |
|||
padding-bottom: $logo-padding; |
|||
|
|||
.head_bg { |
|||
border-radius: 50px; |
|||
width: 100px; |
|||
height: 100px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.head_inner_bg { |
|||
border-radius: 40px; |
|||
width: 80px; |
|||
height: 80px; |
|||
display: flex; |
|||
background-color: #0081ff; |
|||
align-items: flex-end; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.login_form { |
|||
display: flex; |
|||
margin: 20px; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
border: 1px solid $form-border-color; |
|||
border-radius: 10px; |
|||
|
|||
.line { |
|||
width: 100%; |
|||
height: 1px; |
|||
background-color: $form-border-color; |
|||
} |
|||
|
|||
.input { |
|||
width: 100%; |
|||
max-height: 45px; |
|||
display: flex; |
|||
padding: 3px; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.img { |
|||
min-width: 40px; |
|||
min-height: 40px; |
|||
margin: 5px; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.img_del { |
|||
width: 21px; |
|||
height: 21px; |
|||
} |
|||
|
|||
.img_pwd_switch { |
|||
width: 28px; |
|||
height: 12px; |
|||
} |
|||
|
|||
input { |
|||
outline: none; |
|||
height: 30px; |
|||
width: 100%; |
|||
|
|||
&:focus { |
|||
outline: none; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.submit { |
|||
margin-top: 30px; |
|||
margin-left: 20px; |
|||
margin-right: 20px; |
|||
color: white; |
|||
background-color: #0081ff; |
|||
-webkit-tap-highlight-color: #0081ff; |
|||
|
|||
&:active { |
|||
color: #b6b6b6; |
|||
background-color: #0081ff; |
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,208 @@ |
|||
<template> |
|||
<view class="center"> |
|||
<view class="logo" :hover-class="!hasLogin ? 'logo-hover' : ''"> |
|||
<image class="logo-img" :src="hasLogin ? userInfo.avatarUrl :avatarUrl"></image> |
|||
<view class="logo-title"> |
|||
<text class="uer-name">Hi,{{hasLogin ? userInfo.nickName : '您未登录'}}</text> |
|||
<text class="go-login navigat-arrow" v-if="!hasLogin"></text> |
|||
</view> |
|||
</view> |
|||
<view class="center-list"> |
|||
<view class="center-list-item border-bottom"> |
|||
<text class="list-icon"></text> |
|||
<text class="list-text">帐号管理</text> |
|||
<text class="navigat-arrow"></text> |
|||
</view> |
|||
<view class="center-list-item"> |
|||
<text class="list-icon"></text> |
|||
<text class="list-text">新消息通知</text> |
|||
<text class="navigat-arrow"></text> |
|||
</view> |
|||
</view> |
|||
<view class="center-list"> |
|||
<view class="center-list-item border-bottom"> |
|||
<text class="list-icon"></text> |
|||
<text class="list-text">帮助与反馈</text> |
|||
<text class="navigat-arrow"></text> |
|||
</view> |
|||
<view class="center-list-item"> |
|||
<text class="list-icon"></text> |
|||
<text class="list-text">服务条款及隐私</text> |
|||
<text class="navigat-arrow"></text> |
|||
</view> |
|||
</view> |
|||
<view class="center-list"> |
|||
<view class="center-list-item"> |
|||
<text class="list-icon"></text> |
|||
<text class="list-text">关于应用</text> |
|||
<text class="navigat-arrow"></text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import {mapGetters} from 'vuex' |
|||
export default { |
|||
data() { |
|||
return { |
|||
hasLogin: this.token ? true:false, |
|||
avatarUrl: "../../static/logo.png", |
|||
userInfo: {}, |
|||
} |
|||
}, |
|||
computed: { |
|||
...mapGetters([ |
|||
'token' |
|||
]) |
|||
}, |
|||
onLoad() { |
|||
this.getUserInfo() |
|||
}, |
|||
methods: { |
|||
getUserInfo() { |
|||
uni.getUserInfo({ |
|||
provider: 'weixin', |
|||
success: (result) => { |
|||
this.hasLogin = true; |
|||
this.userInfo = result.userInfo; |
|||
}, |
|||
fail: (error) => { |
|||
console.log('getUserInfo fail', error); |
|||
let content = error.errMsg; |
|||
if (~content.indexOf('uni.login')) { |
|||
content = '请在登录页面完成登录操作'; |
|||
} |
|||
uni.showModal({ |
|||
title: '获取用户信息失败', |
|||
content: '错误原因' + content, |
|||
showCancel: false |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style> |
|||
@font-face { |
|||
font-family: texticons; |
|||
font-weight: normal; |
|||
font-style: normal; |
|||
src: url('https://at.alicdn.com/t/font_984210_5cs13ndgqsn.ttf') format('truetype'); |
|||
} |
|||
|
|||
page, |
|||
view { |
|||
display: flex; |
|||
} |
|||
|
|||
page { |
|||
background-color: #f8f8f8; |
|||
} |
|||
|
|||
.center { |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.logo { |
|||
width: 750upx; |
|||
height: 240upx; |
|||
padding: 20upx; |
|||
box-sizing: border-box; |
|||
background-color: #4cd964; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
} |
|||
|
|||
.logo-hover { |
|||
opacity: 0.8; |
|||
} |
|||
|
|||
.logo-img { |
|||
width: 150upx; |
|||
height: 150upx; |
|||
border-radius: 150upx; |
|||
} |
|||
|
|||
.logo-title { |
|||
height: 150upx; |
|||
flex: 1; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
flex-direction: row; |
|||
margin-left: 20upx; |
|||
} |
|||
|
|||
.uer-name { |
|||
height: 60upx; |
|||
line-height: 60upx; |
|||
font-size: 38upx; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
.go-login.navigat-arrow { |
|||
font-size: 38upx; |
|||
color: #FFFFFF; |
|||
} |
|||
|
|||
.login-title { |
|||
height: 150upx; |
|||
align-items: self-start; |
|||
justify-content: center; |
|||
flex-direction: column; |
|||
margin-left: 20upx; |
|||
} |
|||
|
|||
.center-list { |
|||
background-color: #FFFFFF; |
|||
margin-top: 20upx; |
|||
width: 750upx; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.center-list-item { |
|||
height: 90upx; |
|||
width: 750upx; |
|||
box-sizing: border-box; |
|||
flex-direction: row; |
|||
padding: 0upx 20upx; |
|||
} |
|||
|
|||
.border-bottom { |
|||
border-bottom-width: 1upx; |
|||
border-color: #c8c7cc; |
|||
border-bottom-style: solid; |
|||
} |
|||
|
|||
.list-icon { |
|||
width: 40upx; |
|||
height: 90upx; |
|||
line-height: 90upx; |
|||
font-size: 34upx; |
|||
color: #4cd964; |
|||
text-align: center; |
|||
font-family: texticons; |
|||
margin-right: 20upx; |
|||
} |
|||
|
|||
.list-text { |
|||
height: 90upx; |
|||
line-height: 90upx; |
|||
font-size: 34upx; |
|||
color: #555; |
|||
flex: 1; |
|||
text-align: left; |
|||
} |
|||
|
|||
.navigat-arrow { |
|||
height: 90upx; |
|||
width: 40upx; |
|||
line-height: 90upx; |
|||
font-size: 34upx; |
|||
color: #555; |
|||
text-align: right; |
|||
font-family: texticons; |
|||
} |
|||
</style> |
|||
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 975 B |
|
After Width: | Height: | Size: 635 B |
|
After Width: | Height: | Size: 842 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,7 @@ |
|||
const getters = { |
|||
token: state => state.user.token, |
|||
avatar: state => state.user.avatar, |
|||
name: state => state.user.name, |
|||
roles: state => state.user.roles |
|||
} |
|||
export default getters |
|||
@ -0,0 +1,15 @@ |
|||
import Vue from 'vue' |
|||
import Vuex from 'vuex' |
|||
import user from './user' |
|||
import getters from './getters' |
|||
|
|||
Vue.use(Vuex) |
|||
|
|||
const store = new Vuex.Store({ |
|||
modules: { |
|||
user |
|||
}, |
|||
getters |
|||
}) |
|||
|
|||
export default store |
|||
@ -0,0 +1,87 @@ |
|||
import { login, logout, getInfo } from '@/api/login' |
|||
import { getToken, setToken, removeToken } from '@/common/auth' |
|||
|
|||
const user = { |
|||
state: { |
|||
token: getToken(), |
|||
name: '', |
|||
avatar: '', |
|||
roles: [] |
|||
}, |
|||
|
|||
mutations: { |
|||
SET_TOKEN: (state, token) => { |
|||
state.token = token |
|||
}, |
|||
SET_NAME: (state, name) => { |
|||
state.name = name |
|||
}, |
|||
SET_AVATAR: (state, avatar) => { |
|||
state.avatar = avatar |
|||
}, |
|||
SET_ROLES: (state, roles) => { |
|||
state.roles = roles |
|||
} |
|||
}, |
|||
|
|||
actions: { |
|||
// 登录
|
|||
Login({ commit }, userInfo) { |
|||
const username = userInfo.username.trim() |
|||
return new Promise((resolve, reject) => { |
|||
login(username, userInfo.password).then(response => { |
|||
const data = response.data |
|||
setToken(data.token) |
|||
commit('SET_TOKEN', data.token) |
|||
resolve() |
|||
}).catch(error => { |
|||
reject(error) |
|||
}) |
|||
}) |
|||
}, |
|||
|
|||
// 获取用户信息
|
|||
GetInfo({ commit, state }) { |
|||
return new Promise((resolve, reject) => { |
|||
getInfo(state.token).then(response => { |
|||
const data = response.data |
|||
// if (data.roles && data.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
|||
// commit('SET_ROLES', data.roles)
|
|||
// } else {
|
|||
// reject('getInfo: roles must be a non-null array !')
|
|||
// }
|
|||
commit('SET_NAME', data.name) |
|||
commit('SET_AVATAR', data.avatar) |
|||
resolve(response) |
|||
}).catch(error => { |
|||
reject(error) |
|||
}) |
|||
}) |
|||
}, |
|||
|
|||
// 登出
|
|||
LogOut({ commit, state }) { |
|||
return new Promise((resolve, reject) => { |
|||
logout(state.token).then(() => { |
|||
commit('SET_TOKEN', '') |
|||
commit('SET_ROLES', []) |
|||
removeToken() |
|||
resolve() |
|||
}).catch(error => { |
|||
reject(error) |
|||
}) |
|||
}) |
|||
}, |
|||
|
|||
// 前端 登出
|
|||
FedLogOut({ commit }) { |
|||
return new Promise(resolve => { |
|||
commit('SET_TOKEN', '') |
|||
removeToken() |
|||
resolve() |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
|
|||
export default user |
|||
@ -0,0 +1,106 @@ |
|||
/** |
|||
* 这里是uni-app内置的常用样式变量 |
|||
* |
|||
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 |
|||
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App |
|||
* |
|||
*/ |
|||
|
|||
/** |
|||
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 |
|||
* |
|||
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 |
|||
*/ |
|||
|
|||
/* 颜色变量 */ |
|||
|
|||
/* 行为相关颜色 */ |
|||
$uni-color-primary: #007aff; |
|||
$uni-color-success: #4cd964; |
|||
$uni-color-warning: #f0ad4e; |
|||
$uni-color-error: #dd524d; |
|||
|
|||
/* 文字基本颜色 */ |
|||
$uni-text-color:#333;//基本色 |
|||
$uni-text-color-inverse:#fff;//反色 |
|||
$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 |
|||
$uni-text-color-placeholder: #808080; |
|||
$uni-text-color-disable:#c0c0c0; |
|||
|
|||
/* 背景颜色 */ |
|||
$uni-bg-color:#ffffff; |
|||
$uni-bg-color-grey:#f8f8f8; |
|||
$uni-bg-color-hover:#f1f1f1;//点击状态颜色 |
|||
$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 |
|||
|
|||
/* 边框颜色 */ |
|||
$uni-border-color:#c8c7cc; |
|||
|
|||
/* 尺寸变量 */ |
|||
|
|||
/* 文字尺寸 */ |
|||
$uni-font-size-sm:24upx; |
|||
$uni-font-size-base:28upx; |
|||
$uni-font-size-lg:32upx; |
|||
|
|||
/* 图片尺寸 */ |
|||
$uni-img-size-sm:40upx; |
|||
$uni-img-size-base:52upx; |
|||
$uni-img-size-lg:80upx; |
|||
|
|||
/* Border Radius */ |
|||
$uni-border-radius-sm: 4upx; |
|||
$uni-border-radius-base: 6upx; |
|||
$uni-border-radius-lg: 12upx; |
|||
$uni-border-radius-circle: 50%; |
|||
|
|||
/* 水平间距 */ |
|||
$uni-spacing-row-sm: 10px; |
|||
$uni-spacing-row-base: 20upx; |
|||
$uni-spacing-row-lg: 30upx; |
|||
|
|||
/* 垂直间距 */ |
|||
$uni-spacing-col-sm: 8upx; |
|||
$uni-spacing-col-base: 16upx; |
|||
$uni-spacing-col-lg: 24upx; |
|||
|
|||
/* 透明度 */ |
|||
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 |
|||
|
|||
/* 文章场景相关 */ |
|||
$uni-color-title: #2C405A; // 文章标题颜色 |
|||
$uni-font-size-title:40upx; |
|||
$uni-color-subtitle: #555555; // 二级标题颜色 |
|||
$uni-font-size-subtitle:36upx; |
|||
$uni-color-paragraph: #3F536E; // 文章段落颜色 |
|||
$uni-font-size-paragraph:30upx; |
|||
|
|||
// custom |
|||
|
|||
/* 页面左右间距 */ |
|||
$page-row-spacing: 30upx; |
|||
$page-color-base: #f8f8f8; |
|||
$page-color-light: #f8f6fc; |
|||
$base-color: #fa436a; |
|||
|
|||
/* 文字尺寸 */ |
|||
$font-sm: 24upx; |
|||
$font-base: 28upx; |
|||
$font-lg: 32upx; |
|||
/*文字颜色*/ |
|||
$font-color-dark: #303133; |
|||
$font-color-base: #606266; |
|||
$font-color-light: #909399; |
|||
$font-color-disabled: #C0C4CC; |
|||
$font-color-spec: #4399fc; |
|||
/* 边框颜色 */ |
|||
$border-color-dark: #DCDFE6; |
|||
$border-color-base: #E4E7ED; |
|||
$border-color-light: #EBEEF5; |
|||
/* 图片加载中颜色 */ |
|||
$image-bg-color: #eee; |
|||
/* 行为相关颜色 */ |
|||
$uni-color-primary:#fa436a; |
|||
$uni-color-success: #4cd964; |
|||
$uni-color-warning: #f0ad4e; |
|||
$uni-color-error: #dd524d; |
|||