Browse Source

项目初始

master
453530270@qq.com 2 years ago
commit
e5fc3b6c25
  1. 44
      .gitignore
  2. 159
      App.vue
  3. 1
      README.md
  4. 177
      api/Socket.js
  5. 251
      api/assets.js
  6. 104
      api/cache.js
  7. 25
      api/college.js
  8. 110
      api/contract.js
  9. 42
      api/currency.js
  10. 41
      api/exchange.js
  11. 25
      api/home.js
  12. 56
      api/market.js
  13. 129
      api/member.js
  14. 119
      api/option.js
  15. 95
      api/order.js
  16. 69
      api/otc.js
  17. 81
      api/profile.js
  18. 101
      api/record.js
  19. 31
      api/resIntercept.js
  20. 327
      api/serve/index.js
  21. 200
      api/serve/market-socket.js
  22. 62
      api/serve/webaxios.js
  23. 197
      api/setting.js
  24. 31
      api/subscride.js
  25. 175
      api/wallet.js
  26. 55
      app.js
  27. BIN
      assets/fontIcon/iconfont.eot
  28. 77
      assets/fontIcon/iconfont.svg
  29. BIN
      assets/fontIcon/iconfont.ttf
  30. BIN
      assets/fontIcon/iconfont.woff
  31. BIN
      assets/fontIcon/iconfont.woff2
  32. BIN
      assets/img/7coin_qidongye.gif
  33. BIN
      assets/img/Bitmap3x.png
  34. BIN
      assets/img/Fill13x.png
  35. BIN
      assets/img/Upload_File3x.png
  36. BIN
      assets/img/auth_fanmian.png
  37. BIN
      assets/img/auth_shouchi.png
  38. BIN
      assets/img/auth_zhengmain.png
  39. BIN
      assets/img/border_bottom.png
  40. BIN
      assets/img/border_bottom_g.png
  41. BIN
      assets/img/che.png
  42. BIN
      assets/img/fenzu23x.png
  43. BIN
      assets/img/fenzu_73x.png
  44. BIN
      assets/img/initve.png
  45. BIN
      assets/img/invite-1.png
  46. BIN
      assets/img/invite-2.png
  47. BIN
      assets/img/invite-3.png
  48. BIN
      assets/img/invite-4.png
  49. BIN
      assets/img/invite-5.png
  50. BIN
      assets/img/invite-6.png
  51. BIN
      assets/img/invite-bg.png
  52. BIN
      assets/img/invite-fy.png
  53. BIN
      assets/img/invite-sy.png
  54. BIN
      assets/img/invite-tg.png
  55. BIN
      assets/img/invite-yq.png
  56. BIN
      assets/img/shengji.png
  57. 611
      assets/scss/app.scss
  58. 3
      assets/scss/base.scss
  59. 22
      assets/scss/mixin.scss
  60. 24
      assets/scss/size.scss
  61. 85
      assets/scss/theme.scss
  62. 226
      assets/scss/vant.scss
  63. 11
      components/README.md
  64. 70
      components/bing-progress/bing-progress.css
  65. 727
      components/bing-progress/bing-progress.vue
  66. 113
      components/dt-dropdown/dt-dropdown.vue
  67. 454
      components/lb-picker/README.md
  68. 369
      components/lb-picker/index.vue
  69. 46
      components/lb-picker/mixins/index.js
  70. 94
      components/lb-picker/pickers/multi-selector-picker.vue
  71. 67
      components/lb-picker/pickers/selector-picker.vue
  72. 75
      components/lb-picker/pickers/unlinked-selector-picker.vue
  73. 23
      components/lb-picker/style/picker-item.scss
  74. 179
      components/lb-picker/style/picker.scss
  75. 110
      components/lb-picker/utils.js
  76. 148
      components/uni-badge/uni-badge.vue
  77. 546
      components/uni-calendar/calendar.js
  78. 151
      components/uni-calendar/uni-calendar-item.vue
  79. 431
      components/uni-calendar/uni-calendar.vue
  80. 327
      components/uni-calendar/util.js
  81. 403
      components/uni-card/uni-card.vue
  82. 217
      components/uni-collapse-item/uni-collapse-item.vue
  83. 59
      components/uni-collapse/uni-collapse.vue
  84. 212
      components/uni-combox/uni-combox.vue
  85. 200
      components/uni-countdown/uni-countdown.vue
  86. 170
      components/uni-drawer/uni-drawer.vue
  87. 428
      components/uni-fab/uni-fab.vue
  88. 136
      components/uni-fav/uni-fav.vue
  89. 230
      components/uni-goods-nav/uni-goods-nav.vue
  90. 167
      components/uni-grid-item/uni-grid-item copy.vue
  91. 125
      components/uni-grid-item/uni-grid-item.vue
  92. 142
      components/uni-grid/uni-grid.vue
  93. 132
      components/uni-icons/icons.js
  94. 67
      components/uni-icons/uni-icons.vue
  95. 142
      components/uni-indexed-list/uni-indexed-list-item.vue
  96. 317
      components/uni-indexed-list/uni-indexed-list.vue
  97. 72
      components/uni-link/uni-link.vue
  98. 270
      components/uni-list-item/uni-list-item.vue
  99. 78
      components/uni-list/uni-list.vue
  100. 65
      components/uni-list/uni-refresh.vue

44
.gitignore

@ -0,0 +1,44 @@
# ---> Vue
# gitignore template for Vue.js projects
#
# Recommended template: Node.gitignore
# TODO: where does this rule come from?
docs/_book
# TODO: where does this rule come from?
test/
# ---> macOS
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
node_modules/
unpackage
.idea/
.hbuilderx/

159
App.vue

@ -0,0 +1,159 @@
<script>
import Member from "@/api/member";
import { mapActions } from "vuex";
import app from "@/app.js"
export default {
onLaunch: function () {
setInterval(() => {
this.$navFontColor();
}, 1000);
this.mobileLogo();
// this.update()
},
methods: {
...mapActions({
setLogoMap: "logoMap",
}),
mobileLogo() {
Member.mobileLogo().then((res) => {
let data = res.data;
this.setLogoMap({
home_logo: data.home_logo,
login_logo: data.login_logo,
title_logo: data.title_logo,
share_logo: data.share_logo,
name: data.name,
});
});
},
// update(){
// var baseUrl=app.baseUrl + '/api/app/getNewestVersion'
// console.log(baseUrl)
// var _this = this;
// uni.request({
// url: baseUrl, //
// method: 'GET',
// success: result => {
// console.log(result)
// if (result.data.code == 200) {
// plus.runtime.getProperty(plus.runtime.appid, function(inf) {
// console.log(inf)
// if(inf.version != result.data.data.versions){
// uni.showModal({
// title: "",
// content: "",
// success: (res) => {
// if (res.confirm == true) {//
// _this.doUpData();
// }
// }
// })
// }
// });
// }
// },
// })
// },
// doUpData() {
// uni.showLoading({
// title: ''
// })
// uni.downloadFile({//
// url: 'https://www.AMATAKex.net/download/android/AMATAK.apk', //
// success: downloadResult => {//
// uni.hideLoading();
// if (downloadResult.statusCode == 200) {
// uni.showModal({
// title: '',
// content: '',
// confirmText: '',
// confirmColor: '#EE8F57',
// success: function(res) {
// if (res.confirm == true) {
// plus.runtime.install(//
// downloadResult.tempFilePath, {
// force: true
// },
// function(res) {
// utils.showToast('');
// plus.runtime.restart();
// }
// );
// }
// }
// });
// }
// }
// });
// }
},
// onLaunch: function() {
// console.log('App Launch');
//
// #ifdef APP-PLUS
// var baseUrl=app.baseUrl + '/api/app/getNewestVersion'
// uni.request({
// url: baseUrl, //
// method: 'GET',
// // data: {
// // appid: plus.runtime.appid,
// // version: plus.runtime.version,
// // imei: plus.device.imei
// // },
// success: (res) => {
// console.log(res)
// if (res.data.code == 200) {
// console.log(res.data.data.android.url)
// var openUrl = plus.os.name === 'iOS' ? res.data.data.ios.url : res.data.data.android.url;
// //
// uni.showModal({
// title: '',
// content: '',
// success: (showResult) => {
// if (showResult.confirm) {
// plus.runtime.openURL(openUrl);
// }
// }
// })
// }
// }
// })
// #endif
// },
onLaunch() {
},
onShow: function () {
uni.$emit("appShow");
this.$navFontColor();
},
onHide: function () {},
};
</script>
<style lang="scss">
.layout-page {
height: 100vh;
font-size: 14px;
background: $panel-1;
color: $text-color;
}
/* #ifdef H5 */
.layout-page {
height: 100%!important;
}
uni-page-body{
height: 100%;
}
/* #endif */
@import "./assets/scss/app.scss";
/* 解决头条小程序组件内引入字体不生效的问题 */
/* #ifdef MP-TOUTIAO */
@font-face {
font-family: uniicons;
src: url("/static/uni.ttf");
}
/* #endif */
</style>

1
README.md

@ -0,0 +1 @@
初始化

177
api/Socket.js

@ -0,0 +1,177 @@
class Socket {
constructor(link) {
// 初始化socket
if (link.constructor === WebSocket) {
this.socket = link;
} else {
this.socket = new WebSocket(link);
}
// this.socket.binaryType = 'arraybuffer';
this.doOpen();
// 连接状态的标识符
this.readyState = this.socket.readyState;
// 订阅/发布模型
this._events = {
// 订阅的事件 : 发布的方法
};
// 定时验证的标识符
this.heartBeatTimer = null;
}
// 执行socket并发布事件
doOpen() {
this.afterOpenEmit = [];
// 执行socket连接 并初始化验证请求
this.socket.addEventListener("open", evt => this.onOpen(evt));
// 接收socket数据
this.socket.addEventListener("message", evt => this.onMessage(evt));
// 关闭socket连接
this.socket.addEventListener("close", evt => this.onClose(evt));
// 请求发生错误
this.socket.addEventListener("error", err => this.onError(err));
}
// 发布后通知订阅者
Notify(entry) {
// 检查是否有订阅者 返回队列
const cbQueue = this._events[entry.Event];
if (cbQueue && cbQueue.length) {
for (let callback of cbQueue) {
if (callback instanceof Function) callback(entry.Data);
}
}
}
// 请求数据的方法
onOpen(evt) {
// 每隔20s检查连接
// this.heartBeatTimer = setInterval(() => this.send({
// 'cmd': 'ping',
// 'args': ''
// }), 20000);
// 通知订阅
this.Notify({Event: 'open', Data : evt});
}
/**
* 订阅所有的数据
* @param {array|object} datas 订阅参数集合
*/
send(datas) {
if (datas.constructor != Array) {
datas = [datas];
}
for (let item of datas) {
this.socket.send(JSON.stringify(item));
}
}
onMessage(evt) {
try {
// 解析推送的数据
const data = JSON.parse(evt.data);
// 通知订阅者
this.Notify({
Event: 'message',
Data: data
});
} catch (err) {
console.error(' >> Data parsing error:', err);
// 通知订阅者
this.Notify({
Event: 'error',
Data: err
});
}
}
// 添加事件监听
on(name, handler) {
this.subscribe(name, handler);
}
// 取消订阅事件
off(name, handler) {
this.unsubscribe(name, handler);
}
// 订阅事件的方法
subscribe(name, handler) {
if (this._events[name]) {
this._events[name].push(handler); // 追加事件
} else {
this._events[name] = [handler]; // 添加事件
}
}
// 取消订阅事件
unsubscribe(name, handler) {
let start = this._events[name].findIndex(item => item === handler);
// 删除该事件
this._events[name].splice(start, 1);
}
checkOpen() {
return this.readyState >= 2;
}
onClose(evt) {
this.Notify({Event: 'close', Data : evt});
}
onError(err) {
this.Notify({Event: 'error', Data : err});
}
emit(data) {
return new Promise((resolve) => {
this.send(JSON.stringify(data));
this.on('message', function (data) {
resolve(data);
});
});
}
doClose() {
this.socket.close();
}
destroy() {
if (this.heartBeatTimer) {
clearInterval(this.heartBeatTimer);
this.heartBeatTimer = null;
}
this.doClose();
this._events = {};
this.readyState = 0;
this.socket = null;
}
}
export default Socket

251
api/assets.js

@ -0,0 +1,251 @@
import {$get,$post,$postFile} from '@/api'
class Assets {
/**
* 数字货币提现
* @param {Object} data
*/
static cryptocurrenciesWithdrawal(data) {
return $post(`/withdraw/cryptocurrenciesWithdrawal`, data);
}
/**
* assets页面
* @param {Object} data
*/
static assets(data) {
return $post(`/userCoin/assets`, data);
}
/**
* assets页面
* @param {Object} data
*/
static getAllList(data) {
return $get(`coin/getAllList`, data);
}
/**
* 币币用户历史委托
* @param {Object} data
*/
static history(data) {
return $post(`/coin/orders/history`, data);
}
/**
* 资金划转记录
* @param {Object} data
*/
static fundsTransferRecordPageList(data) {
return $post(`/fundsTransferRecord/pageList`, data);
}
/**
* 资金划转
* @param {Object} data
*/
static transfer(data) {
return $post(`/userCoin/transfer`, data);
}
/**
* 数字货币充值
* @param {int} params
*/
static cryptocurrenciesRecharge(coinId) {
return $get(`/recharge/cryptocurrenciesRecharge/${coinId}`);
}
/**
* 数字货币充值记录
* @param {Object} data
*/
static cryptocurrenciesRechargeRecords(data) {
return $post(`/recharge/cryptocurrenciesRechargeRecords`, data);
}
/**
* 用户数字货币提现记录
* @param {Object} data
*/
static cryptocurrenciesWithdrawRecords(data) {
return $post(`/withdraw/cryptocurrenciesWithdrawRecords`, data);
}
/**
* 删除提现地址
* @param {Object} data
*/
static deleteById(data) {
return $post(`/withdrawAddress/deleteById`, data);
}
/**
* 编辑提现地址
* @param {Object} data
*/
static editById(data) {
return $post(`/withdrawAddress/editById`, data);
}
/**
* 移除添加地址
* @param {Object} data
*/
static addRemove(data) {
return $post(`/withdrawAddress/addOrRemoveWhiteList`, data);
}
/**
* 提现地址分页列表
* @param {Object} data
*/
static pageList(data) {
return $post(`/withdrawAddress/pageList`, data);
}
/**
* 添加提现地址
* @param {Object} data
*/
static save(data) {
return $post(`/withdrawAddress/save`, data);
}
/**
* 费率列表(手续费)
* @param {Object} data
*/
static getList(data) {
return $get(`/transferFee/getList`, data);
}
/**
* 用户合约资金
* @param {Object} data
*/
static contractsAccount(data) {
return $get(`/futuresUserCoin/contractsAccount`, data);
}
/**
* 用户合约资金(详情)
* @param {number} accountId
*/
static contractsAccountDetail(accountId) {
return $get(`/futuresUserCoin/contractsAccountDetail/${accountId}`);
}
/**
* 用户资金历史记录
* @param {Object} data
*/
static transactionHistory(data) {
return $post(`/futuresUserCoin/transactionHistory`, data);
}
/**
* 确认是否白名单地址
* @param {Object} data
*/
static checkIsWhiteList(data) {
return $post(`/withdrawAddress/checkIsWhiteList`, data);
}
/**
* 用户已实现盈亏列表
* @param {Object} data
*/
static realisedPnlLog(data) {
return $post(`/realisedPnlLog/list`, data);
}
/**
* 定期宝列表
* @param {Object} data
*/
static financeList(data) {
return $post(`/finance/list`, data);
}
/**
* 理财订单列表
* @param {Object} data
*/
static financeOrderList(data) {
return $post(`/finance/order/list`, data);
}
/**
* 定期宝列表
* @param {Object} data
*/
static financeApply(data) {
return $post(`/finance/apply`, data);
}
/**
* 定期理财账户资产
* @param {Object} data
*/
static financeAccount(data) {
return $post(`/finance/account`, data);
}
}
export default Assets;

104
api/cache.js

@ -0,0 +1,104 @@
/**
* @description 返回数据时提示的消息
* @param {String} msg 返回的消息
* @param {Object} data 返回的数据
* @returns { { msg,data } }
*/
function msg(msg = '请传递消息', data = null) {
return { msg, data };
}
let map = new Map(),
storage = uni.getStorageInfoSync(); //所有数据缓存
/**
* @description 缓存类
* @class 缓存类class
*/
class Cache {
/**
* @constructor
* @param {Number} [timeout=86400] 缓存时间默认86400秒等于一天传0为永久存储
*/
constructor(timeout = 86400) {
// 把本地缓存数据存入map中
storage.keys.forEach((key) => map.set(key, uni.getStorageSync(key)));
this.map = map; //map数据
this.timeout = timeout;
}
/**
* @description 设置缓存数据
* @param {String} key 存储的key
* @param {Object} data 存储的data数据
* @param {String} [timeout] 缓存时间传0为永久存储
* @returns { {msg,data} }
*/
set(key, data, timeout = this.timeout) {
//data = 数据value值,超时时间,加入缓存时间
Object.assign(data, { createTime: Date.now(), timeout });
uni.setStorageSync(key, data); //保存到本地缓存
this.map.set(key, data); //保存到map
return msg('保存成功', data);
}
/**
* @description 获取缓存数据
* @param {String} key 存储的key
* @returns { {msg,data | data:null} }
*/
get(key) {
let value = this.map.get(key); //取值
if (!value) return msg('没有key值'); //如果没有值,那就就返回空
// 数据,超时时间,加入缓存时间 现在时间 时间差(秒)
let { timeout, createTime, ...data } = value,
presentTime = Date.now(),
tdoa = (presentTime - createTime) / 1000;
// 超出缓存时间,那么就清除缓存返回空
if (timeout != 0 && tdoa > timeout) {
uni.removeStorageSync(key);
this.map.delete(key); //删除map中对应的key值
return msg('数据过期');
} else {
return msg('ok', data);
}
}
/**
* @description 清除某个缓存数据
* @param {String} key 存储的key
* @returns { {msg,data:null} }
*/
remove(key) {
uni.removeStorageSync(key); //删除缓存的数据
this.map.delete(key); //删除map中对应的key值
return msg('删除成功');
}
/**
* @description 清除整个缓存数据
* @returns { {msg,data:null} }
*/
clear() {
uni.clearStorageSync(); //清空缓存的数据
this.map.clear(); //清空map
return msg('清空成功');
}
/**
* @description 获取缓存数据大小
* @param { function } cb - 缓存大小
*/
getSize(cb) {
plus.cache.calculate((size) => {
let sizeCache = parseInt(size);
let cacheSize;
if (sizeCache == 0) {
cacheSize = '0B';
} else if (sizeCache < 1024) {
cacheSize = sizeCache + 'B';
} else if (sizeCache < 1048576) {
cacheSize = (sizeCache / 1024).toFixed(2) + 'KB';
} else if (sizeCache < 1073741824) {
cacheSize = (sizeCache / 1048576).toFixed(2) + 'MB';
} else {
cacheSize = (sizeCache / 1073741824).toFixed(2) + 'GB';
}
cb(cacheSize);
});
}
}
export default new Cache();

25
api/college.js

@ -0,0 +1,25 @@
import Serve from '@/api/serve'
class College {
static college(data) {
return Serve.get(`/college`,data);
}
static getArticleList(data) {
return Serve.get(`/articleList`,data);
}
static getCategoryList() {
return Serve.get(`/categoryList`);
}
static getArticleDetail(data) {
return Serve.get(`/article/detail`,data);
}
static getRecommend() {
return Serve.get(`/recommend`);
}
}
export default College;

110
api/contract.js

@ -0,0 +1,110 @@
import Serve from '@/api/serve'
class Contract {
/**
* 合约初始化面板数据
* @param {Object} data
*/
static getMarketInfo(data) {
return Serve.get(`/contract/getMarketInfo`, data);
}
/**
* 获取合约市场
*/
static getMarketList(data) {
return Serve.get('/contract/getMarketList', data)
}
/**
* 获取合约账户信息
*/
static contractAccount(data, config) {
return Serve.get('/contract/contractAccount', data, config)
}
/**
* 获取合约详情
*/
static getSymbolDetail(data) {
return Serve.get('/contract/getSymbolDetail', data)
}
/**
* 可开张数(合约上限)
* */
static openNum(data,config) {
return Serve.get('/contract/openNum', data,config)
}
/**
* 合约开仓
*/
static openPosition(data, config) {
return Serve.post('/contract/openPosition', data, config)
}
// 获取合约持仓
static holdPosition(data, config) {
return Serve.get('/contract/holdPosition', data, config)
}
// 合约平仓
static closePosition(data, config) {
return Serve.post('/contract/closePosition', data, config)
}
// 一键全平
static closeAllPosition(data, config) {
return Serve.post('/contract/closeAllPosition', data, config)
}
// 获取当前合约委托
static getCurrentEntrust(data, config) {
return Serve.get('/contract/getCurrentEntrust', data, config)
}
// 撤单
static cancelEntrust(data, config) {
return Serve.post('/contract/cancelEntrust', data, config)
}
// 历史委托
static getHistoryEntrust(data, config) {
return Serve.get('/contract/getHistoryEntrust', data, config)
}
// 获取k线数据
static getKline(data, config) {
return Serve.get('/contract/getKline', data, config)
}
// 获取委托明细
static getEntrustDealList(data, config) {
return Serve.get('/contract/getEntrustDealList', data, config)
}
// 获取开通状态
static openStatus() {
return Serve.get('/contract/openStatus')
}
// 开通永续合约
static opening() {
return Serve.post('/contract/opening')
}
static setStrategy(data, config) {
return Serve.post('/contract/setStrategy', data, config)
}
// 委托盈亏分享
static entrustShare(data) {
return Serve.get('/contract/entrustShare', data, { loading: true })
}
// 持仓盈亏分享
static positionShare(data) {
return Serve.get('/contract/positionShare', data, { loading: true })
}
// 一键全平
static onekeyAllFlat(data) {
return Serve.post('/contract/onekeyAllFlat', data, { loading: true })
}
// 一键反向
static onekeyReverse(data) {
return Serve.post('/contract/onekeyReverse', data, { loading: true })
}
// 合约说明
static instruction() {
return Serve.get('/contract/instruction')
}
}
export default Contract;

42
api/currency.js

@ -0,0 +1,42 @@
import Serve from '@/api/serve'
class Currency {
// 获取平台收款方式列表
static legalPayList() {
return Serve.post(`/collection/legalPayList`);
}
//用户收款地址列表
static paymentsList() {
return Serve.post(`/user/paymentsList`);
}
//法币交易创建订单
static legalCurrency(data) {
return Serve.post(`/user/legalCurrency`,data);
}
//法币交易订单列表
static legalList(data) {
return Serve.post(`/user/legalList`,data);
}
//法币交易订单详情
static legalInfo(data) {
return Serve.post(`/user/legalInfo`,data);
}
//用户收款地址编辑
static paymentsSubmit(data) {
return Serve.post(`/user/paymentsSubmit`,data);
}
//订单确认支付/收款/取消
static legalPay(data) {
return Serve.post(`/user/legalPay`,data);
}
//用户收款方式详情
static paymentsInfo(data) {
return Serve.post(`/user/paymentsInfo`,data);
}
//用户收款方式开启关闭
static paymentsStatus(data) {
return Serve.post(`/user/paymentsStatus`,data);
}
}
export default Currency;

41
api/exchange.js

@ -0,0 +1,41 @@
import Serve from '@/api/serve'
class Exchange {
// 获取账户余额
static getUserBalance(data) {
return Serve.get(`/exchange/getUserCoinBalance`,data);
}
/**
* 提交订单
* @param {object} data
* @param {string} data.direction buy sell
* @param {string} data.type 1限价2市价
* @param {string} data.symbol 交易对
* @param {number} data.entrust_price 限价单价
* @param {number} data.amount 限价数量
* @param {number} data.trigger_price 条件单单价
* @param {number} data.total 市价单总价
*
*/
static storeEntrust(data,config) {
return Serve.post(`/exchange/storeEntrust`, data,config);
}
// 获取币种基本信息
static getSymbolInfo(data) {
return Serve.post(`/user/tradingPairCurrency`, data);
}
// 查询最新资讯
static newTrends() {
return Serve.get(`/newTrends`);
}
// 获取汇率
static getCurrencyExCny(data){
return Serve.get('/market/getCurrencyExCny',data)
}
}
export default Exchange;

25
api/home.js

@ -0,0 +1,25 @@
import Serve from '@/api/serve/index'
class Home {
// 获取大部分数据
static indexList(data,config){
return Serve.get('/indexList',data,config)
}
// 获取自选数据
static getCollect(){
return Serve.get('/getCollect')
}
/**
* 添加自选
* @param {object} data
* @param {string} data.pair_id
* @param {string} data.pair_name
*/
static option(data){
return Serve.post('/option',data)
}
}
export default Home;

56
api/market.js

@ -0,0 +1,56 @@
import Serve from '@/api/serve'
class Market {
/**
* 轮播图列表
* @param {Object} data
* @param {int} type 请求类型 1是pc(默认) 2是app
* @param {int} position 位置 1(币币首页) 2(预留扩展)
*/
static cryptocurrenciesWithdrawal(type, position) {
return Serve.post(`/market/banner/${type}/${position}`);
}
/**
* 用户收藏交易对信息 需要先登录
*/
static userFavList() {
return Serve.get(`/coin/market/collection/list`);
}
static blogCategoryList() {
return Serve.get(`/blogCategory/list`);
}
static blogDetailList(categoryId, params) {
return Serve.get(`/blog/list/${categoryId}`, params );
}
static blogDetailContent(id) {
return Serve.get(`/blog/detail/${id}`);
}
// 初始化查询市场行情
static getMarketList() {
return Serve.get(`/exchange/getMarketList`);
}
// 初始化买卖盘数据
static getBooks(data) {
return Serve.get(`/exchange/getMarketInfo`,data);
}
// 获取币种信息
static getCoinInfo(data){
return Serve.get(`/exchange/getCoinInfo`,data)
}
}
export default Market;

129
api/member.js

