Browse Source

init

master
lihua 6 years ago
parent
commit
31c78a5ed7
  1. 2
      .gitignore
  2. 21
      App.vue
  3. 84
      api/device.js
  4. 20
      api/login.js
  5. 26
      common/auth.js
  6. 22
      common/deviceData.js
  7. 1226
      common/icon.css
  8. 74
      common/iconfont.css
  9. 3912
      common/main.css
  10. 14633
      common/mqtt.js
  11. 40
      common/mqttClient.js
  12. 114
      common/qiun.css
  13. 42
      common/request.js
  14. 1447
      common/uni.css
  15. BIN
      components/mehaotian-search/.DS_Store
  16. 182
      components/mehaotian-search/mehaotian-search.vue
  17. 174
      components/refresh/refresh.vue
  18. 34
      components/uni-badge/readme.md
  19. 109
      components/uni-badge/uni-badge.vue
  20. 368
      components/uni-fab/uni-fab.vue
  21. 35
      components/uni-icon/readme.md
  22. 421
      components/uni-icon/uni-icon.vue
  23. 196
      components/uni-list-item/uni-list-item.vue
  24. 88
      components/uni-list/readme.md
  25. 45
      components/uni-list/uni-list.vue
  26. 209
      components/uni-nav-bar/uni-nav-bar.vue
  27. 134
      components/uni-segmented-control/uni-segmented-control.vue
  28. 30
      components/uni-status-bar/uni-status-bar.vue
  29. 133
      components/wuc-tab/wuc-tab.vue
  30. 57
      js_sdk/luch-request/index.js
  31. 148
      js_sdk/luch-request/readme.md
  32. 111
      js_sdk/luch-request/request.js
  33. 165
      js_sdk/u-charts/u-charts/component.vue
  34. 4383
      js_sdk/u-charts/u-charts/u-charts.js
  35. 1
      js_sdk/u-charts/u-charts/u-charts.min.js
  36. 14
      main.js
  37. 76
      manifest.json
  38. 79
      pages.json
  39. 50
      pages/device/detail/device-info.vue
  40. 78
      pages/device/detail/index.vue
  41. 35
      pages/device/detail/real-data.vue
  42. 180
      pages/device/device.vue
  43. 229
      pages/home/home.vue
  44. 38
      pages/location/location.vue
  45. 177
      pages/login/login.vue
  46. 208
      pages/user/user.vue
  47. BIN
      static/banner1.png
  48. BIN
      static/banner2.png
  49. BIN
      static/device.png
  50. BIN
      static/device_active.png
  51. BIN
      static/dtu.png
  52. BIN
      static/gateway.png
  53. BIN
      static/head.png
  54. BIN
      static/home.png
  55. BIN
      static/home_active.png
  56. BIN
      static/humiture.png
  57. BIN
      static/icon_del.png
  58. BIN
      static/icon_pwd.png
  59. BIN
      static/icon_pwd_switch.png
  60. BIN
      static/icon_user.png
  61. BIN
      static/location.png
  62. BIN
      static/location_active.png
  63. BIN
      static/logo.png
  64. BIN
      static/user.png
  65. BIN
      static/user_active.png
  66. 7
      store/getters.js
  67. 15
      store/index.js
  68. 87
      store/user.js
  69. 106
      uni.scss

2
.gitignore

@ -75,4 +75,4 @@ typings/
# FuseBox cache
.fusebox/
unpackage

21
App.vue

@ -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>

84
api/device.js

@ -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'
})
}

20
api/login.js

@ -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'
})
}

26
common/auth.js

@ -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 ');
}
}

22
common/deviceData.js

@ -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;

1226
common/icon.css

File diff suppressed because one or more lines are too long

74
common/iconfont.css

@ -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";
}

3912
common/main.css

File diff suppressed because it is too large

14633
common/mqtt.js

File diff suppressed because it is too large

40
common/mqttClient.js

@ -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
}

114
common/qiun.css

@ -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;
}

42
common/request.js

@ -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;

1447
common/uni.css