@ -0,0 +1,129 @@
import server from '@/api/serve'
class Member {
/**
* 注册滑块验证码
* @param {object} data
*/
static sliderVerify(data) {
return server.post(`/sliderVerify`, data);
}
/**
* 注册发送手机验证码
* @param data {phone,country_code,token}
*/
static sendSmsCode(data) {
return server.post(`/register/sendSmsCode`, data);
}
/**
* 注册发送验证码
* @param data {email,token}
*/
static sendEmailCode(data) {
return server.post(`/register/sendEmailCode`, data);
}
/**
* 获取国家区号
* @param {object} data
*/
static getCountryCode() {
return server.get(`/getCountryList`);
}
/**
* 注册提交
* @param {object} data
*/
static register(data) {
return server.post(`/user/register`, data);
}
/**
* 登陆发送短信验证码
* @param {object} data
*/
static sendSmsCodeBeforeLogin(data) {
return server.post(`/login/sendSmsCodeBeforeLogin`, data)
}
/**
* 登陆发送邮箱验证码
* @param {object} data
*/
static sendEmailCodeBeforeLogin(data) {
return server.post(`/login/sendEmailCodeBeforeLogin`, data);
}
/**
* 登陆初始化验证
* @param {object} data
*/
static login(data) {
return server.post(`/user/login`, data);
}
/**
* 登陆二次验证
* @param {object} data
*/
static loginConfirm(data,{loading}) {
return server.post(`/user/loginConfirm`, data,{loading})
}
/**
* 退出登录
*/
static logout(data) {
return server.post(`/user/logout`,data);
}
/**
* 上传文件
* @param {FormData} data
*/
static uploadImage(data) {
return server.uploadFile(`/uploadImage`,data);
}
// 页面底部信息
static floor(){
return server.get('/floor')
}
// 移动端logo
static mobileLogo(){
return server.get('/index/logo',{},{loading:false})
}
// 消息通知
static myNotifiables(data){
return server.get('/user/myNotifiables',data)
}
// 消息通知详情
static readNotifiable(data){
return server.get('/user/readNotifiable',data)
}
// 移动端文章
static article(data){
return server.get('/article/list',data)
}
// 文章详情
static articleDetail(data){
return server.get('/article/detail',data)
}
// 获取协议
static clause(){
return server.get('/login/clause')
}
// 获取app更新信息
static getNewestVersion(){
return server.get('/getNewestVersion')
}
static serviceDetail(data) {
return server.get(`/article/serviceDetail`, data);
}
}
export default Member;

119
api/option.js

@ -0,0 +1,119 @@
import Serve from '@/api/serve'
class Option {
// 交易对
static getOptionSymbol() {
return Serve.get(`/option/getOptionSymbol`);
}
/**
* 获取期权交割记录
* @param {object} data
* @param {string} data.pair_id
* @param {string} data.time_id
*/
static getSceneResultList(data) {
return Serve.get(`/option/getSceneResultList`, data)
}
/**
* 获取k线数据
* @param {object} data
* @param {string} data.symbol
* @param {string} data.period
* @param {string} data.size
* @param {string} data.form
* @param {string} data.to
*/
static getKline(data) {
// let url = `https://api.hadax.com/market/history/kline`;
let url = `/option/getKline`;
return Serve.get(url, data)
}
/**
* 获取可用于期权交易的币种列表
*/
static getBetCoinList() {
return Serve.get(`/option/getBetCoinList`)
}
/**
* 获取指定币种的余额
* @param {object} data
* @param {string} data.coin_id
*/
static getUserCoinBalance(data) {
return Serve.get(`/option/getUserCoinBalance`, data)
}
/**
* 获取当前最新期权场景
* @param {object} data
* @param {string} data.pair_id
* @param {string} data.time_id
*/
static sceneDetail(data) {
return Serve.get(`/option/sceneDetail`, data)
}
/**
* 获取全部期权场景
*/
static sceneListByPairs() {
return Serve.get(`/option/sceneListByPairs`)
}
/**
* 获取当前最新期权场景赔率
* @param {object} data
* @param {string} data.pair_id
* @param {string} data.time_id
*/
static getOddsList(data) {
return Serve.get(`/option/getOddsList`, data)
}
/**
* 获取用户期权购买记录
* @param {object} data
* @param {string} data.status
* @param {string} data.pair_id
* @param {string} data.time_id
*/
static getOptionHistoryOrders(data) {
return Serve.get(`/option/getOptionHistoryOrders`, data)
}
/**
* 购买期权
* @param {object} data
* @param {string} data.bet_amount
* @param {string} data.bet_coin_id
* @param {string} data.odds_id
* */
static betScene(data) {
return Serve.post(`/option/betScene`, data)
}
/**
* 获取交易价格组
* @param {object} data
* @param {string} data.symbol
*
*/
static getNewPriceBook(data) {
return Serve.get('/option/getNewPriceBook', data)
}
/**
* 移动端期权列表
*/
static sceneListByTimes() {
return Serve.get('/option/sceneListByTimes')
}
/**
* 移动端详情
* @param {object} data
* @param {string} data.order_id
*/
static getOptionOrderDetail(data) {
return Serve.get('/option/getOptionOrderDetail', data)
}
//期权说明
static instruction() {
return Serve.get('/option/instruction')
}
}
export default Option;

95
api/order.js

@ -0,0 +1,95 @@
import Serve from '@/api/serve'
class Order {
/**
* 发布委托
* @param {object} data
* @param {string} data.direction 方向
* @param {number} data.type - 类型
* @param {string} data.symbol - 交易对
* @param {number} data.entrust_price - 价格
* @param {number} data.amount - 数量
*
*/
static storeEntrust(data) {
return Serve.post(`/exchange/storeEntrust`,data);
}
/**
* 获取历史委托
* @param {object} data
* @param {string} data.direction 方向
* @param {number} data.type - 类型
* @param {string} data.symbol - 交易对
*
*/
static getHistoryEntrust(data) {
return Serve.get(`/exchange/getHistoryEntrust`,data);
}
/**
* 获取当前委托
* @param {object} data
* @param {string} data.direction 方向
* @param {number} data.type - 类型
* @param {string} data.symbol - 交易对
*
*/
static getCurrentEntrust(data) {
return Serve.get(`/exchange/getCurrentEntrust`,data);
}
// 获取止盈止损单
static getConditionEntrust(data) {
return Serve.get(`/exchange/getConditionEntrust`,data);
}
/**
* 获取委托成交记录
* @param {object} data
* @param {string} data.entrust_id 委托id
* @param {number} data.entrust_type - 买入卖出
* @param {string} data.symbol - 交易对
*
*/
static getEntrustTradeRecord(data) {
return Serve.get(`/exchange/getEntrustTradeRecord`,data);
}
/**
* 撤单
* @param {object} data
* @param {string} data.entrust_id 委托id
* @param {number} data.entrust_type - 买入卖出
* @param {string} data.symbol - 交易对
*
*/
static cancelEntrust(data) {
return Serve.post(`/exchange/cancelEntrust`,data);
}
/**
* 批量撤单
* @param {object} data
* @param {string} data.symbol - 交易对
*
*/
static batchCancelEntrust(data) {
return Serve.post(`/exchange/batchCancelEntrust`,data);
}
// 获取交易对
static getExchangeSymbol(){
return Serve.get('/exchange/getExchangeSymbol')
}
/**
* 期权交易记录
* @param {object} [data]
* @param {string} data.status
* @param {string} data.pair_id
* @param {string} data.time_id
*
*/
static getOptionHistoryOrders(data){
return Serve.get('/option/getOptionHistoryOrders',data)
}
}
export default Order;

69
api/otc.js

@ -0,0 +1,69 @@
import Serve from '@/api/serve/index'
class Otc {
static userPayment(data) {
return Serve.get(`/userPayment`,data,{loading:true});
}
static editUserPayment(data) {
return Serve.post(`/userPayment/${data.id}`,data,{loading:true});
}
static getUserPayment(data) {
return Serve.post(`/userPayment/${data.id}`,{},{loading:true});
}
static addUserPayment(data) {
return Serve.post(`/userPayment`,data,{loading:true});
}
static otcTicker(){
return Serve.get(`/otc/otcTicker`,{});
}
static tradingEntrusts(data){
return Serve.get(`/otc/tradingEntrusts`,data,{loading:true})
}
static storeEntrust(data){
return Serve.post(`/otc/storeEntrust`,data,{loading:true})
}
static storeOrder(data){
return Serve.post(`/otc/storeOrder`,data,{loading:true})
}
static myEntrusts(data){
return Serve.get(`/otc/myEntrusts`,data,{loading:true})
}
static myOrders(data){
return Serve.get(`/otc/myOrders`,data,{loading:true})
}
static cancelEntrust(data){
return Serve.post(`/otc/cancelEntrust`,data,{loading:true})
}
static cancelOrder(data){
return Serve.post(`/otc/cancelOrder`,data,{loading:true})
}
static confirmPaidOrder(data){
return Serve.post(`/otc/confirmPaidOrder`,data,{loading:true})
}
static confirmOrder(data){
return Serve.post(`/otc/confirmOrder`,data,{loading:true})
}
static notConfirmOrder(data){
return Serve.post(`/otc/notConfirmOrder`,data,{loading:true})
}
static orderDetail(data){
return Serve.get(`/otc/orderDetail`,data,{loading:true})
}
static otcAccount(data){
return Serve.get(`/otc/otcAccount`,data,{loading:true})
}
static legalBuy(data){
return Serve.post(`/user/legal-buy-sell`,data)
}
static legalPrice(data){
return Serve.post(`/user/legal-unit-price`,data)
}
static legalList(data){
return Serve.post(`/user/legal-order-list`,data)
}
static otcWalletLogs(data){
return Serve.get(`/user/otcWalletLogs`,data)
}
}
export default Otc;

81
api/profile.js

@ -0,0 +1,81 @@
import Serve from '@/api/serve/index'
class Profile {
// 获取用户信息
static getUserInfo() {
return Serve.get(`/user/getUserInfo`);
}
// 获取实名认证信息
static getAuthInfo() {
return Serve.get(`/user/getAuthInfo`);
}
/**
* 初级认证 认证第一步
* @param {object} data
* @param {number} data.country_code
* @param {number} data.country_id // 区号id
* @param {string} data.realname
* @param {number} data.id_card //证件号
* @param {number} data.type //证件类型
* @param {string} data.birthday //出生日期
* @param {string} data.address //地址
* @param {string} data.city //城市
* @param {string} data.extra //额外信息
* @param {string} data.postal_code //邮政编码
* @param {string} data.phone //手机号
*/
static primaryAuth(data) {
return Serve.post(`/user/primaryAuth`, data);
}
/**
* 高级认证认证第二步
* @param {object} data
* @param {string} data.front_img //证件照正面
* @param {string} data.back_img //证件照反面
* @param {string} data.hand_img //手持证件照
*/
static topAuth(data) {
return Serve.post(`/user/topAuth`, data);
}
/**
* 登录记录
*/
static getLoginLogs(data){
return Serve.get(`/user/getLoginLogs`, data)
}
/**
* 邀请推广
*/
static generalizeInfo(){
return Serve.get(`/generalize/info`,)
}
/**
* 推广记录
*/
static generalizeList(data){
return Serve.get(`/generalize/list`,data)
}
/**
* 返佣记录
*/
static rewardLogs(data){
return Serve.get('/generalize/rewardLogs',data)
}
/**
* 获取用户等级详情
*/
static getGradeInfo(){
return Serve.get('/user/getGradeInfo')
}
/**
* 海报图
*/
static poster(data){
return Serve.get('/generalize/poster',data)
}
static qrcode(){
return Serve.get('/generalize/invite_qrcode')
}
}
export default Profile;

101
api/record.js

@ -0,0 +1,101 @@
import {$get,$post,$postFile} from '@/api'
class Record {
/**
* fundHistory列表
* @param {Object} data
*/
static fundList(data) {
return $get(`/coin/getAllList`, data);
}
/**
* 币币用户当前委托记录
* @param {Object} data
*/
static openOrder(data) {
return $post(`/coin/orders/openOrder`, data);
}
static conditionOrders(data) {
return $post(`/coin/orders/condition/openOrder`, data);
}
/**
     * 币币用户取消接口
     * @param {Object} data 
    */
   static openOrderCancel(data) {
      return $post(`/coin/orders/cancel/${data}`);
 }
static orderConditionCancel(data) {
return $post(`/coin/orders/condition/cancel/${data}`)
}
/**
     * 币币用户筛选接口
     * @param {Object} data 
    */
   static openOrderfilter() {
      return $post(`/coin/orders/filter`);
 }
/**
* 用户返佣记录
* @param {Object} data
*/
static rewardList(data) {
return $post(`/member/rewardList`, data);
}
/**
* 币币用户历史委托
* @param {Object} data
*/
static history(data) {
return $post(`/coin/orders/history`, data);
}
/**
     * 联系我们
     * @param {Object} data 
    */
   static contactUs(data) {
      return $post(`/contactUs/save` , data);
 }
/**
     * 取消订单
     * @param {Object} data 
    */
   static cancelActiveOrder(data) {
      return $get(`/futuresOrders/cancelActiveOrder/${data}`);
 }
/**
     * 取消订单
     * @param {Object} data 
    */
   static cancelById(data) {
      return $get(`/futuresConditionOrders/cancelById/${data}`);
 }
}
export default Record;

31
api/resIntercept.js

@ -0,0 +1,31 @@
import vue from "vue";
import router from '@/router'
const resIntercept = (result) => {
let res = result.data
let config = result.config
return new Promise(
function (resolve, reject) {
// 是否提示
if (typeof config.toast == 'boolean') {
if (config.toast) {
vue.prototype.$toast(res.msg)
}
} else {
if (res.code != 200 && res.code != 100) {
vue.prototype.$toast(res.msg)
}
}
// 过滤
if (res.code == 200) {//成功
resolve(res)
} else {//失败
reject(res)
if (res.code == 100 && !config.notLogin) {
router.push('/InterceptAccount')
}
}
}
)
}
export default resIntercept

327
api/serve/index.js

@ -0,0 +1,327 @@
import app from "@/app.js"
import Cache from "../cache.js"
let settings = {
method: "get", // 默认请求方法
contentType: "application/json", // 传参格式
dataType: "json", // 返回值类型
baseUrl: app.baseUrl + '/api/app', // 请求根地址
}
let loadNum = 0; //加载框的数量
let loadingShow = () => {
loadNum++
uni.showLoading({
title: 'loading...'
});
}
let loadingHide = () => {
loadNum--
if (loadNum <= 0) {
uni.hideLoading();
}
}
function x(options = null) {
// 返回当前实例对象 无需手动return
return new x.fn.init(options);
}
x.fn = x.prototype = {
constructor: x,
config(options) {
// 解构并设置默认值
let {
baseUrl,
url,
data,
method,
contentType,
dataType
} = options;
// 请求头参数 写入token和language
let auth = null;
if (uni.getStorageSync('token')) {
auth = uni.getStorageSync('token');
}
let lang=uni.getStorageSync('language')
const header = auth ? {
'X-Requested-With': 'XMLHttpRequest',
lang: lang || 'en',
authorization: `bearer ${auth}`,
'content-type': 'application/x-www-form-urlencoded'
} : {
'X-Requested-With': 'XMLHttpRequest',
lang: lang || 'en',
'content-type': 'application/x-www-form-urlencoded'
};
this.header = header;
// 请求地址解析
if (url.startsWith('http')) { // 外部链接
this.url = url;
} else { // 本地相对路径
this.url = baseUrl + url;
}
if (data) this.data = data;
if (method) this.method = method;
if (contentType) this.contentType = contentType;
if (dataType) this.dataType = dataType;
},
init(options) {
// 将用户参数 写入配置信息
this.config(Object.assign(settings, options));
let { config = {} } = options
return new Promise((resolve, reject) => {
let reg=new RegExp('/','g')//g代表全部
let newMsg=options.url.replace(reg,'_');
console.info(newMsg)
if(Cache.get(newMsg).data){
if(newMsg!='_user_walletImage'&& newMsg!='_user_getAuthInfo' && newMsg!='_user_withdrawalBalance'
&& newMsg!='_wallet_getBalance' && newMsg!='_contract_getMarketInfo'&& newMsg!='_contract_openNum' && newMsg!='_indexList'&& newMsg!='_user_subscribe'){
resolve(Cache.get(newMsg).data);
}
uni.request({
url: this.url,
data: this.data,
method: this.method,
header: this.header,
dataType: this.dataType,
sslVerify: false,
success: (res) => {
console.info(res)
let message = res.data.message
let code = res.data.code
if (code != 200) {
switch (code) {
case 1003: // 登陆失效 清除状态 重新登陆
// 清除session
uni.removeStorageSync('token');
uni.redirectTo({
url: "/pages/login/index",
});
break;
case 1021:
resolve(res.data);
break;
case 4001:
resolve(res.data);
break;
default:
reject(message);
break;
}
if (config.toast !== false&&message) {
uni.showToast({
title: message,
duration: 2000,
icon: 'none'
});
}
} else {
// console.info(Cache.set(newMsg,res.data))
Cache.set(newMsg,res.data);
// console.info(res.data)
resolve(res.data); // 直接返回数据
if (config.toast&&message) {
uni.showToast({
title: message,
duration: 2000,
icon: 'none'
});
}
}
},
fail: (err) => {
console.log(err)
reject(err)
if (config.toast !== false) {
// uni.showToast({
// title: 'error reload!',
// icon: "none"
// });
}
if (err) {
throw new Error();
}
},
complete: (er) => {
console.log()
reject(er)
}
})
}else{
// 提示状态
if (config.loading) {
loadingShow()
}
uni.request({
url: this.url,
data: this.data,
method: this.method,
header: this.header,
dataType: this.dataType,
sslVerify: false,
success: (res) => {
let message = res.data.message
let code = res.data.code
if (code != 200) {
switch (code) {
case 1003: // 登陆失效 清除状态 重新登陆
// 清除session
uni.removeStorageSync('token');
uni.redirectTo({
url: "/pages/login/index",
});
break;
case 1021:
resolve(res.data);
break;
case 4001:
resolve(res.data);
break;
default:
reject(message);
break;
}
if (config.toast !== false&&message) {
uni.showToast({
title: message,
duration: 2000,
icon: 'none'
});
}
} else {
Cache.set(newMsg,res.data);
resolve(Cache.get(newMsg).data); // 直接返回数据
if (config.toast&&message) {
uni.showToast({
title: message,
duration: 2000,
icon: 'none'
});
}
}
},
fail: (err) => {
reject(err)
if (config.toast !== false) {
uni.showToast({
title: 'error reload!',
icon: "none"
});
}
if (err) {
throw new Error();
}
},
complete: () => {
loadingHide()
}
})
}
})
},
// 使用promise封装同步化的确认框
confirmSync(content, fullfilled, rejected = null) {
let showCancel = false;
if (rejected instanceof Function) {
showCancel = true;
}
return new Promise(function (resolve, reject) {
uni.showModal({
content,
showCancel,
success(res) { // confirm or cancel
if (res.confirm) {
resolve(fullfilled()); // 执行动作 需要返回值 则标记到resolve中
} else if (res.cancel && rejected) {
reject(rejected()); // 执行动作 需要返回值 则标记到reject中
}
}
})
})
},
get(url, data = null, config = {}) {
return x({
method: "get",
url,
data,
config
})
},
post(url, data, config = {}) {
return x({
method: "post",
url,
data,
config
})
},
// data 为uni的chooseImage
uploadFile(url, data, config = {}) {
let auth = null;
if (uni.getStorageSync('token')) {
auth = uni.getStorageSync('token');
}
let lang=uni.getStorageSync('language')
let header = {
'X-Requested-With': 'XMLHttpRequest',
lang: lang || "en",
}
if (auth) header.authorization = `bearer ${auth}`;
if (config.loading !== false) {
loadingShow()
}
return new Promise((resolve, reject) => {
uni.uploadFile({
url: settings.baseUrl + url, //仅为示例,非真实的接口地址
filePath: data.tempFilePaths[0],
name: 'image',
formData: {},
sslVerify: false,
header,
success: (res) => {
resolve(JSON.parse(res.data))
},
fail: () => {
reject()
},
complete: () => {
loadingHide()
}
});
})
},
head() {
},
put() {
},
// ...
}
x.fn.init.prototype = x.fn, x.extend = x.fn.extend = function (obj, prop) {
if (!prop) { //如果未设置prop 则表示给this扩展一个对象的内容
prop = obj;
obj = this;
}
for (var i in prop) obj[i] = prop[i];
}, x.extend(x.fn);
export default x;

200
api/serve/market-socket.js

@ -0,0 +1,200 @@
class Ws {
constructor(ws, data, ...args) { // [{url, data, method...},,,,]
this._ws = ws;
this._data = data;
// 待发送的消息列
this._msgs = []
this.socket = this.doLink();
this.doOpen();
// 订阅/发布模型
this._events = {};
// 是否保持连接
this._isLink = true;
// 订阅列表(交易所专用)
this.subs = []
// 循环检查
setInterval(() => {
if (this._isLink) {
if (this.socket.readyState == 2 || this.socket.readyState == 3) {
this.resetLink()
}
}
}, 3000)
}
// 重连
resetLink() {
this.socket = this.doLink(() => {
this.Notify({
Event: 'resetLink'
});
this.resetSub()
});
this.doOpen();
}
// 连接
doLink(call) {
let ws = uni.connectSocket({
url: this._ws,
// 可选参数 设置默认值
header: {
'content-type': 'application/json'
},
method: 'GET',
success: () => {
call && call()
}
})
return ws;
}
doOpen() {
this.socket.onOpen((ev) => {
this.onOpen(ev)
})
this.socket.onMessage((ev) => {
this.onMessage(ev)
})
this.socket.onClose((ev) => {
this.onClose(ev)
})
this.socket.onError((ev) => {
this.onError(ev)
})
}
// 打开
onOpen() {
// 打开时重发未发出的消息
let list = Object.assign([], this._msgs)
list.forEach((item) => {
if (this.send(item)) {
let idx = this._msgs.indexOf(item)
if (idx != -1) {
this._msgs.splice(idx, 1)
}
}
})
}
// 手动关闭
doClose() {
this._isLink = false
this._events = {}
this._msgs = []
this.socket.close({
success: () => {
console.log('socket close success')
}
})
}
// 添加监听
on(name, handler) {
this.subscribe(name, handler);
}
// 取消监听
off(name, handler) {
this.unsubscribe(name, handler);
}
// 关闭事件
onClose() {
// 是否重新连接
if (this._isLink) {
setTimeout(() => {
this.resetLink()
}, 3000)
}
}
// 错误
onError(evt) {
this.Notify({
Event: 'error',
Data: evt
});
}
// 接受数据
onMessage(evt) {
try {
// 解析推送的数据
const data = JSON.parse(evt.data);
// 通知订阅者
this.Notify({
Event: 'message',
Data: data
});
} catch (err) {
console.error(' >> Data parsing error:', err);
// 通知订阅者
this.Notify({
Event: 'error',
Data: err
});
}
}
// 订阅事件的方法
subscribe(name, handler) {
if (this._events.hasOwnProperty(name)) {
this._events[name].push(handler); // 追加事件
} else {
this._events[name] = [handler]; // 添加事件
}
}
// 取消订阅事件
unsubscribe(name, handler) {
let start = this._events[name].findIndex(item => item === handler);
// 删除该事件
this._events[name].splice(start, 1);
}
// 发布后通知订阅者
Notify(entry) {
// 检查是否有订阅者 返回队列
const cbQueue = this._events[entry.Event];
if (cbQueue && cbQueue.length) {
for (let callback of cbQueue) {
if (callback instanceof Function) callback(entry.Data);
}
}
}
// 发送消息
send(data) {
this.changeSubs(data)
if (this.socket.readyState == 1) {
this.socket.send({ data: JSON.stringify(data) })
return true
} else {
// 保存到待发送信息
if (!this._msgs.includes(data)) {
this._msgs.push(data)
};
return false
}
}
// 修改订阅列表(交易所用)
changeSubs(data) {
if (data.cmd == 'sub') {
if (!this.subs.includes(data.msg)) {
this.subs.push(data.msg)
}
} else if (data.cmd == 'unsub') {
let idx = this.subs.indexOf(data.msg)
if (idx != -1) {
this.subs.splice(idx, 1)
}
}
}
// 重新订阅(交易所用)
resetSub() {
let list = Object.assign([], this.subs)
list.forEach((item) => {
this.send({
cmd: 'sub',
msg: item
})
})
}
}
export default Ws

62
api/serve/webaxios.js

@ -0,0 +1,62 @@
import axios from 'axios'
import app from '@/app'
import qs from 'qs';
// 初始化配置
let setting = {
baseURL: app.baseUrl + '/api/app',
timeout: 10000,
withCredentials: true,
crossDomain: true,
responseType: 'json',
headers: {
'content-type': 'application/x-www-form-urlencoded'
}
}
const server = axios.create(setting)
// 请求拦截
server.interceptors.request.use(function (config) {
if (config.method === 'post') {
if (!config.file) {
config.data = qs.stringify(config.data)
}
}
config.headers = Object.assign(config.headers, {
'X-Requested-With': 'XMLHttpRequest',
})
return config;
}, function (error) {
return Promise.reject(error);
})
// 响应拦截
server.interceptors.response.use(function (response) {
return response.data;
}, function (error) {
return Promise.reject(error);
})
export default server;
const $get = (url, data, config) => {
return server.get(url, {
params: data,
...config
})
}
const $post = (url, data, config) => {
return server.post(url, data, config)
}
const $postFile = (url, data, config) => {
let form = new FormData()
for (let i in data) {
form.append(i, data[i])
}
let postConfig = {
file: true
}
return server.post(url, form, Object.assign(postConfig, config))
}
export { $get, $post, $postFile }

197
api/setting.js

@ -0,0 +1,197 @@
import Serve from '@/api/serve'
class Setting {
// 获取用户信息
static getUserInfo() {
return Serve.get(`/user/getUserInfo`);
}
/**
* 修改用户信息
* @param {{username:string,avatar:'url'}} data
*/
static updateUserInfo(data) {
return Serve.post(`/user/updateUserInfo`, data);
}
/**
* 关闭手机号/邮箱/谷歌验证
* @param {object} data
* @param {number} data.type 1:手机 2邮箱 3谷歌
* @param {number} data.sms_code 手机验证码
* @param {number} data.email_code 邮箱验证码
* @param {number} data.google_code 谷歌验证码
*/
static disableSmsEmailGoogle(data,{btn}) {
return Serve.post(`/user/disableSmsEmailGoogle`, data,{btn});
}
/**
* 开启手机号/邮箱/谷歌验证
* @param {object} data
* @param {number} data.type 1:手机 2邮箱 3谷歌
* @param {number} data.sms_code 手机验证码
* @param {number} data.email_code 邮箱验证码
* @param {number} data.google_code 谷歌验证码
*/
static enableSmsEmailGoogle(data,{btn}) {
return Serve.post(`/user/enableSmsEmailGoogle`, data,{btn});
}
/**
* 发送邮箱验证码
* @param {object} data
* @param {string} data.email 邮箱号
*/
static sendBindEmailCode(data) {
return Serve.post(`/user/sendBindEmailCode`, data);
}
/**
* 登录二次验证开关
*/
static switchSecondVerify() {
return Serve.get(`/user/switchSecondVerify`);
}
/**
* 账号安全信息
*/
static accountSecurity() {
return Serve.get(`/user/security/home`);
}
/**
* 设置或重置交易密码
* @param {object} data
* @param {string} data.payword
* @param {string} data.payword_confirmation
* @param {string} data.sms_code
* @param {string} data.email_code
* @param {string} data.google_code
*/
static setOrResetPaypwd(data) {
return Serve.post(`/user/setOrResetPaypwd`, data);
}
/**
* 设置或重置登录密码
* @param {object} data
* @param {string} data.password
* @param {string} data.password_confirmation
* @param {string} data.sms_code
* @param {string} data.email_code
* @param {string} data.google_code
*/
static updatePassword(data,{btn}) {
return Serve.post(`/user/updatePassword`, data,{btn});
}
/**
* 绑定邮箱
* @param {object} data
* @param {string} data.email
* @param {string} data.email_code
* @param {string} data.sms_code
* @param {string} data.google_code
*/
static bindEmail(data,{btn}) {
return Serve.post(`/user/bindEmail`, data,{btn});
}
/**
* 绑定手机
* @param {object} data
* @param {string} data.phone
* @param {string} data.country_code - 手机区号
* @param {string} data.sms_code
* @param {string} data.email_code
* @param {string} data.google_code
*/
static bindPhone(data,{btn}) {
return Serve.post(`/user/bindPhone`, data,{btn});
}
/**
* 解绑邮箱
* @param {object} data
* @param {string} data.sms_code
* @param {string} data.email_code
* @param {string} data.google_code
*/
static unbindEmail(data) {
return Serve.post(`/user/unbindEmail`, data);
}
/**
* 解绑手机
* @param {object} data
* @param {string} data.sms_code
* @param {string} data.email_code
* @param {string} data.google_code
*/
static unbindPhone(data) {
return Serve.post(`/user/unbindPhone`, data);
}
/**
* 忘记登录密码 - 账号确认
* @param {object} data
* @param {string} data.account
*/
static forgetPasswordAttempt(data) {
return Serve.post(`/user/forgetPasswordAttempt`, data,{toast:false});
}
/**
* 忘记登录密码 - 提交
* @param {object} data
* @param {string} data.account
* @param {string} data.email_code
* @param {string} data.google_code
* @param {string} data.password
* @param {string} data.password_confirmation
*/
static forgetPassword(data,{btn}) {
return Serve.post(`/user/forgetPassword`, data,{btn});
}
/**
* 获取谷歌密钥
*/
static getGoogleToken(data) {
return Serve.get(`/user/getGoogleToken`, data);
}
/**
* 绑定谷歌
* @param {object} data
* @param {string} data.google_token
* @param {string} data.google_code
* @param {string} data.sms_code
* @param {string} data.email_code
*/
static bindGoogleToken(data,{btn}) {
return Serve.post(`/user/bindGoogleToken`, data,{btn});
}
/**
* 解绑谷歌
* @param {object} data
* @param {string} data.sms_code
* @param {string} data.google_code
* @param {string} data.email_code
*/
static unbindGoogleToken(data) {
return Serve.post(`/user/unbindGoogleToken`, data);
}
/**
* 发送绑定手机短信验证码
* @param {object} data
* @param {string} data.phone
* @param {string} data.country_code
*/
static sendBindSmsCode(data) {
return Serve.post(`/user/sendBindSmsCode`, data);
}
/**
* 在线获取验证码
* @param {object} data
* @param {string} data.type 1手机 2邮箱
*/
static getCode(data) {
return Serve.post(`/user/getCode`, data);
}
}
export default Setting;

31
api/subscride.js

@ -0,0 +1,31 @@
import Serve from '@/api/serve'
class Subscribe {
/**
* 请求数据
*/
static subscribeTokenList(data){
return Serve.post('/user/subscribeTokenList',data)
}
static activity(data){
return Serve.get('/subscribe/activity',data)
}
static subscribe(data){
return Serve.post('/user/subscribe',data)
}
/**
* 提交数据
* @param {object} data
* @param {string} data.amount
* @param {string} data.coin_name
*/
static subscribeNow(data){
return Serve.post('/user/subscribeNow',data)
}
static changePurchaseCode(data){
return Serve.post('/user/changePurchaseCode',data)
}
}
export default Subscribe;

175
api/wallet.js

@ -0,0 +1,175 @@
import Serve from '@/api/serve'
class Wallet {
// 提币记录
static withdrawalRecord() {
return Serve.post(`/user/withdrawalRecord`);
}
// PayPal支付
static rechargeManualPost(data) {
return Serve.post(`/user/rechargeManualPost`, data);
}
// PayPal账号
static paypal() {
return Serve.get(`/user/paypal`);
}
// PayPal支付记录
static rechargeManualLog(data) {
return Serve.post(`/user/rechargeManualLog`,data);
}
// 充值记录
static depositHistory(data) {
return Serve.post(`/user/depositHistory`,data);
}
// 钱包划转记录
static transferRecord() {
return Serve.post(`/user/transferRecord`);
}
// 申购记录
static subscribeRecords() {
return Serve.post(`/user/subscribeRecords`);
}
// 个人资产管理
static personalAssets() {
return Serve.post(`/user/personalAssets`);
}
// 各个币种的资产
static fundAccount(data) {
return Serve.post(`/user/fundAccount`, data);
}
// 代币以及对应的余额
static tokenList(data) {
return Serve.post(`/user/tokenList`, data);
}
// 资金划转
static fundsTransfer(data) {
return Serve.post(`/user/appFundsTransfer`, data);
}
// 生成充值地址
static walletImage(data) {
return Serve.post(`/user/walletImage`, data);
}
// 提交充值
static recharge(data) {
return Serve.post(`/user/recharge`, data);
}
// 提交提币
static withdraw(data) {
return Serve.post(`/user/withdraw`, data);
}
// 提币地址管理
static getUserWithdrawAdress(data) {
return Serve.post(`/user/withdrawalAddressManagement`, data);
}
// 编辑提币地址
static editUserWithdrawAdress (data) {
return Serve.post(`/user/editUserAdress`, data);
}
// 删除提币地址
static delUserWithdrawAdress(data) {
return Serve.post(`/user/withdrawalAddressManagement`, data);
}
// 添加提币地址
static addUserWithdrawAdress(data) {
return Serve.post(`/`, data);
}
static withdrawalSelectAddress() {
return Serve.post(`/user/withdrawalSelectAddress`);
}
static addWithdrawAddress(data) {
return Serve.post(`/user/withdrawalAddressAdd`, data);
}
// 查询币种余额
static withdrawalBalance(data) {
return Serve.post(`/user/withdrawalBalance`, data);
}
// 修改用户地址
static withdrawalAddressModify(data) {
return Serve.post(`/user/withdrawalAddressModify`, data);
}
// 删除币种地址
static withdrawalAddressDeleted(data) {
return Serve.post(`/user/withdrawalAddressDeleted`, data);
}
// 资金余额
static appTokenAssets(data){
return Serve.post(`/user/appTokenAssets`,data)
}
// 指定币种划转记录
static appTransferRecord(data){
return Serve.post(`/user/appTransferRecord`,data)
}
// 指定币种提币记录
static appWithdrawalRecord(data){
return Serve.post(`/user/appWithdrawalRecord`,data)
}
// 指定币种提币记录
static appDepositHistory(data){
return Serve.post(`/user/appDepositHistory`,data)
}
// 指定币种币币记录
static getWalletLogs(data){
return Serve.get(`/user/getWalletLogs`,data)
}
// 生成钱包地址
static createWalletAddress() {
return Serve.post(`/user/createWalletAddress`);
}
// 获取充币地址
static collectionType(data) {
return Serve.post(`/user/collectionType`, data)
}
// 获取转换列表
static accounts(data){
return Serve.get(`/wallet/accounts`, data)
}
// 获取子账户类别
static accountPairList(data){
return Serve.get('/wallet/accountPairList',data)
}
// 获取转换币种列表
static coinList(data){
return Serve.get('/wallet/coinList',data)
}
// 获取余额
static getBalance(data){
return Serve.get('/wallet/getBalance',data)
}
// 资金划转
static transfer(data){
return Serve.post('/wallet/transfer',data)
}
// 合约资金列表
static accountList(data){
return Serve.get('/contract/accountList',data)
}
// 合约资金流水
static accountFlow(data){
return Serve.get('/contract/accountFlow',data)
}
// 合约资金流水
static cancelWithdraw(data){
return Serve.post('/user/cancelWithdraw',data,{loading:true})
}
}
export default Wallet;

55
app.js

@ -0,0 +1,55 @@
let config = {};
if (process.env.NODE_ENV == 'production'||true) { //生产环境
config = {
// ajax地址
baseUrl: 'https://seees.ybgcoins.com/',
// 图片地址 (暂时无用)
imgUrl: 'https://seees.ybgcoins.com/storage',
// socket地址
socketUrl: 'wss://seees.ybgcoins.com/ws1',
socketUrl1: 'wss://seees.ybgcoins.com/ws2',
// pc端地址
pcUrl:'https://www.ybgcoins.com/',
// app名称
appName: 'YBGCoins',
// 版本
version: '1.0.0',
// 移动端地址
mobile: 'https://h5.ybgcoins.com/',
down:"https://h5.ybgcoins.com/download/CINSCoin.html"
};
} else { //开发环境
config = {
baseUrl: 'http://qkladmin2.ruanmeng.top',
imgUrl: 'http://qkladmin2.ruanmeng.top/upload',
socketUrl: 'ws://qkladmin2.ruanmeng.top:2346/',
socketUrl1: 'ws://qkladmin2.ruanmeng.top:2348/',
// pc端地址
pcUrl:'http://qklhome2.ruanmeng.top',
// app名称
appName: '本地开发',
// 版本
version: '0.0.0',
// 移动端地址
mobile: ''
}
// config = {
// // ajax地址
// baseUrl: 'https://server.7coin.in',
// // 图片地址 (暂时无用)
// imgUrl: 'https://server.7coin.in/upload',
// // socket地址
// socketUrl: 'wss://server.7coin.in:2346/',
// socketUrl1: 'wss://server.7coin.in:2348/',
// // pc端地址
// pcUrl:'https://www.7coin.in',
// // app名称
// appName: '7COIN test',
// // 版本
// version: '1.0.0',
// // 移动端地址
// mobile: 'https://h5.7coin.in'
// };
}
export default config;

BIN
assets/fontIcon/iconfont.eot

Binary file not shown.

77
assets/fontIcon/iconfont.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 49 KiB

BIN
assets/fontIcon/iconfont.ttf

Binary file not shown.

BIN
assets/fontIcon/iconfont.woff

Binary file not shown.

BIN
assets/fontIcon/iconfont.woff2

Binary file not shown.

BIN
assets/img/7coin_qidongye.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

BIN
assets/img/Bitmap3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 KiB

BIN
assets/img/Fill13x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

BIN
assets/img/Upload_File3x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
assets/img/auth_fanmian.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/img/auth_shouchi.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/img/auth_zhengmain.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/img/border_bottom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
assets/img/border_bottom_g.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
assets/img/che.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
assets/img/fenzu23x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
assets/img/fenzu_73x.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/img/initve.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
assets/img/invite-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
assets/img/invite-2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
assets/img/invite-3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
assets/img/invite-4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/img/invite-5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/img/invite-6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets/img/invite-bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

BIN
assets/img/invite-fy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
assets/img/invite-sy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/img/invite-tg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
assets/img/invite-yq.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
assets/img/shengji.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

611
assets/scss/app.scss