File diff suppressed because it is too large

BIN
components/mehaotian-search/.DS_Store

Binary file not shown.

182
components/mehaotian-search/mehaotian-search.vue

@ -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">&#xe61c;</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">&#xe644;</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>

174
components/refresh/refresh.vue

@ -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>

34
components/uni-badge/readme.md

@ -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 触发事件 |

109
components/uni-badge/uni-badge.vue

@ -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>

368
components/uni-fab/uni-fab.vue

@ -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>

35
components/uni-icon/readme.md

@ -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 类型:**
![icon](https://img-cdn-qiniu.dcloud.net.cn/img/icon.png)

421
components/uni-icon/uni-icon.vue

File diff suppressed because one or more lines are too long

196
components/uni-list-item/uni-list-item.vue

@ -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>

88
components/uni-list/readme.md

@ -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}|

45
components/uni-list/uni-list.vue

@ -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>

209
components/uni-nav-bar/uni-nav-bar.vue

@ -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>

134
components/uni-segmented-control/uni-segmented-control.vue

@ -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>

30
components/uni-status-bar/uni-status-bar.vue

@ -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>

133
components/wuc-tab/wuc-tab.vue

@ -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>

57
js_sdk/luch-request/index.js

@ -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
};

148
js_sdk/luch-request/readme.md

@ -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,所以写的不好的地方,还请见谅~
####创作不易,五星好评你懂得!

111
js_sdk/luch-request/request.js

@ -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
})
}
}

165
js_sdk/u-charts/u-charts/component.vue

@ -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'
}
}
});
},
// cidcanvas-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>

4383
js_sdk/u-charts/u-charts/u-charts.js

File diff suppressed because it is too large

1
js_sdk/u-charts/u-charts/u-charts.min.js

File diff suppressed because one or more lines are too long

14
main.js

@ -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()

76
manifest.json

@ -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
}
}

79
pages.json

@ -0,0 +1,79 @@
{
"pages": [ //pageshttps://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": "我的"
}
]
}
}

50
pages/device/detail/device-info.vue

@ -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>

78
pages/device/detail/index.vue

@ -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>

35
pages/device/detail/real-data.vue

@ -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>

180
pages/device/device.vue

@ -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&#8451;</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>

229
pages/home/home.vue

@ -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>

38
pages/location/location.vue

@ -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>

177
pages/login/login.vue

@ -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>

208
pages/user/user.vue

@ -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">&#xe65e;</text>
</view>
</view>
<view class="center-list">
<view class="center-list-item border-bottom">
<text class="list-icon">&#xe60f;</text>
<text class="list-text">帐号管理</text>
<text class="navigat-arrow">&#xe65e;</text>
</view>
<view class="center-list-item">
<text class="list-icon">&#xe639;</text>
<text class="list-text">新消息通知</text>
<text class="navigat-arrow">&#xe65e;</text>
</view>
</view>
<view class="center-list">
<view class="center-list-item border-bottom">
<text class="list-icon">&#xe60b;</text>
<text class="list-text">帮助与反馈</text>
<text class="navigat-arrow">&#xe65e;</text>
</view>
<view class="center-list-item">
<text class="list-icon">&#xe65f;</text>
<text class="list-text">服务条款及隐私</text>
<text class="navigat-arrow">&#xe65e;</text>
</view>
</view>
<view class="center-list">
<view class="center-list-item">
<text class="list-icon">&#xe614;</text>
<text class="list-text">关于应用</text>
<text class="navigat-arrow">&#xe65e;</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>

BIN
static/banner1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
static/banner2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

BIN
static/device.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/device_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/dtu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
static/gateway.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/head.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
static/home.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/home_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
static/humiture.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/icon_del.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

BIN
static/icon_pwd.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

BIN
static/icon_pwd_switch.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B

BIN
static/icon_user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
static/location.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/location_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
static/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
static/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
static/user_active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

7
store/getters.js

@ -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

15
store/index.js

@ -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

87
store/user.js

@ -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

106
uni.scss

@ -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;
Loading…
Cancel
Save