@ -0,0 +1,611 @@
// 样式重置
table {
border-collapse: collapse;
th,
td {
border-color: inherit;
}
}
body{
font-family: 'Microsoft YaHei';
}
p {
margin: 0;
}
input {
background: transparent;
border: none;
}
ul {
padding: 0;
margin: 0;
list-style: none;
}
// 边框
$dir1: (top t, right r, bottom b, left l);
$dir2: (top left tl, top right tr, bottom right br, bottom left bl);
// 颜色
$color-list: (
plain $plain,
dark $black,
light $light,
gray-1 $gray-1,
gray-2 $gray-2,
gray-3 $gray-3,
gray-4 $gray-4,
gray-5 $gray-5,
gray-6 $gray-6,
gray-7 $gray-7,
gray-8 $gray-8,
gray-9 $gray-9,
danger $red,
primary $blue,
warning $orange,
yellows $yellow,
warning-dark $orange-dark,
warning-light $orange-light,
success $green,
buy $buy,
sell $sell,
theme-1 $theme-1,
theme-2 $theme-2,
panel $panel,
panel-1 $panel-1,
panel-2 $panel-2,
panel-3 $panel-3,
panel-4 $panel-4,
panel-5 $panel-5,
panel-6 $panel-6,
form-panel-4 $form-panel-4,
form-panel-3 $form-panel-3,
tab-nav $tab-nav
);
// 颜色(不包含css变量)
$color-list2: (
dark $black,
gray-1 $gray-1,
gray-2 $gray-2,
gray-3 $gray-3,
gray-4 $gray-4,
gray-5 $gray-5,
gray-6 $gray-6,
gray-7 $gray-7,
gray-8 $gray-8,
gray-9 $gray-9,
danger $red,
primary $blue,
warning $orange,
yellows $yellow,
warning-dark $orange-dark,
warning-light $orange-light,
success $green,
buy $buy,
sell $sell,
theme-1 $theme-1,
theme-2 $theme-2,
panel-5 $panel-5,
panel-6 $panel-6
);
// 间距
$pad: (
base: $padding-base,
xs: $padding-xs,
ms: $padding-ms,
sm: $padding-sm,
md: $padding-md,
lg: $padding-lg,
xl: $padding-xl,
);
// 公用样式
.d-flex {
display: flex;
}
.d-inline-flex {
display: inline-flex;
}
.d-inline {
display: inline;
}
.d-inline-block {
display: inline-block;
}
.d-block {
display: block;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.justify-around {
justify-content: space-around;
}
.justify-start {
justify-content: flex-start;
}
.justify-end {
justify-content: flex-end;
}
.align-center {
align-items: center;
}
.align-stretch {
align-items: stretch;
}
.align-start {
align-items: flex-start;
}
.align-end {
align-items: flex-end;
}
.flex-fill {
flex: 1;
}
.flex-col {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.flex-shrink {
flex-shrink: 0;
}
.float-r {
float: right;
}
.float-l {
float: left;
}
.clear {
&::after {
display: block;
content: "";
clear: both;
}
}
// 字体尺寸
.fn-xs {
font-size: $font-size-xs;
}
.fn-sm {
font-size: $font-size-sm;
}
.fn-md {
font-size: $font-size-md;
}
.fn-lg {
font-size: $font-size-lg;
}
.fn-bold {
font-weight: 600;
}
.fn-center {
text-align: center;
}
.fn-left {
text-align: start;
}
.fn-right {
text-align: end;
}
.fn-middle {
vertical-align: middle;
}
.fn-wrap {
white-space: normal;
word-break: break-word;
}
.fn-nowrap {
white-space: nowrap;
}
$i: 10;
@while $i <=40 {
.fn-#{$i} {
font-size: $i + px;
}
$i: $i + 1;
}
$i: 1;
@while $i<=4 {
.eps-#{$i} {
@include eps($i);
}
$i: $i + 1;
}
.color-default {
color: $text-color;
}
@each $item1, $item2 in $color-list {
.color-#{$item1} {
color: $item2;
}
.bg-#{$item1} {
background-color: $item2;
}
.border-#{$item1} {
&::after {
border-color: $item2 !important;
}
}
.border-#{$item1}-original {
border-color: $item2 !important;
}
}
@each $item1, $item2 in $color-list2 {
.bg-#{$item1}-transparent {
background: rgba($item2, 0.1);
}
}
.border {
// border: 1px solid $border-color;
position: relative;
&::after {
@include harirline-common();
border: 1px solid $border-color;
}
}
.bg-gradient-blue {
background: $gradient-blue;
}
.bg-gradient-red {
background: $gradient-red;
}
.bg-gradient-green {
background: $gradient-green;
}
.border-original {
border: 1.02px solid $border-color;
}
.border-original-0 {
border-width: 0px;
}
@each $item1, $item2 in $dir1 {
.border-#{$item2} {
position: relative;
&::after {
@include harirline-common();
border-#{$item1}: 1px solid $border-color;
}
}
.border-#{$item2}-original {
border-#{$item1}: 1.02px solid $border-color;
}
.border-#{$item2}-0 {
&::after {
border-#{$item1}: 0 !important;
}
}
.border-#{$item2}-original-0 {
border-#{$item1}: 0 !important;
}
}
.rounded {
border-radius: $border-radius-md;
}
.rounded-xs {
border-radius: $border-radius-xs;
}
.rounded-sm {
border-radius: $border-radius-sm;
}
.rounded-md {
border-radius: $border-radius-md;
}
.rounded-lg {
border-radius: $border-radius-lg;
}
.rounded-max {
border-radius: $border-radius-max;
}
@each $item1, $item2, $item3 in $dir2 {
.rounded-#{$item3}-xs {
border-#{$item1}-#{$item2}-radius: $border-radius-xs;
}
.rounded-#{$item3}-sm {
border-#{$item1}-#{$item2}-radius: $border-radius-sm;
}
.rounded-#{$item3}-md {
border-#{$item1}-#{$item2}-radius: $border-radius-md;
}
.rounded-#{$item3}-lg {
border-#{$item1}-#{$item2}-radius: $border-radius-lg;
}
.rounded-#{$item3}-max {
border-#{$item1}-#{$item2}-radius: $border-radius-max;
}
}
@each $idx, $var in $pad {
// 外间距
.m-#{$idx} {
margin: $var !important;
}
.m-x-#{$idx} {
margin-left: $var !important;
margin-right: $var !important;
}
.m-y-#{$idx} {
margin-top: $var !important;
margin-bottom: $var !important;
}
@each $item1, $item2 in $dir1 {
.m-#{$item2}-#{$idx} {
margin-#{$item1}: $var !important;
}
}
// 内间距
.p-#{$idx} {
padding: $var !important;
}
.p-x-#{$idx} {
padding-left: $var !important;
padding-right: $var !important;
}
.p-y-#{$idx} {
padding-top: $var !important;
padding-bottom: $var !important;
}
@each $item1, $item2 in $dir1 {
.p-#{$item2}-#{$idx} {
padding-#{$item1}: $var !important;
}
}
}
.m-x-auto {
margin-left: auto;
margin-right: auto;
}
.m-l-auto {
margin-left: auto;
}
.m-r-auto {
margin-right: auto;
}
.box-size {
box-sizing: border-box;
}
// 尺寸
$i: 1;
@while $i <=100 {
.w-#{$i} {
width: $i + px;
}
.min-w-#{$i} {
min-width: $i + px;
}
.max-w-#{$i} {
max-width: $i + px;
}
.h-#{$i} {
height: $i + px;
}
.min-h-#{$i} {
min-height: $i + px;
}
.max-h-#{$i} {
max-height: $i + px;
}
$i: $i + 1;
}
$i: 1;
@while $i <=12 {
.w-#{$i}\/#{12} {
width: #{$i/12 * 100}#{"%"};
}
.h-#{$i}\/#{12} {
height: #{$i/12 * 100}#{"%"};
}
$i: $i + 1;
}
.w-max {
width: 100%;
}
.h-max {
height: 100%;
}
$i: 1;
@while $i <=4 {
.line-height-#{$i} {
line-height: $i;
}
$i: $i + 1;
}
// 公共布局
.layout-page {
display: flex;
flex-direction: column;
height: 100vh;
background: $bg;
}
.layout-main {
overflow: auto;
-webkit-overflow-scrolling: touch;
flex: 1;
}
// 引入字体图标
@font-face {
font-family: "iconfont";
src: url("./assets/fontIcon/iconfont.eot?t=1594112878280");
/* IE9 */
src: url("./assets/fontIcon/iconfont.eot?t=1594112878280#iefix") format("embedded-opentype"),
/* IE6-IE8 */ url("./assets/fontIcon/iconfont.woff?t=1594112878280") format("woff"),
url("./assets/fontIcon/iconfont.ttf?t=1594112878280") format("truetype"),
/* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url("./assets/fontIcon/iconfont.svg?t=1594112878280#iconfont") format("svg");
/* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// 富文本容器
.edit-content {
img {
max-width: 100%;
height: auto;
}
}
.overflow-hidden {
overflow: hidden;
}
.overflow-scroll {
overflow: auto;
}
// 背景选中
.link-active {
&:active {
background: $panel-1;
}
}
.color-white{
color: white;
}
// 拟态投影 panel-4
.shadow-panel-4 {
box-shadow: var(--mimicry-shadow);
}
.shadow-panel-nei {
box-shadow: var(--nei-shadow);
}
.shadow-panel-nei-5 {
box-shadow: var(--nei-shadow-5);
}
.box-shadow {
box-shadow: $shadow;
}
.transition-default {
transition: all 0.3s;
}
navigator {
display: inline-block;
}
// picker
.lb-picker-header {
&::before,
&::after {
border: none !important;
}
}
.lb-picker-header-actions {
background-color: $panel-3;
}
.uni-picker-view-indicator {
&::before,
&::after {
border: none !important;
}
}
.lb-picker-content {
background-color: $panel-4 !important;
}
.uni-picker-view-mask {
background: var(--picker-mask);
background-position: top, bottom;
background-size: 100% 102px;
background-repeat: no-repeat;
}
.lb-picker-action-confirm-text {
color: $green !important;
}
.app-nav {
height: $status-bar;
}
.padding-nav {
padding-top: $status-bar;
}
@import "./vant.scss";

3
assets/scss/base.scss

@ -0,0 +1,3 @@
@import './theme.scss';
@import './size.scss';
@import './mixin.scss';

22
assets/scss/mixin.scss

@ -0,0 +1,22 @@
// 边框
@mixin harirline-common {
position: absolute;
box-sizing: border-box;
display: block;
content: ' ';
pointer-events: none;
border-radius: inherit;
top: -50%;
right: -50%;
bottom: -50%;
left: -50%;
transform: scale(0.5);
}
// 溢出省略
@mixin eps($num:1) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $num;
overflow: hidden;
}

24
assets/scss/size.scss

@ -0,0 +1,24 @@
// 字体尺寸
$font-size-xs: 10px;
$font-size-sm: 12px;
$font-size-md: 14px;
$font-size-lg: 16px;
$font-weight-bold: 500;
$border-radius-xs: 6px;
$border-radius-sm: 14px;
$border-radius-md: 20px;
$border-radius-lg: 26px;
$border-radius-max: 50%;
// 间距
$padding-base: 4px;
$padding-xs: 6px;
$padding-ms: 8px;
$padding-sm: 12px;
$padding-md: 15px;
$padding-lg: 20px;
$padding-xl: 25px;
// 状态栏高度
$status-bar: var(--status-bar-height);

85
assets/scss/theme.scss

@ -0,0 +1,85 @@
// -------不可修改--------
$black: #000 !default;
$white: #fff !default;
$gray-1: #f7f8fa !default;
$gray-2: #f2f3f5 !default;
$gray-3: #ebedf0 !default;
$gray-4: #dcdee0 !default;
$gray-5: #c8c9cc !default;
$gray-6: #888894 !default;
$gray-7: #646566 !default;
$gray-8: #323233 !default;
$gray-9: #202635 !default;
$red: #ce5b67 !default;
$blue: #1989fa !default;
$orange: #ff976a !default;
$yellow: #f8b936 !default;
$orange-dark: #ed6a0c !default;
$orange-light: #fffbe8 !default;
$green: #60c08c !default;
// 采用字体样式
// $base-font-family: -apple-system,
// BlinkMacSystemFont,
// "Helvetica Neue",
// Helvetica,
// Segoe UI,
// Arial,
// Roboto,
// "PingFang SC",
// "Hiragino Sans GB",
// "Microsoft Yahei",
// sans-serif;
// $price-integer-font-family: Avenir-Heavy,
// PingFang SC,
// Helvetica Neue,
// Arial,
// sans-serif;
// --------------------
// ------随便你改-------
$plain:var(--plain,#fff);
$theme-1: #ceb359 !default;
$theme-2: #f0c947 !default;
// $theme-1:#ff4d5c !default;
$buy: #60c08c !default;
$sell: #ea3131 !default;
$panel: var(--panel,#36332c) !default;
$panel-1: var(--panel-1,#2A2A38) !default;
$panel-2: var(--panel-2,#343445) !default;
$panel-3: var(--panel-3,#393948) !default;
$panel-4: var(--panel-4,#484859) !default;
$panel-5: #202635 !default;
$panel-6: #646566 !default;
$form-panel-3: var(--form-panel-3,#393948) !default;
$form-panel-4: var(--form-panel-4,#484859) !default;
$text-color: var(--text-color,#9FA6B5) !default;
$active-color: $gray-2 !default;
$active-opacity: 0.7 !default;
$disabled-opacity: 0.5 !default;
$text-link-color: #576b95 !default;
$light:var(--light,#333);
$bg-top:var(--bg-top,#383847);
$bg-bottom:var(--bg-bottom,#242230);
$bg: linear-gradient(to bottom,$bg-top,$bg-bottom);
$tab-nav:var(--tab-nav,#31313F);
// 边框
$border-color: var(--border-color,#49495F) !default;
// 阴影
$shadow:var(--shadow, 0px 0px 33px 0px rgba(34, 34, 44, 0.25));
// 渐变
$gradient-blue:linear-gradient(315deg, #4048EF 0%, #5A7BEF 100%);
$gradient-green:linear-gradient(328deg, #2FAD66 0%, #9DE686 100%);
$gradient-red: linear-gradient(270deg, #F15887 0%, #FE9B86 100%);
$gradient-orange: linear-gradient(to bottom, #ef9b7e 0%, #ea673e 100%);
$gradient-yellow: linear-gradient(to right, #f6c769 0%, #f9ba44 100%);
// 底部阴影
$tab-nav-shadow:var(--tab-nav-shadow,0px -7px 20px 0px rgba(37, 37, 48, 0.83));
// 按钮颜色
$bg-buy:#60c08c;
$bg-sell:#ce5b67;

226
assets/scss/vant.scss

@ -0,0 +1,226 @@
:root {
--tabs-nav-background-color: transparent;
--button-plain-background-color: transparent;
--button-primary-background-color: #60c08c;
--button-primary-border-color: #60c08c;
--button-default-background-color: $panel-3;
--tab-text-color:#888894;
--button-default-border-color:#49495F;
--button-default-background-color:transparent;
}
.van-toast__loading {
color: $theme-1;
}
// nav-bar
.van-nav-bar__left {
.van-nav-bar__arrow {
color: $text-color;
font-weight: bold;
}
}
.van-hairline--bottom::after {
border-color: $border-color;
}
.van-nav-bar__content {
min-height: var(--nav-bar-height, 44px);
}
.nav-timename{
.van-tab--active{
.van-ellipsis{
font-weight: bold;
font-size: 16px;
}
}
.van-tabs__line {
background-color: transparent;
height: 6px;
&::before {
top: -4px !important;
width: 40px !important;
height: 20px !important;
// background: url(~@/assets/img/border_bottom.png) no-repeat !important;
}
}
.van-sticky{
z-index: 1;
.van-tabs__wrap {
border-bottom: 1px solid #f1a68c;
overflow: visible;
}
}
}
.hg {
.van-tabs__line{
&::before {
background: url(~@/assets/img/border_bottom.png) no-repeat !important;
background-size: 100% 100% !important;
}}
.van-tab--active{
.van-ellipsis{
color: #333 !important;
}
}
}
.sun {
.van-tabs__line{
&::before {
background: url(~@/assets/img/border_bottom_g.png) no-repeat !important;
background-size: 100% 100% !important;
}}
.van-tab--active{
.van-ellipsis{
color: #f2f2f2 !important;
}
}
}
.m-t-md .d-inline-block .rounded-lg .van-button{
box-shadow: 1px 5px 12px -2px #ef9b7e ;
}
.layout-page {
.van-swipe,
.van-tab--active {
// color: var(--nav-tab-active,#fff);
}
.van-nav-bar__title {
color: $light;
}
.van-nav-bar {
background-color: transparent;
&.van-hairline--bottom:after {
border-bottom-width: 0;
}
}
.van-popup {
background-color: $panel-3;
}
.van-popup--left {
background: $bg;
}
.van-field__input {
color: $light;
}
.van-steps {
background-color: transparent;
}
.van-stepper__minus,
.van-stepper__plus {
background-color: $panel-3;
color: $light;
}
.van-stepper__input {
background-color: $panel-3;
color: $light;
}
.van-count-down {
color: $light;
}
// search
.van-search {
background-color: transparent !important;
.van-search__content {
background-color: $panel-4 !important;
border-radius: 20px;
}
}
.van-hairline--bottom:after,
.van-hairline--left:after,
.van-hairline--right:after,
.van-hairline--surround:after,
.van-hairline--top-bottom:after,
.van-hairline--top:after,
.van-hairline:after {
border-color: var(--border-color);
}
.van-tag {
background-color: transparent;
color: $light;
}
.van-tab {
font-size: $font-size-md;
}
.van-tabs__line {
background-color: transparent;
height: 6px;
&::before {
content: "";
display: block;
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--nav-tab-active,#fff);
}
}
.van-search__action,
.van-cell {
color: $light;
}
.van-button--default {
.van-button__text {
color: $text-color;
}
&.van-dialog__confirm{
.van-button__text {
color: $blue;
}
}
}
}
.vant-toast-index{
position: relative;
z-index: 99999999;
}
// picker
.van-picker {
background-color: $panel-4;
}
.van-picker-column__item {
color: $light;
}
[class*="van-hairline"]::after {
border-color: $border-color;
}
.van-number-keyboard__keys {
color: $gray-9;
}
.vant-popup-index {
position: fixed;
z-index: 6;
}
.default .van-button {
color: $black !important;
}
.layout-page {
.van-tabs__wrap--scrollable .van-tabs__nav--complete {
padding-left: 0;
padding-right: 0;
}
}
::v-deep .tab-active-class{
color: $theme-1!important;
}

11
components/README.md

@ -0,0 +1,11 @@
## 引入插件
`在main.js中添加`
import dropdown from './components/dropdown/dt-dropDown.vue'
Vue.component('dropdown', dropdown)
## 使用
<dropdown :list="list" @onClick="dropDownChange"></dropdown>

70
components/bing-progress/bing-progress.css

@ -0,0 +1,70 @@
.bing-progress {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: space-around;
overflow: hidden;
}
.bp-marea {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
position: absolute;
left: 0;
top: 0;
flex-direction: row;
align-items: center;
text-align: center;
justify-content: space-around;
background-color: rgba(0,0,0,0);
z-index: 6;
}
.bp-mview,
.bp-handle {
position: absolute;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
text-align: center;
justify-content: center;
z-index: 5;
overflow: hidden;
}
.bp-handle-text {
text-align: center;
z-index: 5;
overflow: hidden;
}
.bp-bar_max {
position: absolute;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
margin: 0;
padding: 0;
z-index: 1;
overflow: hidden;
}
.bp-bar_active {
position: absolute;
z-index: 3;
overflow: hidden;
}
.bp-bar_sub_active {
position: absolute;
z-index: 2;
overflow: hidden;
}
.bp-value {
position: absolute;
text-align: center;
overflow: hidden;
z-index: 4;
}

727
components/bing-progress/bing-progress.vue

@ -0,0 +1,727 @@
<template>
<view class="bing-progress" :style="{width:bpWidth,height:bpHeight,borderRadius:borderRadius,
backgroundColor:backgroundColor,flexDirection:direction!='vertical'?'row':'column'}">
<!-- 进度 -->
<!-- #ifdef APP-NVUE -->
<view class="bp-bar_max"
:style="{width:barMaxWidth,height:barMaxHeight,backgroundColor:noActiveColor,
flexDirection:direction!='vertical'?'row':'column',left:barMaxLeft,borderRadius:barBorderRadius}">
<view class="bp-bar_sub_active"
:style="{width:barSubActiveWidth,height:barSubActiveHeight,backgroundColor:subActiveColor,
top:subActiveTop,bottom:subActiveBottom,left:subActiveLeft,right:subActiveRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view>
<view class="bp-bar_active"
:style="{width:barActiveWidth,height:barActiveHeight,backgroundColor:activeColor,
top:activeTop,bottom:activeBottom,left:activeLeft,right:activeRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view>
</view>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view class="bp-bar_max"
:style="{width:barMaxWidth,height:barMaxHeight,backgroundColor:noActiveColor,borderRadius:barBorderRadius,
flexDirection:direction!='vertical'?'row':'column',left:barMaxLeft}">
<view class="bp-bar_sub_active"
:style="{width:barSubActiveWidth,height:barSubActiveHeight,backgroundColor:subActiveColor,
top:subActiveTop,bottom:subActiveBottom,left:subActiveLeft,right:subActiveRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view>
<view class="bp-bar_active"
:style="{width:barActiveWidth,height:barActiveHeight,backgroundColor:activeColor,
top:activeTop,bottom:activeBottom,left:activeLeft,right:activeRight,borderRadius:isActiveCircular?barBorderRadius:0}"></view>
</view>
<!-- #endif -->
<movable-area class="bp-marea" @touchmove.stop.prevent="touchmove" @touchstart="touchstart" @touchcancel="touchend" @touchend="touchend"
:style="{width:mareaWidth,height:mareaHeight,left:mareaLeft}">
<!-- 拖柄 -->
<movable-view class="bp-mview" :direction="direction=='vertical'?'vertical':'horizontal'" :animation="false"
:disabled="true" :x="handleX" :y="handleY" friction="10" damping="100"
:style="{width:mhandleWidth,height:mhandleHeight,backgroundColor:handleColor,
borderRadius:handleBorderRadius,fontSize:infoFontSize,top:mhandleTop}">
<view class="bp-handle" :style="{fontSize:infoFontSize,width:mhandleWidth,height:mhandleHeight,borderRadius:handleBorderRadius}">
<image class="bp-handle-img" :src="handleImgUrl" v-if="handleImgUrl"
:style="{fontSize:infoFontSize,width:mhandleWidth,height:mhandleHeight,borderRadius:handleBorderRadius}"></image>
<text class="bp-handle-text" v-if="handleImgUrl=='' && infoAlign=='handle' && showInfo"
:style="{fontSize:infoFontSize,color:infoColor,width:mhandleWidth,height:textHeight,borderRadius:'20px'}">{{ infoContent=='subValue'?msubValue:showValue }}{{ infoEndText }}</text>
</view>
</movable-view>
</movable-area>
<text class="bp-value" v-if="showValueState() || (infoAlign=='center'&&direction!='vertical' && showInfo)"
:style="{color:infoColor,fontSize:infoFontSize,left:valueLeft,width:valueWidth()+'px'}">{{ infoContent=='subValue'?msubValue:showValue }}{{ infoEndText }}</text>
</view>
</template>
<script>
/**
* 进度条副进度条
*/
export default {
created() {
/**
* 获取系统屏幕信息用于后续单位换算
*/
const systemInfo = uni.getSystemInfoSync()
this.px2rpx = 750 / systemInfo.screenWidth
this.screenWidth = systemInfo.screenWidth
this.screenHeight = systemInfo.screenHeight
},
mounted() {
// #ifndef APP-NVUE
/**
* 非NVUE滑动事件获取到的位置是相对于屏幕的获取组件位置用于计算滑块位置
*/
let query = uni.createSelectorQuery().in(this)
query.select('.bing-progress').boundingClientRect(data => {
this.mainInfo.top = data.top
this.mainInfo.left = data.left
this.mainInfo.bottom = data.bottom
this.mainInfo.right = data.right
}).exec()
// #endif
this.percent = Math.abs((this.valueFormat(this.value) - this.min) / (this.max - this.min))
this.subPercent = Math.abs((this.valueFormat(this.subValue,true) - this.min) / (this.max - this.min))
if(this.reverse) {
if(this.direction!='vertical') {
this.handleX = (1 - this.percent) * this.barMaxLength
}
else {
this.handleY = this.percent * this.barMaxLength
}
}
else {
if(this.direction!='vertical') {
this.handleX = this.percent * this.barMaxLength
}
else {
this.handleY = (1 - this.percent) * this.barMaxLength
}
}
},
/**
* sub表示副进度条属性
*/
props: {
width: {
type: String,
default: '300px'
},
strokeWidth: {
type: String,
default: '30px'
},
backgroundColor: {
type: String,
default: 'rgba(0,0,0,0)'
},
noActiveColor: {
type: String,
default: "#00ffff"
},
activeColor: {
type: String,
default: "#0000ff"
},
subActiveColor: {
type: String,
default: "#ffaaaa"
},
handleColor: {
type: String,
default: "#ffff00"
},
infoColor: {
type: String,
default: "#000000"
},
//
borderRadius: {
type: String,
default: '5px'
},
//
barBorderRadius: {
type: String,
default: '5px'
},
// active and sunActive NVUEtruefalse
// #ifdef APP-NVUE
isActiveCircular: {
type: Boolean,
default: true
},
// #endif
// #ifndef APP-NVUE
isActiveCircular: {
type: Boolean,
default: false
},
// #endif
handleWidth: {
type: String,
default: '50px'
},
handleHeight: {
type: String,
default: '40px'
},
handleBorderRadius: {
type: String,
default: '5px'
},
handleImgUrl: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
direction: {
type: String,
default: 'horizontal'
},
infoEndText: {
type: String,
default: ""
},
infoFontSize: {
type: String,
default: '18px'
},
showInfo: {
type: Boolean,
default: true
},
// valuesubValue
infoContent: {
type: String,
default: 'value'
},
infoAlign: {
type: String,
default: 'right'
},
max: {
type: Number,
default: 100
},
min: {
type: Number,
default: 0
},
value: {
type: Number,
default: 0
},
subValue: {
type: Number,
default: 0
},
step: {
type: Number,
default: 1
},
//
subStep: {
type: Number,
default: 1
},
// truefalsestep
continuous: {
type: Boolean,
default: true
},
// continuous
subContinuous: {
type: Boolean,
default: true
},
reverse: {
type: Boolean,
default: false
},
},
data() {
return {
handleX: 50,
handleY: 0,
px2rpx: 1,
percent: 0, // 0-1
subPercent: 0, // 0-1
mainInfo: {
left: 0,
top: 0,
bottom: 0,
right: 0
},
touchState: false,
screenHeight: 0,
screenWidth: 0,
msubValue: 0
}
},
watch: {
/**
* @param {Object} newValue
* @param {Object} oldValue
*/
value(newValue, oldValue) {
if(!this.touchState) {
newValue = this.valueSetBoundary(newValue)
this.percent = Math.abs((newValue - this.min) / (this.max - this.min))
}
},
showValue(newValue, oldValue) {
//
if(!this.continuous) {
if(this.reverse) {
if(this.direction!='vertical') {
this.handleX = Math.abs(1 - (newValue - this.min) / (this.max - this.min)) * this.barMaxLength
}
else {
this.handleY = Math.abs((newValue - this.min) / (this.max - this.min)) * this.barMaxLength
}
}
else {
if(this.direction!='vertical') {
this.handleX = Math.abs((newValue - this.min) / (this.max - this.min)) * this.barMaxLength
}
else {
this.handleY = (1 - Math.abs((newValue - this.min) / (this.max - this.min))) * this.barMaxLength
}
}
}
this.$emit("change", {type: 'change',value:this.showValue,subValue:this.msubValue})
this.$emit("valuechange", {type: 'valuechange',value:this.showValue,subValue:this.msubValue})
},
percent(newValue, oldValue) {
//
if(this.continuous) {
if(this.reverse) {
if(this.direction!='vertical') {
this.handleX = (1 - newValue) * this.barMaxLength
}
else {
this.handleY = newValue * this.barMaxLength
}
}
else {
if(this.direction!='vertical') {
this.handleX = newValue * this.barMaxLength
}
else {
this.handleY = (1 - newValue) * this.barMaxLength
}
}
}
},
subValue(newValue, oldValue) {
newValue = this.valueSetBoundary(newValue)
if(this.subContinuous) {
this.msubValue = newValue
}
else {
this.msubValue = this.valueFormat(newValue, true)
}
this.subPercent = Math.abs((newValue - this.min) / (this.max - this.min))
this.$emit("change", {type: 'change',value:this.showValue,subValue:this.msubValue})
this.$emit("subvaluechange", {type: 'subvaluechange',value:this.showValue,subValue:this.msubValue})
},
},
computed: {
bpWidth() {
if(this.direction=="vertical") {
return this.maxHeight()[2]
}
return this.sizeDeal(this.width)[2]
},
bpHeight() {
if(this.direction=="vertical") {
return this.sizeDeal(this.width)[2]
}
return this.maxHeight()[2]
},
mareaWidth() {
if(this.direction=="vertical") {
return this.maxHeight()[2]
}
let width = this.sizeDeal(this.width)[0]
return (width - this.textWidth()) + 'px'
},
mareaHeight() {
if(this.direction=="vertical") {
let width = this.sizeDeal(this.width)[0]
return (width - this.textWidth()) + 'px'
}
return this.maxHeight()[2]
},
mareaLeft() {
if(this.showValueState()) {
if(this.infoAlign == 'left') {
return this.textWidth() + 'px'
}
}
return 0
},
barMaxHeight() {
if(this.direction=="vertical") {
let width = this.sizeDeal(this.width)[0]
let handleWidth = this.sizeDeal(this.handleWidth)
return (width - this.textWidth() - handleWidth[0]) + 'px'
}
return this.sizeDeal(this.strokeWidth)[2]
},
barMaxWidth() {
if(this.direction=="vertical") {
return this.sizeDeal(this.strokeWidth)[2]
}
let width = this.sizeDeal(this.width)[0]
let handleWidth = this.sizeDeal(this.handleWidth)
return (width - this.textWidth() - handleWidth[0]) + 'px'
},
barMaxLeft() {
if(this.showValueState()) {
if(this.infoAlign == 'left') {
return this.textWidth() + this.sizeDeal(this.handleWidth)[0] / 2 + 'px'
}
}
if(this.direction != 'vertical') {
return this.sizeDeal(this.handleWidth)[0] / 2 + 'px'
}
// vertical
return (this.maxHeight()[0] - this.sizeDeal(this.strokeWidth)[0]) / 2 + 'px'
},
activeRight() {
if(this.reverse) {
return 0
}
return 'unset'
},
activeLeft() {
if(this.reverse) {
return 'unset'
}
return 0
},
activeTop() {
if(this.reverse) {
return 0
}
return 'unset'
},
activeBottom() {
if(this.reverse) {
return 'unset'
}
return 0
},
barActiveWidth() {
if(this.direction=="vertical") {
return this.sizeDeal(this.strokeWidth)[2]
}
let percent
if(this.continuous) {
percent = this.percent
}
else {
percent = Math.abs((this.showValue - this.min) / (this.max - this.min))
}
return this.barMaxLength * percent + 'px'
},
barActiveHeight() {
if(this.direction=="vertical") {
let percent
if(this.continuous) {
percent = this.percent
}
else {
percent = Math.abs((this.showValue - this.min) / (this.max - this.min))
}
return this.barMaxLength * percent + 'px'
}
return this.sizeDeal(this.strokeWidth)[2]
},
subActiveTop() {
if(this.reverse) {
return 0
}
return 'unset'
},
subActiveBottom() {
if(this.reverse) {
return 'unset'
}
return 0
},
subActiveRight() {
if(this.reverse) {
return 0
}
return 'unset'
},
subActiveLeft() {
if(this.reverse) {
return 'unset'
}
return 0
},
barSubActiveWidth() {
if(this.direction == "vertical") {
return this.sizeDeal(this.strokeWidth)[2]
}
if(this.subContinuous) {
return this.barMaxLength * this.subPercent + 'px'
}
else {
return this.barMaxLength * Math.abs((this.msubValue - this.min) / (this.max - this.min)) + 'px'
}
},
barSubActiveHeight() {
if(this.direction == "vertical") {
if(this.subContinuous) {
return this.barMaxLength * this.subPercent + 'px'
}
else {
this.barMaxLength * Math.abs((this.msubValue - this.min) / (this.max - this.min)) + 'px'
}
}
return this.sizeDeal(this.strokeWidth)[2]
},
mhandleWidth() {
if(this.direction == "vertical") {
return this.sizeDeal(this.handleHeight)[2]
}
return this.sizeDeal(this.handleWidth)[2]
},
mhandleHeight() {
if(this.direction == "vertical") {
return this.sizeDeal(this.handleWidth)[2]
}
return this.sizeDeal(this.handleHeight)[2]
},
mhandleTop() {
if(this.direction == 'vertical') {
return 0
}
else {
//
let handle = this.sizeDeal(this.handleHeight)[0]
let top = this.maxHeight()[0] / 2 - handle / 2 + 'px'
return top
}
},
showValue() {
return this.valueFormat(this.percent * (this.max - this.min) + this.min)
},
textHeight() {
let infoSize = this.sizeDeal(this.infoFontSize)
return infoSize[0]*1.2 + infoSize[1]
},
valueLeft() {
if(this.infoAlign=='left') {
return 0
}
else if(this.infoAlign == 'center') {
let width = this.sizeDeal(this.width)
return width[0]/2 - this.valueWidth()/2 + 'px'
}
else if(this.infoAlign=='right'){
let width = this.sizeDeal(this.width)
return width[0] - this.textWidth() + 'px'
}
return 0
},
barMaxLength() {
let width = this.sizeDeal(this.width)[0]
let handleWidth = this.sizeDeal(this.handleWidth)
return width - this.textWidth() - handleWidth[0]
},
},
methods: {
touchstart(e) {
if(!this.disabled) {
this.touchState = true
let detail = e.changedTouches[0]
this.handleMove(detail)
this.$emit("dragstart", {type: 'dragstart',value:this.showValue,subValue:this.msubValue})
}
},
touchmove(e) {
if(!this.disabled) {
e.stopPropagation()
let detail = e.changedTouches[0]
this.handleMove(detail)
this.$emit("dragging", {type: 'dragging',value:this.showValue,subValue:this.msubValue})
}
},
touchend(e) {
if(!this.disabled) {
let detail = e.changedTouches[0]
this.handleMove(detail)
this.touchState = false
this.$emit("dragend", {type: 'dragend',value:this.showValue,subValue:this.msubValue})
}
},
handleMove(detail) {
let width = this.sizeDeal(this.width)[0]
let handleWidth = this.sizeDeal(this.handleWidth)
let percent
if(this.direction!='vertical') {
if(this.infoAlign=='left') {
// #ifndef APP-NVUE
percent = (detail.pageX - this.mainInfo.left - this.textWidth() - handleWidth[0]/2)/ this.barMaxLength
// #endif
// #ifdef APP-NVUE
percent = (detail.pageX - handleWidth[0]/2)/ this.barMaxLength
// #endif
}
else {
// #ifndef APP-NVUE
percent = (detail.pageX - this.mainInfo.left - handleWidth[0]/2)/ this.barMaxLength
// #endif
// #ifdef APP-NVUE
percent = (detail.pageX - handleWidth[0]/2)/ this.barMaxLength
// #endif
}
}
else {
// #ifdef APP-NVUE
percent = 1 - (detail.pageY - handleWidth[0]/2- 1) / this.barMaxLength
// #endif
// #ifndef APP-NVUE
percent = 1 - (detail.pageY - this.mainInfo.top - handleWidth[0]/2)/ this.barMaxLength
// #endif
}
percent = percent > 0 ? percent : 0
percent = percent < 1 ? percent : 1
if(this.reverse) {
this.percent = 1 - percent
}
else {
this.percent = percent
}
},
showValueState() {
if(this.direction != 'vertical' && this.showInfo && (this.infoAlign=='left' || this.infoAlign=='right')) {
return true
}
return false
},
valueSetBoundary(value) {
// value
if(this.max > this.min) {
value = value < this.max ? value : this.max
value = value > this.min ? value : this.min
}
else {
value = value > this.max ? value : this.max
value = value < this.min ? value : this.min
}
return value
},
/**
* @param {Object} v
* @param {Object} isSub 是否是副副进度条
*/
valueFormat (v,isSub){
// set step
v = this.valueSetBoundary(v)
let stepInfo = this.stepInfo(isSub)
v = Number(v - this.min).toFixed(stepInfo[1])
let step = stepInfo[0] * 10 ** stepInfo[1]
let valueE = v * 10 ** stepInfo[1]
let remainder = valueE % step
let remainderInt = Math.floor(remainder)
// 0-1
let sub = Math.round(remainder / step)
let value = (Math.floor(valueE) - remainderInt + sub*step) / (10 ** stepInfo[1])
value = Number((value + this.min).toFixed(stepInfo[1]))
return value
},
/**
* @param {Object} v
* @param {Object} isSub 是否是副副进度条
*/
stepInfo(isSub) {
// return step, decimal
let step
if(isSub) {
step = Number(this.subStep)
}
else {
step = Number(this.step)
}
if (step <= 0 || !step){
return [1, 0]
}
else{
let steps = step.toString().split('.')
if (steps.length == 1){
return [step,0]
}
else {
return [step,steps[1].length]
}
}
},
textWidth() {
if(this.showValueState()) {
let numWidth = this.max.toString().length> this.min.toString().length? this.max.toString().length: this.min.toString().length
let textWidth = ((numWidth + this.stepInfo()[1]) * 0.7 + this.infoEndText.length) * this.sizeDeal(this.infoFontSize)[0]
return Number(textWidth.toFixed(2))
}
return 0
},
valueWidth() {
let numWidth = this.max.toString().length> this.min.toString().length? this.max.toString().length: this.min.toString().length
let textWidth = ((numWidth + this.stepInfo()[1]) * 0.7 + this.infoEndText.length) * this.sizeDeal(this.infoFontSize)[0]
return Number(textWidth.toFixed(2))
},
maxHeight() {
let h = []
if (this.direction!='vertical'){ // direction vertical info
let subt = this.infoEndText.match(/[^\x00-\xff]/g)
if (subt){
h.push(this.sizeDeal(this.infoFontSize)[0] * 1.1)
}
else{
h.push(this.sizeDeal(this.infoFontSize)[0])
}
}
h.push(this.sizeDeal(this.strokeWidth)[0])
h.push(this.sizeDeal(this.handleHeight)[0])
h.sort(function(a, b) {
return b - a
}) //
return [h[0], 'px', h[0] + 'px']
},
sizeDeal(size) {
// ,rpx px
let s = Number.isNaN(parseFloat(size)) ? 0 : parseFloat(size)
let u = size.toString().replace(/[0-9\.]/g, '')
if (u == 'rpx') {
s /= this.px2rpx
u = 'px'
}else if (u == 'vw') {
u = 'px'
s = s / 100 * this.screenWidth
} else if(u == 'vh') {
u = 'px'
s = s / 100 * this.screenHeight
} else{
u = 'px'
}
return [s, u, s + u]
},
}
}
</script>
<style scoped>
@import "bing-progress.css"
</style>

113
components/dt-dropdown/dt-dropdown.vue

@ -0,0 +1,113 @@
<template>
<view>
<view @click="showShadow" class="dropWrap">{{ list[current] }}<view class="sanjiao"></view></view>
<view class="dropdown">
<view :class="showIf ? 'dropdown-mask' : 'undropdown-mask'" @click="hideShadow"></view>
<!-- <view class="ul" :style="showIf?'height:'+list.length*30+'px':''"> -->
<view class="ul" :style="'--i:'+list.length" :class="showIf?'show':''"> <!-- 不支持就用上面那种 -->
<view class="li" v-for="(item, index) in list" :key="index" @click="handlerItem(index)">{{ item }}</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'dropdown',
props: {
list: {
type: Array,
default: () => []
},
current: {
type: [String, Number],
default: 0
}
},
data() {
return {
showIf: false
};
},
methods: {
handlerItem(value) {
this.showIf = false
this.$emit('onClick', value);
},
hideShadow() {
this.showIf = false;
},
showShadow() {
this.showIf = true;
}
}
};
</script>
<style scoped lang="scss">
.dropWrap {
box-sizing: border-box;
width: 96px;
height: 26px;
border: 1px solid #e8ecef;
font-size: 12px;
color: #8c9fad;
padding: 4px 8px;
display: flex;
align-items: center;
justify-content: space-between;
.sanjiao{
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right:6px solid transparent;
border-top:6px solid #C5CFD5;
border-bottom:6px solid transparent;
transform: translateY(3px);
}
}
.dropdown {
position: absolute;
z-index: 100;
.ul {
width: 96px;
position: relative;
z-index: 101;
list-style: none;
background-color: #fff;
border-radius: 4rpx;
padding-left: 0;
box-shadow: 6rpx 6rpx 10rpx rgba(122, 122, 122, 0.2);
transition: all 0.2s;
height: 0;
overflow: hidden;
.li {
box-sizing: border-box;
color: #000;
height: 30px;
border-bottom: 1px solid #e6eaeb;
font-size: 24rpx;
line-height: 30px; //
padding-left: 8px;
}
.li:last-child {
border-bottom: none;
}
}
.show {
height: calc( var(--i) * 30px ); //
}
.dropdown-mask {
top: 0;
left: 0;
position: fixed;
width: 100vw;
height: 100vh;
z-index: 100;
pointer-events: auto;
}
.undropdown-mask {
pointer-events: none;
}
}
</style>

454
components/lb-picker/README.md

@ -0,0 +1,454 @@
<p align="center">
<a href="https://github.com/liub1934/uni-lb-picker">
<img src="https://img.shields.io/github/stars/liub1934/uni-lb-picker">
</a>
<a href="https://github.com/liub1934/uni-lb-picker/fork">
<img src="https://img.shields.io/github/forks/liub1934/uni-lb-picker">
</a>
<a href="https://github.com/liub1934/uni-lb-picker/issues">
<img src="https://img.shields.io/github/issues/liub1934/uni-lb-picker">
</a>
<a href="https://www.npmjs.com/package/uni-lb-picker">
<img src="https://img.shields.io/npm/v/uni-lb-picker">
</a>
<a href="https://npmcharts.com/compare/uni-lb-picker?minimal=true">
<img src="https://img.shields.io/npm/dm/uni-lb-picker">
</a>
<a href="https://standardjs.com">
<img src="https://img.shields.io/badge/code%20style-standard-brightgreen">
</a>
<a href="https://github.com/liub1934/uni-lb-picker/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/liub1934/uni-lb-picker">
</a>
</p>
插件市场里面的 picker 选择器不满足自己的需求,所以自己写了一个简单的 picker 选择器,可扩展、可自定义,一般满足日常需要。
Github:[uni-lb-picker](https://github.com/liub1934/uni-lb-picker)
插件市场:[uni-lb-picker](https://ext.dcloud.net.cn/plugin?id=1111)
H5 Demo:[点击预览](https://github.liubing.me/uni-lb-picker)
> 如果问题最好去 github 反馈,插件市场评论区留下五星好评即可,[点我去反馈](https://github.com/liub1934/uni-lb-picker/issues/new)
> **由于之前`cancel`拼写失误,写成了`cancle`,`v1.08`现已修正,如果之前版本有使用`cancel`事件的,更新后请及时修正。**
## 兼容性
App + H5 + 各平台小程序(快应用及 360 未测试,nvue 待支持)
## 功能
1、单选
2、多级联动,非多级联动,理论支持任意级数
3、省市区选择,基于多级联动
4、自定义选择器头部确定取消按钮颜色及插槽支持
5、选择器可视区自定义滚动个数
6、自定义数据字段,满足不同人的需求
7、自定义选择器样式
8、单选及非联动选择支持扁平化的简单数据,如下形式:
```javascript
// 单选列表
list1: ['选项1', '选项2', '选项2'],
// 非联动选择列表
list2: [
['选项1', '选项2', '选项3'],
['选项11', '选项22', '选项33'],
['选项111', '选项222', '选项333']
]
```
## 引入插件
单独引入,在需要使用的页面上 import 引入即可
```html
<template>
<view>
<lb-picker></lb-picker>
</view>
</template>
<script>
import LbPicker from '@/components/lb-picker'
export default {
components: {
LbPicker
}
}
</script>
```
全局引入,`main.js`中 import 引入并注册即可全局使用
```jsvascript
import LbPicker from '@/components/lb-picker'
Vue.component("lb-picker", LbPicker)
```
easycom 引入
`pages.json`加上如下配置:
```json
"easycom": {
"autoscan": true,
"custom": {
"lb-picker": "@/components/lb-picker/index.vue"
}
}
```
npm 安装引入:
```shell
npm install uni-lb-picker
```
```jsvascript
import LbPicker from 'uni-lb-picker'
```
## 选择器数据格式
### 单选
常规数据
```javascript
list: [
{
label: '选项1',
value: '1'
},
{
label: '选项2',
value: '2'
}
]
```
扁平化简单数据
```javascript
list: ['选项1', '选项2']
```
### 多级联动
```javascript
list: [
{
label: '选项1',
value: '1',
children: [
{
label: '选项1-1',
value: '1-1',
children: [
{
label: '选项1-1-1',
value: '1-1-1'
}
]
}
]
}
]
```
### 非联动选择
常规数据
```javascript
list: [
[
{ label: '选项1', value: '1' },
{ label: '选项2', value: '2' },
{ label: '选项3', value: '3' }
],
[
{ label: '选项11', value: '11' },
{ label: '选项22', value: '22' },
{ label: '选项33', value: '33' }
],
[
{ label: '选项111', value: '111' },
{ label: '选项222', value: '222' },
{ label: '选项333', value: '333' }
]
]
```
扁平化简单数据
```javascript
list: [
['选项1', '选项2', '选项3'],
['选项11', '选项22', '选项33'],
['选项111', '选项222', '选项333']
]
```
## 调用显示选择器
通过`ref`形式手动调用`show`方法显示,隐藏同理调用`hide`
```html
<lb-picker ref="picker"></lb-picker>
```
```javascript
this.$refs.picker.show() // 显示
this.$refs.picker.hide() // 隐藏
```
`v1.1.3`新增,将需要点击的元素包裹在`lb-picker`中即可。
```html
<lb-picker>
<button>点我直接打开选择器</button>
</lb-picker>
```
## 绑定值及设置默认值
支持 vue 中`v-model`写法绑定值,无需自己维护选中值的索引。
```javascript
<lb-picker v-model="value1"></lb-picker>
<lb-picker v-model="value2"></lb-picker>
data () {
return {
value1: '' // 单选
value2: [] // 多列联动选择
}
}
```
## 多个选择器
通过设置不同的`ref`,然后调用即可
```javascript
<lb-picker ref="picker1"></lb-picker>
<lb-picker ref="picker2"></lb-picker>
this.$refs.picker1.show() // picker1显示
this.$refs.picker2.show() // picker2显示
```
## 省市区选择
省市区选择是基于多列联动选择,数据来源:[https://github.com/modood/Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China),
省市区文件位于`/pages/demos/area-data-min.js`,自行引入即可,可参考`demo3省市区选择`,
也可使用自己已有的省市区数据,如果数据字段不一样,也可以自定义,参考下方自定义数据字段。
## 自定义数据字段
为了满足不同人的需求,插件支持自定义数据字段名称, 插件默认的数据字段如下形式:
```javascript
list: [
{
label: '选择1',
value: 1,
children: []
},
{
label: '选择1',
value: 1,
children: []
}
]
```
如果你的数据字段和上面不一样,如下形式:
```javascript
list: [
{
text: '选择1',
id: 1,
child: []
},
{
text: '选择1',
id: 1,
child: []
}
]
```
通过设置参数中的`props`即可,如下所示:
```javascript
<lb-picker :props="myProps"></lb-picker>
data () {
return {
myProps: {
label: 'text',
value: 'id',
children: 'child'
}
}
}
```
## 插槽使用
选择器支持一些可自定义化的插槽,如选择器的取消和确定文字按钮,如果需要对其自定义处理的话,比如加个 icon 图标之类的,可使用插槽,使用方法如下:
```html
<lb-picker>
<view slot="cancel-text">我是自定义取消</view>
<view slot="confirm-text">我是自定义确定</view>
</lb-picker>
```
也可参考示例中的`demo5`,自定义插槽元素样式交给开发者自由调整,插槽仅提供预留位置。
其他插槽见下。
## 参数及事件
### Props
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | :------------------ | :--------------------------------------------------------------- | :------------------------------------------------ |
| value/v-model | 绑定值,联动选择为 Array 类型 | String/Number/Array | - | - |
| mode | 选择器类型,支持单列,多列联动 | String | selector 单选/multiSelector 多级联动/unlinkedSelector 多级非联动 | selector |
| list | 选择器数据(v1.0.7 单选及非联动多选支持扁平数据:['选项 1', '选项 2']) | Array | - | - |
| level | 多列联动层级,仅 mode 为 multiSelector 有效 | Number | - | 2 |
| props | 自定义数据字段 | Object | - | {label:'label',value:'value',children:'children'} |
| cancel-text | 取消文字 | String | - | 取消 |
| cancel-color | 取消文字颜色 | String | - | #999 |
| confirm-text | 确定文字 | String | - | 确定 |
| confirm-color | 确定文字颜色 | String | - | #007aff |
| empty-text | (v1.0.7 新增)选择器列表为空的时候显示的文字 | String | - | 暂无数据 |
| empty-color | (v1.0.7 新增)暂无数据文字颜色 | String | - | #999 |
| column-num | 可视滚动区域内滚动个数,最好设置奇数值 | Number | - | 5 |
| radius | 选择器顶部圆角,支持 rpx,如 radius="10rpx" | String | - | - |
| ~~column-style~~ | ~~选择器默认样式(已弃用,见下方自定义样式说明)~~ | Object | - | - |
| ~~active-column-style~~ | ~~选择器选中样式(已弃用,见下方自定义样式说明)~~ | Object | - | - |
| loading | 选择器是否显示加载中,可使用 loading 插槽自定义加载效果 | Boolean | - | - |
| mask-color | 遮罩层颜色 | String | - | rgba(0, 0, 0, 0.4) |
| show-mask | (v1.1.0 新增)是否显示遮罩层 | Boolean | true/false | true |
| close-on-click-mask | 点击遮罩层是否关闭选择器 | Boolean | true/false | true |
| ~~change-on-init~~ | ~~(v1.0.7 已弃用)初始化时是否触发 change 事件~~ | Boolean | true/false | - |
| dataset | (v1.0.7 新增)可以向组件中传递任意的自定义的数据(对象形式数据),如`:dataset="{name:'test'}"`,在`confirm`或`change`事件中可以取到 | Object | - | - |
| show-header | (v1.0.8 新增)是否显示选择器头部 | Boolean | - | true |
| inline | (v1.0.8 新增)inline 模式,开启后默认显示选择器,无需点击弹出,可以配合`show-header`一起使用 | Boolean | - | - |
| z-index | (v1.0.9 新增)选择器层级,遮罩层默认-1 | Number | - | 999 |
### 方法
| 方法名 | 说明 | 参数 | 返回值 |
| :------------- | :------------------------------------- | :-------------- | :----------------------------------------------------------------------------------------------------------- |
| show | 打开选择器 | - | |
| hide | 关闭选择器 | - | |
| getColumnsInfo | (v1.1.0 新增)根据 value 获取选择器信息 | 绑定值的`value` | 同`change` `confirm`回调参数,如果传入的`value`获取不到信息则只返回一个含有`dataset`的对象,具体自行打印查看 |
### Events
| 事件名称 | 说明 | 回调参数 |
| :------- | :--------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| show | 选择器打开时触发 | - |
| hide | 选择器隐藏时触发 | - |
| change | 选择器滚动时触发,此时不会改变绑定的值 | `{ index, item, value, change }` `index`触发滚动后新的索引,单选时是具体的索引值,多列联动选择时为数组。`item`触发滚动后新的的完整内容,包括`label`、`value`等,单选时为对象,多列选择时为数组对象。`value`触发滚动后新的 value 值,单列选择时为具体值,多列联动选择时为数组。`change`触发事件的类型,详情参考下面的 change 事件备注 |
| confirm | 点击选择器确定时触发,此时会改变绑定的值 | 同上`change`事件说明 |
| cancel | 点击选择器取消时触发 | 同上`change`事件说明 |
### `change` 事件备注
如果绑定的值是空的,`change`触发后里面的内容都是列表的第一项。
`change`事件会在以下情况触发:
- 初始化
- 绑定值 value 变化
- 选择器 list 列表变化
- 滚动选择器
以上情况会在回调函数中都可以取到`change`变化的类型,对应上面的情况包括以下:
- `init`
- `value`
- `list`
- `scroll`
根据这些类型大家可以在`change`的时候按需处理自己的业务逻辑,`init`现在指挥在调用选择器弹出的时候触发。
下面的说明情况已失效,如需要在页面显示的时候根据`value`的值显示相应的中文,调用`v1.10`新增的方法`getColumnsInfo`,传入绑定的值即可获取到你想要的所有信息。
~~比如一种常见的情况,有默认值的时候需要显示默认值的文字,此时可以`change`事件中判断`change`的类型是否是`init`,如果是的话可以取事件回调中的`item`进行显示绑定值对应的文字信息。~~
```javascript
handleChange (e) {
if (e.change === 'init') {
console.log(e.item.label) // 单选 选项1
console.log(e.item.map(item => item.label).join('-')) // 多选 选项1-选项11
}
}
```
### 插槽
| 插槽名 | 说明 |
| :------------ | :--------------------- |
| cancel-text | 选择器取消文字插槽 |
| action-center | 选择器顶部中间插槽 |
| confirm-text | 选择器确定文字插槽 |
| loading | 选择器 loading 插槽 |
| empty | 选择器 空数据 插槽 |
| header-top | 选择器头部顶部插槽 |
| header-bottom | 选择器头部底部插槽 |
| picker-top | 选择器滚动部分顶部插槽 |
| picker-bottom | 选择器滚动部分底部插槽 |
### 选择器自定义样式
原先的`column-style`和`active-column-style`已弃用,如需修改默认样式及选中样式参考`demo9`
```css
<style lang="scss" scoped>
/deep/ .lb-picker {
.lb-picker-column-label {
color: #f0ad4e;
}
.lb-picker-column-active {
.lb-picker-column-label {
color: #007aff;
font-weight: 700;
}
}
}
</style>
```
### 获取选中值的文字
`@confirm`事件中可以拿到:
单选:
```javascript
handleConfirm (e) {
console.log(e.item.label) // 选项1
}
```
联动选择:
```javascript
handleConfirm (e) {
console.log(e.item.map(item => item.label).join('-')) // 选项1-选项11
}
```
## Tips
微信小程序端,滚动时在 iOS 自带振动反馈,可在系统设置 -> 声音与触感 -> 系统触感反馈中关闭
## 其他
其他功能参考示例 Demo 代码。

369
components/lb-picker/index.vue

File diff suppressed because one or more lines are too long

46
components/lb-picker/mixins/index.js

@ -0,0 +1,46 @@
import { getColumns } from '../utils'
export const commonMixin = {
data () {
return {
isConfirmChange: false,
indicatorStyle: `height: 34px`
}
},
created () {
this.init('init')
},
methods: {
init (changeType) {
if (this.list && this.list.length) {
const column = getColumns({
value: this.value,
list: this.list,
mode: this.mode,
props: this.props,
level: this.level
})
const { columns, value, item, index } = column
this.selectValue = value
this.selectItem = item
this.pickerColumns = columns
this.pickerValue = index
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: this.pickerValue,
change: changeType
})
}
}
},
watch: {
value () {
if (!this.isConfirmChange) {
this.init('value')
}
},
list () {
this.init('list')
}
}
}

94
components/lb-picker/pickers/multi-selector-picker.vue

@ -0,0 +1,94 @@
<template>
<view class="lb-multi-selector lb-picker-item"
:style="{ height: height }">
<picker-view :value="pickerValue"
:indicator-style="indicatorStyle"
:style="{ height: height }"
@change="handleChange">
<picker-view-column v-for="(column, index) in pickerColumns"
:key="index">
<view v-for="(item, i) in column || []"
:class="[
'lb-picker-column',
item[props.value] === selectValue[index]
? 'lb-picker-column-active'
: ''
]"
:key="i">
<text class="lb-picker-column-label">
{{ item[props.label] || item }}
</text>
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import { commonMixin } from '../mixins'
export default {
props: {
value: Array,
list: Array,
mode: String,
props: Object,
level: Number,
visible: Boolean,
height: String
},
mixins: [commonMixin],
data () {
return {
pickerValue: [],
pickerColumns: [],
selectValue: [],
selectItem: []
}
},
methods: {
handleChange (item) {
const pickerValue = item.detail.value
const columnIndex = pickerValue.findIndex(
(item, i) => item !== this.pickerValue[i]
)
const valueIndex = pickerValue[columnIndex]
this.setPickerChange(pickerValue, valueIndex, columnIndex)
},
setPickerChange (pickerValue, valueIndex, columnIndex) {
for (let i = 0; i < this.level; i++) {
if (i > columnIndex) {
pickerValue[i] = 0
const column =
this.pickerColumns[i - 1][valueIndex] ||
this.pickerColumns[i - 1][0]
this.$set(this.pickerColumns, i, column[this.props.children] || [])
valueIndex = 0
}
this.$set(this.pickerValue, i, pickerValue[i])
const selectItem = this.pickerColumns[i][pickerValue[i]]
if (selectItem) {
this.selectItem[i] = selectItem
this.selectValue[i] = selectItem[this.props.value]
} else {
const spliceNum = this.level - i
this.pickerValue.splice(i, spliceNum)
this.selectValue.splice(i, spliceNum)
this.selectItem.splice(i, spliceNum)
this.pickerColumns.splice(i, spliceNum)
break
}
}
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: this.pickerValue,
change: 'scroll'
})
}
}
}
</script>
<style lang="scss" scoped>
@import "../style/picker-item.scss";
</style>

67
components/lb-picker/pickers/selector-picker.vue

@ -0,0 +1,67 @@
<template>
<view class="lb-selector-picker lb-picker-item"
:style="{ height: height }">
<picker-view :value="pickerValue"
:style="{ height: height }"
:indicator-style="indicatorStyle"
@change="handleChange">
<picker-view-column>
<view v-for="(item, i) in list"
:class="[
'lb-picker-column',
(item[props.value] || item) === selectValue
? 'lb-picker-column-active'
: ''
]"
:key="i">
<text class="lb-picker-column-label">
{{ item[props.label] || item }}
</text>
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import { isObject } from '../utils'
import { commonMixin } from '../mixins'
export default {
props: {
value: [String, Number],
list: Array,
mode: String,
props: Object,
visible: Boolean,
height: String
},
mixins: [commonMixin],
data () {
return {
pickerValue: [],
selectValue: '',
selectItem: null
}
},
methods: {
handleChange (item) {
const index = item.detail.value[0] || 0
this.selectItem = this.list[index]
this.selectValue = isObject(this.selectItem)
? this.selectItem[this.props.value]
: this.selectItem
this.pickerValue = item.detail.value
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: index,
change: 'scroll'
})
}
}
}
</script>
<style lang="scss" scoped>
@import "../style/picker-item.scss";
</style>

75
components/lb-picker/pickers/unlinked-selector-picker.vue

@ -0,0 +1,75 @@
<template>
<view class="lb-selector-picker lb-picker-item"
:style="{ height: height }">
<picker-view :value="pickerValue"
:indicator-style="indicatorStyle"
:style="{ height: height }"
@change="handleChange">
<picker-view-column v-for="(column, index) in pickerColumns"
:key="index">
<view v-for="(item, i) in column || []"
:class="[
'lb-picker-column',
(item[props.value] || item) === selectValue[index]
? 'lb-picker-column-active'
: ''
]"
:key="i">
<text class="lb-picker-column-label">
{{ item[props.label] || item }}
</text>
</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import { isObject } from '../utils'
import { commonMixin } from '../mixins'
export default {
props: {
value: Array,
list: Array,
mode: String,
props: Object,
visible: Boolean,
height: String
},
mixins: [commonMixin],
data () {
return {
pickerValue: [],
pickerColumns: [],
selectValue: [],
selectItem: []
}
},
methods: {
handleChange (item) {
const pickerValue = item.detail.value
const columnIndex = pickerValue.findIndex((item, i) => item !== this.pickerValue[i])
if (columnIndex > -1) {
const valueIndex = pickerValue[columnIndex]
const columnItem = this.list[columnIndex][valueIndex]
const valueItem = isObject(columnItem)
? columnItem[this.props.value]
: columnItem
this.pickerValue = pickerValue
this.$set(this.selectValue, columnIndex, valueItem)
this.$set(this.selectItem, columnIndex, columnItem)
this.$emit('change', {
value: this.selectValue,
item: this.selectItem,
index: this.pickerValue,
change: 'scroll'
})
}
}
}
}
</script>
<style lang="scss" scoped>
@import "../style/picker-item.scss";
</style>

23
components/lb-picker/style/picker-item.scss

@ -0,0 +1,23 @@
.lb-picker-column {
height: 34px;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
white-space: nowrap;
overflow: hidden;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.lb-picker-column-label {
font-size: 16px;
text-align: center;
text-overflow: ellipsis;
transition-property: color;
transition-duration: 0.3s;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

179
components/lb-picker/style/picker.scss

@ -0,0 +1,179 @@
.lb-picker {
position: relative;
}
.lb-picker-mask {
background-color: rgba(0, 0, 0, 0.0);
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
.lb-picker-mask-animation {
transition-property: background-color;
transition-duration: 0.3s;
}
.lb-picker-container {
position: relative;
}
.lb-picker-container-fixed {
position: fixed;
left: 0;
right: 0;
bottom: 0;
transform: translateY(100%);
/* #ifndef APP-PLUS */
overflow: hidden;
/* #endif */
}
.lb-picker-container-animation {
transition-property: transform;
transition-duration: 0.3s;
}
.lb-picker-container-show {
transform: translateY(0);
}
.lb-picker-header {
position: relative;
background-color: #fff;
/* #ifdef APP-NVUE */
border-bottom-width: 1px;
border-bottom-style: solid;
border-bottom-color: #e5e5e5;
border-top-width: 1px;
border-top-style: solid;
border-top-color: #e5e5e5;
/* #endif */
/* #ifndef APP-NVUE */
box-sizing: border-box;
/* #endif */
}
.lb-picker-header-actions {
height: 45px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
flex-wrap: nowrap;
}
/* #ifndef APP-PLUS */
.lb-picker-header::before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
height: 1px;
clear: both;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
.lb-picker-header::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
clear: both;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
/* #endif */
.lb-picker-action {
padding-left: 14px;
padding-right: 14px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.lb-picker-action-item {
text-align: center;
height: 45px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
}
.lb-picker-action-cancel-text {
font-size: 16px;
color: #999;
}
.lb-picker-action-confirm-text {
font-size: 16px;
color: #007aff;
}
.lb-picker-content {
position: relative;
background-color: #fff;
}
.lb-picker-content-main {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
flex-direction: column;
}
.lb-picker-loading,
.lb-picker-empty {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
}
.lb-picker-empty-text {
color: #999;
font-size: 16px;
}
.lb-picker-loading-img {
width: 25px;
height: 25px;
/* #ifndef APP-NVUE */
animation: rotating 2s linear infinite;
/* #endif */
}
/* #ifndef APP-NVUE */
@keyframes rotating {
0% {
transform: rotate(0deg)
}
to {
transform: rotate(1turn)
}
}
/* #endif */

110
components/lb-picker/utils.js

@ -0,0 +1,110 @@
/**
* 判断是否是对象
*
* @export
* @param {*} val
* @returns true/false
*/
export function isObject (val) {
return Object.prototype.toString.call(val) === '[object Object]'
}
/**
* 根据value获取columns信息
*
* @export
* @param {*} { value, list, mode, props, level }
* @param {number} [type=2] 查询不到value数据返回数据类型 1空值null 2默认第一个选项
* @returns
*/
export function getColumns ({ value, list, mode, props, level }, type = 2) {
let pickerValue = []
let pickerColumns = []
let selectValue = []
let selectItem = []
let columnsInfo = null
switch (mode) {
case 'selector':
let index = list.findIndex(item => {
return isObject(item) ? item[props.value] === value : item === value
})
if (index === -1 && type === 1) {
columnsInfo = null
} else {
index = index > -1 ? index : 0
selectItem = list[index]
selectValue = isObject(selectItem)
? selectItem[props.value]
: selectItem
pickerColumns = list
pickerValue = [index]
columnsInfo = {
index: pickerValue,
value: selectValue,
item: selectItem,
columns: pickerColumns
}
}
break
case 'multiSelector':
const setPickerItems = (data = [], index = 0) => {
if (!data.length) return
const defaultValue = value || []
if (index < level) {
const value = defaultValue[index] || ''
let i = data.findIndex(item => item[props.value] === value)
if (i === -1 && type === 1) return
i = i > -1 ? i : 0
pickerValue[index] = i
pickerColumns[index] = data
if (data[i]) {
selectValue[index] = data[i][props.value]
selectItem[index] = data[i]
setPickerItems(data[i][props.children] || [], index + 1)
}
}
}
setPickerItems(list)
if (!selectValue.length && type === 1) {
columnsInfo = null
} else {
columnsInfo = {
index: pickerValue,
value: selectValue,
item: selectItem,
columns: pickerColumns
}
}
break
case 'unlinkedSelector':
list.forEach((item, i) => {
let index = item.findIndex(item => {
return isObject(item)
? item[props.value] === value[i]
: item === value[i]
})
if (index === -1 && type === 1) return
index = index > -1 ? index : 0
const columnItem = list[i][index]
const valueItem = isObject(columnItem)
? columnItem[props.value]
: columnItem
pickerValue[i] = index
selectValue[i] = valueItem
selectItem[i] = columnItem
})
pickerColumns = list
if (!selectValue.length && type === 1) {
columnsInfo = null
} else {
columnsInfo = {
index: pickerValue,
value: selectValue,
item: selectItem,
columns: pickerColumns
}
}
break
}
return columnsInfo
}

148
components/uni-badge/uni-badge.vue

@ -0,0 +1,148 @@
<template>
<text v-if="text" :class="inverted ? 'uni-badge--' + type + ' uni-badge--' + size + ' uni-badge--' + type + '-inverted' : 'uni-badge--' + type + ' uni-badge--' + size" :style="badgeStyle" class="uni-badge" @click="onClick()">{{ text }}</text>
</template>
<script>
/**
* Badge 数字角标
* @description 数字角标一般和其它控件列表9宫格等配合使用用于进行数量提示默认为实心灰色背景
* @tutorial https://ext.dcloud.net.cn/plugin?id=21
* @property {String} text 角标内容
* @property {String} type = [default|primary|success|warning|error] 颜色类型
* @value default 灰色
* @value primary 蓝色
* @value success 绿色
* @value warning 黄色
* @value error 红色
* @property {String} size = [normal|small] Badge 大小
* @value normal 一般尺寸
* @value small 小尺寸
* @property {String} inverted = [true|false] 是否无需背景颜色
* @event {Function} click 点击 Badge 触发事件
* @example <uni-badge text="1"></uni-badge>
*/
export default {
name: 'UniBadge',
props: {
type: {
type: String,
default: 'default'
},
inverted: {
type: Boolean,
default: false
},
text: {
type: [String, Number],
default: ''
},
size: {
type: String,
default: 'normal'
}
},
data() {
return {
badgeStyle: ''
};
},
watch: {
text() {
this.setStyle()
}
},
mounted() {
this.setStyle()
},
methods: {
setStyle() {
this.badgeStyle = `width: ${String(this.text).length * 8 + 12}px`
},
onClick() {
this.$emit('click');
}
}
};
</script>
<style scoped>
.uni-badge {
/* #ifndef APP-PLUS */
display: flex;
box-sizing: border-box;
overflow: hidden;
/* #endif */
justify-content: center;
flex-direction: row;
height: 20px;
line-height: 20px;
color: #333;
border-radius: 100px;
background-color: #f1f1f1;
background-color: transparent;
text-align: center;
font-family: 'sans-serif';
font-size: 12px;
padding: 0px 6px;
}
.uni-badge--inverted {
padding: 0 5px 0 0;
color: #f1f1f1;
}
.uni-badge--default {
color: #333;
background-color: #f1f1f1;
}
.uni-badge--default-inverted {
color: #999;
background-color: transparent;
}
.uni-badge--primary {
color: #fff;
background-color: #007aff;
}
.uni-badge--primary-inverted {
color: #007aff;
background-color: transparent;
}
.uni-badge--success {
color: #fff;
background-color: #4cd964;
}
.uni-badge--success-inverted {
color: #4cd964;
background-color: transparent;
}
.uni-badge--warning {
color: #fff;
background-color: #f0ad4e;
}
.uni-badge--warning-inverted {
color: #f0ad4e;
background-color: transparent;
}
.uni-badge--error {
color: #fff;
background-color: #dd524d;
}
.uni-badge--error-inverted {
color: #dd524d;
background-color: transparent;
}
.uni-badge--small {
transform: scale(0.8);
transform-origin: center center;
}
</style>

546
components/uni-calendar/calendar.js

@ -0,0 +1,546 @@
/**
* @1900-2100区间内的公历农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hexAttribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function (y) {
var i; var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function (y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (02930)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function (y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-12930)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function (y, m) {
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-128293031)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function (y, m) {
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function (lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function (cMonth, cDay) {
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function (offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function (y, n) {
if (y < 1900 || y > 2100) { return -1 }
if (n < 1 || n > 24) { return -1 }
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function (m) { // 月 => \u6708
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708'// 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function (d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341'; break
case 20:
s = '\u4e8c\u5341'; break
break
case 30:
s = '\u4e09\u5341'; break
break
default :
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是立春
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function (y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1// undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i; var leap = 0; var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp; i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i)// 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) { isLeap = false }
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true; --i
}
}
if (offset < 0) {
offset += temp; --i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0; var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y); isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) { offset += day }
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

151
components/uni-calendar/uni-calendar-item.vue

@ -0,0 +1,151 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
'uni-calendar-item--multiple': weeks.multiple
}" @click="choiceDate(weeks)">
<view class="uni-calendar-item__weeks-box-item">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text" :class="{
'uni-calendar-item--isDay-text': weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.date}}</text>
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
}">今天</text>
<text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.isDay?'今天': (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--extra':weeks.extraInfo.info,
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
export default {
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style scoped>
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
}
.uni-calendar-item__weeks-box-text {
font-size: 14px;
color: #333;
}
.uni-calendar-item__weeks-lunar-text {
font-size: 12px;
color: #333;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100rpx;
height: 100rpx;
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #dd524d;
}
.uni-calendar-item--disable {
background-color: rgba(249, 249, 249, 0.3);
color: #c0c0c0;
}
.uni-calendar-item--isDay-text {
color: #007aff;
}
.uni-calendar-item--isDay {
background-color: #007aff;
opacity: 0.8;
color: #fff;
}
.uni-calendar-item--extra {
color: #dd524d;
opacity: 0.8;
}
.uni-calendar-item--checked {
background-color: #007aff;
color: #fff;
opacity: 0.8;
}
.uni-calendar-item--multiple {
background-color: #007aff;
color: #fff;
opacity: 0.8;
}
</style>

431
components/uni-calendar/uni-calendar.vue

@ -0,0 +1,431 @@
<template>
<view class="uni-calendar" @touchmove.stop.prevent="clean">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
<view class="uni-calendar__header-btn-box" @click="close">
<text class="uni-calendar__header-text uni-calendar--fixed-width">取消</text>
</view>
<view class="uni-calendar__header-btn-box" @click="confirm">
<text class="uni-calendar__header-text uni-calendar--fixed-width">确定</text>
</view>
</view>
<view class="uni-calendar__header">
<view class="uni-calendar__header-btn-box" @click="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'') +'月'}}</text>
<view class="uni-calendar__header-btn-box" @click="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<text class="uni-calendar__backtoday" @click="backtoday">回到今天</text>
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text"></text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<uni-calendar-item :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></uni-calendar-item>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import uniCalendarItem from './uni-calendar-item.vue'
/**
* Calendar 日历
* @description 日历组件可以查看日期选择任意范围内的日期打点操作常用场景如酒店日期预订火车机票选择购买日期上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
* @property {Boolean} insert = [true|false] 插入模式,默认为false
* @value true 弹窗模式
* @value false 插入模式
* @property {Array} selected 打点期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @event {Function} change 日期改变`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
uniCalendarItem
},
props: {
date: {
type: String,
default: ''
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false
}
},
watch: {
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
}
},
created() {
//
this.cale = new Calendar({
date: this.date,
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
this.init(this.cale.date.fullDate)
},
methods: {
// 穿
clean() {},
init(date) {
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
},
open() {
this.show = true
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
}, 300)
})
},
confirm() {
this.setEmit('confirm')
this.close()
},
change() {
if (!this.insert) return
this.setEmit('change')
},
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
},
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
//
this.cale.setMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
this.change()
},
backtoday() {
this.cale.setDate(this.date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(this.date)
this.change()
},
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style scoped>
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.4);
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
border-bottom-color: #e5e5e5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: #e5e5e5;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
/* padding: 0 15px;
*/
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
padding-left: 10px;
height: 25px;
line-height: 25px;
font-size: 12px;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
color: #333;
background-color: #f1f1f1;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: 14px;
color: #333;
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 10px;
height: 10px;
border-left-color: #808080;
border-left-style: solid;
border-left-width: 2px;
border-top-color: #555555;
border-top-style: solid;
border-top-width: 2px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 45px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 14px;
}
.uni-calendar__box {
position: relative;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: #999;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
</style>

327
components/uni-calendar/util.js

@ -0,0 +1,327 @@
import CALENDAR from './calendar.js'
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range
} = {}) {
// 当前日期
this.date = this.getDate(date) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
this.range = range
// 多选状态
this.multipleStatus = {
before: '',
after: '',
data: []
}
// 每周日期
this.weeks = {}
this._getWeek(this.date.fullDate)
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
lunar: this.getlunar(full.year, full.month - 1, beforeDate),
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let isinfo = false
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
let dateCompBefore = this.dateCompare(this.startDate, fullDate)
disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
}
if (this.endDate) {
let dateCompAfter = this.dateCompare(fullDate, this.endDate)
disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
month: full.month,
lunar: this.getlunar(full.year, full.month, i),
disable: !disableBefore || !disableAfter,
isDay
}
if (info) {
data.extraInfo = info
}
dateArr.push(data)
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
lunar: this.getlunar(full.year, Number(full.month) + 1, i),
disable: true
})
}
return dateArr
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this._getWeek(date)
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
geDateAll(begin, end) {
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 计算阴历日期显示
*/
getlunar(year, month, date) {
return CALENDAR.solar2lunar(year, month, date)
}
/**
* 设置打点
*/
setSelectInfo(data, value) {
this.selected = value
this._getWeek(data)
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
this.multipleStatus.before = ''
this.multipleStatus.after = ''
this.multipleStatus.data = []
this._getWeek(fullDate)
} else {
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
this._getWeek(fullDate)
}
}
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
const {
fullDate,
year,
month,
date,
day
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
this.canlender = canlender
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

403
components/uni-card/uni-card.vue

@ -0,0 +1,403 @@
<template>
<view class="uni-card uni-border" :class="{ 'uni-card--full': isFull === true || isFull === 'true', 'uni-card--shadow': isShadow === true || isShadow === 'true'}">
<!-- 基础 -->
<view v-if="mode === 'basic' && title" class="uni-card__header uni-border-bottom" @click.stop="onClick">
<view v-if="thumbnail" class="uni-card__header-extra-img-view">
<image :src="thumbnail" class="uni-card__header-extra-img" />
</view>
<text class="uni-card__header-title-text">{{ title }}</text>
<text v-if="extra" class="uni-card__header-extra-text">{{ extra }}</text>
</view>
<!-- 标题 -->
<view v-if="mode === 'title'" class="uni-card__title uni-border-bottom" @click.stop="onClick">
<view class="uni-card__title-box">
<view class="uni-card__title-header">
<image class="uni-card__title-header-image" :src="thumbnail" mode="scaleToFill" />
</view>
<view class="uni-card__title-content">
<text class="uni-card__title-content-title uni-ellipsis">{{ title }}</text>
<text class="uni-card__title-content-extra uni-ellipsis">{{ subTitle }}</text>
</view>
</view>
<view v-if="extra">
<text class="uni-card__header-extra-text">{{ extra }}</text>
</view>
</view>
<!-- 图文 -->
<view v-if="mode === 'style'" class="uni-card__thumbnailimage" @click.stop="onClick">
<view class="uni-card__thumbnailimage-box">
<image class="uni-card__thumbnailimage-image" :src="thumbnail" mode="aspectFill" />
</view>
<view v-if="title" class="uni-card__thumbnailimage-title"><text class="uni-card__thumbnailimage-title-text">{{ title }}</text></view>
</view>
<!-- 内容 -->
<view class="uni-card__content uni-card__content--pd" @click.stop="onClick">
<view v-if="mode === 'style' && extra" class=""><text class="uni-card__content-extra">{{ extra }}</text></view>
<slot />
</view>
<!-- 底部 -->
<view v-if="note" class="uni-card__footer uni-border-top">
<slot name="footer">
<text class="uni-card__footer-text">{{ note }}</text>
</slot>
</view>
</view>
</template>
<script>
/**
* Card 卡片
* @description 卡片视图组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=22
* @property {String} title 标题文字
* @property {String} subTitle 副标题仅仅mode=title下生效
* @property {String} extra 标题额外信息
* @property {String} note 标题左侧缩略图
* @property {String} thumbnail 底部信息
* @property {String} mode = [basic|style|title] 卡片模式
* @value basic 基础卡片
* @value style 图文卡片
* @value title 标题卡片
* @property {Boolean} isFull = [true | false] 卡片内容是否通栏 true 时将去除padding值
* @property {Boolean} isShadow = [true | false] 卡片内容是否开启阴影
* @event {Function} click 点击 Card 触发事件
* @example <uni-card title="标题文字" thumbnail="https://img-cdn-qiniu.dcloud.net.cn/new-page/uni.png" extra="额外信息" note="Tips">内容主体可自定义内容及样式</uni-card>
*/
export default {
name: 'UniCard',
props: {
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
extra: {
type: String,
default: ''
},
note: {
type: String,
default: ''
},
thumbnail: {
type: String,
default: ''
},
mode: {
type: String,
default: 'basic'
},
isFull: {
//
type: Boolean,
default: false
},
isShadow: {
//
type: [Boolean, String],
default: false
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style scoped>
.uni-card {
/* #ifndef APP-NVUE */
display: flex;
flex: 1;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
/* #endif */
margin: 12px 15px;
background-color: #ffffff;
position: relative;
flex-direction: column;
border-radius: 5px;
overflow: hidden;
}
.uni-border {
position: relative;
/* #ifdef APP-NVUE */
border-color: #e5e5e5;
border-style: solid;
border-width: 0.5px;
/* #endif */
z-index: 1;
}
/* #ifndef APP-NVUE */
.uni-border:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
top: 0;
right: 0;
border: 1px solid #e5e5e5;
border-radius: 10px;
box-sizing: border-box;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
z-index: -1;
}
/* #endif */
.uni-border-bottom {
position: relative;
/* #ifdef APP-NVUE */
border-bottom-color: #e5e5e5;
border-bottom-style: solid;
border-bottom-width: 0.5px;
/* #endif */
z-index: 1;
}
/* #ifndef APP-NVUE */
.uni-border-bottom:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
top: 0;
right: 0;
border-bottom: 1px solid #e5e5e5;
box-sizing: border-box;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
z-index: -1;
}
/* #endif */
.uni-border-top {
position: relative;
/* #ifdef APP-NVUE */
border-top-color: #e5e5e5;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
z-index: 1;
}
/* #ifndef APP-NVUE */
.uni-border-top:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
top: 0;
right: 0;
border-top: 1px solid #e5e5e5;
box-sizing: border-box;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
z-index: -1;
}
/* #endif */
.uni-card__thumbnailimage {
position: relative;
flex-direction: column;
justify-content: center;
height: 150px;
overflow: hidden;
}
.uni-card__thumbnailimage-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
overflow: hidden;
}
.uni-card__thumbnailimage-image {
flex: 1;
}
.uni-card__thumbnailimage-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
position: absolute;
bottom: 0;
left: 0;
right: 0;
flex-direction: row;
padding: 8px 12px;
background-color: rgba(0, 0, 0, 0.4);
}
.uni-card__thumbnailimage-title-text {
flex: 1;
font-size: 14px;
color: #fff;
}
.uni-card__title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 10px;
}
.uni-card__title-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
overflow: hidden;
}
.uni-card__title-header {
width: 40px;
height: 40px;
overflow: hidden;
border-radius: 5px;
}
.uni-card__title-header-image {
width: 40px;
height: 40px;
}
.uni-card__title-content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
flex: 1;
padding-left: 10px;
height: 40px;
overflow: hidden;
}
.uni-card__title-content-title {
font-size: 14px;
line-height: 22px;
}
.uni-card__title-content-extra {
font-size: 12px;
line-height: 27px;
color: #999;
}
.uni-card__header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
position: relative;
flex-direction: row;
padding: 12px;
align-items: center;
}
.uni-card__header-title {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
margin-right: 8px;
justify-content: flex-start;
align-items: center;
}
.uni-card__header-title-text {
font-size: 16;
flex: 1;
color: #333;
}
.uni-card__header-extra-img {
height: 20px;
width: 20px;
margin-right: 8px;
}
.uni-card__header-extra-text {
flex: 1;
margin-left: 8px;
font-size: 12px;
text-align: right;
color: #999;
}
.uni-card__content {
color: #333;
}
.uni-card__content--pd {
padding: 12px;
}
.uni-card__content-extra {
font-size: 14px;
padding-bottom: 10px;
color: #999;
}
.uni-card__footer {
justify-content: space-between;
padding: 12px;
}
.uni-card__footer-text {
color: #999;
font-size: 12px;
}
.uni-card--shadow {
position: relative;
/* #ifndef APP-NVUE */
box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.1);
/* #endif */
}
.uni-card--full {
margin: 0;
border-radius: 0;
}
/* #ifndef APP-NVUE */
.uni-card--full:after {
border-radius: 0;
}
/* #endif */
.uni-ellipsis {
/* #ifndef APP-NVUE */
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1
/* #endif */
}
</style>

217
components/uni-collapse-item/uni-collapse-item.vue

@ -0,0 +1,217 @@
<template>
<view :class="{ 'uni-collapse-cell--disabled': disabled,'uni-collapse-cell--notdisabled': !disabled, 'uni-collapse-cell--open': isOpen,'uni-collapse-cell--hide':!isOpen }" class="uni-collapse-cell">
<view class="uni-collapse-cell__title" @click="onClick">
<image v-if="thumb" :src="thumb" class="uni-collapse-cell__title-img" />
<text class="uni-collapse-cell__title-text">{{ title }}</text>
<!-- #ifdef MP-ALIPAY -->
<view :class="{ 'uni-collapse-cell__title-arrow-active': isOpen, 'uni-collapse-cell--animation': showAnimation === true }" class="uni-collapse-cell__title-arrow">
<uni-icons color="#bbb" size="20" type="arrowdown" />
</view>
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<uni-icons :class="{ 'uni-collapse-cell__title-arrow-active': isOpen, 'uni-collapse-cell--animation': showAnimation === true }" class="uni-collapse-cell__title-arrow" color="#bbb" size="20" type="arrowdown" />
<!-- #endif -->
</view>
<view :class="{'uni-collapse-cell__content--hide':!isOpen}" class="uni-collapse-cell__content">
<view :class="{ 'uni-collapse-cell--animation': showAnimation === true }" class="uni-collapse-cell__wrapper" :style="{'transform':isOpen?'translateY(0)':'translateY(-50%)','-webkit-transform':isOpen?'translateY(0)':'translateY(-50%)'}">
<slot />
</view>
</view>
</view>
</template>
<script>
import uniIcons from '../uni-icons/uni-icons.vue'
/**
* CollapseItem 折叠面板子组件
* @description 折叠面板子组件
* @property {String} title 标题文字
* @property {String} thumb 标题左侧缩略图
* @property {Boolean} disabled = [true|false] 是否展开面板
* @property {Boolean} showAnimation = [true|false] 开启动画
*/
export default {
name: 'UniCollapseItem',
components: {
uniIcons
},
props: {
title: {
//
type: String,
default: ''
},
name: {
//
type: [Number, String],
default: 0
},
disabled: {
//
type: Boolean,
default: false
},
showAnimation: {
//
type: Boolean,
default: false
},
open: {
//
type: Boolean,
default: false
},
thumb: {
//
type: String,
default: ''
}
},
data() {
return {
isOpen: false
}
},
watch: {
open(val) {
this.isOpen = val
}
},
inject: ['collapse'],
created() {
this.isOpen = this.open
this.nameSync = this.name ? this.name : this.collapse.childrens.length
this.collapse.childrens.push(this)
if (String(this.collapse.accordion) === 'true') {
if (this.isOpen) {
let lastEl = this.collapse.childrens[this.collapse.childrens.length - 2]
if (lastEl) {
this.collapse.childrens[this.collapse.childrens.length - 2].isOpen = false
}
}
}
},
methods: {
onClick() {
if (this.disabled) {
return
}
if (String(this.collapse.accordion) === 'true') {
this.collapse.childrens.forEach(vm => {
if (vm === this) {
return
}
vm.isOpen = false
})
}
this.isOpen = !this.isOpen
this.collapse.onChange && this.collapse.onChange()
this.$forceUpdate()
}
}
}
</script>
<style scoped>
.uni-collapse-cell {
flex-direction: column;
border-color: #e5e5e5;
border-bottom-width: 1px;
border-bottom-style: solid;
}
.uni-collapse-cell--hover {
background-color: #f1f1f1;
}
.uni-collapse-cell--open {
background-color: #f1f1f1;
}
.uni-collapse-cell--disabled {
background-color: #f1f1f1;
/* opacity: 0.3;
*/
}
.uni-collapse-cell--hide {
height: 48px;
}
.uni-collapse-cell--animation {
/* transition: transform 0.3s ease;
*/
transition-property: transform;
transition-duration: 0.3s;
transition-timing-function: ease;
}
.uni-collapse-cell__title {
padding: 12px 12px;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
box-sizing: border-box;
/* #endif */
height: 48px;
line-height: 24px;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.uni-collapse-cell__title:active {
background-color: #f1f1f1;
}
.uni-collapse-cell__title-img {
height: 26px;
width: 26px;
margin-right: 10px;
}
.uni-collapse-cell__title-arrow {
width: 20px;
height: 20px;
transform: rotate(0deg);
transform-origin: center center;
}
.uni-collapse-cell__title-arrow-active {
transform: rotate(180deg);
}
.uni-collapse-cell__title-text {
flex: 1;
font-size: 14px;
/* #ifndef APP-NVUE */
white-space: nowrap;
color: inherit;
/* #endif */
/* #ifdef APP-NVUE */
lines: 1;
/* #endif */
overflow: hidden;
text-overflow: ellipsis;
}
.uni-collapse-cell__content {
overflow: hidden;
}
.uni-collapse-cell__wrapper {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-collapse-cell__content--hide {
height: 0px;
line-height: 0px;
}
</style>

59
components/uni-collapse/uni-collapse.vue

@ -0,0 +1,59 @@
<template>
<view class="uni-collapse">
<slot />
</view>
</template>
<script>
/**
* Collapse 折叠面板
* @description 展示可以折叠 / 展开的内容区域
* @tutorial https://ext.dcloud.net.cn/plugin?id=23
* @property {Boolean} accordion = [true|false] 是否开启手风琴效果是否开启手风琴效果
* @event {Function} change 切换面板时触发activeNamesArray展开状态的uniCollapseItem的 name
*/
export default {
name: 'UniCollapse',
props: {
accordion: {
//
type: [Boolean, String],
default: false
}
},
data() {
return {}
},
provide() {
return {
collapse: this
}
},
created() {
this.childrens = []
},
methods: {
onChange() {
let activeItem = []
this.childrens.forEach((vm, index) => {
if (vm.isOpen) {
activeItem.push(vm.nameSync)
}
})
this.$emit('change', activeItem)
}
}
}
</script>
<style scoped>
.uni-collapse {
/* #ifndef APP-NVUE */
width: 100%;
display: flex;
/* #endif */
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
flex-direction: column;
background-color: #ffffff;
}
</style>

212
components/uni-combox/uni-combox.vue

@ -0,0 +1,212 @@
<template>
<view class="uni-combox">
<view v-if="label" class="uni-combox__label" :style="labelStyle">
<text>{{label}}</text>
</view>
<view class="uni-combox__input-box">
<input class="uni-combox__input" type="text" :placeholder="placeholder" v-model="inputVal" @input="onInput" @focus="onFocus" @blur="onBlur" />
<uni-icons class="uni-combox__input-arrow" type="arrowdown" size="14" @click="toggleSelector"></uni-icons>
<view class="uni-combox__selector" v-if="showSelector">
<scroll-view scroll-y="true" class="uni-combox__selector-scroll">
<view class="uni-combox__selector-empty" v-if="filterCandidatesLength === 0">
<text>{{emptyTips}}</text>
</view>
<view class="uni-combox__selector-item" v-for="(item,index) in filterCandidates" :key="index" @click="onSelectorClick(index)">
<text>{{item}}</text>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
import uniIcons from '../uni-icons/uni-icons.vue'
/**
* Combox 组合输入框
* @description 组合输入框一般用于既可以输入也可以选择的场景
* @tutorial https://ext.dcloud.net.cn/plugin?id=1261
* @property {String} label 左侧文字
* @property {String} labelWidth 左侧内容宽度
* @property {String} placeholder 输入框占位符
* @property {Array} candidates 候选项列表
* @property {String} emptyTips 筛选结果为空时显示的文字
* @property {String} value 组合框的值
*/
export default {
name: 'uniCombox',
components: {
uniIcons
},
props: {
label: {
type: String,
default: ''
},
labelWidth: {
type: String,
default: 'auto'
},
placeholder: {
type: String,
default: ''
},
candidates: {
type: Array,
default () {
return []
}
},
emptyTips: {
type: String,
default: '无匹配项'
},
value: {
type: String,
default: ''
}
},
data() {
return {
showSelector: false,
inputVal: ''
}
},
computed: {
labelStyle() {
if (this.labelWidth === 'auto') {
return {}
}
return {
width: this.labelWidth
}
},
filterCandidates() {
return this.candidates.filter((item) => {
return item.indexOf(this.inputVal) > -1
})
},
filterCandidatesLength() {
return this.filterCandidates.length
}
},
watch: {
value: {
handler(newVal) {
this.inputVal = newVal
},
immediate: true
}
},
methods: {
toggleSelector() {
this.showSelector = !this.showSelector
},
onFocus() {
this.showSelector = true
},
onBlur() {
setTimeout(() => {
this.showSelector = false
}, 50)
},
onSelectorClick(index) {
this.inputVal = this.filterCandidates[index]
this.showSelector = false
this.$emit('input', this.inputVal)
},
onInput() {
setTimeout(() => {
this.$emit('input', this.inputVal)
})
}
}
}
</script>
<style scoped>
.uni-combox {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: 40px;
flex-direction: row;
align-items: center;
/* border-bottom: solid 1px #DDDDDD; */
}
.uni-combox__label {
font-size: 16px;
line-height: 22px;
padding-right: 10px;
color: #999999;
}
.uni-combox__input-box {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
align-items: center;
}
.uni-combox__input {
flex: 1;
font-size: 16px;
height: 22px;
line-height: 22px;
}
.uni-combox__input-arrow {
padding: 10px;
}
.uni-combox__selector {
box-sizing: border-box;
position: absolute;
top: 42px;
left: 0;
width: 100%;
background-color: #FFFFFF;
border-radius: 6px;
box-shadow: #DDDDDD 4px 4px 8px, #DDDDDD -4px -4px 8px;
z-index: 2;
}
.uni-combox__selector-scroll {
max-height: 200px;
box-sizing: border-box;
}
.uni-combox__selector::before {
content: '';
position: absolute;
width: 0;
height: 0;
border-bottom: solid 6px #FFFFFF;
border-right: solid 6px transparent;
border-left: solid 6px transparent;
left: 50%;
top: -6px;
margin-left: -6px;
}
.uni-combox__selector-empty,
.uni-combox__selector-item {
/* #ifdef APP-NVUE */
display: flex;
/* #endif */
line-height: 36px;
font-size: 14px;
text-align: center;
border-bottom: solid 1px #DDDDDD;
margin: 0px 10px;
}
.uni-combox__selector-empty:last-child,
.uni-combox__selector-item:last-child {
border-bottom: none;
}
</style>

200
components/uni-countdown/uni-countdown.vue

@ -0,0 +1,200 @@
<template>
<view class="uni-countdown">
<text v-if="showDay" :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ d }}</text>
<text v-if="showDay" :style="{ color: splitorColor }" class="uni-countdown__splitor"></text>
<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ h }}</text>
<text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '时' }}</text>
<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ i }}</text>
<text :style="{ color: splitorColor }" class="uni-countdown__splitor">{{ showColon ? ':' : '分' }}</text>
<text :style="{ borderColor: borderColor, color: color, backgroundColor: backgroundColor }" class="uni-countdown__number">{{ s }}</text>
<text v-if="!showColon" :style="{ color: splitorColor }" class="uni-countdown__splitor"></text>
</view>
</template>
<script>
/**
* Countdown 倒计时
* @description 倒计时组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=25
* @property {String} backgroundColor 背景色
* @property {String} color 文字颜色
* @property {Number} day 天数
* @property {Number} hour 小时
* @property {Number} minute 分钟
* @property {Number} second
* @property {Boolean} showDay = [true|false] 是否显示天数
* @property {Boolean} showColon = [true|false] 是否以冒号为分隔符
* @property {String} splitorColor 分割符号颜色
* @event {Function} timeup 倒计时时间到触发事件
* @example <uni-countdown :day="1" :hour="1" :minute="12" :second="40"></uni-countdown>
*/
export default {
name: 'UniCountdown',
props: {
showDay: {
type: Boolean,
default: true
},
showColon: {
type: Boolean,
default: true
},
backgroundColor: {
type: String,
default: '#FFFFFF'
},
borderColor: {
type: String,
default: '#000000'
},
color: {
type: String,
default: '#000000'
},
splitorColor: {
type: String,
default: '#000000'
},
day: {
type: Number,
default: 0
},
hour: {
type: Number,
default: 0
},
minute: {
type: Number,
default: 0
},
second: {
type: Number,
default: 0
}
},
data() {
return {
timer: null,
syncFlag: false,
d: '00',
h: '00',
i: '00',
s: '00',
leftTime: 0,
seconds: 0
}
},
watch: {
day(val) {
this.changeFlag()
},
hour(val) {
this.changeFlag()
},
minute(val) {
this.changeFlag()
},
second(val) {
this.changeFlag()
}
},
created: function(e) {
this.startData();
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
toSeconds(day, hours, minutes, seconds) {
return day * 60 * 60 * 24 + hours * 60 * 60 + minutes * 60 + seconds
},
timeUp() {
clearInterval(this.timer)
this.$emit('timeup')
},
countDown() {
let seconds = this.seconds
let [day, hour, minute, second] = [0, 0, 0, 0]
if (seconds > 0) {
day = Math.floor(seconds / (60 * 60 * 24))
hour = Math.floor(seconds / (60 * 60)) - (day * 24)
minute = Math.floor(seconds / 60) - (day * 24 * 60) - (hour * 60)
second = Math.floor(seconds) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60)
} else {
this.timeUp()
}
if (day < 10) {
day = '0' + day
}
if (hour < 10) {
hour = '0' + hour
}
if (minute < 10) {
minute = '0' + minute
}
if (second < 10) {
second = '0' + second
}
this.d = day
this.h = hour
this.i = minute
this.s = second
},
startData() {
this.seconds = this.toSeconds(this.day, this.hour, this.minute, this.second)
if (this.seconds <= 0) {
return
}
this.countDown()
this.timer = setInterval(() => {
this.seconds--
if (this.seconds < 0) {
this.timeUp()
return
}
this.countDown()
}, 1000)
},
changeFlag() {
if (!this.syncFlag) {
this.seconds = this.toSeconds(this.day, this.hour, this.minute, this.second)
this.startData();
this.syncFlag = true;
}
}
}
}
</script>
<style scoped>
.uni-countdown {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: flex-start;
padding: 2rpx 0;
}
.uni-countdown__splitor {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
line-height: 48rpx;
padding: 5rpx;
font-size: 12px;
}
.uni-countdown__number {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
width: 52rpx;
height: 48rpx;
line-height: 48rpx;
margin: 5rpx;
text-align: center;
font-size: 12px;
}
</style>

170
components/uni-drawer/uni-drawer.vue

@ -0,0 +1,170 @@
<template>
<view v-if="visibleSync" :class="{ 'uni-drawer--visible': showDrawer }" class="uni-drawer" @touchmove.stop.prevent="clear">
<view class="uni-drawer__mask" :class="{ 'uni-drawer__mask--visible': showDrawer && mask }" @tap="close('mask')" />
<view class="uni-drawer__content" :class="{'uni-drawer--right': rightMode,'uni-drawer--left': !rightMode, 'uni-drawer__content--visible': showDrawer}" :style="{width:drawerWidth+'px'}">
<slot />
</view>
</view>
</template>
<script>
/**
* Drawer 抽屉
* @description 抽屉侧滑菜单
* @tutorial https://ext.dcloud.net.cn/plugin?id=26
* @property {Boolean} mask = [true | false] 是否显示遮罩
* @property {Boolean} maskClick = [true | false] 点击遮罩是否关闭
* @property {Boolean} mode = [left | right] Drawer 滑出位置
* @value left 从左侧滑出
* @value right 从右侧侧滑出
* @property {Number} width 抽屉的宽度 vue 页面生效
* @event {Function} close 组件关闭时触发事件
*/
export default {
name: 'UniDrawer',
props: {
/**
* 显示模式只在初始化生效
*/
mode: {
type: String,
default: ''
},
/**
* 蒙层显示状态
*/
mask: {
type: Boolean,
default: true
},
/**
* 遮罩是否可点击关闭
*/
maskClick: {
type: Boolean,
default: true
},
/**
* 抽屉宽度
*/
width: {
type: Number,
default: 220
}
},
data() {
return {
visibleSync: false,
showDrawer: false,
rightMode: false,
watchTimer: null,
drawerWidth: 220
}
},
created() {
// #ifndef APP-NVUE
this.drawerWidth = this.width
// #endif
this.rightMode = this.mode === 'right'
},
methods: {
clear() {},
close(type) {
// fixed by mehaotian
if ((type === 'mask' && !this.maskClick) || !this.visibleSync) return
this._change('showDrawer', 'visibleSync', false)
},
open() {
// fixed by mehaotian
if (this.visibleSync) return
this._change('visibleSync', 'showDrawer', true)
},
_change(param1, param2, status) {
this[param1] = status
if (this.watchTimer) {
clearTimeout(this.watchTimer)
}
this.watchTimer = setTimeout(() => {
this[param2] = status
this.$emit('change', status)
}, status ? 50 : 300)
}
}
}
</script>
<style scoped>
/*
*/
.uni-drawer {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
z-index: 999;
}
.uni-drawer__content {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
position: absolute;
top: 0;
width: 220px;
bottom: 0;
background-color: #ffffff;
transition: transform 0.3s ease;
}
.uni-drawer--left {
left: 0;
/* #ifdef APP-NVUE */
transform: translateX(-220px);
/* #endif */
/* #ifndef APP-NVUE */
transform: translateX(-100%);
/* #endif */
}
.uni-drawer--right {
right: 0;
/* #ifdef APP-NVUE */
transform: translateX(220px);
/* #endif */
/* #ifndef APP-NVUE */
transform: translateX(100%);
/* #endif */
}
.uni-drawer__content--visible {
transform: translateX(0px);
}
.uni-drawer__mask {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
opacity: 0;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.4);
transition: opacity 0.3s;
}
.uni-drawer__mask--visible {
/* #ifndef APP-NVUE */
display: block;
/* #endif */
opacity: 1;
}
</style>

428
components/uni-fab/uni-fab.vue

@ -0,0 +1,428 @@
<template>
<view>
<view v-if="popMenu && (leftBottom||rightBottom||leftTop||rightTop)" :class="{
'uni-fab--leftBottom': leftBottom,
'uni-fab--rightBottom': rightBottom,
'uni-fab--leftTop': leftTop,
'uni-fab--rightTop': rightTop
}" class="uni-fab">
<view :class="{
'uni-fab__content--left': horizontal === 'left',
'uni-fab__content--right': horizontal === 'right',
'uni-fab__content--flexDirection': direction === 'vertical',
'uni-fab__content--flexDirectionStart': flexDirectionStart,
'uni-fab__content--flexDirectionEnd': flexDirectionEnd,
'uni-fab__content--other-platform': !isAndroidNvue
}" :style="{ width: boxWidth, height: boxHeight, backgroundColor: styles.backgroundColor }" class="uni-fab__content" elevation="5">
<view v-if="flexDirectionStart || horizontalLeft" class="uni-fab__item uni-fab__item--first" />
<view v-for="(item, index) in content" :key="index" :class="{ 'uni-fab__item--active': isShow }" class="uni-fab__item" @click="_onItemClick(index, item)">
<image :src="item.active ? item.selectedIconPath : item.iconPath" class="uni-fab__item-image" mode="widthFix" />
<text class="uni-fab__item-text" :style="{ color: item.active ? styles.selectedColor : styles.color }">{{ item.text }}</text>
</view>
<view v-if="flexDirectionEnd || horizontalRight" class="uni-fab__item uni-fab__item--first" />
</view>
</view>
<view :class="{
'uni-fab__circle--leftBottom': leftBottom,
'uni-fab__circle--rightBottom': rightBottom,
'uni-fab__circle--leftTop': leftTop,
'uni-fab__circle--rightTop': rightTop,
'uni-fab__content--other-platform': !isAndroidNvue
}" class="uni-fab__circle uni-fab__plus" :style="{ 'background-color': styles.buttonColor }" @click="_onClick">
<view class="fab-circle-v" :class="{'uni-fab__plus--active': isShow}"></view>
<view class="fab-circle-h" :class="{'uni-fab__plus--active': isShow}"></view>
</view>
</view>
</template>
<script>
let platform = 'other'
// #ifdef APP-NVUE
platform = uni.getSystemInfoSync().platform
// #endif
/**
* Fab 悬浮按钮
* @description 点击可展开一个图形按钮菜单
* @tutorial https://ext.dcloud.net.cn/plugin?id=144
* @property {Object} pattern 可选样式配置项
* @property {Object} horizontal = [left | right] 水平对齐方式
* @value left 左对齐
* @value right 右对齐
* @property {Object} vertical = [bottom | top] 垂直对齐方式
* @value bottom 下对齐
* @value top 上对齐
* @property {Object} direction = [horizontal | vertical] 展开菜单显示方式
* @value horizontal 水平显示
* @value vertical 垂直显示
* @property {Array} content 展开菜单内容配置项
* @property {Boolean} popMenu 是否使用弹出菜单
* @event {Function} trigger 展开菜单点击事件返回点击信息
* @event {Function} fabClick 悬浮按钮点击事件
*/
export default {
name: 'UniFab',
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 []
}
},
show: {
type: Boolean,
default: false
},
popMenu: {
type: Boolean,
default: true
}
},
data() {
return {
fabShow: false,
isShow: false,
isAndroidNvue: platform === 'android',
styles: {
color: '#3c3e49',
selectedColor: '#007AFF',
backgroundColor: '#fff',
buttonColor: '#3c3e49'
}
}
},
computed: {
contentWidth(e) {
return (this.content.length + 1) * 55 + 10 + 'px'
},
contentWidthMin() {
return 55 + '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')
}
},
watch: {
pattern(newValue, oldValue) {
//console.log(JSON.stringify(newValue))
this.styles = Object.assign({}, this.styles, newValue)
}
},
created() {
this.isShow = this.show
if (this.top === 0) {
this.fabShow = true
}
//
this.styles = Object.assign({}, this.styles, this.pattern)
},
methods: {
_onClick() {
this.$emit('fabClick')
if (!this.popMenu) {
return
}
this.isShow = !this.isShow
},
open() {
this.isShow = true
},
close() {
this.isShow = false
},
/**
* 按钮点击事件
*/
_onItemClick(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.isShow && this.direction === paramA ? this.contentWidth : this.contentWidthMin
}
}
}
}
</script>
<style scoped>
.uni-fab {
position: fixed;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
z-index: 10;
}
.uni-fab--active {
opacity: 1;
}
.uni-fab--leftBottom {
left: 5px;
bottom: 20px;
/* #ifdef H5 */
bottom: calc(20px + var(--window-bottom));
/* #endif */
padding: 10px;
}
.uni-fab--leftTop {
left: 5px;
top: 30px;
/* #ifdef H5 */
top: calc(30px + var(--window-top));
/* #endif */
padding: 10px;
}
.uni-fab--rightBottom {
right: 5px;
bottom: 20px;
/* #ifdef H5 */
bottom: calc(20px + var(--window-bottom));
/* #endif */
padding: 10px;
}
.uni-fab--rightTop {
right: 5px;
top: 30px;
/* #ifdef H5 */
top: calc(30px + var(--window-top));
/* #endif */
padding: 10px;
}
.uni-fab__circle {
position: fixed;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
background-color: #3c3e49;
border-radius: 55px;
z-index: 11;
}
.uni-fab__circle--leftBottom {
left: 15px;
bottom: 30px;
/* #ifdef H5 */
bottom: calc(30px + var(--window-bottom));
/* #endif */
}
.uni-fab__circle--leftTop {
left: 15px;
top: 40px;
/* #ifdef H5 */
top: calc(40px + var(--window-top));
/* #endif */
}
.uni-fab__circle--rightBottom {
right: 15px;
bottom: 30px;
/* #ifdef H5 */
bottom: calc(30px + var(--window-bottom));
/* #endif */
}
.uni-fab__circle--rightTop {
right: 15px;
top: 40px;
/* #ifdef H5 */
top: calc(40px + var(--window-top));
/* #endif */
}
.uni-fab__circle--left {
left: 0;
}
.uni-fab__circle--right {
right: 0;
}
.uni-fab__circle--top {
top: 0;
}
.uni-fab__circle--bottom {
bottom: 0;
}
.uni-fab__plus {
font-weight: bold;
}
.fab-circle-v {
position: absolute;
width: 3px;
height: 31px;
left: 26px;
top: 12px;
background-color: white;
transform: rotate(0deg);
transition: transform 0.3s;
}
.fab-circle-h {
position: absolute;
width: 31px;
height: 3px;
left: 12px;
top: 26px;
background-color: white;
transform: rotate(0deg);
transition: transform 0.3s;
}
.uni-fab__plus--active {
transform: rotate(135deg);
}
.uni-fab__content {
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: flex;
/* #endif */
flex-direction: row;
border-radius: 55px;
overflow: hidden;
transition-property: width, height;
transition-duration: 0.2s;
width: 55px;
border-color: #DDDDDD;
border-width: 1rpx;
border-style: solid;
}
.uni-fab__content--other-platform {
border-width: 0px;
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.2);
}
.uni-fab__content--left {
justify-content: flex-start;
}
.uni-fab__content--right {
justify-content: flex-end;
}
.uni-fab__content--flexDirection {
flex-direction: column;
justify-content: flex-end;
}
.uni-fab__content--flexDirectionStart {
flex-direction: column;
justify-content: flex-start;
}
.uni-fab__content--flexDirectionEnd {
flex-direction: column;
justify-content: flex-end;
}
.uni-fab__item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 55px;
height: 55px;
opacity: 0;
transition: opacity 0.2s;
}
.uni-fab__item--active {
opacity: 1;
}
.uni-fab__item-image {
width: 25px;
height: 25px;
margin-bottom: 3px;
}
.uni-fab__item-text {
color: #FFFFFF;
font-size: 12px;
}
.uni-fab__item--first {
width: 55px;
}
</style>

136
components/uni-fav/uni-fav.vue

@ -0,0 +1,136 @@
<template>
<view :class="[circle === true || circle === 'true' ? 'uni-fav--circle' : '']" :style="[{ backgroundColor: checked ? bgColorChecked : bgColor }]" @click="onClick" class="uni-fav">
<!-- #ifdef MP-ALIPAY -->
<view class="uni-fav-star" v-if="!checked && (star === true || star === 'true')">
<uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" size="14" type="star-filled" />
</view>
<!-- #endif -->
<!-- #ifndef MP-ALIPAY -->
<uni-icons :color="fgColor" :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-star" size="14" type="star-filled" v-if="!checked && (star === true || star === 'true')" />
<!-- #endif -->
<text :style="{color: checked ? fgColorChecked : fgColor}" class="uni-fav-text">{{ checked ? contentText.contentFav : contentText.contentDefault }}</text>
</view>
</template>
<script>
import uniIcons from "../uni-icons/uni-icons.vue";
/**
* Fav 收藏按钮
* @description 用于收藏功能可点击切换选中不选中的状态
* @tutorial https://ext.dcloud.net.cn/plugin?id=864
* @property {Boolean} star = [true|false] 按钮是否带星星
* @property {String} bgColor 未收藏时的背景色
* @property {String} bgColorChecked 已收藏时的背景色
* @property {String} fgColor 未收藏时的文字颜色
* @property {String} fgColorChecked 已收藏时的文字颜色
* @property {Boolean} circle = [true|false] 是否为圆角
* @property {Boolean} checked = [true|false] 是否为已收藏
* @property {Object} contentText = [true|false] 收藏按钮文字
* @event {Function} click 点击 fav按钮触发事件
* @example <uni-fav :checked="true"/>
*/
export default {
name: "UniFav",
components: {
uniIcons
},
props: {
star: {
type: [Boolean, String],
default: true
},
bgColor: {
type: String,
default: "#eeeeee"
},
fgColor: {
type: String,
default: "#666666"
},
bgColorChecked: {
type: String,
default: "#007aff"
},
fgColorChecked: {
type: String,
default: "#FFFFFF"
},
circle: {
type: [Boolean, String],
default: false
},
checked: {
type: Boolean,
default: false
},
contentText: {
type: Object,
default () {
return {
contentDefault: "收藏",
contentFav: "已收藏"
};
}
}
},
watch: {
checked() {
if (uni.report) {
if (this.checked) {
uni.report("收藏", "收藏");
} else {
uni.report("取消收藏", "取消收藏");
}
}
}
},
methods: {
onClick() {
this.$emit("click");
}
}
};
</script>
<style scoped>
.uni-fav {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 60px;
height: 25px;
line-height: 25px;
text-align: center;
border-radius: 3px;
}
.uni-fav--circle {
border-radius: 30px;
}
.uni-fav-star {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: 25px;
line-height: 24px;
margin-right: 3px;
align-items: center;
justify-content: center;
}
.uni-fav-text {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
height: 25px;
line-height: 25px;
align-items: center;
justify-content: center;
font-size: 14px;
}
</style>

230
components/uni-goods-nav/uni-goods-nav.vue

@ -0,0 +1,230 @@
<template>
<view class="uni-goods-nav">
<!-- 底部占位 -->
<view class="uni-tab__seat" />
<view class="uni-tab__cart-box flex">
<view class="flex uni-tab__cart-sub-left">
<view v-for="(item,index) in options" :key="index" class="flex uni-tab__cart-button-left uni-tab__shop-cart" @click="onClick(index,item)">
<view class="uni-tab__icon">
<uni-icons :type="item.icon" size="20" color="#646566"></uni-icons>
<!-- <image class="image" :src="item.icon" mode="widthFix" /> -->
</view>
<text class="uni-tab__text">{{ item.text }}</text>
<view class="flex uni-tab__dot-box">
<text v-if="item.info" :class="{ 'uni-tab__dots': item.info > 9 }" class="uni-tab__dot " :style="{'backgroundColor':item.infoBackgroundColor?item.infoBackgroundColor:'#ff0000',
color:item.infoColor?item.infoColor:'#fff'
}">{{ item.info }}</text>
</view>
</view>
</view>
<view :class="{'uni-tab__right':fill}" class="flex uni-tab__cart-sub-right ">
<view v-for="(item,index) in buttonGroup" :key="index" :style="{backgroundColor:item.backgroundColor,color:item.color}" class="flex uni-tab__cart-button-right" @click="buttonClick(index,item)"><text :style="{color:item.color}" class="uni-tab__cart-button-right-text">{{ item.text }}</text></view>
</view>
</view>
</view>
</template>
<script>
import uniIcons from '../uni-icons/uni-icons.vue'
/**
* GoodsNav 商品导航
* @description 商品加入购物车立即购买等
* @tutorial https://ext.dcloud.net.cn/plugin?id=865
* @property {Array} options 组件参数
* @property {Array} buttonGroup 组件按钮组参数
* @property {Boolean} fill = [true | false] 组件按钮组参数
* @event {Function} click 左侧点击事件
* @event {Function} buttonClick 右侧按钮组点击事件
* @example <uni-goods-nav :fill="true" options="" buttonGroup="buttonGroup" @click="" @buttonClick="" />
*/
export default {
name: 'UniGoodsNav',
components: {
uniIcons
},
props: {
options: {
type: Array,
default () {
return [{
icon: 'shop',
text: '店铺',
}, {
icon: 'cart',
text: '购物车'
}]
}
},
buttonGroup: {
type: Array,
default () {
return [{
text: '加入购物车',
backgroundColor: '#ffa200',
color: '#fff'
},
{
text: '立即购买',
backgroundColor: '#ff0000',
color: '#fff'
}
]
}
},
fill: {
type: Boolean,
default: false
}
},
methods: {
onClick(index, item) {
this.$emit('click', {
index,
content: item,
})
},
buttonClick(index, item) {
if (uni.report) {
uni.report(item.text, item.text)
}
this.$emit('buttonClick', {
index,
content: item
})
}
}
}
</script>
<style scoped>
.flex {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-goods-nav {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
}
.uni-tab__cart-box {
flex: 1;
height: 50px;
background-color: #fff;
z-index: 900;
}
.uni-tab__cart-sub-left {
padding: 0 5px;
}
.uni-tab__cart-sub-right {
flex: 1;
}
.uni-tab__right {
margin: 5px 0;
margin-right: 10px;
border-radius: 100px;
overflow: hidden;
}
.uni-tab__cart-button-left {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
/* flex: 1;
*/
position: relative;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0 10px;
}
.uni-tab__icon {
width: 18px;
height: 18px;
}
.image {
width: 18px;
height: 18px;
}
.uni-tab__text {
margin-top: 3px;
font-size: 12px;
color: #646566;
}
.uni-tab__cart-button-right {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
flex: 1;
justify-content: center;
align-items: center;
}
.uni-tab__cart-button-right-text {
font-size: 14px;
color: #fff;
}
.uni-tab__cart-button-right:active {
opacity: 0.7;
}
.uni-tab__dot-box {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
position: absolute;
right: -2px;
top: 2px;
justify-content: center;
align-items: center;
/* width: 0;
*/
/* height: 0;
*/
}
.uni-tab__dot {
/* width: 30rpx;
*/
/* height: 30rpx;
*/
padding: 0 4px;
line-height: 15px;
color: #ffffff;
text-align: center;
font-size: 12px;
background-color: #ff0000;
border-radius: 15px;
}
.uni-tab__dots {
padding: 0 4px;
/* width: auto;
*/
border-radius: 15px;
}
.uni-tab__color-y {
background-color: #ffa200;
}
.uni-tab__color-r {
background-color: #ff0000;
}
</style>

167
components/uni-grid-item/uni-grid-item copy.vue

@ -0,0 +1,167 @@
<template>
<view v-if="width" :style="'width:'+width+';'+(square?'height:'+width:'')" class="uni-grid-item">
<view :class="{ 'uni-grid-item--border': showBorder, 'uni-grid-item--border-top': showBorder && index < column, 'uni-highlight': highlight }" :style="{'border-right-color': borderColor ,'border-bottom-color': borderColor ,'border-top-color': borderColor }" class="uni-grid-item__box" @click="_onClick">
<slot />
</view>
</view>
</template>
<script>
/**
* GridItem 宫格
* @description 宫格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=27
* @property {Number} index 子组件的唯一标识 点击gird会返回当前的标识
*/
export default {
name: 'UniGridItem',
inject: ['grid'],
props: {
index: {
type: Number,
default: 0
}
},
data() {
return {
column: 0,
showBorder: true,
square: true,
highlight: true,
left: 0,
top: 0,
openNum: 2,
width: 0,
borderColor: '#e5e5e5'
}
},
created() {
this.column = this.grid.column
this.showBorder = this.grid.showBorder
this.square = this.grid.square
this.highlight = this.grid.highlight
this.top = this.hor === 0 ? this.grid.hor : this.hor
this.left = this.ver === 0 ? this.grid.ver : this.ver
this.borderColor = this.grid.borderColor
this.grid.children.push(this)
// this.grid.init()
this.width = this.grid.width
},
beforeDestroy() {
this.grid.children.forEach((item, index) => {
if (item === this) {
this.grid.children.splice(index, 1)
}
})
},
methods: {
_onClick() {
this.grid.change({
detail: {
index: this.index
}
})
}
}
}
</script>
<style scoped>
.uni-grid-item {
/* #ifndef APP-NVUE */
height: 100%;
display: flex;
/* #endif */
}
.uni-grid-item__box {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
position: relative;
flex: 1;
flex-direction: column;
/* justify-content: center;
*/
/* align-items: center;
*/
}
.uni-grid-item--border {
position: relative;
/* #ifdef APP-NVUE */
border-bottom-color: #e5e5e5;
border-bottom-style: solid;
border-bottom-width: 0.5px;
border-right-color: #e5e5e5;
border-right-style: solid;
border-right-width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
z-index: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-grid-item--border:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
top: 0;
right: 0;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: inherit;
border-right-style: solid;
border-right-width: 1px;
border-right-color: inherit;
box-sizing: border-box;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
z-index: -1;
}
/* #endif */
.uni-grid-item--border-top {
position: relative;
/* #ifdef APP-NVUE */
border-top-color: #e5e5e5;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
height: 100%;
box-sizing: border-box;
z-index: 0;
/* #endif */
}
/* #ifndef APP-NVUE */
.uni-grid-item--border-top:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
top: 0;
right: 0;
border-top-style: solid;
border-top-width: 1px;
border-top-color: inherit;
box-sizing: border-box;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
z-index: -1;
}
/* #endif */
.uni-highlight:active {
background-color: #f1f1f1;
}
</style>

125
components/uni-grid-item/uni-grid-item.vue

@ -0,0 +1,125 @@
<template>
<view v-if="width" :style="'width:'+width+';'+(square?'height:'+width:'')" class="uni-grid-item">
<view :class="{ 'uni-grid-item--border': showBorder, 'uni-grid-item--border-top': showBorder && index < column, 'uni-highlight': highlight }" :style="{'border-right-color': borderColor ,'border-bottom-color': borderColor ,'border-top-color': borderColor }" class="uni-grid-item__box" @click="_onClick">
<slot />
</view>
</view>
</template>
<script>
/**
* GridItem 宫格
* @description 宫格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=27
* @property {Number} index 子组件的唯一标识 点击gird会返回当前的标识
*/
export default {
name: 'UniGridItem',
inject: ['grid'],
props: {
index: {
type: Number,
default: 0
}
},
data() {
return {
column: 0,
showBorder: true,
square: true,
highlight: true,
left: 0,
top: 0,
openNum: 2,
width: 0,
borderColor: '#e5e5e5'
}
},
created() {
this.column = this.grid.column
this.showBorder = this.grid.showBorder
this.square = this.grid.square
this.highlight = this.grid.highlight
this.top = this.hor === 0 ? this.grid.hor : this.hor
this.left = this.ver === 0 ? this.grid.ver : this.ver
this.borderColor = this.grid.borderColor
this.grid.children.push(this)
// this.grid.init()
this.width = this.grid.width
},
beforeDestroy() {
this.grid.children.forEach((item, index) => {
if (item === this) {
this.grid.children.splice(index, 1)
}
})
},
methods: {
_onClick() {
this.grid.change({
detail: {
index: this.index
}
})
}
}
}
</script>
<style scoped>
.uni-grid-item {
/* #ifndef APP-NVUE */
height: 100%;
display: flex;
/* #endif */
}
.uni-grid-item__box {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
position: relative;
flex: 1;
flex-direction: column;
/* justify-content: center;
*/
/* align-items: center;
*/
}
.uni-grid-item--border {
position: relative;
/* #ifdef APP-NVUE */
border-bottom-color: #e5e5e5;
border-bottom-style: solid;
border-bottom-width: 0.5px;
border-right-color: #e5e5e5;
border-right-style: solid;
border-right-width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
z-index: 0;
border-bottom: 1px #e5e5e5 solid;
border-right: 1px #e5e5e5 solid;
/* #endif */
}
.uni-grid-item--border-top {
position: relative;
/* #ifdef APP-NVUE */
border-top-color: #e5e5e5;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
border-top: 1px #e5e5e5 solid;
z-index: 0;
/* #endif */
}
.uni-highlight:active {
background-color: #f1f1f1;
}
</style>

142
components/uni-grid/uni-grid.vue

@ -0,0 +1,142 @@
<template>
<view class="uni-grid-wrap">
<view :id="elId" ref="uni-grid" class="uni-grid" :class="{ 'uni-grid--border': showBorder }" :style="{ 'border-left-color':borderColor}">
<slot />
</view>
</view>
</template>
<script>
// #ifdef APP-NVUE
const dom = uni.requireNativePlugin('dom');
// #endif
/**
* Grid 宫格
* @description 宫格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=27
* @property {Number} column 每列显示个数
* @property {String} borderColor 边框颜色
* @property {Boolean} showBorder 是否显示边框
* @property {Boolean} square 是否方形显示
* @property {Boolean} Boolean 点击背景是否高亮
* @event {Function} change 点击 grid 触发e={detail:{index:0}}index 为当前点击 gird 下标
*/
export default {
name: 'UniGrid',
props: {
//
column: {
type: Number,
default: 3
},
//
showBorder: {
type: Boolean,
default: true
},
//
borderColor: {
type: String,
default: '#e5e5e5'
},
// , true
square: {
type: Boolean,
default: true
},
highlight: {
type: Boolean,
default: true
}
},
provide() {
return {
grid: this
}
},
data() {
const elId = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}`
return {
elId,
width: 0
}
},
created() {
this.children = []
},
mounted() {
this.$nextTick(() => {
this.init()
})
},
methods: {
init() {
setTimeout(() => {
this._getSize((width) => {
this.children.forEach((item, index) => {
item.width = width
})
})
}, 50)
},
change(e) {
this.$emit('change', e)
},
_getSize(fn) {
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select(`#${this.elId}`)
.boundingClientRect()
.exec(ret => {
this.width = parseInt((ret[0].width - 1) / this.column) + 'px'
fn(this.width)
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['uni-grid'], (ret) => {
this.width = parseInt((ret.size.width - 1) / this.column) + 'px'
fn(this.width)
})
// #endif
}
}
}
</script>
<style scoped>
.uni-grid-wrap {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: column;
/* #ifdef H5 */
width: 100%;
/* #endif */
}
.uni-grid {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
/* flex: 1;
*/
flex-direction: row;
flex-wrap: wrap;
}
.uni-grid--border {
position: relative;
/* #ifdef APP-NVUE */
border-left-color: #e5e5e5;
border-left-style: solid;
border-left-width: 0.5px;
/* #endif */
/* #ifndef APP-NVUE */
z-index: 1;
border-left: 1px #e5e5e5 solid;
/* #endif */
}
</style>

132
components/uni-icons/icons.js

@ -0,0 +1,132 @@
export default {
"pulldown": "\ue588",
"refreshempty": "\ue461",
"back": "\ue471",
"forward": "\ue470",
"more": "\ue507",
"more-filled": "\ue537",
"scan": "\ue612",
"qq": "\ue264",
"weibo": "\ue260",
"weixin": "\ue261",
"pengyouquan": "\ue262",
"loop": "\ue565",
"refresh": "\ue407",
"refresh-filled": "\ue437",
"arrowthindown": "\ue585",
"arrowthinleft": "\ue586",
"arrowthinright": "\ue587",
"arrowthinup": "\ue584",
"undo-filled": "\ue7d6",
"undo": "\ue406",
"redo": "\ue405",
"redo-filled": "\ue7d9",
"bars": "\ue563",
"chatboxes": "\ue203",
"camera": "\ue301",
"chatboxes-filled": "\ue233",
"camera-filled": "\ue7ef",
"cart-filled": "\ue7f4",
"cart": "\ue7f5",
"checkbox-filled": "\ue442",
"checkbox": "\ue7fa",
"arrowleft": "\ue582",
"arrowdown": "\ue581",
"arrowright": "\ue583",
"smallcircle-filled": "\ue801",
"arrowup": "\ue580",
"circle": "\ue411",
"eye-filled": "\ue568",
"eye-slash-filled": "\ue822",
"eye-slash": "\ue823",
"eye": "\ue824",
"flag-filled": "\ue825",
"flag": "\ue508",
"gear-filled": "\ue532",
"reload": "\ue462",
"gear": "\ue502",
"hand-thumbsdown-filled": "\ue83b",
"hand-thumbsdown": "\ue83c",
"hand-thumbsup-filled": "\ue83d",
"heart-filled": "\ue83e",
"hand-thumbsup": "\ue83f",
"heart": "\ue840",
"home": "\ue500",
"info": "\ue504",
"home-filled": "\ue530",
"info-filled": "\ue534",
"circle-filled": "\ue441",
"chat-filled": "\ue847",
"chat": "\ue263",
"mail-open-filled": "\ue84d",
"email-filled": "\ue231",
"mail-open": "\ue84e",
"email": "\ue201",
"checkmarkempty": "\ue472",
"list": "\ue562",
"locked-filled": "\ue856",
"locked": "\ue506",
"map-filled": "\ue85c",
"map-pin": "\ue85e",
"map-pin-ellipse": "\ue864",
"map": "\ue364",
"minus-filled": "\ue440",
"mic-filled": "\ue332",
"minus": "\ue410",
"micoff": "\ue360",
"mic": "\ue302",
"clear": "\ue434",
"smallcircle": "\ue868",
"close": "\ue404",
"closeempty": "\ue460",
"paperclip": "\ue567",
"paperplane": "\ue503",
"paperplane-filled": "\ue86e",
"person-filled": "\ue131",
"contact-filled": "\ue130",
"person": "\ue101",
"contact": "\ue100",
"images-filled": "\ue87a",
"phone": "\ue200",
"images": "\ue87b",
"image": "\ue363",
"image-filled": "\ue877",
"location-filled": "\ue333",
"location": "\ue303",
"plus-filled": "\ue439",
"plus": "\ue409",
"plusempty": "\ue468",
"help-filled": "\ue535",
"help": "\ue505",
"navigate-filled": "\ue884",
"navigate": "\ue501",
"mic-slash-filled": "\ue892",
"search": "\ue466",
"settings": "\ue560",
"sound": "\ue590",
"sound-filled": "\ue8a1",
"spinner-cycle": "\ue465",
"download-filled": "\ue8a4",
"personadd-filled": "\ue132",
"videocam-filled": "\ue8af",
"personadd": "\ue102",
"upload": "\ue402",
"upload-filled": "\ue8b1",
"starhalf": "\ue463",
"star-filled": "\ue438",
"star": "\ue408",
"trash": "\ue401",
"phone-filled": "\ue230",
"compose": "\ue400",
"videocam": "\ue300",
"trash-filled": "\ue8dc",
"download": "\ue403",
"chatbubble-filled": "\ue232",
"chatbubble": "\ue202",
"cloud-download": "\ue8e4",
"cloud-upload-filled": "\ue8e5",
"cloud-upload": "\ue8e6",
"cloud-download-filled": "\ue8e9",
"headphones":"\ue8bf",
"shop":"\ue609"
}

67
components/uni-icons/uni-icons.vue

File diff suppressed because one or more lines are too long

142
components/uni-indexed-list/uni-indexed-list-item.vue

@ -0,0 +1,142 @@
<template>
<view>
<view v-if="loaded || list.itemIndex < 15" class="uni-indexed-list__title-wrapper">
<text v-if="list.items && list.items.length > 0" class="uni-indexed-list__title">{{ list.key }}</text>
</view>
<view v-if="(loaded || list.itemIndex < 15) && list.items && list.items.length > 0" class="uni-indexed-list__list">
<view v-for="(item, index) in list.items" :key="index" class="uni-indexed-list__item" hover-class="uni-indexed-list__item--hover">
<view class="uni-indexed-list__item-container" @click="onClick(idx, index)">
<view class="uni-indexed-list__item-border" :class="{'uni-indexed-list__item-border--last':index===list.items.length-1}">
<view v-if="showSelect" style="margin-right: 20rpx;">
<uni-icons :type="item.checked ? 'checkbox-filled' : 'circle'" :color="item.checked ? '#007aff' : '#aaa'" size="24" />
</view>
<text class="uni-indexed-list__item-content">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import uniIcons from '../uni-icons/uni-icons.vue'
export default {
name: 'UniIndexedList',
components: {
uniIcons
},
props: {
loaded: {
type: Boolean,
default: false
},
idx: {
type: Number,
default: 0
},
list: {
type: Object,
default () {
return {}
}
},
showSelect: {
type: Boolean,
default: false
}
},
methods: {
onClick(idx, index) {
this.$emit("itemClick", {
idx,
index
})
}
}
}
</script>
<style scoped>
.uni-indexed-list__list {
background-color: #ffffff;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
border-top-style: solid;
border-top-width: 1px;
border-top-color: #e5e5e5;
}
.uni-indexed-list__item {
font-size: 16;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.uni-indexed-list__item-container {
padding-left: 15px;
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.uni-indexed-list__item-border {
flex: 1;
position: relative;
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
flex-direction: row;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 15px;
padding-left: 0;
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom-color: #e5e5e5;
}
.uni-indexed-list__item-border--last {
border-bottom-width: 0px;
}
.uni-indexed-list__item-content {
flex: 1;
font-size: 14px;
}
.uni-indexed-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__title-wrapper {
/* #ifndef APP-NVUE */
display: flex;
width: 100%;
/* #endif */
background-color: #f7f7f7;
}
.uni-indexed-list__title {
padding: 6px 12px;
line-height: 24px;
font-size: 12px;
}
</style>

317
components/uni-indexed-list/uni-indexed-list.vue

@ -0,0 +1,317 @@
<template>
<view class="uni-indexed-list" ref="list" id="list">
<!-- #ifdef APP-NVUE -->
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
<!-- #endif -->
<uni-indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect" @itemClick="onClick"></uni-indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view :class="touchmove ? 'uni-indexed-list__menu--active' : ''" @touchstart="touchStart" @touchmove.stop.prevent="touchMove" @touchend="touchEnd" class="uni-indexed-list__menu">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item">
<text class="uni-indexed-list__menu-text" :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
</view>
</view>
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
</view>
</view>
</template>
<script>
import uniIcons from '../uni-icons/uni-icons.vue'
import uniIndexedListItem from './uni-indexed-list-item.vue'
// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
// #ifdef APP-PLUS
function throttle(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function touchMove(e) {
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
// #ifndef APP-NVUE
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #endif
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
this.touchmoveIndex = index
// #endif
}
}
const throttleTouchMove = throttle(touchMove, 40)
// #endif
/**
* IndexedList 索引列表
* @description 用于展示索引列表
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
* @property {Boolean} showSelect = [true|false] 展示模式
* @value true 展示模式
* @value false 选择模式
* @property {Object} options 索引列表需要的数据对象
* @event {Function} click 点击列表事件 返回当前选择项的事件对象
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
*/
export default {
name: 'UniIndexedList',
components: {
uniIcons,
uniIndexedListItem
},
props: {
options: {
type: Array,
default () {
return []
}
},
showSelect: {
type: Boolean,
default: false
}
},
data() {
return {
lists: [],
winHeight: 0,
itemHeight: 0,
winOffsetY: 0,
touchmove: false,
touchmoveIndex: -1,
scrollViewId: '',
touchmoveTimeout: '',
loaded: false
}
},
watch: {
options: {
handler: function() {
this.setList()
},
deep: true
}
},
mounted() {
setTimeout(() => {
this.setList()
}, 50)
setTimeout(() => {
this.loaded = true
}, 300);
},
methods: {
setList() {
let index = 0;
this.lists = []
this.options.forEach((value, index) => {
if (value.data.length === 0) {
return
}
let indexBefore = index
let items = value.data.map(item => {
let obj = {}
obj['key'] = value.letter
obj['name'] = item
obj['itemIndex'] = index
index++
obj.checked = item.checked ? item.checked : false
return obj
})
this.lists.push({
title: value.letter,
key: value.letter,
items: items,
itemIndex: indexBefore
})
})
// #ifndef APP-NVUE
uni.createSelectorQuery()
.in(this)
.select('#list')
.boundingClientRect()
.exec(ret => {
this.winOffsetY = ret[0].top
this.winHeight = ret[0].height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
// #ifdef APP-NVUE
dom.getComponentRect(this.$refs['list'], (res) => {
this.winOffsetY = res.size.top
this.winHeight = res.size.height
this.itemHeight = this.winHeight / this.lists.length
})
// #endif
},
touchStart(e) {
this.touchmove = true
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
// #ifdef APP-NVUE
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
animated: false
})
// #endif
}
},
touchMove(e) {
// #ifndef APP-PLUS
let pageY = e.touches[0].pageY
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
if (this.touchmoveIndex === index) {
return false
}
let item = this.lists[index]
if (item) {
this.scrollViewId = 'uni-indexed-list-' + index
this.touchmoveIndex = index
}
// #endif
// #ifdef APP-PLUS
throttleTouchMove.call(this, e)
// #endif
},
touchEnd() {
this.touchmove = false
this.touchmoveIndex = -1
},
onClick(e) {
let {
idx,
index
} = e
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
let select = []
if (this.showSelect) {
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
this.lists.forEach((value, idx) => {
value.items.forEach((item, index) => {
if (item.checked) {
let obj = {}
for (let key in this.lists[idx].items[index]) {
obj[key] = this.lists[idx].items[index][key]
}
select.push(obj)
}
})
})
}
this.$emit('click', {
item: obj,
select: select
})
}
}
}
</script>
<style scoped>
.uni-indexed-list {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-indexed-list__scroll {
flex: 1;
}
.uni-indexed-list__menu {
width: 24px;
background-color: lightgrey;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-indexed-list__menu-item {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
align-items: center;
justify-content: center;
}
.uni-indexed-list__menu-text {
line-height: 20px;
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu-text--active {
color: #007aff;
}
.uni-indexed-list__alert-wrapper {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
}
.uni-indexed-list__alert {
width: 80px;
height: 80px;
border-radius: 80px;
text-align: center;
line-height: 80px;
font-size: 35px;
color: #fff;
background-color: rgba(0, 0, 0, 0.5);
}
</style>

72
components/uni-link/uni-link.vue

@ -0,0 +1,72 @@
<template>
<text class="uni-link" :class="{'uni-link--withline':showUnderLine===true||showUnderLine==='true'}" :style="{color,fontSize:fontSize+'px'}" @click="openURL">{{text}}</text>
</template>
<script>
/**
* Link 外部网页超链接组件
* @description uni-link是一个外部网页超链接组件在小程序内复制url在app内打开外部浏览器在h5端打开新网页
* @tutorial https://ext.dcloud.net.cn/plugin?id=1182
* @property {String} href 点击后打开的外部网页url
* @property {String} text 显示的文字
* @property {Boolean} showUnderLine 是否显示下划线
* @property {String} copyTips 在小程序端复制链接时显示的提示语
* @property {String} color 链接文字颜色
* @property {String} fontSize 链接文字大小
* @example * <uni-link href="https://ext.dcloud.net.cn" text="https://ext.dcloud.net.cn"></uni-link>
*/
export default {
name: 'uniLink',
props: {
href: {
type: String,
default: ''
},
text: {
type: String,
default: ''
},
showUnderLine: {
type: [Boolean, String],
default: true
},
copyTips: {
type: String,
default: '已自动复制网址,请在手机浏览器里粘贴该网址'
},
color: {
type: String,
default: '#999999'
},
fontSize: {
type: [Number, String],
default: 14
}
},
methods: {
openURL() {
// #ifdef APP-PLUS
plus.runtime.openURL(this.href)
// #endif
// #ifdef H5
window.open(this.href)
// #endif
// #ifdef MP
uni.setClipboardData({
data: this.href
});
uni.showModal({
content: this.copyTips,
showCancel: false
});
// #endif
}
}
}
</script>
<style scoped>
.uni-link--withline {
text-decoration: underline;
}
</style>

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

@ -0,0 +1,270 @@
<template>
<!-- #ifdef APP-NVUE -->
<cell>
<!-- #endif -->
<view :class="disabled ? 'uni-list-item--disabled' : ''" :hover-class="disabled || showSwitch ? '' : 'uni-list-item--hover'" class="uni-list-item" @click="onClick">
<view class="uni-list-item__container" :class="{'uni-list-item--first':isFirstChild}">
<view v-if="thumb" class="uni-list-item__icon">
<image :src="thumb" class="uni-list-item__icon-img" />
</view>
<view v-else-if="showExtraIcon" class="uni-list-item__icon">
<uni-icons :color="extraIcon.color" :size="extraIcon.size" :type="extraIcon.type" class="uni-icon-wrapper" />
</view>
<view class="uni-list-item__content">
<slot />
<text class="uni-list-item__content-title">{{ title }}</text>
<text v-if="note" class="uni-list-item__content-note">{{ note }}</text>
</view>
<view class="uni-list-item__extra">
<text v-if="rightText" class="uni-list-item__extra-text">{{rightText}}</text>
<uni-badge v-if="showBadge" :type="badgeType" :text="badgeText" />
<switch v-if="showSwitch" :disabled="disabled" :checked="switchChecked" @change="onSwitchChange" />
<slot name="right"></slot>
<uni-icons v-if="showArrow" :size="20" class="uni-icon-wrapper" color="#bbb" type="arrowright" />
</view>
</view>
</view>
<!-- #ifdef APP-NVUE -->
</cell>
<!-- #endif -->
</template>
<script>
import uniIcons from '../uni-icons/uni-icons.vue'
import uniBadge from '../uni-badge/uni-badge.vue'
/**
* ListItem 列表子组件
* @description 列表子组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
* @property {String} title 标题
* @property {String} note 描述
* @property {String} thumb 左侧缩略图若thumb有值则不会显示扩展图标
* @property {String} badgeText 数字角标内容
* @property {String} badgeType 数字角标类型参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21)
* @property {String} rightText 右侧文字内容
* @property {Boolean} disabled = [true|false]是否禁用
* @property {Boolean} showArrow = [true|false] 是否显示箭头图标
* @property {Boolean} showBadge = [true|false] 是否显示数字角标
* @property {Boolean} showSwitch = [true|false] 是否显示Switch
* @property {Boolean} switchChecked = [true|false] Switch是否被选中
* @property {Boolean} showExtraIcon = [true|false] 左侧是否显示扩展图标
* @property {Boolean} scrollY = [true|false] 允许纵向滚动需要显式的设置其宽高
* @property {Object} extraIcon 扩展图标参数格式为 {color: '#4cd964',size: '22',type: 'spinner'}
* @event {Function} click 点击 uniListItem 触发事件
* @event {Function} switchChange 点击切换 Switch 时触发
*/
export default {
name: 'UniListItem',
components: {
uniIcons,
uniBadge
},
props: {
title: {
type: String,
default: ''
}, //
note: {
type: String,
default: ''
}, //
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: {
// badge
type: String,
default: ''
},
badgeType: {
// badge
type: String,
default: 'success'
},
rightText: {
//
type: String,
default: ''
},
thumb: {
//
type: String,
default: ''
},
showExtraIcon: {
//
type: [Boolean, String],
default: false
},
extraIcon: {
type: Object,
default () {
return {
type: 'contact',
color: '#000000',
size: 20
}
}
}
},
inject: ['list'],
data() {
return {
isFirstChild: false
}
},
mounted() {
if (!this.list.firstChildAppend) {
this.list.firstChildAppend = true
this.isFirstChild = true
}
},
methods: {
onClick() {
this.$emit('click')
},
onSwitchChange(e) {
this.$emit('switchChange', e.detail)
}
}
}
</script>
<style scoped>
.uni-list-item {
font-size: 16;
position: relative;
flex-direction: column;
justify-content: space-between;
padding-left: 15px;
}
.uni-list-item--disabled {
opacity: 0.3;
}
.uni-list-item--hover {
background-color: #f1f1f1;
}
.uni-list-item__container {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
padding: 12px 15px;
padding-left: 0;
flex: 1;
position: relative;
justify-content: space-between;
align-items: center;
/* #ifdef APP-PLUS */
border-top-color: #e5e5e5;
border-top-style: solid;
border-top-width: 0.5px;
/* #endif */
}
.uni-list-item--first {
border-top-width: 0px;
}
/* #ifndef APP-NVUE */
.uni-list-item__container:after {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 1px;
content: '';
-webkit-transform: scaleY(.5);
transform: scaleY(.5);
background-color: #e5e5e5;
}
.uni-list-item--first:after {
height: 0px;
}
/* #endif */
.uni-list-item__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex: 1;
overflow: hidden;
flex-direction: column;
color: #3b4144;
}
.uni-list-item__content-title {
font-size: 14px;
color: #3b4144;
overflow: hidden;
}
.uni-list-item__content-note {
margin-top: 6rpx;
color: #999;
font-size: 12px;
overflow: hidden;
}
.uni-list-item__extra {
/* width: 25%;
*/
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.uni-list-item__icon {
margin-right: 18rpx;
flex-direction: row;
justify-content: center;
align-items: center;
}
.uni-list-item__icon-img {
height: 26px;
width: 26px;
}
.uni-list-item__extra-text {
color: #999;
font-size: 12px;
}
</style>

78
components/uni-list/uni-list.vue

@ -0,0 +1,78 @@
<template>
<!-- #ifndef APP-NVUE -->
<view class="uni-list">
<slot />
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<list class="uni-list" :enableBackToTop="enableBackToTop" loadmoreoffset="15" :scroll-y="scrollY" @loadmore="loadMore">
<slot />
</list>
<!-- #endif -->
</template>
<script>
/**
* List 列表
* @description 列表组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=24
*/
export default {
name: 'UniList',
'mp-weixin': {
options: {
multipleSlots: false
}
},
props: {
enableBackToTop: {
type: [Boolean, String],
default: false
},
scrollY: {
type: [Boolean, String],
default: false
}
},
provide() {
return {
list: this
}
},
created() {
this.firstChildAppend = false
},
methods: {
loadMore(e) {
this.$emit("scrolltolower");
}
}
}
</script>
<style scoped>
.uni-list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
background-color: #ffffff;
position: relative;
flex-direction: column;
/* border-bottom-color: $uni-border-color;
*/
/* border-bottom-style: solid;
*/
/* border-bottom-width: 1px;
*/
}
/* #ifndef APP-NVUE */
.uni-list:before {
height: 0;
}
.uni-list:after {
height: 0;
}
/* #endif */
</style>

65
components/uni-list/uni-refresh.vue

@ -0,0 +1,65 @@
<template>
<!-- #ifdef APP-NVUE -->
<refresh :display="display" @refresh="onrefresh" @pullingdown="onpullingdown">
<slot />
</refresh>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<view ref="uni-refresh" class="uni-refresh" v-show="isShow">
<slot />
</view>
<!-- #endif -->
</template>
<script>
export default {
name: 'UniRefresh',
props: {
display: {
type: [String],
default: "hide"
}
},
data() {
return {
pulling: false
}
},
computed: {
isShow() {
if (this.display === "show" || this.pulling === true) {
return true;
}
return false;
}
},
created() {},
methods: {
onchange(value) {
this.pulling = value;
},
onrefresh(e) {
this.$emit("refresh", e);
},
onpullingdown(e) {
// #ifdef APP-NVUE
this.$emit("pullingdown", e);
// #endif
// #ifndef APP-NVUE
var detail = {
viewHeight: 90,
pullingDistance: e.height
}
this.$emit("pullingdown", detail);
// #endif
}
}
}
</script>
<style scoped>
.uni-refresh {
height: 0;
overflow: hidden;
}
</style>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save