初始化代码

This commit is contained in:
2025-12-22 17:13:05 +08:00
parent ed0de08e3a
commit 1f7e9d401b
2947 changed files with 526137 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
{ // launch.json 配置了启动调试时相关设置configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"app-plus" :
{
"launchtype" : "remote"
},
"default" :
{
"launchtype" : "remote"
},
"h5" :
{
"launchtype" : "remote"
},
"mp-weixin" :
{
"launchtype" : "remote"
},
"type" : "uniCloud"
}
]
}

102
uniapp/uni-app/App.vue Normal file
View File

@@ -0,0 +1,102 @@
<script>
import $store from "@/store/index.js"
// #ifdef APP-PLUS
import skJGPush from "@/utils/jpush.js"
// #endif
export default {
onLaunch() {
let arr = ['autograph', 'userInfo', 'appLogin', 'loginType', 'push_id']
arr.map(key => {
let val = uni.getStorageSync(key) || ''
if (val) {
$store.commit('updateUserItem', {
key,
val
})
}
})
// #ifdef APP-PLUS
// 极光推送-init
// skJGPush.initJPushService()
// 极光推送-打开debug
// skJGPush.openDebug()
// 极光推送-获取rid
// skJGPush.getRegistrationID(result => {
// console.log('【业务getRegistrationID】', result)
// $store.commit('updateUserItem', {
// key: 'push_id',
// val: result
// })
// })
// #endif
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style lang="scss">
@import "./styles/index.scss";
page {
font-size: 28rpx;
color: #222;
line-height: 1.5;
background: #F6F6F6;
font-family: -apple-system-font, Helvetica Neue, Helvetica, sans-serif;
}
input {
// font-family: PingFangSC-Medium, PingFang SC, -apple-system-font, Helvetica Neue, Helvetica, sans-serif;
}
input::-webkit-input-placeholder {
/* WebKit browsers */
color: #A9A9A9;
}
input:-moz-placeholder {
/* Mozilla Firefox 4 to 18 */
color: #A9A9A9;
}
input::-moz-placeholder {
/* Mozilla Firefox 19+ */
color: #A9A9A9;
}
input:-ms-input-placeholder {
/* Internet Explorer 10+ */
color: #A9A9A9;
}
view {
box-sizing: border-box;
}
image {
display: block;
}
/*隐藏滚动条*/
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
/* #ifdef MP-BAIDU */
.swan-button.swan-button-radius-ios {
border-radius: 0;
}
/* #endif */
</style>

View File

@@ -0,0 +1,9 @@
const files = require.context('./modules', false, /\.js$/)
const modules = {}
files.keys().forEach(key => {
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
export default{
...modules
}

View File

@@ -0,0 +1,38 @@
import {
req,
uploadFile
} from '../../utils/req.js';
export default {
// 登录
login(param) {
return req.post("index/login", param)
},
// 登录
logappLoginin(param) {
return req.post("index/logappLoginin", param)
},
// 苹果登录
iosLogin(param) {
return req.post("index/iosLogin", param)
},
// app登录配置
getConfig(param) {
return req.get("index/getConfig", param)
},
// 解析二维码
getWxCodeData(param) {
return req.get("card/app/getWxCodeData", param)
},
// 系统配置
configInfo(param) {
return req.get("farm/app/Index/configInfo", param)
},
// 上传图片
uploadFile(param) {
return uploadFile("admin/app/wx/uploadFile", param)
},
// 上传视频
uploadVideo(param) {
return uploadFile("admin/app/wx/uploadVideo", param)
}
}

View File

@@ -0,0 +1,101 @@
import {
req
} from '../../utils/req.js';
export default {
// 店铺详情
storeInfo(param) {
return req.get("farm/app/IndexStore/storeInfo", param)
},
// 编辑店铺
storeUpdate(param) {
return req.post("farm/app/IndexStore/storeUpdate", param)
},
// 商品分类列表
goodsCateList(param) {
return req.get("farm/app/IndexStore/goodsCateList", param)
},
// 商品分类详情
goodsCateInfo(param) {
return req.get("farm/app/IndexStore/goodsCateInfo", param)
},
// 新增商品分类
goodsCateAdd(param) {
return req.post("farm/app/IndexStore/goodsCateAdd", param)
},
// 编辑商品分类
goodsCateUpdate(param) {
return req.post("farm/app/IndexStore/goodsCateUpdate", param)
},
// 商品数量
goodsCount(param) {
return req.get("farm/app/IndexStore/goodsCount", param)
},
// 商品列表
goodsList(param) {
return req.get("farm/app/IndexStore/goodsList", param)
},
// 商品详情
goodsInfo(param) {
return req.get("farm/app/IndexStore/goodsInfo", param)
},
// 新增商品
goodsAdd(param) {
return req.post("farm/app/IndexStore/goodsAdd", param)
},
// 编辑商品
goodsUpdate(param) {
return req.post("farm/app/IndexStore/goodsUpdate", param)
},
// 关联农场
goodsFarmerSelect(param) {
return req.get("farm/app/IndexStore/goodsFarmerSelect", param)
},
// 关联溯源
goodsSourceSelect(param) {
return req.get("farm/app/IndexStore/goodsSourceSelect", param)
},
// 关联农场
goodsFarmerSelect(param) {
return req.get("farm/app/IndexStore/goodsFarmerSelect", param)
},
// 上下架/删除商品
goodsStatusUpdate(param) {
return req.post("farm/app/IndexStore/goodsStatusUpdate", param)
},
// 批量操作商品
someGoodsUpdate(param) {
return req.post("farm/app/IndexStore/someGoodsUpdate", param)
},
// 订单管理
orderList(param) {
return req.get("farm/app/IndexStore/orderList", param)
},
// 订单详情
orderInfo(param) {
return req.get("farm/app/IndexStore/orderInfo", param)
},
// 移交农场
distributionOrderFarmer(param) {
return req.post("farm/app/IndexStore/distributionOrderFarmer", param)
},
// 退款管理
refundOrderList(param) {
return req.get("farm/app/IndexStore/refundOrderList", param)
},
// 退款详情
refundOrderInfo(param) {
return req.get("farm/app/IndexStore/refundOrderInfo", param)
},
// 同意退款
passRefund(param) {
return req.post("farm/app/IndexStore/passRefund", param)
},
// 拒绝退款
noPassRefund(param) {
return req.post("farm/app/IndexStore/noPassRefund", param)
},
// 申请提现
applyWallet(param) {
return req.post("farm/app/IndexStore/applyWallet", param)
},
}

View File

@@ -0,0 +1,137 @@
import {
req
} from '../../utils/req.js';
export default {
// 分类 1土地2认养
claimCateList(param) {
return req.get("farm/app/IndexClaim/claimCateList", param)
},
// 认养轮播图
claimBanner(param) {
return req.get("farm/app/IndexClaim/claimBanner", param)
},
// 认养列表
claimList(param) {
return req.get("farm/app/IndexClaim/claimList", param)
},
// 认养详情
claimInfo(param) {
return req.get("farm/app/IndexClaim/claimInfo", param)
},
// 可使用卡券
canUseCouponList(param) {
return req.get("farm/app/IndexClaim/couponList", param)
},
//获取下单信息
claimPayOrderInfo(param) {
return req.get("farm/app/IndexClaim/claimPayOrderInfo", param)
},
//下单
claimPayOrder(param) {
return req.post("farm/app/IndexClaim/claimPayOrder", param)
},
//订单列表
orderList(param) {
return req.get("farm/app/IndexClaim/orderList", param)
},
//订单详情
orderInfo(param) {
return req.get("farm/app/IndexClaim/orderInfo", param)
},
//取消订单
cancelOrder(param) {
return req.post("farm/app/IndexClaim/cancelOrder", param)
},
//确认收货
endOrder(param) {
return req.post("farm/app/IndexClaim/endOrder", param)
},
//重新支付
claimRePayOrder(param) {
return req.post("farm/app/IndexClaim/claimRePayOrder", param)
},
//我的配送订单
userSendOrderList(param) {
return req.get("farm/app/IndexClaim/userSendOrderList", param)
},
// 配送订单退款
sendOrderRefund(param) {
return req.post("farm/app/IndexClaim/sendOrderRefund", param)
},
// 配送订单确认收货
sendOrderReceiving(param) {
return req.post("farm/app/IndexClaim/sendOrderReceiving", param)
},
//配送时间段
sendTime(param) {
return req.get("farm/app/IndexClaim/sendTime", param)
},
//配送下单
sendOrderPayInfo(param) {
return req.get("farm/app/IndexClaim/sendOrderPayInfo", param)
},
//我要配送
sendOrderApply(param) {
return req.post("farm/app/IndexClaim/sendOrderApply", param)
},
// 配送订单详情
sendOrderInfo(param) {
return req.get("farm/app/IndexClaim/sendOrderInfo", param)
},
// 养殖管理
breedList(param) {
return req.get("farm/app/IndexClaim/breedList", param)
},
// 养殖管理支付
breedOrder(param) {
return req.post("farm/app/IndexClaim/breedOrder", param)
},
// 我的养殖列表
breedOrderList(param) {
return req.get("farm/app/IndexClaim/breedOrderList", param)
},
// 我的养殖详情
breedOrderInfo(param) {
return req.get("farm/app/IndexClaim/breedOrderInfo", param)
},
// 购物车
carInfo(param) {
return req.get("farm/app/IndexUser/carInfo", param)
},
// 加入购物车 goods_id,goods_num = 1
addCar(param) {
return req.post("farm/app/IndexUser/addCar", param)
},
// 编辑购物车 goods_id,goods_num = 1
carUpdate(param) {
return req.post("farm/app/IndexUser/carUpdate", param)
},
// 删除购物车数量 id,goods_num=1
delCar(param) {
return req.post("farm/app/IndexUser/delCar", param)
},
// 批量删除购物车ID arr)
delSomeCar(param) {
return req.post("farm/app/IndexUser/delSomeCar", param)
},
// 众筹活动商品列表
collageList(param) {
return req.get("farm/app/IndexClaim/collageList", param)
},
// 我的众筹记录3个
userCollageLimit(param) {
return req.get("farm/app/IndexClaim/userCollageLimit", param)
},
// 我的众筹列表
userCollageList(param) {
return req.get("farm/app/IndexClaim/userCollageList", param)
},
// 所有正在进行中的众筹
claimCollageList(param) {
return req.get("farm/app/IndexClaim/claimCollageList", param)
},
// 我的众筹详情
claimCollageInfo(param) {
return req.get("farm/app/IndexClaim/claimCollageInfo", param)
},
}

View File

@@ -0,0 +1,197 @@
import {
req
} from '../../utils/req.js';
export default {
// 申请农场主
applyFarmer(param) {
return req.post("farm/app/IndexUser/applyFarmer", param)
},
// 农场主详情
farmerInfo(param) {
return req.get("farm/app/IndexUser/farmerInfo", param)
},
// 修改农场信息
farmerUpdate(param) {
return req.post("farm/app/IndexFarmer/farmerUpdate", param)
},
// 土地列表
landList(param) {
return req.get("farm/app/IndexFarmer/landList", param)
},
// 土地/认养分类 type 1土地2认养
landAndClaimCate(param) {
return req.get("farm/app/IndexFarmer/landAndClaimCate", param)
},
// 土地详情
landInfo(param) {
return req.get("farm/app/IndexFarmer/landInfo", param)
},
// 新增土地
landAdd(param) {
return req.post("farm/app/IndexFarmer/landAdd", param)
},
// 编辑土地
landUpdate(param) {
return req.post("farm/app/IndexFarmer/landUpdate", param)
},
// 上下架/删除土地
landStatusUpdate(param) {
return req.post("farm/app/IndexFarmer/landStatusUpdate", param)
},
// 仪器下拉
machineSelect(param) {
return req.get("farm/app/IndexFarmer/machineSelect", param)
},
// 监控下拉
monitorSelect(param) {
return req.get("farm/app/IndexFarmer/monitorSelect", param)
},
// 地块列表
massifList(param) {
return req.get("farm/app/IndexFarmer/massifList", param)
},
// 地块下拉
massifSelect(param) {
return req.get("farm/app/IndexFarmer/massifSelect", param)
},
// 地块详情
massifInfo(param) {
return req.get("farm/app/IndexFarmer/massifInfo", param)
},
// 新增地块
massifAdd(param) {
return req.post("farm/app/IndexFarmer/massifAdd", param)
},
// 编辑地块
massifUpdate(param) {
return req.post("farm/app/IndexFarmer/massifUpdate", param)
},
// 种子列表
seedList(param) {
return req.get("farm/app/IndexFarmer/seedList", param)
},
// 种子下拉
seedSelect(param) {
return req.get("farm/app/IndexFarmer/seedSelect", param)
},
// 种子详情
seedInfo(param) {
return req.get("farm/app/IndexFarmer/seedInfo", param)
},
// 新增种子
seedAdd(param) {
return req.post("farm/app/IndexFarmer/seedAdd", param)
},
// 编辑种子
seedUpdate(param) {
return req.post("farm/app/IndexFarmer/seedUpdate", param)
},
// 上下架/删除种子
seedStatusUpdate(param) {
return req.post("farm/app/IndexFarmer/seedStatusUpdate", param)
},
// 溯源列表
sourceList(param) {
return req.get("farm/app/IndexFarmer/sourceList", param)
},
// 溯源下拉
sourceSelect(param) {
return req.get("farm/app/IndexFarmer/sourceSelect", param)
},
// 溯源详情
sourceInfo(param) {
return req.get("farm/app/IndexFarmer/sourceInfo", param)
},
// 新增溯源
sourceAdd(param) {
return req.post("farm/app/IndexFarmer/sourceAdd", param)
},
// 编辑溯源
sourceUpdate(param) {
return req.post("farm/app/IndexFarmer/sourceUpdate", param)
},
// 溯源二维码
getSourceQr(param) {
return req.post("farm/app/IndexFarmer/getSourceQr", param)
},
// 养殖列表
breedList(param) {
return req.get("farm/app/IndexFarmer/breedList", param)
},
// 养殖详情
breedInfo(param) {
return req.get("farm/app/IndexFarmer/breedInfo", param)
},
// 新增养殖
breedAdd(param) {
return req.post("farm/app/IndexFarmer/breedAdd", param)
},
// 编辑养殖
breedUpdate(param) {
return req.post("farm/app/IndexFarmer/breedUpdate", param)
},
// 上下架/删除养殖
claimStatusUpdate(param) {
return req.post("farm/app/IndexFarmer/claimStatusUpdate", param)
},
// 认养列表
claimList(param) {
return req.get("farm/app/IndexFarmer/claimList", param)
},
// 认养详情
claimInfo(param) {
return req.get("farm/app/IndexFarmer/claimInfo", param)
},
// 新增认养
claimAdd(param) {
return req.post("farm/app/IndexFarmer/claimAdd", param)
},
// 编辑认养
claimUpdate(param) {
return req.post("farm/app/IndexFarmer/claimUpdate", param)
},
// 认养订单列表
claimOrderList(param) {
return req.get("farm/app/IndexFarmer/claimOrderList", param)
},
// 土地订单列表
landOrderList(param) {
return req.get("farm/app/IndexFarmer/landOrderList", param)
},
// 商城订单列表
shopOrderList(param) {
return req.get("farm/app/IndexFarmer/shopOrderList", param)
},
// 商城订单详情
shopOrderInfo(param) {
return req.get("farm/app/IndexFarmer/shopOrderInfo", param)
},
// 商城订单发货
shopOrderSend(param) {
return req.post("farm/app/IndexFarmer/shopOrderSend", param)
},
// 配送订单列表
farmerSendOrderList(param) {
return req.get("farm/app/IndexFarmer/farmerSendOrderList", param)
},
// 配送订单确认取货
sendOrderPickup(param) {
return req.post("farm/app/IndexFarmer/sendOrderPickup", param)
},
// 配送订单发货
sendOrderSend(param) {
return req.post("farm/app/IndexFarmer/sendOrderSend", param)
},
// 财务管理
farmerFinanceInfo(param) {
return req.get("farm/app/IndexFarmer/farmerFinanceInfo", param)
},
// 流水记录
farmerFinanceList(param) {
return req.get("farm/app/IndexFarmer/farmerFinanceList", param)
},
// 申请提现
applyWallet(param) {
return req.post("farm/app/IndexFarmer/applyWallet", param)
},
}

View File

@@ -0,0 +1,45 @@
import {
req
} from '../../utils/req.js';
export default {
// 首页数据
index(param) {
return req.get("farm/app/Index/index", param)
},
// 关于我们
aboutUsList(param) {
return req.get("farm/app/Index/aboutUsList", param)
},
// 下单协议内容
aboutUsInfoType(param) {
return req.get("farm/app/Index/aboutUsInfoType", param)
},
// 文章详情
articleInfo(param) {
return req.get("farm/app/Index/articleInfo", param)
},
// 公益栏目列表
welfareColumnList(querys) {
return req.get('farm/app/Index/welfareColumnList', querys)
},
// 公益栏目详情
welfareColumnInfo(querys) {
return req.get('/farm/app/Index/welfareColumnInfo', querys)
},
// 附近农场
farmerList(param) {
return req.get("farm/app/Index/farmerList", param)
},
// 农场详情
farmerInfo(param) {
return req.get("farm/app/Index/farmerInfo", param)
},
// 监控列表
monitorList(param) {
return req.get("farm/app/Index/monitorList", param)
},
// 监控详情
getMonitorInfo(param) {
return req.get("farm/app/Index/getMonitorInfo", param)
},
}

View File

@@ -0,0 +1,77 @@
import {
req
} from '../../utils/req.js';
export default {
// 租地列表
landList(param) {
return req.get("farm/app/IndexLand/landList", param)
},
// 租地详情
landInfo(param) {
return req.get("farm/app/IndexLand/landInfo", param)
},
// 种子列表
seedList(param) {
return req.get("farm/app/IndexLand/seedList", param)
},
// 种子详情
seedInfo(param) {
return req.get("farm/app/IndexLand/seedInfo", param)
},
// 可使用卡券
canUseCouponList(param) {
return req.post("farm/app/IndexLand/couponList", param)
},
//获取下单信息
landPayOrderInfo(param) {
return req.post("farm/app/IndexLand/landPayOrderInfo", param)
},
//下单
landPayOrder(param) {
return req.post("farm/app/IndexLand/landPayOrder", param)
},
//订单列表
orderList(param) {
return req.get("farm/app/IndexLand/orderList", param)
},
//订单详情
orderInfo(param) {
return req.get("farm/app/IndexLand/orderInfo", param)
},
//取消订单
cancelOrder(param) {
return req.post("farm/app/IndexLand/cancelOrder", param)
},
//确认收货
endOrder(param) {
return req.post("farm/app/IndexLand/endOrder", param)
},
//重新支付
landRePayOrder(param) {
return req.post("farm/app/IndexLand/landRePayOrder", param)
},
//我的配送订单
userSendOrderList(param) {
return req.get("farm/app/IndexLand/userSendOrderList", param)
},
// 配送订单退款
sendOrderRefund(param) {
return req.post("farm/app/IndexLand/sendOrderRefund", param)
},
// 配送订单确认收货
sendOrderReceiving(param) {
return req.post("farm/app/IndexLand/sendOrderReceiving", param)
},
//配送下单
sendOrderPayInfo(param) {
return req.get("farm/app/IndexLand/sendOrderPayInfo", param)
},
//我要配送
sendOrderApply(param) {
return req.post("farm/app/IndexLand/sendOrderApply", param)
},
// 配送订单详情
sendOrderInfo(param) {
return req.get("farm/app/IndexLand/sendOrderInfo", param)
},
}

View File

@@ -0,0 +1,121 @@
import {
req
} from '../../utils/req.js';
export default {
// 个人中心页面
index(param) {
return req.get("farm/app/IndexUser/index", param)
},
// 获取默认地址
getDefultAddress(param) {
return req.get("farm/app/IndexUser/getDefultAddress", param)
},
// 地址列表
addressList(param) {
return req.get("farm/app/IndexUser/addressList", param)
},
// 地址详情
addressInfo(param) {
return req.get("farm/app/IndexUser/addressInfo", param)
},
// 新增地址
addressAdd(param) {
return req.post("farm/app/IndexUser/addressAdd", param)
},
// 修改地址
addressUpdate(param) {
return req.post("farm/app/IndexUser/addressUpdate", param)
},
// 删除地址
addressDel(param) {
return req.post("farm/app/IndexUser/addressDel", param)
},
//卡券列表
userCouponList(param) {
return req.get("farm/app/IndexUser/userCouponList", param)
},
//删除卡券
couponDel(param) {
return req.post("farm/app/IndexUser/couponDel", param)
},
//储值充值卡列表
cardList(param) {
return req.get("farm/app/IndexBalance/cardList", param)
},
//充值余额(card_id)
payBalanceOrder(param) {
return req.post("farm/app/IndexBalance/payBalanceOrder", param)
},
//充值订单列表(时间筛选 start_time,end_time)
balaceOrder(param) {
return req.get("farm/app/IndexBalance/balaceOrder", param)
},
//交易记录(add:支出0收入1时间筛选 start_time,end_time)
userConsumeWater(param) {
return req.get("farm/app/IndexUser/userConsumeWater", param)
},
// 待评价列表
noEvaluateOrderList(param) {
return req.get("farm/app/Index/noEvaluateOrderList", param)
},
// 评价列表
userEvaluateList(param) {
return req.get("farm/app/Index/userEvaluateList", param)
},
// 根据type获取对应物品的评价
goodsEvaluateList(param) {
return req.get("farm/app/Index/goodsEvaluateList", param)
},
// 评价详情
evaluateInfo(param) {
return req.get("farm/app/Index/evaluateInfo", param)
},
// 新增评价
evaluateAdd(param) {
return req.post("farm/app/Index/evaluateAdd", param)
},
// 编辑评价
evaluateUpdate(param) {
return req.post("farm/app/Index/evaluateUpdate", param)
},
// 申请分销商
applyReseller(param) {
return req.post("farm/app/IndexUser/applyReseller", param)
},
// 获取推广码
getAppDownloadQr(param) {
return req.get("farm/app/IndexUser/getAppDownloadQr", param)
},
// 分销中心
resellerInfo(param) {
return req.get("farm/app/IndexReseller/resellerInfo", param)
},
// 分销订单列表
fxOrderList(param) {
return req.get("farm/app/IndexReseller/orderList", param)
},
// 我的团队
myTeam(param) {
return req.get("farm/app/IndexReseller/myTeam", param)
},
// 分销申请提现
applyWallet(param) {
return req.post("farm/app/IndexReseller/applyWallet", param)
},
// 分销提现记录
walletList(param) {
return req.get("farm/app/IndexReseller/walletList", param)
},
// 系统通知
systemInfoList(param) {
return req.get("farm/app/IndexInfo/systemInfoList", param)
},
// 运营公告
operateInfoList(param) {
return req.get("farm/app/IndexInfo/operateInfoList", param)
},
// 运营公告详情
operateInfoInfo(param) {
return req.get("farm/app/IndexInfo/operateInfoInfo", param)
},
}

View File

@@ -0,0 +1,161 @@
import {
req
} from '../../utils/req.js';
export default {
// 商品首页
goodsIndex(param) {
return req.get("farm/app/IndexGoods/goodsIndex", param)
},
// 店铺列表
indexStoreList(param) {
return req.get("farm/app/IndexGoods/indexStoreList", param)
},
// 热门商品列表
hotGoodsList(param) {
return req.get("shop/app/IndexGoods/hotGoodsList", param)
},
// 商品列表
goodsList(param) {
return req.get("farm/app/IndexGoods/goodsList", param)
},
// 商品详情
goodsInfo(param) {
return req.get("farm/app/IndexGoods/goodsInfo", param)
},
// 购物车
carInfo(param) {
return req.get("farm/app/IndexGoods/carInfo", param)
},
// 加入购物车
addCar(param) {
return req.post("farm/app/IndexGoods/addCar", param)
},
// 删除购物车数量
delCar(param) {
return req.post("farm/app/IndexGoods/delCar", param)
},
//清空购物车
delSomeCar(param) {
return req.post("farm/app/IndexGoods/delSomeCar", param)
},
// 编辑购物车数据
carUpdate(param) {
return req.post("farm/app/IndexGoods/carUpdate", param)
},
//获取下单信息
payOrderInfo(param) {
return req.get("farm/app/IndexOrder/payOrderInfo", param)
},
//下单
payOrder(param) {
return req.post("farm/app/IndexOrder/payOrder", param)
},
//订阅消息
tmplList(param) {
return req.get("farm/app/IndexOrder/tmplList", param)
},
//订单列表
orderList(param) {
return req.get("farm/app/IndexOrder/orderList", param)
},
//订单详情
orderInfo(param) {
return req.get("farm/app/IndexOrder/orderInfo", param)
},
//刷新二维码
refreshQr(param) {
return req.post("farm/app/IndexOrder/refreshQr", param)
},
//取消订单
cancelOrder(param) {
return req.post("farm/app/IndexOrder/cancelOrder", param)
},
//确认收货
endOrder(param) {
return req.post("farm/app/IndexOrder/endOrder", param)
},
//重新支付
rePayOrder(param) {
return req.post("farm/app/IndexOrder/rePayOrder", param)
},
//申请退款
applyOrder(param) {
return req.post("farm/app/IndexOrder/applyOrder", param)
},
//我的售后
refundOrderList(param) {
return req.get("farm/app/IndexOrder/refundOrderList", param)
},
//售后详情
refundOrderInfo(param) {
return req.get("farm/app/IndexOrder/refundOrderInfo", param)
},
//取消退款
cancelRefundOrder(param) {
return req.post("farm/app/IndexOrder/cancelRefundOrder", param)
},
// 卡券列表
couponList(param) {
return req.get("farm/app/Index/couponList", param)
},
// 可使用卡券
canUseCouponList(param) {
return req.get("farm/app/IndexOrder/couponList", param)
},
// 用户领取卡券
userGetCoupon(param) {
return req.post("farm/app/Index/userGetCoupon", param)
},
// 秒杀活动
killAtvList(param) {
return req.get("shop/app/IndexGoods/killAtvList", param)
},
// 秒杀商品
killGoodsList(param) {
return req.get("shop/app/IndexGoods/killGoodsList", param)
},
// 秒杀订阅
killNotice(param) {
return req.post("shop/app/IndexGoods/killNotice", param)
},
// 积分个人信息
userInfo(param) {
return req.get("shop/app/IndexUser/userInfo", param)
},
// 积分商品
integralGoodsList(param) {
return req.get("shop/app/IndexUser/integralGoodsList", param)
},
// 积分明细
integralList(param) {
return req.get("shop/app/IndexUser/integralList", param)
},
// 签到首页
signinIndex(param) {
return req.get("shop/app/IndexUser/signinIndex", param)
},
// 用户签到
signin(param) {
return req.post("shop/app/IndexUser/signin", param)
},
// 签到记录
signinRecordList(param) {
return req.get("shop/app/IndexUser/signinRecordList", param)
},
// 抽奖活动
luckInfo(param) {
return req.get("shop/app/IndexGoods/luckInfo", param)
},
// 抽奖
luckDraw(param) {
return req.post("shop/app/IndexGoods/luckDraw", param)
},
// 中奖记录
luckRecord(param) {
return req.get("shop/app/IndexGoods/luckRecord", param)
},
// 抽奖记录
userLuckRecord(param) {
return req.get("shop/app/IndexGoods/userLuckRecord", param)
}
}

View File

@@ -0,0 +1,33 @@
import {
req
} from '../../utils/req.js';
export default {
// 用户信息
userInfo(param) {
return req.get("farm/app/IndexUser/userInfo", param)
},
// 更新用户信息
userUpdate(param) {
return req.post("farm/app/IndexUser/userUpdate", param)
},
// 获取手机号
reportPhone(param) {
return req.post("farm/app/IndexUser/reportPhone", param)
},
// 验证码
sendShortMsg(param) {
return req.post("farm/app/IndexUser/sendShortMsg", param)
},
// 绑定手机号
bindUserPhone(param) {
return req.post("farm/app/IndexUser/bindUserPhone", param)
},
// 通过分销码查询分销商
fxcodeUser(param) {
return req.get("farm/app/IndexUser/fxcodeUser", param)
},
// 绑定分销商
bindReseller(param) {
return req.post("farm/app/IndexUser/bindReseller", param)
},
}

View File

@@ -0,0 +1,301 @@
<template>
<view class="business-order-detail" v-if="detail.id">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="scrollTop < 20 ?``:primaryColor" leftIcon="icon-left" title="订单详情">
</uni-nav-bar>
<view :style="{height:`${configInfo.navBarHeight}px`}"></view>
<image mode="aspectFill" lazy-load class="common-bg abs" src="https://lbqny.migugu.com/admin/farm/bg-cash.png">
</image>
<view class="pl-lg pr-lg c-base" style="height: 120rpx;">
<view class="f-sm-title text-bold pt-md">{{statusType[detail.pay_type]}}</view>
</view>
<view class="order-text-info ml-md mr-md pd-lg fill-base f-paragraph c-title box-shadow radius-24"
v-if="detail.send_type == 2">
<view @tap.stop="toMap" class="flex-warp">
<i class="iconfont icon-dingwei mr-sm" style="font-size: 28rpx;margin-top: 6rpx;"></i>
<view class="flex-1">{{`${detail.address_info.address}${detail.address_info.address_info}`}}</view>
</view>
<view @tap.stop="$util.goUrl({url:detail.address_info.mobile,openType:'call'})"
class="flex-y-baseline c-caption" style="margin:5rpx 0 0 38rpx;">{{detail.address_info.user_name}}
<view class="ml-lg">
{{detail.address_info.mobile}}
</view>
</view>
</view>
<view class="order-text-info mt-md ml-md mr-md pl-lg pr-lg fill-base f-paragraph c-title box-shadow radius-24">
<view class="flex-between pt-lg pb-lg">
<view class="text c-desc">{{detail.send_type == 1 ? '自提时间':'配送时间'}}</view>
<view>{{detail.user_send_time}}</view>
</view>
<view class="pt-lg pb-lg b-1px-t" v-if="detail.text">
<view class="text c-desc">备注</view>
<view class="mt-sm">
<text decode="emsp" style="word-break:break-all;">{{detail.text}}</text>
</view>
</view>
</view>
<view class="mt-md ml-md mr-md pd-lg fill-base f-paragraph c-title box-shadow radius-24">
<view class="flex-y-center pb-lg b-1px-b">
<i class="iconfont icon-dianpu mr-sm"></i>
<view class="f-title c-title text-bold ellipsis">{{detail.store_info.title}}</view>
</view>
<view class="order-item flex-center" :class="[{'mt-lg':index==0},{'mt-md':index!=0}]"
v-for="(item,index) in detail.order_goods" :key="index">
<image mode="aspectFill" lazy-load class="avatar box-shadow-mini radius-24" :src="item.goods_cover">
</image>
<view class="flex-1 ml-lg">
<view class="f-paragraph c-title text-bold max-490 ellipsis"> {{item.goods_name}}
</view>
<view class="f-caption c-title" :class="[{'flex-center':item.refund_num*1>0}]">
<view class="ellipsis" :style="{maxWidth:item.refund_num*1>0?'300rpx':'490rpx'}">
{{item.spe_name}}
</view>
<view class="flex-1 c-warning text-right" v-if="item.refund_num*1>0">已退x{{item.refund_num}}
</view>
</view>
<view class="flex-y-baseline mt-sm">
<view class="flex-1 f-paragraph c-warning">¥{{item.goods_price}} </view>
<view class="f-caption c-caption">x {{item.goods_num}} </view>
</view>
</view>
</view>
<view class="mt-lg pt-lg pb-lg f-paragraph c-title b-1px-tb">
<view class="flex-between">
<view>商品金额</view>
<view>¥{{detail.init_goods_price}}</view>
</view>
<block v-if="detail.send_type == 2">
<view class="flex-between mt-sm">
<view>配送费</view>
<view>¥{{detail.freight}}</view>
</view>
</block>
<view class="flex-between mt-sm" v-if="detail.kill_discount_price*1>0">
<view>秒杀优惠</view>
<view class="c-warning">-¥{{detail.kill_discount_price}}</view>
</view>
<view class="flex-between mt-sm" v-if="detail.integral_discount_price*1>0">
<view>积分抵扣</view>
<view class="c-warning">-¥{{detail.integral_discount_price}}</view>
</view>
<view class="flex-between mt-sm" v-if="detail.discount*1>0">
<view>卡券优惠</view>
<view class="c-warning">-¥{{detail.discount}}</view>
</view>
</view>
<view class="mt-lg f-paragraph c-title flex-between mt-sm">
<view>合计</view>
<view class="f-title c-warning text-bold">¥{{detail.pay_price}}</view>
</view>
</view>
<video class="item-video mt-md ml-md mr-md radius-24" :loop="false" enable-play-gesture enable-progress-gesture
:src="detail.video" @timeupdate="onTimeUpdate" @waiting="onWaiting" @progress="onProgress"
@loadedmetadata="onLoadedMetaData" v-if="detail.video">
</video>
<view class="order-text-info mt-md ml-md mr-md pd-lg fill-base f-caption c-title box-shadow radius-24">
<view class="flex-warp pt-sm">
<view class="text c-caption">订单编号</view>
<view class="flex-1 flex-y-center">{{detail.order_code}}
<view @tap="$util.goUrl( {url:`${detail.order_code}`,openType:'copy'})"
class="copy-btn sm flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
<block v-if="detail.send_type == 2">
<view class="flex-warp pt-md" v-if="detail.transaction_id">
<view class="text c-caption">付款单号</view>
<view class="flex-1 flex-y-center">{{detail.transaction_id}}
<view @tap="$util.goUrl( {url:`${detail.transaction_id}`,openType:'copy'})"
class="copy-btn sm flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
<view class="flex-warp pt-md" v-if="detail.pay_type == -1 && detail.refund_code">
<view class="text c-caption">退款单号</view>
<view class="flex-1 flex-y-center">{{detail.refund_code}}
<view @tap="$util.goUrl( {url:`${detail.refund_code}`,openType:'copy'})"
class="copy-btn sm flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
</block>
<view class="flex-warp pt-md">
<view class="text c-caption">下单时间</view>
<view class="flex-1">{{detail.create_time}}</view>
</view>
<block v-if="detail.send_type == 2">
<view class="flex-warp pt-md" v-if="detail.pay_time">
<view class="text c-caption">支付时间</view>
<view class="flex-1">{{detail.pay_time}}</view>
</view>
<view class="flex-warp pt-md">
<view class="text c-caption">支付方式</view>
<view class="flex-1">{{payType[detail.pay_model]}}</view>
</view>
</block>
</view>
<view class="order-text-info mt-md ml-md mr-md pd-lg fill-base f-caption c-title box-shadow radius-24"
v-if="detail.pay_type == 7">
<view class="flex-warp pt-sm" v-if="detail.hx_time">
<view class="text c-caption">收货时间</view>
<view class="flex-1">{{detail.hx_time}}</view>
</view>
<view class="flex-warp pt-md">
<view class="text c-caption">收货人</view>
<view class="flex-1">{{detail.hx_user_name}}</view>
</view>
</view>
<view class="space-max-footer"></view>
<view class="footer-btn fix fill-base pl-lg pr-lg" v-if="detail.pay_type!=7">
<view class="flex-between">
<view></view>
<view @tap.stop="toTel" class="common-btn flex-center f-caption c-base radius ml-lg"
:style="{background:primaryColor}">联系买家
</view>
</view>
<view class="space-safe"></view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
scrollTop: 0,
options: {},
detail: {},
payType: {
1: '微信支付',
2: '余额支付',
3: '支付宝支付'
},
statusType: {
'-1': '已取消',
1: '待支付',
2: '待发货',
3: '已发货',
7: '已完成',
},
sendType: {
1: '自提',
2: '快递'
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false,
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
haveOperItem: state => state.order.haveOperItem,
}),
onLoad(options) {
this.options = options
this.$util.showLoading()
this.updateOrderItem({
key: 'haveOperItem',
val: false
})
this.initIndex()
},
onUnload() {
if (!this.haveOperItem) return
this.$util.back()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
methods: {
...mapActions(['getConfigInfo']),
...mapMutations(['updateOrderItem']),
async initIndex() {
let {
id,
} = this.options
let data = await this.$api.business.orderInfo({
id
})
this.detail = data
this.$util.hideAll()
},
initRefresh() {
this.initIndex()
},
async toMap() {
let {
lat,
lng,
address
} = this.detail
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: lat * 1,
longitude: lng * 1,
name: address,
scale: 28
})
},
toTel() {
let {
mobile: url
} = this.detail.address_info
this.$util.goUrl({
url,
openType: 'call'
})
},
}
}
</script>
<style lang="scss">
.business-order-detail {
.order-seed-item {
.seed-img {
width: 80rpx;
height: 80rpx;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
}
}
.item-video {
width: 710rpx;
height: 380rpx;
display: block;
}
.footer-btn {
width: 100%;
bottom: 0;
.flex-between {
height: 98rpx;
}
}
}
</style>

View File

@@ -0,0 +1,211 @@
<template>
<view class="business-order-list" v-if="isLoad">
<fixed>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex"
:activeColor="primaryColor" :width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<view @tap.stop="goDetail(index)" class="fill-base mt-md ml-md mr-md pd-lg box-shadow-mini radius-16"
v-for="(item,index) in list.data" :key="index">
<view class="flex-between pb-lg">
<view class="f-paragraph c-title"
:class="[{'c-warning': item.pay_type < 3 && item.pay_type != -1},{'c-success': item.pay_type == 3}]">
{{statusType[item.pay_type]}}
</view>
<i class="iconfont icon-right"></i>
</view>
<view class="order-item flex-center" :class="[{'mt-md':aindex!=0}]"
v-for="(aitem,aindex) in item.order_goods" :key="aindex">
<image mode="aspectFill" lazy-load class="avatar box-shadow-mini radius-24" :src="aitem.goods_cover">
</image>
<view class="flex-1 ml-lg">
<view class="f-paragraph c-title text-bold max-490 ellipsis"> {{aitem.goods_name}}
</view>
<view class="f-caption c-title" :class="[{'flex-center':aitem.refund_num*1>0}]">
<view class="ellipsis" :style="{maxWidth:aitem.refund_num*1>0?'300rpx':'490rpx'}">
{{aitem.spe_name}}
</view>
<view class="flex-1 c-warning text-right" v-if="aitem.refund_num*1>0">已退x{{aitem.refund_num}}
</view>
</view>
<view class="flex-y-baseline mt-sm">
<view class="flex-1 f-paragraph c-warning">¥{{aitem.goods_price}} </view>
<view class="f-caption c-caption">x {{aitem.goods_num}} </view>
</view>
</view>
</view>
<view class="flex-between mt-lg pt-md b-1px-t">
<view class="c-title ml-sm mr-sm"> {{`${item.all_goods_num}`}}</view>
<view class="flex-y-baseline f-desc c-title"> 实付
<view class="f-title c-warning">¥{{item.true_price}}</view>
</view>
</view>
<view class="flex-between mt-md" v-if="item.pay_type!=7">
<view></view>
<view @tap.stop="toTel(index)" class="common-btn flex-center f-caption c-base radius-4 ml-lg"
:style="{background:primaryColor}">联系买家
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
isLoad: false,
options: {},
activeIndex: 0,
tabList: [{
id: 0,
title: '全部'
}, {
id: 1,
title: '待支付',
number: 0,
key: 'no_pay_count'
}, {
id: 2,
title: '待发货',
number: 0,
key: 'no_send_count'
}, {
id: 3,
title: '已发货',
number: 0,
key: 'have_send_count'
}, {
id: 7,
title: '已完成'
}],
statusType: {
'-1': '已取消',
1: '待支付',
2: '待发货',
3: '已发货',
7: '已完成',
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false,
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
let {
tab = 0,
} = options
this.options = options
this.activeIndex = tab
this.isLoad = true
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
let {
expressList
} = this.configInfo
expressList.map(item => {
item.currentCheck = false
item.preCheck = false
})
this.expressList = expressList
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
param,
tabList,
activeIndex
} = this
param.pay_type = tabList[activeIndex].id
let newList = await this.$api.business
.orderList(param)
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.tabList.map((item, index) => {
if (!item.key) return
this.tabList[index].number = newList[item.key]
})
this.$util.hideAll()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
toTel(index) {
let {
mobile: url
} = this.list.data[index].address_info
this.$util.goUrl({
url,
openType: 'call'
})
},
// 订单详情
goDetail(index) {
let {
id
} = this.list.data[index]
let url = `/business/pages/order/detail?id=${id}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,295 @@
<template>
<view class="mine-store-manage">
<view class="fill-base pl-lg pb-sm flex-between b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">店铺头像</view>
</view>
<view class="flex-1 flex-between mr-lg">
<upload @upload="imgUpload" :imagelist="subForm.cover" imgtype="cover" text="添加封面" :imgsize="1"
imgclass="sm">
</upload>
<view></view>
</view>
</view>
<view class="fill-base pd-lg flex-between b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">店铺名称</view>
</view>
<input v-model="subForm.title" type="text" class="item-input flex-1" maxlength="20" placeholder-class="c-placeholder"
:placeholder="rule[1].errorMsg" />
</view>
<view class="fill-base pd-lg flex-between b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">店铺地址</view>
</view>
<view class="item-input flex-1">
<view @tap.stop="toChooseLocation" class="flex-y-center">
<view class="flex-1" :class="[{'c-placeholder':!subForm.address},{'c-title':subForm.address}]">
{{subForm.address || `点击右边图标设置`}}
</view><i class="iconfont icon-dingwei ml-sm" :style="{color:primaryColor}"></i>
</view>
</view>
</view>
<view class="fill-base pd-lg">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">店铺描述</view>
</view>
<textarea v-model="subForm.desc" type="text" class="item-textarea mt-md flex-1" maxlength="200"
placeholder-class="c-placeholder" :placeholder="rule[3].errorMsg"></textarea>
</view>
<view class="fill-base mt-md pd-lg flex-between b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">姓名</view>
</view>
<input v-model="subForm.user_name" type="text" class="item-input flex-1" placeholder-class="c-placeholder"
:placeholder="rule[4].errorMsg" />
</view>
<view class="fill-base mt-md pd-lg flex-between b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">联系电话</view>
</view>
<input v-model="subForm.mobile" type="number" class="item-input flex-1" placeholder-class="c-placeholder"
:placeholder="rule[5].errorMsg" />
</view>
<!-- <view class="fill-base pd-lg flex-between b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">开店时间</view>
</view>
<view class="flex-y-center f-paragraph c-title flex-1">
<view @tap.stop="toShowTime('start_time')" :style="{color:subForm.start_time ? primaryColor : '#999'}">
{{subForm.start_time || '选择开始时间'}}
</view>
<view class="ml-md mr-md"></view>
<view @tap.stop="toShowTime('end_time')" :style="{color:subForm.end_time ? primaryColor : '#999'}">
{{subForm.end_time || '选择结束时间'}}
</view>
</view>
</view> -->
<view class="fill-base pd-lg flex-between">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">是否营业</view>
</view>
<view class="flex-1 flex-between">
<view class="f-desc">{{workType[subForm.business_status]}}</view>
<view @tap.stop="toChangeItem('business_status')">
<i class="iconfont icon-switch c-caption ml-sm"
:class="[{'icon-switch-on':subForm.business_status==1}]"
:style="{ color: subForm.business_status==1 ? primaryColor : ''}"></i>
</view>
</view>
</view>
<view class="space-max-footer"></view>
<w-picker :visible.sync="showTime" mode="time" :value="toDayTime" :current="false" :second="false"
:themeColor="primaryColor" @confirm="onConfirm" ref="time"></w-picker>
<fix-bottom-button @confirm="submit" :text="[{text:'保存',type:'confirm'}]" bgColor="#fff"></fix-bottom-button>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from "vuex"
import wPicker from "@/components/w-picker/w-picker.vue";
export default {
components: {
wPicker
},
data() {
return {
toDay: '',
toDayTime: '',
showKey: '',
showTime: false,
workType: {
0: '休息',
1: '营业',
},
subForm: {
id: 0,
cover: [],
title: '',
address: '',
lat: '',
lng: '',
desc: '',
user_name: '',
mobile: '',
start_time: '',
end_time: '',
business_status: 0,
},
rule: [{
name: "cover",
checkType: "isNotNull",
errorMsg: "请上传店铺头像"
}, {
name: "title",
checkType: "isNotNull",
errorMsg: "请输入店铺名称",
regType: 2
}, {
name: "address",
checkType: "isNotNull",
errorMsg: "请设置店铺地址"
}, {
name: "desc",
checkType: "isNotNull",
errorMsg: "请输入店铺描述",
regType: 2
}, {
name: "user_name",
checkType: "isNotNull",
errorMsg: "请输入您的姓名",
regType: 2
}, {
name: "mobile",
checkType: "isAllPhone",
errorMsg: "请输入联系电话",
regText: "联系电话"
}],
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
userInfo: state => state.user.userInfo,
}),
onLoad() {
this.initIndex()
},
methods: {
...mapMutations(['updateUserItem']),
async initIndex() {
let cur_time = new Date(Math.ceil(new Date().getTime()))
this.toDay = this.$util.formatTime(cur_time, 'YY-M-D')
this.toDayTime = this.$util.formatTime(cur_time, 'h:m')
let data = await this.$api.business.storeInfo()
data.cover = data.cover && data.cover.length > 0 ? [{
path: data.cover
}] : []
for (let key in this.subForm) {
this.subForm[key] = data[key] == null ? '' : data[key]
}
},
imgUpload(e) {
let {
imagelist,
imgtype
} = e;
this.subForm[imgtype] = imagelist;
},
// 选择地区
async toChooseLocation(e) {
await this.$util.checkAuth({
type: 'userLocation'
})
let [, {
address = '',
longitude,
latitude
} = {}] = await uni.chooseLocation();
if (!address) return
this.subForm.address = address
this.subForm.lng = longitude
this.subForm.lat = latitude
},
toChangeItem(key) {
this.subForm[key] = this.subForm[key] == 1 ? 0 : 1
},
toShowTime(key) {
this.showKey = key
this.showTime = true
},
onConfirm(val) {
this.subForm[this.showKey] = val.result;
},
//表单验证
validate(param) {
let validate = new this.$util.Validate();
this.rule.map(item => {
let {
name,
} = item
validate.add(param[name], item);
})
let message = validate.start();
console.log(message, "message");
return message;
},
async submit() {
let param = this.$util.deepCopy(this.subForm)
let msg = this.validate(param);
if (msg) {
this.$util.showToast({
title: msg
})
return
}
param.cover = param.cover.length > 0 ? param.cover[0].path : ''
// if (!param.start_time || !param.end_time) {
// this.$util.showToast({
// title: !param.start_time ? `请选择开始时间` : `请选择结束时间`
// })
// return
// }
// let start_time = `${this.toDay} ${param.start_time}`
// let end_time = `${this.toDay} ${param.end_time}`
// if (this.$util.DateToUnix(start_time) > this.$util.DateToUnix(end_time) - 1) {
// this.$util.showToast({
// title: `结束时间不能小于或等于开始时间`
// })
// return
// }
await this.$api.business.storeUpdate(param)
this.$util.showToast({
title: `保存成功`
});
setTimeout(() => {
this.$util.back()
this.$util.goUrl({
url: 1,
openType: `navigateBack`
})
}, 1000)
}
}
}
</script>
<style lang="scss">
.mine-store-manage {
.item-text {
width: 200rpx;
}
.item-textarea {
width: 690rpx;
height: 300rpx;
overflow-y: auto;
}
.icon-dingwei {
font-size: 40rpx;
}
.icon-switch,
.icon-switch-on {
font-size: 80rpx;
line-height: 34rpx;
}
}
</style>

View File

@@ -0,0 +1,287 @@
<template>
<view class="claim-collage">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="color == '#ffffff' ?``:primaryColor" leftIcon="icon-left"
:title="color == '#ffffff' ?``:`众筹认养`">
</uni-nav-bar>
<image mode="aspectFill" lazy-load class="common-bg nav abs"
src="https://lbqny.migugu.com/admin/farm/nav-collage.png"></image>
<image mode="aspectFill" lazy-load class="common-bg collage abs"
src="https://lbqny.migugu.com/admin/farm/bg-collage.png"></image>
<view class="space-collage rel"></view>
<view class="collage-info rel fill-base mt-md ml-md mr-md radius-24 box-shadow">
<image lazy-load class="title-img abs" src="https://lbqny.migugu.com/admin/farm/collage-title.png">
</image>
<view class="title flex-center f-title c-base">简单4步 获得宠物</view>
<view class="flex-x-between mt-md pd-lg">
<block v-for="(item,index) in collageList" :key="index">
<view class="flex-center flex-column">
<image class="collage-icon" :src="`/static/image/farm/${item.img}.png`"></image>
<view class="f-caption mt-sm">
<text space="ensp" decode="emsp"
style="word-break:break-all;color:#899E95">{{item.text}}</text>
</view>
</view>
<i class="flex-y-center iconfont icon-xiangyou" v-if="index!=3"></i>
</block>
</view>
</view>
<view class="space-lg"></view>
<view class="flex-between pd-md c-title">
<view class="f-sm-title text-bold">我参与的众筹</view>
<view @tap="$util.goUrl({url:`/claim/pages/order/collage/list`})" class="flex-y-center f-caption">查看全部<i
class="iconfont icon-right" style="font-size: 24rpx;"></i></view>
</view>
<scroll-view scroll-x class="my-collage-list pl-md" :scroll-with-animation="true" v-if="user_list.length>0">
<block v-for="(item,index) in user_list" :key="index">
<view @tap.stop="goDetail(1,index)" class="my-item mr-md radius-16">
<view class="flex-center rel">
<view class="collage-status abs flex-center f-caption c-base"
:style="{background:primaryColor}">
<view class="flex-y-center f-icontext">
<view class="mr-sm">距成团</view>
<min-countdown :type="4" :targetTime="item.end_time * 1000" color="#fff"
@callback="countEnd">
</min-countdown>
</view>
</view>
<image mode="aspectFill" lazy-load class="cover" :src="item.claim_cover"></image>
<view class="flex-1 pl-md">
<view class="pr-sm f-title c-title text-bold max-300 ellipsis">
{{item.claim_title}}
</view>
<view class="flex-y-center f-desc c-warning mt-sm">众筹价<view
class="flex-y-baseline f-caption ml-sm">
¥<view class="f-sm-title text-bold">{{item.price}}</view>
</view>
</view>
<view class="flex-y-center f-icontext c-title mt-sm">已参与<view :style="{color:primaryColor}">
{{item.have_num||0}}
</view>
, 还差
<view :style="{color:primaryColor}">{{item.surplus_num||0}}</view>人众筹成功
</view>
</view>
</view>
</view>
</block>
</scroll-view>
<view class="pd-md f-sm-title c-title text-bold">众筹商品</view>
<view class="pl-md pr-md" :class="[{'mt-lg':index!=0}]" v-for="(item,index) in list.data" :key="index">
<view @tap.stop="goDetail(2,index)" class="collage-item flex-center">
<image mode="aspectFill" lazy-load class="cover radius-10" :src="item.claim_cover"></image>
<view class="flex-1 ml-md">
<view class="f-title c-title text-bold max-510 ellipsis">{{item.claim_title}}</view>
<view class="flex-y-center f-desc c-warning mt-sm">众筹价<view class="flex-y-baseline f-caption ml-sm">
¥<view class="f-sm-title text-bold">{{item.price}}</view>
</view>
</view>
<view class="f-icontext c-caption text-delete mt-md">原价 ¥{{item.init_price}}</view>
</view>
</view>
<view class="flex-between mt-md mb-md">
<view class="flex-y-center f-desc" :style="{color:primaryColor}">
剩余
<min-countdown :type="2" :targetTime="item.end_time * 1000" :color="primaryColor"
:textColor="primaryColor" :borderColor="primaryColor" @callback="countEnd">
</min-countdown>
</view>
<view class="common-btn flex-center f-paragraph c-base radius" :style="{background:primaryColor}">去众筹
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapActions,
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
color: '#ffffff',
collageList: [{
img: 'pay',
text: ' 支付\n开团/参团'
}, {
img: 'users',
text: '达到人数\n众筹成功'
}, {
img: 'claim',
text: ' 获得\n认养商品'
}, {
img: 'send',
text: '认养到期\n配送到家'
}],
user_list: [],
param: {
page: 1,
},
list: {
data: []
},
loading: true,
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad() {
this.$util.showLoading()
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
onPageScroll(e) {
let color = e.scrollTop < 20 ? '#ffffff' : '#000000'
if (this.color == color) return
this.color = color
},
methods: {
...mapActions(['getConfigInfo', 'getUserInfo']),
async initIndex() {
this.user_list = await this.$api.claim.userCollageLimit()
await this.getList()
this.$util.hideAll()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
countEnd() {
this.$util.log("倒计时完了")
setTimeout(() => {
this.initRefresh()
}, 1000)
},
async getList() {
let {
list: oldList,
param,
} = this
let newList = await this.$api.claim.collageList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
goDetail(type, index) {
let {
start_id,
claim_id = 0
} = type == 1 ? this.user_list[index] : this.list.data[index]
let page = {
1: `/claim/pages/order/collage/detail?id=${start_id}`,
2: `/claim/pages/detail?id=${claim_id}`
}
this.$util.goUrl({
url: page[type]
})
}
}
}
</script>
<style lang="scss">
page {
background: #fff;
}
.claim-collage {
.space-collage {
width: 100%;
height: 494rpx;
top: 0;
z-index: -1;
}
.collage-info {
height: 312rpx;
z-index: 1;
.title-img {
width: 390rpx;
height: 100rpx;
top: -19rpx;
left: 160rpx;
z-index: -1;
}
.title {
height: 58rpx;
}
.collage-icon {
width: 88rpx;
height: 88rpx;
}
.icon-xiangyou {
color: rgba(0, 146, 84, 0.3);
}
}
.my-collage-list {
white-space: nowrap;
width: calc(100% - 20rpx);
.my-item {
width: 550rpx;
height: 180rpx;
background: #F6F7F8;
display: inline-block;
.collage-status {
width: 188rpx;
height: 38rpx;
top: 0;
left: 0;
border-radius: 16rpx 0 0 0;
}
.cover {
width: 188rpx;
height: 180rpx;
border-radius: 16rpx 0 0 16rpx;
}
}
}
.collage-item {
.cover {
width: 180rpx;
height: 170rpx;
}
}
}
</style>

View File

@@ -0,0 +1,152 @@
<template>
<view class="claim-collage-more" v-if="isLoad">
<view @tap="toJoin(index)" class="collage-item fill-base flex-center pd-lg" :class="[{'b-1px-t':index!=0}]"
v-for="(item,index) in list.data" :key="index">
<image class="avatar radius" :src="item.user_info.avatarUrl"></image>
<view class="flex-1 flex-center ml-md">
<view class="flex-1 mr-md flex-between">
<view class="f-desc c-title max-150 ellipsis">{{item.user_info.nickName}}</view>
<view>
<view class="flex-between">
<view></view>
<view class="flex-y-center f-paragraph"><view class="c-warning">{{item.surplus_num}}
</view>人成团
</view>
</view>
<view class="flex-y-center f-icontext c-caption">
<min-countdown :type="5" :targetTime="item.end_time * 1000" color="#999"
@callback="countEnd">
</min-countdown>结束
</view>
</view>
</view>
<view class="common-btn flex-center f-paragraph c-base" :style="{background:primaryColor}">去参与
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapActions
} from "vuex"
export default {
components: {},
data() {
return {
isLoad: false,
options: {},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
lockTap: false
}
},
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
userInfo: state => state.user.userInfo
}),
methods: {
...mapActions(['']),
async initIndex() {
await this.getList()
this.isLoad = true
this.$util.hideAll()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
countEnd() {
this.$util.log("倒计时完了")
setTimeout(() => {
this.initRefresh()
}, 1000)
},
async getList() {
let {
list: oldList,
param,
} = this
let {
id
} = this.options
param.claim_id = id
let newList = await this.$api.claim.claimCollageList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
toJoin(index) {
let {
id: cid,
claim_id,
user_id,
can_join,
} = this.list.data[index]
let {
join_times
} = this.$util.getPage(-1).detail.collage_data
let {
id: uid
} = this.userInfo
if (user_id == uid || !can_join) {
this.$util.showToast({
title: user_id == uid ? `不能参与自己发起的众筹哦` : `您已参与此众筹或已达到参与次数${join_times}`
})
return
}
let url = `/claim/pages/order?id=${claim_id}&cid=${cid}&type=2`
this.$util.goUrl({
url
})
},
}
}
</script>
<style lang="scss">
.claim-collage-more {
.collage-item {
.avatar {
width: 68rpx;
height: 68rpx;
}
}
}
</style>

View File

@@ -0,0 +1,776 @@
<template>
<view class="claim-detail" v-if="detail.id">
<view class='banner-info'>
<swiper class='banner-swiper' @change='handerSwiperChange' :autoplay="true">
<swiper-item v-for="(item,index) in bannerList" :key="index" @tap='handerBannerClick(index)'>
<image mode="aspectFill" class='banner-img' :src='item.img'></image>
<view class="banner-swiper img-bg c-base iconfont icon-zhibo abs flex-center"
v-if="bannerList[bannerCurrent].link_type == 'monitor'"></view>
</swiper-item>
</swiper>
<view class='banner-tagitem banner-tagitem_count'>{{bannerCurrent+1}}/{{bannerList.length}}</view>
<view class='banner-taglist'>
<view class='banner-tagitem'
:class="[{'banner-tagitem_active':item.type==bannerList[bannerCurrent].link_type},{'radius-10':typeList.length == 1},{'radius-left':typeList.length > 1 && index == 0},{'radius-right':typeList.length > 1 && index === typeList.length -1}]"
v-for="(item,index) in typeList" :key="index">
{{item.title}}
</view>
</view>
</view>
<view class="collage-info rel mb-md" v-if="detail.collage_data && detail.collage_data.id">
<image mode="aspectFill" class="collage-img abs" src="https://lbqny.migugu.com/admin/farm/collage.png">
</image>
<view class="collage-count abs">
<min-countdown :type="3" :targetTime="detail.collage_data.end_time * 1000" :color="primaryColor"
textColor="#fff" className="sm" @callback="countEnd">
</min-countdown>
<view class="flex-center f-icontext c-base mt-sm">距结束还剩</view>
</view>
<view class="collage-content pt-md pl-md f-icontext c-base abs">
<view class="f-lg-title"> ¥{{detail.collage_data.price}} </view>
<view class="flex-y-center">
<view class="f-caption text-delete">原价 ¥{{detail.price}} </view>
<view class="collage-num flex-center radius-4 ml-sm pl-sm pr-sm" :style="{color:primaryColor}">
{{detail.collage_data.success_num}}人团
</view>
</view>
</view>
</view>
<view class="pd-lg fill-base">
<view class="flex-center">
<!-- #ifdef MP-WEIXIN -->
<view class="max-566">
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="flex-1">
<!-- #endif -->
<view class="f-title c-title">{{detail.title}}</view>
<view class="flex-y-baseline">
<view class="price c-warning">¥{{detail.price}}</view>
<view class="f-caption c-caption ml-sm">/{{detail.unit}}</view>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
<button open-type="share" class="clear-btn flex-center flex-column">
<i class="iconfont icon-share c-title"></i>
<view class="f-caption c-title">分享</view>
</button>
<!-- #endif -->
</view>
<view class="f-caption c-caption">
<text decode="emsp" style="word-break:break-all;">{{detail.desc}}</text>
</view>
</view>
<view class="fill-base pd-lg mt-md" v-if="detail.collage_list.length > 0">
<view class="flex-between">
<view class="f-paragraph c-title">不想自己开团? 可与下面小伙伴快速成团</view>
<view @tap="$util.goUrl({url:`/claim/pages/collage/more?id=${options.id}`})"
class="flex-y-center f-caption c-caption">
查看更多<i class="iconfont icon-right" style="font-size:24rpx"></i></view>
</view>
<view @tap="toJoin(index)" class="collage-item flex-center pt-lg pb-lg" :class="[{'b-1px-t':index!=0}]"
v-for="(item,index) in detail.collage_list" :key="index">
<image class="avatar radius" :src="item.user_info.avatarUrl"></image>
<view class="flex-1 flex-center ml-md">
<view class="flex-1 mr-md flex-between">
<view class="f-desc c-title max-150 ellipsis">{{item.user_info.nickName}}</view>
<view>
<view class="flex-between">
<view></view>
<view class="flex-y-center f-paragraph"><view class="c-warning">
{{item.surplus_num}}
</view>人成团
</view>
</view>
<view class="flex-y-center f-icontext c-caption">
<min-countdown :type="5" :targetTime="item.end_time * 1000" color="#999"
@callback="countEnd">
</min-countdown>结束
</view>
</view>
</view>
<view class="common-btn flex-center f-paragraph c-base" :style="{background:primaryColor}">去参与
</view>
</view>
</view>
</view>
<view class="count-list flex-y-center fill-base pd-lg mt-md">
<view class="count-item flex-center flex-column">
<view class="f-title c-title ellipsis">{{detail.breed}}</view>
<view class="f-caption c-caption">认养品种</view>
</view>
<view class="line"></view>
<view class="count-item flex-center flex-column">
<view class="f-title c-title ellipsis">{{detail.cycle}}</view>
<view class="f-caption c-caption">认养周期</view>
</view>
<view class="line"></view>
<view class="count-item flex-center flex-column">
<view class="f-title c-title">{{detail.output}}kg</view>
<view class="f-caption c-caption">产量</view>
</view>
</view>
<view class="mt-md pt-sm pl-lg pr-lg pb-lg fill-base f-paragraph c-title">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="f-title text-bold flex-1">认养收获</view>
</view>
<view class="flex-center mt-md">
<image mode="aspectFill" lazy-load class="avatar lg box-shadow-mini radius-10"
:src="detail.harvest_cover">
</image>
<view class="flex-1 ml-lg f-paragraph c-title ellipsis">{{detail.harvest_text}}</view>
</view>
</view>
<view class="mt-md pt-sm pl-lg pr-lg pb-lg fill-base f-paragraph c-title"
v-if="detail.process && detail.process.length > 0">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="f-title text-bold flex-1">认养流程</view>
</view>
<view class="flex-warp" :class="[{'mt-md':index==0},{'mt-lg':index!=0}]"
v-for="(item,index) in detail.process" :key="index">
<image mode="aspectFill" lazy-load class="avatar sm" :src="item.cover"></image>
<view class="flex-1 ml-lg">
<view class="f-paragraph c-title">{{item.title}}</view>
<view class="f-caption c-caption">{{item.time}}</view>
<view class="f-caption c-desc">{{item.sub_title}}</view>
</view>
</view>
</view>
<block v-if="list.all_count>0">
<view class="mt-md pt-sm pl-lg pr-lg fill-base f-paragraph c-title">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="flex-1 flex-between">
<view class="f-title text-bold">评价{{list.all_count}}</view>
<view @tap.stop="$util.goUrl({url:`/mine/pages/evaluate/more?id=${options.id}&type=1`})"
class="flex-y-center f-paragraph c-caption">
<view>查看全部</view>
<i class="iconfont icon-right" style="font-size: 28rpx;"></i>
</view>
</view>
</view>
</view>
<block v-for="(item,index) in list.data" :key="index">
<view class="flex-warp fill-base pd-lg" :class="[{'b-1px-t':index!=0}]" v-if="index<2">
<image mode="aspectFill" lazy-load class="avatar sm radius" :src="item.user_info.avatarUrl">
</image>
<view class="flex-1 ml-lg">
<view class="flex-between">
<view class="f-paragraph c-title max-350 ellipsis">{{item.user_info.nickName}}</view>
<view class="f-icontext c-caption">{{item.create_time}}</view>
</view>
<view class="flex-warp">
<i class="iconfont icon-star-fill icon-font-color mr-sm"
:style="{backgroundImage: aindex < item.star ? '-webkit-linear-gradient(90deg, #FDCD47, #FFC000)' : '-webkit-linear-gradient(90deg, #eee, #eee)'}"
v-for="(aitem,aindex) in 5" :key="aindex"></i>
</view>
<view class="f-caption c-desc mt-md">
<text decode="emsp" style="word-break:break-all;">{{item.text || '该用户没有填写评价哦'}}</text>
</view>
<view class="flex-warp mt-md" v-if="item.imgs && item.imgs.length > 0">
<block v-for="(aitem,aindex) in item.imgs" :key="aindex">
<view class="eva-img sm radius-10 rel" v-if="aindex < 3">
<image @tap.stop="toPreviewImage(index,aindex)" mode="aspectFill"
class="eva-img sm radius-10" :src="aitem">
</image>
<view class="more f-caption c-base abs"
v-if="aindex == 2 && item.imgs.length > 3">
+{{item.imgs.length - 3}}</view>
</view>
</block>
</view>
</view>
</view>
</block>
</block>
<view class="mt-md pt-sm pl-lg pr-lg pb-lg fill-base f-paragraph c-title">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="f-title text-bold flex-1">认养环境</view>
</view>
<view class="flex-center">
<image mode="aspectFill" lazy-load class="avatar mini radius" :src="detail.farmer_info.cover">
</image>
<view class="flex-1 ml-md">{{detail.farmer_info.title}}</view>
</view>
</view>
<view class="map-info rel">
<map :latitude="detail.lat" :longitude="detail.lng" :markers="detail.markers" :enable-satellite="true">
</map>
<view @tap.stop="toMap" class="farmer-title flex-center pl-md pr-sm c-base radius abs">
<view class="f-desc max-500 ellipsis">
{{detail.farmer_info.title}}
</view>
<i class="iconfont icon-right"></i>
</view>
</view>
<view class="order-count-info fill-base"
v-if="detail.machine_info.data && detail.machine_info.data.length > 0">
<view class="flex-between pt-lg pl-lg pr-lg">
<view class="flex-y-baseline f-paragraph c-caption">
<view class="c-title text-bold mr-sm">实时数据</view>
<!-- <view>更新于2021.09.10 16:24</view> -->
</view>
<!-- <i class="iconfont icon-shuaxin"></i> -->
</view>
<!-- <view class="pd-lg flex-center b-1px-b">
<view class="count-item flex-center flex-column">
<view class="f-paragraph c-title">28.7</view>
<view class="f-caption c-caption">热敏温度</view>
</view>
<view class="count-item flex-center flex-column b-1px-l b-1px-r">
<view class="f-paragraph c-title">28.7</view>
<view class="f-caption c-caption">噪声监测</view>
</view>
<view class="count-item flex-center flex-column">
<view class="f-paragraph c-title">28.7</view>
<view class="f-caption c-caption">移动监测</view>
</view>
</view> -->
<view class="pt-lg pl-lg pr-lg pb-md f-paragraph c-desc">
<view class="flex-y-baseline">
<view class="mr-md">无线温湿度</view>
<view>土壤电导率三合一</view>
</view>
<veiw class="flex-warp pb-md">
<view class="machine-item flex-warp f-desc c-caption mt-md"
v-for="(item,index) in detail.machine_info.data" :key="index">
<view class="num">{{`(${index*1+1})`}}</view>
<view class="text">{{item.text}}</view>
<view>{{item.value.length == 0 ? '无' : item.value}}</view>
</view>
</veiw>
<!-- <view class="flex-center pt-md"><i class="iconfont icon-down-bold" style="font-size: 28rpx;"></i></view> -->
</view>
</view>
<view class="mt-md pt-sm pl-lg pr-lg pb-lg fill-base f-paragraph c-title">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="f-title text-bold flex-1">详细说明</view>
</view>
<view class="pt-sm" v-if="detail.text">
<parser :html="detail.text" @linkpress="linkpress" show-with-animation lazy-load>加载中...</parser>
</view>
</view>
<view class="space-max-footer"></view>
<view class="footer-btn fill-base fix">
<view class="footer-item flex-between pr-lg">
<view class="flex-warp f-caption c-title">
<view @tap.stop="$util.goUrl({url:`/pages/home`,openType:`switchTab`})"
class="flex-center flex-column pl-lg pr-lg">
<i class="iconfont icon-home"></i>
<view>首页</view>
</view>
<view @tap.stop="$util.goUrl({url:detail.farmer_info.mobile,openType:'call'})"
class="flex-center flex-column pl-md">
<i class="iconfont icon-dianhua text-bold"></i>
<view>客服</view>
</view>
</view>
<view class="flex-warp" v-if="detail.collage_data && detail.collage_data.id">
<view @tap.stop="toOrder" class="order-btn flex-center f-title c-base text-bold radius-20"
:style="{color:primaryColor,border:`1rpx solid ${primaryColor}`}">
单独认养
</view>
<view @tap.stop="toOrder(1)"
class="order-btn flex-center ml-md f-title c-base text-bold radius-20"
:style="{background:primaryColor,border:`1rpx solid ${primaryColor}`}">
去众筹
</view>
</view>
<view @tap.stop="toOrder" class="order-btn md flex-center f-title c-base text-bold radius-20"
:style="{background:primaryColor}" v-else>立即认养
</view>
</view>
<view class="space-safe"></view>
</view>
<uni-popup ref="show_collage_share" type="center">
<view class="show-collage-share">
<view class="flex-center flex-column fill-base pd-lg radius-24">
<view class="f-md-title text-bold pt-lg pb-lg" :style="{color:primaryColor}">您有一个拼团正在进行中</view>
<view class="user-list flex-center mt-lg mb-lg">
<block v-for="(item,index) in detail.user_start_collage.user_avatar" :key="index">
<view class="avatar-group lg"
v-if="index < detail.user_start_collage.success_num > 3 ? 3 : detail.user_start_collage.success_num -1">
<image mode="aspectFill" lazy-load class="abs avatar fill-body radius" :src="item">
</image>
</view>
</block>
<block v-if="detail.user_start_collage.surplus_num">
<i class="iconfont icon-more ml-sm pl-lg pr-lg"></i>
<view class="more-btn flex-center radius"><i class="iconfont icon-add"></i> </view>
</block>
</view>
<min-countdown :type="2" :targetTime="detail.user_start_collage.end_time * 1000" color="#333"
textColor="#999" bgColor="#F7F7F7" className="md" @callback="countEnd">
</min-countdown>
<view class="f-desc c-caption mt-md mb-lg">还差{{detail.user_start_collage.surplus_num}}人成团距离结束还剩
</view>
<view class="space-lg"></view>
<!-- #ifdef MP-WEIXIN -->
<button open-type="share" class="clear-btn share-btn flex-center f-sm-title"
:style="{background:primaryColor}">
邀请好友
</button>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<view @click="toAppShare" class="share-btn flex-center f-sm-title"
:style="{background:primaryColor}">
邀请好友
</view>
<!-- #endif -->
<view class="space-lg"></view>
</view>
<i @tap="$refs.show_collage_share.close()" class="flex-center mt-lg iconfont icon-close c-base"></i>
</view>
</uni-popup>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
detail: {},
bannerCurrent: 0,
bannerList: [],
typeList: [],
current: 0,
param: {
page: 1,
limit: 10
},
list: {
data: []
},
loading: true,
lockTap: false,
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
haveOperItem: state => state.order.haveOperItem,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
this.updateOrderItem({
key: 'haveOperItem',
val: false
})
this.initIndex()
},
onUnload() {
if (!this.haveOperItem) return
this.$util.back()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onShareAppMessage(e) {
let {
id: pid
} = this.userInfo
let {
id,
title,
cover: imageUrl
} = this.detail
let path = `/claim/pages/detail?id=${id}&pid=${pid}`
this.$util.log(path)
return {
title,
imageUrl,
path,
}
},
methods: {
...mapMutations(['updateOrderItem']),
async initIndex(refresh = false) {
let {
id
} = this.options
let data = await this.$api.claim.claimInfo({
id
})
data.markers = [{
iconPath: "https://lbqnyv2.migugu.com/images/12/2018/11/A33zQycihMM33y337LH23myTqTl3tl.png",
id: 0,
latitude: data.lat,
longitude: data.lng,
width: 28,
height: 28
}]
let {
imgs = [],
monitor = [],
user_start_collage = {
id: 0
}
} = data
let bannerList = []
let typeList = []
if (imgs && imgs.length > 0) {
imgs.map((item) => {
bannerList.push({
img: item,
link: '',
link_type: "img"
})
})
typeList.push({
title: "图片",
type: "img"
})
}
if (monitor && monitor.length > 0) {
monitor.map((item) => {
bannerList.push({
img: item.cover,
link: item.id,
link_type: "monitor"
})
})
typeList.push({
title: "监控",
type: "monitor"
})
}
this.typeList = typeList
this.bannerList = bannerList
this.detail = data
this.$util.hideAll()
await this.getList()
if (user_start_collage && user_start_collage.id && !refresh) {
this.$refs.show_collage_share.open()
}
},
initRefresh() {
this.initIndex(true)
},
countEnd() {
this.$util.log("倒计时完了")
setTimeout(() => {
this.initRefresh()
}, 1000)
},
linkpress(res) {
console.log("linkpress", res);
// #ifdef APP-PLUS
this.$util.goUrl({
url: res.href,
openType: 'web'
})
// #endif
},
async getList() {
let {
list: oldList,
} = this
let {
id: goods_id,
} = this.options
let param = Object.assign({}, this.param, {
goods_id,
type: 1,
is_goods: 0
});
let newList = await this.$api.mine.goodsEvaluateList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
handerSwiperChange(e) {
let {
current,
} = e.detail
this.bannerCurrent = current;
},
handerBannerClick(index) {
let item = this.bannerList[index];
let {
link_type: openType,
link: id,
img
} = item;
if (openType == 'monitor') {
let url = `/home/pages/monitor/detail?id=${id}`
this.$util.goUrl({
url
})
return
}
uni.previewImage({
current: img,
urls: [img]
})
},
async toMap() {
let {
lat,
lng,
address
} = this.detail
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: lat * 1,
longitude: lng * 1,
name: address,
scale: 28
})
},
toPreviewImage(index, aindex) {
let {
imgs: urls
} = this.list.data[index]
this.$util.previewImage({
current: urls[aindex],
urls
})
},
toJoin(index) {
let {
id: cid,
claim_id,
user_id,
can_join,
} = this.detail.collage_list[index]
let {
join_times
} = this.detail.collage_data
let {
id: uid
} = this.userInfo
if (user_id == uid || !can_join) {
this.$util.showToast({
title: user_id == uid ? `不能参与自己发起的众筹哦` : `您已参与此众筹或已达到参与次数${join_times}`
})
return
}
let url = `/claim/pages/order?id=${claim_id}&cid=${cid}&type=2`
this.$util.goUrl({
url
})
},
toOrder(type = 0) {
let {
id
} = this.options
let {
collage_data = {
id: 0
},
user_start_collage = {
id: 0
}
} = this.detail
let {
id: cid = 0,
can_start,
start_times
} = collage_data
if (type == 1 && user_start_collage && user_start_collage.id) {
this.$refs.show_collage_share.open()
return
}
if (type == 1 && !can_start) {
this.$util.showToast({
title: `您已达到发起众筹次数${start_times}`
})
return
}
let url = `/claim/pages/order?id=${id}&cid=${cid}&type=${type}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.claim-detail {
.collage-info {
width: 100%;
height: 143rpx;
.collage-img {
left: 0;
bottom: 0;
width: 100%;
height: 156rpx;
}
.collage-count {
right: 12rpx;
bottom: 30rpx;
}
.collage-content {
top: 0;
width: 300rpx;
height: 140rpx;
.collage-num {
min-width: 76rpx;
height: 28rpx;
background: #FFFB00;
}
}
}
.price,
.icon-share {
font-size: 42rpx;
}
.collage-item {
.avatar {
width: 68rpx;
height: 68rpx;
}
}
.count-list {
.count-item {
width: 249rpx;
.ellipsis {
max-width: 220rpx;
}
}
.line {
width: 1rpx;
height: 58rpx;
background: #CCCCCC;
opacity: 0.4;
}
}
.map-info {
map {
width: 100%;
height: 450rpx;
}
.farmer-title {
z-index: 9;
left: 15rpx;
bottom: 10rpx;
height: 50rpx;
background: rgba(0, 0, 0, 0.5);
.iconfont {
font-size: 24rpx;
}
}
}
.order-count-info {
.count-item {
width: 33.33%;
}
.machine-item {
width: 50%;
.num {
width: 50rpx;
}
.text {
width: 150rpx;
padding-right: 10rpx;
}
}
}
.footer-btn {
bottom: 0;
.footer-item {
height: 114rpx;
.iconfont {
font-size: 42rpx;
}
}
.order-btn {
width: 220rpx;
height: 78rpx;
transform: rotateZ(360deg);
}
.order-btn.md {
width: 444rpx;
}
}
.show-collage-share {
width: 640rpx;
.user-list {
.more-btn {
width: 92rpx;
height: 92rpx;
background: #F8F8F8;
border: 1px solid #DBDBDB;
.icon-add {
color: #D8D8D7
}
}
}
.share-btn {
width: 420rpx;
height: 94rpx;
color: #fff;
border-radius: 100rpx;
}
.icon-close {
font-size: 80rpx;
}
}
}
</style>

View File

@@ -0,0 +1,709 @@
<template>
<view class="claim-order" v-if="orderInfo.claim_info.id">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="scrollTop < 20 ?``:primaryColor" leftIcon="icon-left" title="确认订单">
</uni-nav-bar>
<view :style="{height:`${configInfo.navBarHeight}px`}"></view>
<image mode="aspectFill" lazy-load class="common-bg abs" src="https://lbqny.migugu.com/admin/farm/bg-cash.png">
</image>
<view class="space-lg"></view>
<view class="fill-base mt-md ml-md mr-md pd-lg box-shadow radius-24">
<view class="f-title c-title pb-lg b-1px-b">{{orderInfo.farmer_info.title}}</view>
<view class="flex-warp pt-lg pb-lg">
<image mode="aspectFill" lazy-load class="avatar lg radius-10" :src="orderInfo.claim_info.cover">
</image>
<view class="flex-1 ml-lg">
<view class="f-paragraph max-520 ellipsis">{{orderInfo.claim_info.title}}</view>
<block v-if="options.type">
<view class="flex-y-center f-desc c-warning mt-sm">众筹价<view
class="flex-y-baseline f-caption ml-sm">
¥<view class="f-sm-title text-bold">
{{orderInfo.claim_info[orderType[options.type]].price}}
</view>
</view>
<view class="f-icontext c-caption text-delete ml-sm">原价 ¥{{orderInfo.claim_info.price}}
</view>
</view>
<view class="flex-center mt-md">
<view class="collage-tag flex-center f-caption c-base" :style="{background:primaryColor}">
{{orderInfo.claim_info[orderType[options.type]].success_num}}人团
</view>
<view class="flex-1"></view>
</view>
</block>
<view class="flex-y-baseline f-paragraph c-warning mt-sm" v-else>¥{{orderInfo.claim_info.price}}
<view class="f-caption c-caption ml-sm">
/{{orderInfo.claim_info.unit}}</view>
</view>
</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-tb">
<view class="f-paragraph c-black">认养数量</view>
<view v-if="options.type">{{subForm.num}}</view>
<view class="add-remove-item flex-center" v-else>
<i @tap.stop="changeNum(-1,index)" class="iconfont icon-remove-square c-caption"></i>
<view class="number flex-center f-desc pl-sm pr-sm">{{subForm.num}}</view>
<i @tap.stop="changeNum(1,index)" class="iconfont icon-add-square"
:style="{color:primaryColor}"></i>
</view>
</view>
<view class="pt-lg pb-lg f-paragraph c-black">认养收获</view>
<view class="flex-center">
<image mode="aspectFill" lazy-load class="avatar sm radius-10"
:src="orderInfo.claim_info.harvest_cover"></image>
<view class="flex-1 ml-lg ellipsis">{{orderInfo.claim_info.harvest_text}}</view>
</view>
</view>
<view class="fill-base mt-md ml-md mr-md f-paragraph box-shadow radius-24">
<view class="flex-between pd-lg">
<view class="flex-y-center f-sm-title c-black"><i class="iconfont icon-peisong mr-sm"
:style="{color:primaryColor}"></i>配送周期</view>
<view class="f-paragraph">{{orderInfo.claim_info.send_cycle}}</view>
</view>
</view>
<view class="fill-base mt-md ml-md mr-md f-paragraph box-shadow radius-24">
<view class="flex-between pd-lg f-sm-title c-title b-1px-b">
<view class="text-bold">配送方式</view>
<view class="send-list flex-center radius" :style="{width:sendList.length==1?'90rpx':''}">
<view @tap.stop="toChangeItem('sendInd',index)" class="send-item flex-center f-paragraph radius"
:class="[{'c-base':sendInd == index}]" :style="{background:sendInd==index?primaryColor:''}"
v-for="(item,index) in sendList" :key="index">{{item.title}}</view>
</view>
</view>
<view @tap.stop="toChooseAddr" class="pd-lg flex-center"
v-if="sendList[sendInd].id == 1 || orderInfo.address.id">
<view class="flex-1">
<view class="flex-warp">
<i class="iconfont icon-dingwei mr-sm" style="font-size: 28rpx;margin-top: 6rpx;"></i>
<view class="f-paragraph c-title flex-1" :class="[{'max-520':sendList[sendInd].id == 2}]">
{{sendList[sendInd].id == 1 ? `${orderInfo.farmer_info.address}` : orderInfo.address.id ? `${orderInfo.address.address} ${orderInfo.address.address_info}`:''}}
</view>
</view>
<view class="flex-y-baseline f-paragraph c-caption" style="margin:5rpx 0 0 38rpx;">
{{sendList[sendInd].id == 1 ? orderInfo.farmer_info.user_name : orderInfo.address.id ? orderInfo.address.user_name:'' }}
<view class="ml-lg">
{{sendList[sendInd].id == 1? orderInfo.farmer_info.mobile : orderInfo.address.id ? orderInfo.address.mobile : ''}}
</view>
</view>
</view>
<i class="iconfont"
:class="[{'icon-dingwei':sendList[sendInd].id == 1},{'icon-right':sendList[sendInd].id == 2}]"></i>
</view>
<block v-else>
<view class="space-lg"></view>
<view class="space-lg"></view>
<view @tap.stop="toChooseAddr" class="add-btn flex-center f-paragraph c-desc"><i
class="iconfont icon-add-circle-fill mr-sm"></i>添加地址
</view>
<view class="space-lg"></view>
<view class="space-lg"></view>
</block>
</view>
<view class="fill-base mt-md ml-md mr-md f-paragraph box-shadow radius-24" v-if="sendList[sendInd].id == 1">
<view class="flex-between ml-md mr-md pl-sm pt-lg pb-lg b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">姓名</view>
</view>
<input v-model="subForm.user_name" type="text" class="flex-1 f-paragraph"
placeholder-class="c-placeholder" :placeholder="rule[0].errorMsg" />
</view>
<view class="flex-between ml-md mr-md pl-sm pt-lg pb-lg">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">手机号</view>
</view>
<input v-model="subForm.mobile" type="text" class="flex-1 f-paragraph" placeholder-class="c-placeholder"
:placeholder="rule[1].errorMsg" />
<button open-type="getPhoneNumber" @getphonenumber="toAuthPhone"
class="clear-btn auth-phone-btn flex-center" :style="{color:primaryColor}">立即授权</button>
</view>
</view>
<view class="fill-base mt-md ml-md mr-md f-paragraph box-shadow radius-24">
<view class="flex-between ml-md mr-md pl-sm pt-lg pb-lg b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">认养取名</view>
</view>
<input v-model="subForm.claim_name" type="text" class="flex-1 f-paragraph"
placeholder-class="c-placeholder" :placeholder="rule[2].errorMsg" />
</view>
<view class="flex-between ml-md mr-md pl-sm pt-lg pb-lg">
<view class="flex-y-center">
<i class="iconfont icon-required c-base"></i>
<view class="item-text">订单备注</view>
</view>
<input v-model="subForm.text" type="text" class="flex-1 f-paragraph" maxlength="100"
placeholder-class="c-placeholder" placeholder="请输入订单备注" />
</view>
</view>
<view class="fill-base mt-md ml-md mr-md f-paragraph box-shadow radius-24" v-if="!options.type">
<view class="flex-between ml-md mr-md pl-sm pt-lg pb-lg">
<view class="item-text">卡券优惠</view>
<view
@tap.stop="$util.goUrl({url:`/mine/pages/coupon/use?claim_id=${options.id}&num=${subForm.num}&type=2`})"
class="flex-y-center">
<view class="flex-1 text-right">
{{ orderInfo.coupon_id ? `${orderInfo.coupon_discount}` : `${orderInfo.canUseCoupon}张可用` }}
</view>
<i class="iconfont icon-right"></i>
</view>
</view>
</view>
<view class="fill-base mt-md ml-md mr-md f-sm-title box-shadow radius-24">
<view @tap.stop="toChangeItem('payInd',index)"
class="flex-between ml-md mr-md pl-sm pr-sm pt-lg pb-lg b-1px-b" v-for="(item,index) in payList"
:key="index">
<view class="pay-item flex-y-center"><i class="iconfont mr-md" :class="[item.icon]"
:style="{color:item.id==1?primaryColor:item.id==2?subColor:'#01AAF2'}"></i>
<view class="flex-y-baseline">{{item.title}}
<view class="f-paragraph c-caption ml-sm" v-if="item.id==2">余额{{userInfo.balance || 0}}
</view>
</view>
</view>
<i class="pay-icon iconfont c-caption"
:class="[{'icon-xuanze':payInd != index},{'icon-radio-fill':item.is_disabled || payInd == index}]"
:style="{color:payInd==index?primaryColor:''}"></i>
</view>
</view>
<view @tap.stop="toAgree" class="flex-warp f-paragraph mt-md pd-md"><i
class="agree-icon iconfont c-caption mr-sm"
:class="[{'icon-xuanze': !agree},{'icon-xuanze-fill': agree}]" style="margin-top: 3rpx;"
:style="{color:agree?primaryColor:''}"></i>
<view class="flex-y-center">购买代表您已同意<view @tap.stop="$util.goUrl({url:`/mine/pages/agreement?type=1`})"
:style="{color:primaryColor}">
{{agreement}}</view>
</view>
</view>
<view class="space-max-footer"></view>
<view class="footer-btn fill-base fix">
<view class="footer-item flex-between pl-lg pr-lg">
<view class="flex-y-baseline f-paragraph c-title">
合计<view class="f-sm-title c-warning text-bold">¥{{orderInfo.pay_price}}</view>
</view>
<!-- #ifdef APP-PLUS -->
<view @tap="toOrder" class="order-btn flex-center f-sm-title c-base text-bold radius-20"
:style="{background:primaryColor}">立即支付
</view>
<!-- #endif -->
<!-- #ifndef APP-PLUS -->
<auth :needAuth="userInfo && !userInfo.nickName" :must="true" type="userInfo" @go="toOrder">
<view class="order-btn flex-center f-sm-title c-base text-bold radius-20"
:style="{background:primaryColor}">立即支付
</view>
</auth>
<!-- #endif -->
</view>
<view class="space-safe"></view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
scrollTop: 0,
orderType: {
1: 'collage_start_data',
2: 'collage_join_data'
},
// 1微信支付2余额支付3支付宝支付
payList: [{
id: 1,
title: '微信支付',
icon: 'icon-wechat-pay',
is_disabled: false
}
// #ifdef APP-PLUS
, {
id: 3,
title: '支付宝支付',
icon: 'icon-alipay',
is_disabled: false
}
// #endif
, {
id: 2,
title: '余额支付',
icon: 'icon-qianbao',
is_disabled: false
}
],
payInd: 0,
// #ifdef APP-PLUS
balanceInd: 2,
// #endif
// #ifndef APP-PLUS
balanceInd: 1,
// #endif
sendList: [],
sendInd: 0,
agreement: '',
agree: false,
orderInfo: {
address: {},
farmer_info: {},
claim_info: {},
},
subForm: {
num: 1,
send_type: 1,
address_id: '',
user_name: '',
mobile: '',
claim_name: '',
text: ''
},
rule: [{
name: "user_name",
checkType: "isNotNull",
errorMsg: "请输入姓名",
regType: 2
}, {
name: "mobile",
checkType: "isMobile",
errorMsg: "请输入手机号"
}, {
name: "claim_name",
checkType: "isNotNull",
errorMsg: "快给你认养的小动物取个名吧"
}],
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
let {
type = 0
} = options
options.type = type * 1
this.options = options
this.initIndex()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
methods: {
...mapActions(['getUserInfo', 'getAuthPhone']),
...mapMutations(['updateOrderItem']),
async initIndex(refresh = false) {
if (!refresh) {
let [, info] = await Promise.all([this.getUserInfo(), this.$api.home.aboutUsInfoType({
type: 1
})])
this.agreement = info.title
}
let {
id: claim_id,
type: order_type,
cid: collage_id
} = this.options
let {
phone
} = this.userInfo
let {
mobile = '',
num = 1
} = this.subForm
this.subForm.mobile = mobile || phone
let {
address = {}, coupon_id = 0
} = this.orderInfo
let {
id: address_id = 0
} = address
let param = {
claim_id,
coupon_id,
address_id,
num
}
// order_type 1发起众筹2参与众筹
if (order_type == 1) {
param.collage_start_id = collage_id
}
if (order_type == 2) {
param.collage_join_id = collage_id
}
let orderInfo = await this.$api.claim.claimPayOrderInfo(param)
if (!refresh) {
let {
is_self,
is_send
} = orderInfo.claim_info
let sendList = []
if (is_self) {
sendList.push({
id: 1,
title: '自提',
})
}
if (is_send) {
sendList.push({
id: 2,
title: '快递',
})
}
this.sendList = sendList
}
let {
balance
} = this.userInfo
let {
pay_price
} = orderInfo
let is_disabled = balance * 1 < pay_price * 1
let {
balanceInd
} = this
this.payList[balanceInd].is_disabled = is_disabled
if (pay_price * 1 == 0) {
this.payList[0].is_disabled = true
// #ifdef APP-PLUS
this.payList[1].is_disabled = true
// #endif
this.payInd = balanceInd
}
this.orderInfo = orderInfo
},
initRefresh() {
this.initIndex(true)
},
async changeNum(mod, index) {
let {
num: goods_num
} = this.subForm
let {
unit
} = this.orderInfo.claim_info
let num = goods_num + mod;
if (num < 1) {
this.$util.showToast({
title: `此动物最少购买1${unit}`
})
return;
}
this.subForm.num = num
this.initRefresh()
},
toChangeItem(key, index) {
if (key == 'payInd' && index === 2 && this.payList[index].is_disabled) return
this[key] = index
},
async toMap() {
let {
lat,
lng,
address
} = this.orderInfo.farmer_info
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: lat * 1,
longitude: lng * 1,
name: address,
scale: 28
})
},
toChooseAddr() {
if (this.sendList[this.sendInd].id == 1) {
this.toMap()
return
}
this.$util.goUrl({
url: `/mine/pages/address/list?check=1`
})
},
// 授权手机号
async toAuthPhone(e) {
let phone = await this.getAuthPhone({
e,
})
if (!phone) return
this.$nextTick(() => {
this.subForm.mobile = phone
})
},
toAgree() {
this.agree = !this.agree
},
//表单验证
validate(param) {
let validate = new this.$util.Validate();
let arr = param.send_type == 1 ? ['user_name', 'mobile', 'claim_name'] : [
'claim_name'
]
this.rule.map(item => {
let {
name
} = item
if (!arr.includes(name)) return
validate.add(param[name], item);
})
let message = validate.start();
return message;
},
toOrder() {
let {
payList,
payInd,
sendList,
sendInd,
subForm,
agreement
} = this
let {
id: claim_id,
cid: collage_id,
type: order_type
} = this.options
let {
id: pay_model
} = payList[payInd]
let {
id: send_type
} = sendList[sendInd]
let {
address = {}, coupon_id = 0
} = this.orderInfo
let {
id: address_id = 0
} = address
if (!address_id && send_type == 2) {
this.$util.showToast({
title: `请选择收货地址`
})
return
}
let param = Object.assign({}, subForm, {
claim_id,
address_id,
coupon_id,
pay_model,
send_type
});
// order_type 1发起众筹2参与众筹
if (order_type == 1) {
param.collage_start_id = collage_id
}
if (order_type == 2) {
param.collage_join_id = collage_id
}
let msg = this.validate(param);
if (msg) {
this.$util.showToast({
title: msg
});
return;
}
if (!this.agree) {
this.$util.showToast({
title: `请勾选${agreement}`
})
return
}
this.toPay(param)
},
async toPay(param) {
// #ifdef MP-WEIXIN
let that = this
let {
tmp_list = []
} = that.orderInfo
let tmplIds = []
tmp_list.map(item => {
tmplIds.push(item.tmpl_id)
})
if (tmplIds && tmplIds.length > 0) {
uni.requestSubscribeMessage({
tmplIds,
complete(res) {
that.toConfirmPay(param)
console.log(res, "complete requestSubscribeMessage");
}
})
} else {
that.toConfirmPay(param)
}
// #endif
// #ifndef MP-WEIXIN
this.toConfirmPay(param)
// #endif
},
async toConfirmPay(param) {
if (this.lockTap) return
this.lockTap = true
this.$util.showLoading()
try {
let {
pay_list
} = await this.$api.claim.claimPayOrder(param)
this.$util.hideAll()
if (pay_list) {
if (param.pay_model == 3) {
pay_list = {
orderInfo: pay_list,
provider: 'alipay'
}
}
try {
await this.$util.pay(pay_list)
this.$util.showToast({
title: `支付成功`
})
this.updateOrderItem({
key: 'haveOperItem',
val: true
})
setTimeout(() => {
this.$util.back()
this.$util.goUrl({
url: '/mine/pages/pay-result?type=claim',
openType: 'redirectTo'
})
}, 1000)
this.lockTap = false
return
} catch (e) {
this.$util.showToast({
title: `支付失败`
})
setTimeout(() => {
this.$util.back()
this.$util.goUrl({
url: `/claim/pages/order/list`,
openType: 'redirectTo'
})
}, 1000)
this.lockTap = false
return
}
}
this.$util.showToast({
title: `支付成功`
})
this.updateOrderItem({
key: 'haveOperItem',
val: true
})
setTimeout(() => {
this.$util.back()
this.$util.goUrl({
url: '/mine/pages/pay-result?type=claim',
openType: 'redirectTo'
})
}, 1000)
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
}
}
}
</script>
<style lang="scss">
.claim-order {
.collage-tag {
height: 36rpx;
padding: 0 10rpx;
}
.send-list {
width: 180rpx;
height: 52rpx;
border: 1rpx solid #CCCCCC;
transform: rotateZ(360deg);
overflow: hidden;
.send-item {
width: 90rpx;
height: 50rpx;
}
}
.add-btn {
width: 296rpx;
height: 72rpx;
background: #F6F6F6;
border-radius: 36rpx;
margin: 0 auto;
}
.pay-item {
.iconfont {
font-size: 50rpx;
}
}
.pay-icon {
font-size: 40rpx;
}
.icon-right {
font-size: 28rpx;
}
.icon-peisong,
.agree-icon {
font-size: 36rpx;
}
.item-text {
width: 150rpx;
}
.auth-phone-btn {
width: 106rpx;
height: 36rpx;
line-height: 36rpx;
margin-left: 15rpx;
font-size: 20rpx;
background: #F0F4EC;
border-radius: 10rpx;
}
.footer-btn {
bottom: 0;
.footer-item {
height: 114rpx;
.order-btn {
width: 332rpx;
height: 88rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,170 @@
<template>
<view class="claim-order-detail" v-if="detail.id">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="scrollTop < 20 ?``:primaryColor" leftIcon="icon-left" title="养殖详情">
</uni-nav-bar>
<view :style="{height:`${configInfo.navBarHeight}px`}"></view>
<image mode="aspectFill" lazy-load class="common-bg abs" src="https://lbqny.migugu.com/admin/farm/bg-cash.png"></image>
<view class="order-item fill-base mg-big pd-lg radius-16 box-shadow" :class="[{'mt-md':index!=0}]">
<view class="flex-center" :class="[{'mt-md':aindex!=0}]" v-for="(aitem,aindex) in detail.order_goods"
:key="aindex">
<image mode="aspectFill" lazy-load class="avatar radius-16" :src="aitem.goods_cover"></image>
<view class="flex-1 ml-lg">
<view class="f-paragraph c-title text-bold ellipsis" style="max-width: 498rpx;">
{{aitem.goods_name}}
</view>
<view class="flex-between f-desc c-caption mt-md">
<view class="f-paragraph c-warning"> ¥{{aitem.goods_price}} </view>
<view>x{{aitem.goods_num}}</view>
</view>
</view>
</view>
</view>
<view class="fill-base mg-md pd-lg radius-24" v-if="detail.text">
<view class="f-paragraph c-title">
<view class="c-desc">订单备注</view>
<view class="mt-sm">
<text decode="emsp" style="word-break:break-all;">{{detail.text}}</text>
</view>
</view>
</view>
<view class="fill-base mg-md pd-lg radius-24">
<view class="flex-between f-paragraph c-title">
<view class="c-desc">订单编号</view>
<view class="flex-warp">
<view class="max-400 ellipsis">{{detail.order_code}}</view>
<view @tap="$util.goUrl( {url:`${detail.order_code}`,openType:'copy'})"
class="copy-btn flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
<view class="flex-between f-paragraph c-title mt-lg">
<view class="c-desc">付款单号</view>
<view class="flex-warp">
<view class="max-400 ellipsis">{{detail.transaction_id}}</view>
<view @tap="$util.goUrl( {url:`${detail.transaction_id}`,openType:'copy'})"
class="copy-btn flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
<view class="flex-between f-paragraph c-title mt-lg">
<view class="c-desc">下单时间</view>
<view>{{detail.create_time}}</view>
</view>
<view class="flex-between f-paragraph c-title mt-lg" v-if="detail.pay_time">
<view class="c-desc">支付时间</view>
<view>{{detail.pay_time}}</view>
</view>
<view class="flex-between f-paragraph c-title mt-lg">
<view class="c-desc">实付金额</view>
<view class="c-warning">¥{{detail.pay_price}}</view>
</view>
<view class="flex-between f-paragraph c-title mt-lg">
<view class="c-desc">支付方式</view>
<view>{{payType[detail.pay_model]}}</view>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions
} from "vuex"
export default {
components: {},
data() {
return {
scrollTop: 0,
options: {},
detail: {},
payType: {
1: '微信支付',
2: '余额支付',
3: '支付宝支付'
},
}
},
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo
}),
methods: {
...mapActions(['']),
async initIndex() {
let {
id
} = this.options
let data = await this.$api.claim.breedOrderInfo({
id
})
this.detail = data
this.$util.hideAll()
},
initRefresh() {
this.initIndex()
}
}
}
</script>
<style lang="scss">
.claim-order-detail {
.order-video-info {
width: 750rpx;
height: 380rpx;
margin-top: 26rpx;
.video-img {
width: 750rpx;
height: 380rpx;
}
.video-bg {
top: 0;
left: 0;
width: 750rpx;
height: 380rpx;
background: rgba(0, 0, 0, 0.5);
.iconfont {
font-size: 70rpx;
}
}
}
.order-count-info {
.count-item {
width: 33.33%;
}
}
.order-seed-item {
.seed-img {
width: 80rpx;
height: 80rpx;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
}
}
.send-btn {
width: 180rpx;
height: 56rpx;
}
}
</style>

View File

@@ -0,0 +1,263 @@
<template>
<view class="claim-order-detail">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="scrollTop < 20 ?``:primaryColor" leftIcon="icon-left" title="养殖管理">
</uni-nav-bar>
<view :style="{height:`${configInfo.navBarHeight}px`}"></view>
<image mode="aspectFill" lazy-load class="common-bg breed abs" src="https://lbqny.migugu.com/admin/farm/bg-breed.png"></image>
<view @tap.stop="goDetail(index)" class="order-item flex-center fill-base mg-big pd-lg radius-16 box-shadow"
:class="[{'mt-md':index!=0}]" v-for="(item,index) in list.data" :key="index">
<image mode="aspectFill" lazy-load class="avatar big radius-24" :src="item.cover"></image>
<view class="flex-1 ml-lg">
<view class="f-title c-title text-bold ellipsis" style="max-width: 468rpx;"> {{item.title}}
</view>
<view class="f-caption c-caption mt-md mb-md ellipsis" style="max-width: 468rpx;"> {{item.sub_title}}
</view>
<view class="flex-between">
<view class="f-title c-warning"> ¥{{item.price}} </view>
<view class="add-remove-item flex-warp">
<block v-if="item.car_num>0">
<i @tap.stop="toAddCar(index, -1)" class="iconfont icon-remove-square"></i>
<view class="number-text flex-center">{{item.car_num}}</view>
</block>
<i @tap.stop="toAddCar(index,1)" class="iconfont icon-add-square"
:style="{color:primaryColor}"></i>
</view>
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
<view class="space-max-footer"></view>
<view class="footer-btn fix pl-lg pr-lg fill-base" v-if="list.count > 0">
<image mode="aspectFill" lazy-load class="cart-img abs" src="/static/image/farm/breed-cart.png"></image>
<view class="cart-msg flex-center c-base f-icontext abs" :style="{width: list.count<10 ? '30rpx' : '50rpx'}"
v-if="list.count">
{{list.count < 100 ? list.count : '99+'}}
</view>
<view class="footer-item flex-between">
<view class="flex-y-baseline">总计:<view class="c-warning ml-sm">¥{{list.total_price}}</view>
</view>
<view @tap.stop="toOrder" class="order-btn flex-center f-title c-base text-bold radius-20"
:style="{background:primaryColor}">立即下单
</view>
</view>
<view class="space-safe"></view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
scrollTop: 0,
detail: {},
number: 1,
param: {
page: 1,
},
list: {
data: [],
count: 0,
total_price: 0
},
loading: true,
lockTap: false
}
},
onLoad(options) {
this.options = options
this.updateOrderItem({
key: 'haveOperItem',
val: false
})
this.initIndex()
},
onUnload() {
if (!this.haveOperItem) return
this.$util.back()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
haveOperItem: state => state.order.haveOperItem,
}),
methods: {
...mapMutations(['updateOrderItem']),
async initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
async getList() {
let {
list: oldList,
param
} = this
let {
farmer_id
} = this.options
param.farmer_id = farmer_id
let newList = await this.$api.claim.breedList(param)
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
// 添加购物车
async toAddCar(index, goods_num) {
let {
id: goods_id,
car_id = 0
} = this.list.data[index]
await this.toUpdateCar(goods_id, goods_num, car_id, index)
},
// 修改购物车数量
async toUpdateCar(goods_id, goods_num, car_id, index) {
if (this.lockTap) return;
this.lockTap = true;
let methodModel = goods_num > 0 ? 'addCar' : 'delCar'
let {
farmer_id
} = this.options
let param = goods_num > 0 ? {
farmer_id,
goods_id,
goods_num,
type: 1
} : {
id: car_id,
goods_num: 1
}
try {
let curCarId = await this.$api.claim[methodModel](param)
this.lockTap = false
let {
car_num,
} = this.list.data[index]
let num = car_num + goods_num
let cur_car_id = car_id ? num == 0 ? '' : car_id : curCarId
this.list.data[index].car_id = cur_car_id
this.list.data[index].car_num = num
this.getCount()
} catch (e) {
this.lockTap = false
}
},
async getCount() {
let {
farmer_id
} = this.options
let {
count,
total_price
} = await this.$api.claim.breedList({
farmer_id
});
this.list.count = count
this.list.total_price = total_price
},
toOrder() {
let {
id: claim_order_id,
farmer_id
} = this.options
let {
total_price: pay_price
} = this.list
let param = {
claim_order_id,
pay_price,
page_type: 'breed'
}
this.updateOrderItem({
key: 'orderInfo',
val: param
})
let url = `/mine/pages/pay`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.claim-order-detail {
.footer-btn {
bottom: 0;
.cart-img {
width: 108rpx;
height: 108rpx;
top: -8rpx;
}
.cart-msg {
width: 30rpx;
height: 30rpx;
line-height: 30rpx;
border-radius: 15rpx 15rpx 15rpx 0;
background: #f12c20;
top: -8rpx;
left: 108rpx;
}
.footer-item {
height: 114rpx;
margin-left: 180rpx;
.order-btn {
width: 226rpx;
height: 88rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,253 @@
<template>
<view class="claim-order-collage-detail" v-if="detail.id">
<view class="mg-md">
<view class="collage-item flex-center pd-lg fill-base radius-16">
<image mode="aspectFill" lazy-load class="cover radius-10" :src="detail.claim_cover"></image>
<view class="flex-1 ml-md">
<view class="f-title c-title text-bold ellipsis">{{detail.claim_title}}</view>
<view class="flex-y-center f-desc c-warning mt-sm">众筹价<view class="flex-y-baseline f-caption ml-sm">
¥<view class="f-sm-title text-bold">{{detail.price}}</view>
</view>
<view class="f-icontext c-caption text-delete ml-sm">原价 ¥{{detail.claim_price}}</view>
</view>
<view class="flex-center mt-md">
<view class="collage-tag flex-center f-caption c-base" :style="{background:primaryColor}">
{{detail.success_num}}人团
</view>
<view class="flex-1"></view>
</view>
</view>
</view>
<view class="mt-md pd-lg flex-center flex-column fill-base radius-16">
<view class="space-md"></view>
<view class="flex-center">
<image lazy-load class="tag-img mr-lg" src="/static/image/farm/tag.png"></image>
<view class="f-md-title text-bold" :style="{color:primaryColor}">
{{detail.status==1?`还差${detail.surplus_num}人, 成团立享超值优惠`:statusType[detail.status].title}}
</view>
<image lazy-load class="tag-img ml-lg" src="/static/image/farm/tag.png"></image>
</view>
<block v-if="detail.status==1">
<view class="user-list flex-center mt-lg mb-md">
<block v-for="(item,index) in detail.user_list" :key="index">
<image class="avatar radius" :src="item"
v-if="index< detail.success_num > 4 ? 4 : detail.success_num - 1"></image>
</block>
<block v-if="detail.surplus_num">
<view class="avatar flex-center radius" v-for="(item,index) in detail.more_num"
:key="index"><i class="iconfont icon-add"></i>
</view>
</block>
</view>
<view class="flex-y-center mt-lg mb-lg" :style="{color:primaryColor}">
<min-countdown :type="2" :targetTime="detail.end_time * 1000" color="#333" textColor="#999"
bgColor="#F7F7F7" className="sm" @callback="countEnd">
</min-countdown>
</view>
<view class="f-desc c-caption">还差{{detail.surplus_num}}人成团距离结束还剩</view>
<view class="space-lg"></view>
</block>
<view class="collage-status-icon iconfont icon-biaoqian flex-center"
:style="{color:detail.status==2?primaryColor:'#E82F21'}" v-else>
<view class="f-md-title text-bold abs">{{statusType[detail.status].text}}</view>
</view>
<block v-if="detail.status == 1 && !detail.can_join">
<!-- #ifdef MP-WEIXIN -->
<button open-type="share" class="clear-btn share-btn flex-center f-sm-title"
:style="{background:primaryColor}">
邀请好友
</button>
<!-- #endif -->
<!-- #ifdef APP-PLUS -->
<view @click="toAppShare" class="share-btn flex-center f-sm-title"
:style="{background:primaryColor}">
邀请好友
</view>
<!-- #endif -->
</block>
<view @click="toOrder" class="share-btn flex-center f-sm-title" :style="{background:primaryColor}"
v-if="(detail.status == 1 && detail.can_join) || (detail.status>1 && detail.can_start)">
{{detail.status==1 ? '立即参与' : '再开一团'}}
</view>
<view class="space-lg"></view>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
detail: {},
statusType: {
2: {
title: '众筹活动已人满成功',
text: '众筹成功'
},
3: {
title: '众筹超时失败',
text: '众筹失败'
}
}
}
},
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo
}),
methods: {
...mapActions(['']),
async initIndex() {
let {
id
} = this.options
let data = await this.$api.claim.claimCollageInfo({
id
})
let {
success_num,
user_list
} = data
let len = user_list.length
data.more_num = len > 3 ? 1 : success_num > 4 ? 5 - len : success_num - len
this.detail = data
this.$util.hideAll()
},
initRefresh() {
this.initIndex()
},
countEnd() {
this.$util.log("倒计时完了")
setTimeout(() => {
this.initRefresh()
}, 1000)
},
toAppShare() {
// #ifdef APP-PLUS
let {
siteroot
} = siteInfo
let url = siteroot.split('/index.php')[0]
let href = `${url}/h5/#/user/pages/coupon/share?coupon_atv_id=${id}&pid=${pid}`
let scene = type == 1 ? 'WXSceneSession' : 'WXSceneTimeline'
uni.share({
provider: "weixin",
scene,
type: 0,
href,
title,
summary,
imageUrl,
success: function(res) {
console.log("success:" + JSON.stringify(res));
},
fail: function(err) {
console.log("fail:" + JSON.stringify(err));
}
});
// #endif
},
toOrder() {
let {
id,
atv_id,
claim_id,
status
} = this.detail
let type = status == 1 ? 2 : 1
let cid = type == 1 ? atv_id : id
let url = `/claim/pages/order?id=${claim_id}&cid=${cid}&type=${type}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.claim-order-collage-detail {
.collage-item {
.cover {
width: 180rpx;
height: 170rpx;
}
.ellipsis {
max-width: 450rpx;
}
.collage-tag {
height: 36rpx;
padding: 0 10rpx;
}
}
.tag-img {
width: 26rpx;
height: 24rpx;
}
.collage-status-icon {
width: 210rpx;
height: 200rpx;
font-size: 200rpx;
margin: 50rpx auto;
.text-bold {
height: 40rpx;
transform: rotate(-32deg);
}
}
.user-list {
.avatar {
width: 92rpx;
height: 92rpx;
margin: 0 17rpx;
.icon-add {
color: #D8D8D7
}
}
.avatar.flex-center {
background: #F8F8F8;
border: 1px solid #DBDBDB;
}
}
.share-btn {
width: 590rpx;
height: 90rpx;
color: #fff;
border-radius: 100rpx;
}
}
</style>

View File

@@ -0,0 +1,187 @@
<template>
<view class="claim-order-collage-list">
<fixed>
<view class="fill-base pd-lg f-paragraph c-title b-1px-b">我参与了{{list.total_num||0}}个众筹</view>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
:width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<view @tap.stop="goDetail(index)" class="order-item rel fill-base mg-big pd-lg radius-16"
v-for="(item,index) in list.data" :key="index">
<view class="flex-between pb-lg f-desc b-1px-b" :style="{color:primaryColor}"
v-if="activeIndex==0 && item.status==1">
<view> 距离开团结束仅剩 </view>
<min-countdown :type="2" :targetTime="item.end_time * 1000" :color="primaryColor"
:textColor="primaryColor" :borderColor="primaryColor" @callback="countEnd">
</min-countdown>
</view>
<view class="pb-lg f-sm-title c-title text-bold" :class="[{'pt-lg':activeIndex==0 && item.status ==1}]">
{{item.claim_title}}
</view>
<view class="flex-center">
<view class="flex-warp flex-1">
<block v-for="(aitem,aindex) in item.user_avatar" :key="index">
<view class="avatar-group md" v-if="aindex<4">
<image mode="aspectFill" lazy-load class="abs avatar fill-body radius" :src="aitem">
</image>
</view>
</block>
</view>
<view class="common-btn flex-center f-desc c-base" :style="{background:primaryColor}"
v-if="activeIndex==1">查看订单</view>
</view>
<view class="collage-status-icon abs iconfont icon-biaoqian flex-center"
:style="{color:item.status==1?primaryColor:'#E82F21'}" v-if="activeIndex==0">
<view class="f-icontext text-bold abs">{{statusType[item.status]}}</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
<common-popup @confirm="confirmCancel" ref="cancel_item" type="CANCEL_ORDER" :info="popupInfo"></common-popup>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
activeIndex: 0,
tabList: [{
id: 1,
title: '未成团'
}, {
id: 2,
title: '已成团'
}],
statusType: {
1: '进行中',
2: '众筹成功',
3: '众筹超时'
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
let {
tab = 0
} = options
this.activeIndex = tab
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
countEnd() {
this.$util.log("倒计时完了")
setTimeout(() => {
this.initRefresh()
}, 1000)
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
async getList() {
let {
list: oldList,
param,
tabList,
activeIndex
} = this
param.status = tabList[activeIndex].id
let newList = await this.$api.claim.userCollageList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
// 订单详情
goDetail(index) {
let {
start_id,
order_id
} = this.list.data[index]
let {
activeIndex
} = this
let url = activeIndex == 0 ? `/claim/pages/order/collage/detail?id=${start_id}` :
`/claim/pages/order/detail?id=${order_id}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.claim-order-collage-list {
.order-item {
.collage-status-icon {
right: 30rpx;
bottom: 30rpx;
width: 110rpx;
height: 100rpx;
font-size: 100rpx;
.text-bold {
height: 26rpx;
transform: rotate(-32deg);
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
<template>
<view class="farmer-order-land-list">
<fixed>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
:width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<view @tap.stop="goDetail(index)" class="order-item fill-base mg-big pd-lg radius-16 box-shadow"
v-for="(item,index) in list.data" :key="index">
<view class="flex-between pb-lg c-title">
<view class="f-title flex-y-baseline"><i class="iconfont icon-dianpu mr-sm"></i>
<view class="text-bold max-400 ellipsis">{{item.farmer_info.title}}</view>
</view>
<view class="f-paragraph"
:style="{color:item.pay_type == 1? subColor: item.pay_type == 2? primaryColor: ''}">
{{statusType[item.pay_type]}}
</view>
</view>
<view class="flex-center">
<image mode="aspectFill" lazy-load class="avatar radius-24" :src="item.goods_cover"></image>
<view class="flex-1 ml-lg">
<view class="f-title c-title text-bold ellipsis" style="max-width: 498rpx;"> {{item.goods_name}}
</view>
<view class="f-caption c-caption mt-sm"> 数量{{`${item.num}/${item.unit}`}} </view>
<view class="f-caption c-caption"> 配送周期{{item.send_cycle}} </view>
</view>
</view>
<view class="flex-between f-paragraph mt-md pt-md b-1px-t">
<view class="c-desc max-380 ellipsis">认养编号 {{item.claim_code}}</view>
<view class="flex-y-baseline c-title">总计<view class="f-title c-warning">¥{{item.pay_price}}</view>
</view>
</view>
<view class="flex-between mt-md">
<view></view>
<view class="flex-warp" v-if="item.pay_type == 1">
<view @tap.stop="toChangeOrder(index,'cancel_item')"
class="common-btn disabled flex-center f-caption c-title radius-4">取消订单
</view>
<view @tap.stop="toPay(index)" class="common-btn flex-center f-caption c-base radius-4 ml-lg"
:style="{background:primaryColor}">去支付
</view>
</view>
<view class="flex-warp" v-if="item.pay_type == 2">
<view @tap.stop="goDetail(index,2)" class="common-btn flex-center f-caption c-base radius-4 ml-lg"
:style="{background:primaryColor}">养殖管理
</view>
</view>
<view class="flex-warp" v-if="item.pay_type == 7 && !item.have_eva">
<view @tap.stop="toEvaluate(index)" class="common-btn flex-center f-caption c-base radius-4 ml-lg"
:style="{background:primaryColor}">去评价
</view>
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
<common-popup @confirm="confirmCancel" ref="cancel_item" type="CANCEL_ORDER" :info="popupInfo"></common-popup>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
activeIndex: 0,
tabList: [{
id: 0,
title: '全部'
}, {
id: 2,
title: '认养中',
number: 0
}, {
id: 3,
title: '配送中',
number: 0
}, {
id: 7,
title: '已完成'
}],
statusType: {
'-1': '已取消',
1: '待支付',
2: '认养中',
3: '配送中',
7: '已完成',
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
let {
tab = 0
} = options
this.activeIndex = tab
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
async getList() {
let {
list: oldList,
param,
tabList,
activeIndex
} = this
param.pay_type = tabList[activeIndex].id
let newList = await this.$api.claim.orderList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
let {
claim_count,
send_count
} = newList.count
this.tabList[1].number = claim_count
this.tabList[2].number = send_count
this.$util.hideAll()
},
// 取消订单/确认收货
async toChangeOrder(index, key) {
let {
id,
order_code,
goods_cover: image,
} = this.list.data[index]
this.popupInfo = {
id,
name: `订单编号:${order_code}`,
image,
index,
}
this.$refs[key].open()
},
async confirmCancel() {
let {
id,
index,
} = this.popupInfo
if (this.lockTap) return;
this.lockTap = true;
this.$util.showLoading()
try {
await this.$api.claim.cancelOrder({
id
})
this.$util.hideAll()
if (this.activeIndex == 0) {
this.list.data[index].pay_type = -1
} else {
this.list.data.splice(index, 1)
}
this.$util.showToast({
title: `取消成功`
})
this.lockTap = false
this.$refs.cancel_item.close()
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
},
// 去支付
async toPay(index) {
if (this.lockTap) return;
this.lockTap = true;
this.$util.showLoading()
let {
id,
pay_model
} = this.list.data[index]
try {
let {
pay_list
} = await this.$api.claim.claimRePayOrder({
id
})
this.$util.hideAll()
if (pay_list) {
if (pay_model == 3) {
pay_list = {
orderInfo: pay_list,
provider: 'alipay'
}
}
try {
await this.$util.pay(pay_list)
this.lockTap = false;
if (this.activeIndex == 0) {
this.list.data[index].pay_type = 2
} else {
this.list.data.splice(index, 1)
}
let {
number
} = this.tabList[1]
this.tabList[1].number = number + 1
} catch (e) {
this.lockTap = false;
return;
}
}
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
},
// 去评价
toEvaluate(index) {
let {
id
} = this.list.data[index]
let url = `/mine/pages/evaluate/edit?id=${id}&type=claim`
this.$util.goUrl({
url
})
},
// 订单详情
goDetail(index, tab = 0) {
let {
id
} = this.list.data[index]
let url = `/claim/pages/order/detail?id=${id}&tab=${tab}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,320 @@
<template>
<view class="abnor" :style="{paddingBottom: percent}">
<view class="abnor-box">
<image mode="aspectFit" class="abnor-image" v-if="pimage" :src="pimage"></image>
<view class="abnor-text" v-if="ptitle">{{ptitle}}</view>
<view @tap.stop="emitAbnorTipTap" class="abnor-tip" :class="[{'tip-flex':!tipMax}]"
:style="{maxWidth:tipMax,textAlign:tipMax?'left':''}" v-if="ptip">
<text v-for="(item,index) in ptip" :key="index"
:style="{color:item.color == 1 ? primaryColor:''}">{{item.text}}</text>
</view>
<block v-if="pbutton">
<block v-for="(item,index) in pbutton" :key="index">
<view class="abnor-btn" :class="btnSize"
:style="{background:item.type == 'confirm' ? primaryColor : '',color:item.type == 'confirm'?'white':''}"
@tap.stop="emitAbnorTap(index)">{{item.text}}</view>
</block>
</block>
</view>
</view>
</template>
<script>
const Types = {
'REQUEST_ERROR': {
image: 'https://lbqny.migugu.com/admin/public/request-error.png',
title: '网络加载失败',
button: [{
text: '点击刷新',
type: 'confirm'
}],
tip: []
},
'NOT_FOUND': {
image: 'https://lbqny.migugu.com/admin/public/not-found.png',
title: '很抱歉,找不到你要访问的页面',
button: [{
text: '返回',
type: 'cancel'
}],
tip: []
},
'DATA': {
// image: 'https://lbqny.migugu.com/admin/public/no-data-2.png',
// image: 'https://lbqny.migugu.com/admin/public/no-data.png',
image: '/static/image/loading/no-data.png',
title: '没有相关数据哦',
button: [],
tip: [{
text: '',
color: 0
}]
},
'FOLLOW': {
image: 'https://lbqny.migugu.com/admin/public/no-follow.png',
title: '关注有趣的人',
button: [],
tip: [{
text: '不再错过他们每一条动态',
color: 0
}]
},
'FEED': {
image: 'https://lbqny.migugu.com/admin/public/no-feed.png',
title: '还没有任何反馈哦',
button: [],
tip: []
},
'SHOP': {
image: 'https://lbqny.migugu.com/admin/public/no-shop.png',
title: '稍后再来试试吧~',
button: [],
tip: []
},
'WEIBO': {
image: 'https://lbqny.migugu.com/admin/public/no-weibo.png',
title: '',
button: [],
tip: []
},
'SEARCH': {
image: 'https://lbqny.migugu.com/admin/public/no-search.png',
title: '抱歉!没找到相关商品~',
button: [],
tip: []
},
'TAG': {
image: 'https://lbqny.migugu.com/admin/public/no-tag.png',
title: '',
button: [],
tip: []
},
'MESSAGE': {
image: 'https://lbqny.migugu.com/admin/public/no-message.png',
title: '消息通知空空如也',
button: [],
tip: []
},
'LIVE': {
image: 'https://lbqny.migugu.com/admin/public/no-live.png',
title: '',
button: [],
tip: []
},
'ORDER': {
image: 'https://lbqny.migugu.com/admin/public/no-order.png',
title: "还没有相关订单哦",
button: [],
tip: []
},
'CART': {
image: 'https://lbqny.migugu.com/admin/public/no-cart.png',
title: '购物车还是空的哦~',
button: [{
text: '去逛逛',
type: 'confirm'
}],
tip: []
},
'FOOTPRINT': {
image: 'https://lbqny.migugu.com/admin/public/no-footprint.png',
title: '你还没有足迹~',
button: [],
tip: []
},
'COUPON': {
// image: 'https://lbqny.migugu.com/admin/public/no-coupon.png',
image: '/static/image/loading/coupon.png',
title: '暂无卡券',
button: [],
tip: [{
text: '去首页逛逛吧~',
color: 0
}]
},
'REDUCTION': {
image: 'https://lbqny.migugu.com/admin/reduction/nodata.png',
title: '您当前没有满减券,请向工作人员索取',
button: [],
tip: []
}
}
import {
mapState,
} from 'vuex';
export default {
name: 'abnor',
props: {
type: {
type: String,
default () {
return 'DATA'
}
},
image: {
type: String,
default () {
return ''
}
},
title: {
type: String,
default () {
return ''
}
},
tip: {
type: Array,
default () {
return []
}
},
button: {
type: Array,
default () {
return []
}
},
tipTap: {
type: String,
default () {
return ''
}
},
tipMax: {
type: String,
default () {
return '100%'
}
},
btnSize: {
type: String,
default () {
return 'big'
}
},
percent: {
type: String,
default () {
return '100%'
}
},
},
created() {
this.init();
},
data() {
return {
pimage: '',
ptitle: '',
ptip: '',
pbutton: ''
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
emitAbnorTipTap() {
let {
tipTap = ''
} = this
if (!tipTap) return
this.$emit(tipTap);
},
emitAbnorTap(index) {
let {
type
} = this.pbutton[index]
this.$emit(type);
},
init() {
let type = this.type;
// console.log(type, "=====type", this.image);
if (Types[type]) {
this.pimage = this.image || Types[type].image;
this.ptitle = this.title || Types[type].title;
this.pbutton = this.button.length > 0 ? this.button : Types[type].button;
this.ptip = this.tip.length > 0 ? this.tip : Types[type].tip;
}
}
},
}
</script>
<style lang="scss">
.abnor {
position: relative;
display: block;
width: 100%;
height: 0;
padding-bottom: 100%;
overflow: hidden;
.abnor-box {
position: absolute;
display: flex;
top: 0;
bottom: 0;
left: 0;
right: 0;
flex-direction: column;
justify-content: center;
align-items: center;
.abnor-image {
width: 514rpx;
height: 260rpx;
background: transparent;
}
.abnor-text {
margin-top: 30rpx;
color: #333;
font-size: 30rpx;
font-weight: 400;
}
.abnor-tip {
margin: 15rpx 0 10rpx 0;
color: #999;
font-size: 26rpx;
text-align: center;
max-height: 30vh;
overflow: auto;
}
.tip-flex {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.abnor-btn {
min-width: 228rpx;
height: 66rpx;
margin-top: 30rpx;
padding: 0 30rpx;
background-color: #eee;
border: 0 none;
border-radius: 10rpx;
color: #333;
font-size: 28rpx;
text-align: center;
// overflow: hidden;
line-height: 66rpx;
}
.abnor-btn.big {
width: 690rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
margin-top: 40rpx;
border-radius: 50rpx;
}
}
}
</style>

View File

@@ -0,0 +1,492 @@
<template>
<view style="width:100%">
<block v-if="needAuth">
<view @tap="toShowAuth">
<slot></slot>
</view>
</block>
<block v-else>
<view @tap="go(1)">
<slot></slot>
</view>
</block>
<uni-popup ref="show_auth_item">
<view class="auth-box fill-base flex-column flex-center radius-26">
<block v-if="pType === 'userInfo'">
<view class="auth-top fill-base flex-column flex-center" :style="{backgroundColor:primaryColor}">
<view class="flex-center pd-lg">
<view class="auth-info" style="overflow: hidden;">
<button class="auth-info" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<image class="avatar" :src="avatarUrl || '/static/mine/default_user.png'"></image>
</button>
</view>
</view>
<view class="f-paragraph c-base">选择头像</view>
</view>
<view class="auth-w mt-lg mb-lg">
<view class="auth-input">
<view style="width:110rpx;">昵称</view>
<view>
<input type="nickname" v-module="nickName" placeholder="请输入昵称" @blur="onBlur"/>
</view>
</view>
</view>
</block>
<block v-if="pType == 'phone'">
<image class="auth-img" :src="`/static/mine/auth.png`">
</image>
</block>
<view class="space-sm"></view>
<view class="f-caption" :style="{color:primaryColor}">{{contentList[pType][0]}}</view>
<view class="space-lg"></view>
<view class="space-lg"></view>
<block v-if="pType === 'userInfo'">
<button @tap.stop="authUserProfile" hover-class="btn-hover" class="clear-btn flex-center auth-btn"
:style="{backgroundColor:primaryColor,color:'white'}">{{btn_text||contentList[pType][1]}}</button>
</block>
<block v-if="pType === 'phone'">
<button open-type="getPhoneNumber" hover-class="btn-hover" @getphonenumber="authPhone"
class="clear-btn flex-center auth-btn" :style="{backgroundColor:primaryColor,color:'white'}">
{{btn_text||contentList[pType][1]}}
</button>
</block>
<block v-if="pType === 'setting'">
<button open-type="openSetting" hover-class="btn-hover" @opensetting="openSetting"
class="clear-btn flex-center auth-btn" :style="{backgroundColor:primaryColor,color:'white'}">
{{btn_text||contentList[pType][1]}}
</button>
</block>
<view @tap="go(pType == 'phone' && !userInfo.phone ? 2 : 1)" class="f-caption c-caption mt-md"
v-if="!must">{{pType=='userInfo'?'暂不授权':'暂不登录'}}</view>
<view class="space-md"></view>
</view>
</uni-popup>
<uni-popup ref="show_phone_item" :maskClick="false">
<view class="common-popup-content popup-phone pd-lg flex-center flex-column fill-base">
<view class="f-md-title c-black">请输入手机号</view>
<view class="space-lg pb-lg"></view>
<view class="space-lg pb-lg"></view>
<view class="flex-center mb-lg">
<view class="input-info sm mr-md radius-16">
<input v-model="subForm.phone" type="number"
class="item-input flex-y-center pl-lg pr-lg f-sm-title c-title"
placeholder-class="c-placeholder" :placeholder="rule[0].errorMsg" />
</view>
<view @tap="toSend" class="send-btn flex-center c-base radius-16"
:style="{background:primaryColor}">
{{authTime>0?`(${authTime}s)`:'发送'}}
</view>
</view>
<view class="input-info radius-16">
<input v-model="subForm.short_code" type="number"
class="item-input flex-y-center pl-lg pr-lg f-sm-title c-title" maxlength="6"
placeholder-class="c-placeholder" :placeholder="rule[1].errorMsg" />
</view>
<view class="button">
<view @tap.stop="go(3)" class="item-child">
取消
</view>
<view @tap.stop="submit" class="item-child" :style="{background: primaryColor,color:'#fff'}">
确定
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations,
} from "vuex"
export default {
components: {},
name: 'auth',
props: {
needAuth: {
type: Boolean,
default () {
return false
}
},
must: {
type: Boolean,
default () {
return false
}
},
userMust: {
type: Boolean,
default () {
return true
}
},
showAuth: {
type: Boolean,
default () {
return false
}
},
type: {
type: String,
default () {
return 'phone'
}
},
btn_text: {
type: String,
default () {
return ''
}
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
userInfo: state => state.user.userInfo,
}),
created() {
this.init();
},
data() {
return {
contentList: {
userInfo: ['尊贵的用户,获取授权是为了能更好的为你服务', '立即授权'],
phone: ['尊贵的用户,登录后我们才能更好的为你服务', '立即登录'],
setting: ['为了功能正常使用,你需要打开设置并开启获取相应权限', '打开设置'],
},
pType: '',
pMust: '',
authTime: 0,
timer: null,
subForm: {
phone: '',
short_code: ''
},
rule: [{
name: "phone",
checkType: "isMobile",
errorMsg: "请输入手机号",
regText: "手机号"
}, {
name: "short_code",
checkType: "isNotNull",
errorMsg: "请输入短信验证码",
regText: "短信验证码"
}],
avatarUrl:'',
lockTap: false
}
},
methods: {
...mapActions(['getUserInfo', 'getAuthUserProfile', 'getAuthPhone', ]),
init() {
let {
type,
must,
showAuth
} = this
this.$set(this, 'pType', type)
this.$set(this, 'pMust', must)
if (!showAuth) return
this.$refs.show_auth_item.open()
},
async authPhone(e) {
let {
pMust
} = this
let phone = await this.getAuthPhone({
e,
})
console.log(pMust, phone, "===== pMust auth_phone");
if (!phone) {
this.go(pMust ? 2 : 1)
return false
} else {
this.$set(this, 'pType', 'userInfo')
this.$set(this, 'pMust', this.userMust)
console.log(this.PType, this.pMust, "====auth_to_userInfo");
if (!this.pMust) return
this.$refs.show_auth_item.open()
}
},
async onChooseAvatar(e) {
this.reloadAvatar = await this.$api.base.uploadFile({
filePath: e.detail.avatarUrl,
filetype: 'picture'
})
this.avatarUrl = this.reloadAvatar.attachment_path
},
async onBlur(e){
//console.log(e)
this.nickName = e.detail.value
},
async authUserProfile(e) {
// #ifdef APP-PLUS
uni.getUserInfo({
provider: 'weixin',
success: res => {
let {
userInfo = {}
} = res
this.toUpdateUserInfo(userInfo)
}
})
// #endif
// #ifndef APP-PLUS
uni.getUserProfile({
desc: '用于完善个人资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
let {
userInfo = {},
encryptedData,
iv,
} = res
let param = Object.assign({}, userInfo, {
encryptedData,
iv,
});
this.toUpdateUserInfo(param)
},
fail: (res) => {
console.log(res, "=====fail");
this.toUpdateUserInfo()
}
})
// #endif
},
async toUpdateUserInfo(userInfo = {}) {
let {
pMust
} = this
if (userInfo.nickName) {
let {
coupon_atv_id = 0
} = this.commonOptions
userInfo.coupon_atv_id = coupon_atv_id,
userInfo.avatarUrl = this.avatarUrl,
userInfo.nickName = this.nickName
await this.getAuthUserProfile(userInfo)
}
console.log(pMust, userInfo, "===== pMust auth_userinfo");
this.go(pMust && !userInfo.nickName ? 2 : 1);
},
toShowAuth() {
let {
phone = ''
} = this.userInfo
let type = !phone ? 'phone' : 'userInfo'
this.$set(this, 'pType', type)
console.log(this.userInfo, phone, this.pType, "=====toShowAuth")
let refs_key = 'show_auth_item'
// #ifndef MP-WEIXIN
refs_key = 'show_phone_item'
if (this.configInfo.short_code_status * 1 == 0) {
this.go(1)
return
}
// #endif
this.$refs[refs_key].open()
},
go(type = 1) {
this.lockTap = false
this.$emit(type == 1 ? 'go' : 'hide')
let refs_key = type == 3 ? 'show_phone_item' : 'show_auth_item'
this.$refs[refs_key].close();
this.toResetItem()
},
toResetItem() {
this.timer && clearTimeout(this.timer)
this.authTime = 0
this.subForm = {
phone: '',
short_code: ''
}
},
//表单验证
validate(param, is_send = false) {
let validate = new this.$util.Validate();
this.rule.map(item => {
let {
name,
} = item
if (name == 'short_code' && is_send) return
validate.add(param[name], item);
})
let message = validate.start();
return message;
},
async toSend() {
let {
authTime
} = this
if (authTime) return
let {
phone = ''
} = this.subForm
let msg = this.validate({
phone
}, true);
if (msg) {
this.$util.showToast({
title: msg
});
return;
}
if (this.lockTap) return
this.lockTap = true
this.$util.showLoading()
try {
await this.$api.user.sendShortMsg({
phone
})
this.$util.hideAll()
this.lockTap = false
let time = 60
this.timer = setInterval(() => {
if (time === 0) {
clearTimeout(this.timer)
return
}
time--
this.authTime = time
}, 1000)
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
},
async openSetting(e) {
// Handle the openSetting event
console.log('openSetting event:', e);
// The setting was opened, now check if the required permission was granted
// You may need to implement specific logic based on your permission requirements
},
async submit() {
let param = this.$util.deepCopy(this.subForm)
let msg = this.validate(param);
if (msg) {
this.$util.showToast({
title: msg
});
return;
}
if (param.short_code.length != 6) {
this.$util.showToast({
title: `请输入6位数短信验证码`
})
return
}
if (this.lockTap) return
this.lockTap = true
this.$util.showLoading()
try {
await this.$api.user.bindUserPhone(param)
this.$util.hideAll()
this.lockTap = false
this.$refs.show_phone_item.close()
this.toResetItem()
await this.getUserInfo()
setTimeout(() => {
this.$emit('go')
}, 500)
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
}
},
}
</script>
<style lang="scss">
.auth-box {
width: 630rpx;
height: auto;
padding: 0 30rpx 30rpx 30rpx;
overflow: hidden;
.auth-img {
width: 322rpx;
height: 341rpx;
/* background: #f4f6f8; */
}
.auth-btn {
width: 100%;
height: 85rpx;
border-radius: 8rpx;
}
.auth-info {
width: 120rpx;
height: 120rpx;
background: #f4f6f8;
border-radius: 50%;
}
}
.auth-top {
width: 930rpx;
height: 260rpx;
border-radius: 0 0 50% 50%;
overflow: hidden;
}
.auth-w{
width:100%;
}
.auth-input{
align-items: center;
background: #efeff5;
display: flex;
padding: 20rpx;
border-radius: 8rpx;
}
.popup-phone {
width: 630rpx;
.input-info {
width: 570rpx;
height: 90rpx;
background: #F7F7F7;
.item-input {
height: 90rpx;
font-size: 32rpx;
text-align: left;
}
}
.input-info.sm {
width: 400rpx;
}
.send-btn {
width: 150rpx;
height: 90rpx;
}
}
</style>

View File

@@ -0,0 +1,215 @@
<template>
<!-- 轮播图广告 -->
<view class='swiper-box' :style='{margin:`0 ${margin}rpx`,borderRadius:`${.3125}rem`}'>
<swiper class="swiper-ad" :indicator-dots="false" :autoplay="autoplay" :indicator-color="indicatorColor"
:indicator-active-color="indicatorActiveColor" :circular="circular"
:previous-margin="list.length>1?previousMargin:0" :next-margin="list.length>1?nextMargin:0"
@change="handerChange" :style="{height:`${11.375}rem`,borderRadius:`${.3125}rem`}"
easing-function="linear">
<swiper-item v-for="(item,index) in list" :key="index">
<view class='img-box rel' :style="{borderRadius:`${.3125}rem`}" @tap='changeItem(item,index)'>
<image mode="aspectFill" lazy-load :src='item.img || item.cover || item' class="swiper-ad__img"
:style="{borderRadius:`${.3125}rem`}" />
<view class="abs play-video flex-center" :style="{borderRadius:`${.3125}rem`}"
v-if="haveVideo && index == 0"><i class="iconfont icon-bofang"></i>
</view>
</view>
</swiper-item>
</swiper>
<view class='numbers' v-if="list.length>1&&indicatorType=='number'" :style="{textAlign:indicatorStyle}">
<view class="number">{{activeIndex+1}}/{{list.length}}</view>
</view>
<view class='dots' v-if="list.length>1&&indicatorType=='dot'" :style="{textAlign:indicatorStyle}">
<view class='dot' v-for="(item,index) in list" :key="index"
:style='{backgroundColor:index==activeIndex?indicatorActiveColor:indicatorColor,width:index==activeIndex? `${dotWidth}rpx` :"12rpx"}'>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
} from 'vuex';
export default {
name: 'banner',
props: {
list: {
type: Array,
default () {
return [1, 2, 3]
}
},
height: {
type: Number,
default () {
return 400
}
},
indicatorType: {
type: String,
default () {
return "dot"
}
},
indicatorColor: {
type: String,
default () {
return "#FEFFFE"
}
},
indicatorActiveColor: {
type: String,
default () {
return "#fff"
}
},
indicatorStyle: {
type: String,
default () {
return 'center'
}
},
circular: {
type: Boolean,
default () {
return true
}
},
autoplay: {
type: Boolean,
default () {
return false
}
},
previousMargin: {
type: Number,
default () {
return 0
}
},
nextMargin: {
type: Number,
default () {
return 0
}
},
dotWidth: {
type: Number,
default () {
return 12
}
},
margin: {
type: Number,
default () {
return 0
}
},
borderRadius: {
type: Number,
default () {
return 0
}
},
haveVideo: {
type: Boolean,
default () {
return false
}
}
},
created() {
},
data() {
return {
activeIndex: 0
}
},
computed: mapState({
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
}),
methods: {
handerChange: function(e) {
this.activeIndex = e.detail.current;
},
changeItem(item, index) {
this.$emit("change", {
item,
index
})
}
}
}
</script>
<style lang="scss">
.swiper-box {
position: relative;
overflow: hidden;
.img-box {
width: 100%;
height: 100%;
.swiper-ad__img {
width: 100%;
height: 100%;
background: #fff;
}
.play-video {
width: 100%;
height: 100%;
top: 0;
background: rgba(0, 0, 0, 0.2);
.iconfont {
font-size: 100rpx;
color: #fff;
}
}
}
.dots {
position: absolute;
z-index: 20rpx;
text-align: right;
width: 100%;
transform: translateY(-48rpx);
padding: 0 20rpx;
.dot {
display: inline-block;
height: 12rpx;
width: 12rpx;
background-color: #FEFFFE;
border-radius: 6rpx;
margin: 0 8rpx;
}
}
.numbers {
position: absolute;
z-index: 20rpx;
text-align: right;
width: 100%;
transform: translateY(-80rpx);
padding: 0 20rpx;
.number {
display: inline-block;
width: 90rpx;
line-height: 50rpx;
background: rgba(0, 0, 0, 0.3);
text-align: center;
color: #fff;
border-radius: 45rpx;
}
}
}
</style>

View File

@@ -0,0 +1,323 @@
<template>
<view>
<!-- 轮播图 -->
<view v-if="type=='swiper'" class='column-box' :style='{paddingTop:whiteSpace+"rpx",paddingBottom:whiteSpace+"rpx",paddingLeft:wingBlank+"rpx",paddingRight:wingBlank+"rpx"}'>
<swiper class="swiper-category" @change="handerChange" :style="{height:(146*formatRowNum+20*(formatRowNum-1))+'rpx'}">
<swiper-item class="swiper-category-item" v-for="(pitem,pindex) in formatList" :key="pindex">
<view v-for="(item,index) in pitem" :key="index" class="column-item" :style="{width:100/colNum + '%',marginTop:index<colNum? '0' : '20rpx'}"
@tap='change(item)'>
<image mode="aspectFill" lazy-load v-if="item.icon" class="column-img" :src="item.icon"></image>
<view v-else class="column-no-img" :style="{background:colorList[index%8],borderRadius:'50%',margin:'0rpx auto'}">
{{getFirstText(item.cate_name)}}
</view>
<view class='column-text'>{{item.cate_name}}</view>
</view>
</swiper-item>
</swiper>
<view class='dots' v-if="formatList.length>1">
<view class='dot' v-for="(item,index) in formatList.length" :key="index" :style='{background:index==current?indicatorActiveColor:indicatorColor}'></view>
</view>
</view>
<!-- 滑动 -->
<view v-if="type=='scroll'" class='column-box' :style='{paddingTop:whiteSpace+"rpx",paddingBottom:whiteSpace+"rpx",paddingLeft:wingBlank+"rpx",paddingRight:wingBlank+"rpx"}'>
<scroll-view scroll-x class='scroll-x' @scroll="handerScroll">
<view class='scroll-x-item' v-for="(pItem,pindex) in formatList" :key="pindex">
<view class='column-item' v-for="(item,index) in pItem" :key="index" :style='{width:(750/colNum)+"rpx"}' @tap='change(item)'>
<image mode="aspectFill" lazy-load class='column-img' :src='item.icon'></image>
<view class='column-text'>{{item.cate_name}}</view>
</view>
</view>
</scroll-view>
<view class='ink-bar-box' v-if="list.length>formatRowNum*colNum">
<view class="ui-tabs-ink-bar-wrapper">
<view class="ui-tabs-ink-bar" :style='{left:left+"rpx",background:indicatorActiveColor}'></view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'column',
props: {
type: {
type: String,
default () {
return "swiper"
}
},
list: {
type: Array,
default () {
return []
}
},
colNum: {
type: Number,
default () {
return 4
}
},
rowNum: {
type: Number,
default () {
return 2
}
},
indicatorActiveColor: {
type: String,
default () {
return '#f6f5fa'
}
},
indicatorColor: {
type: String,
default () {
return '#f6f5fa'
}
},
wingBlank: {
type: Number,
default () {
return 0
}
},
whiteSpace: {
type: Number,
default () {
return 30
}
},
borderRadius: {
type: Number,
default () {
return 0
}
}
},
created() {
},
data() {
return {
activeIndex: 0,
newList: [],
left: 0,
current: 0,
colorList: ["#fc7f87", "#56b4fc", "#f8ae41", "#11dd9e", "#fba745", "#fb7c85", "#46adfc", "#fc7d86"]
}
},
computed: {
formatRowNum() {
let {
colNum,
rowNum,
list
} = this;
let length = list.length;
let newRowNum = length <= colNum ? 1 : rowNum;
return newRowNum
},
formatList() {
let {
colNum,
rowNum,
type,
list,
formatRowNum
} = this;
let index = 0;
let length = list.length;
let newList = [];
let count = formatRowNum * colNum
// let count = type == 'scroll' ? colNum : formatRowNum * colNum
while (index < length) {
newList.push(list.slice(index, index += count));
}
return newList
},
},
methods: {
handerScroll: function(e) {
let {
scrollLeft,
scrollWidth
} = e.detail;
let windowWidth = uni.getSystemInfoSync().windowWidth;
let left = scrollLeft * 30 / (scrollWidth - windowWidth);
this.left = left
},
handerChange: function(e) {
this.current = e.detail.current;
},
change(item) {
this.$emit("change", item)
},
getFirstText(d) {
d = d || '名称'
return d.slice(0, 1)
},
},
}
</script>
<style lang="scss">
/* 轮播图 */
.column-box {
background: #fff;
font-size: 24rpx;
color: #888;
.swiper-category {
width: 100%;
background: #fff;
font-size: 24rpx;
color: #666;
border-radius: 10rpx;
.swiper-category-item {
box-sizing: border-box;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
background: #fff;
.column-item {
margin-top: 20rpx;
float: left;
.column-img {
width: 100rpx;
height: 100rpx;
margin: 0 auto;
}
.column-no-img {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
color: #fff;
}
.column-text {
margin-top: 10rpx;
text-align: center;
color: #454A53;
width: 100%;
padding: 0 10%;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
.scroll-x {
white-space: nowrap;
background: #fff;
padding: 20rpx 0;
.scroll-x-item {
position: relative;
display: inline-block;
vertical-align: text-top;
display: flex;
flex-direction: column;
}
.column-item {
margin-top: 20rpx;
float: left;
.column-img {
width: 100rpx;
height: 100rpx;
margin: 0 auto;
}
.column-text {
margin-top: 10rpx;
text-align: center;
color: #454A53;
width: 100%;
padding: 0 10%;
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
.dots {
background: white;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding-top: 20rpx;
.dot {
height: 16rpx;
width: 16rpx;
background: #ddd;
border-radius: 8rpx;
margin: 0 8rpx;
}
}
.ink-bar-box {
background: #fff;
padding-bottom: 20rpx;
.ui-tabs-ink-bar-wrapper {
width: 60rpx;
background: #ddd;
position: relative;
left: 345rpx;
bottom: 0;
height: 8rpx;
transform-origin: 50% 50%;
transition: width 250ms ease-out, left 250ms ease-out;
overflow: hidden;
border-radius: 4rpx;
.ui-tabs-ink-bar {
position: absolute;
width: 30rpx;
background: #e74d45;
height: 8rpx;
border-radius: 4rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<uni-popup type="center" :maskClick="maskClick" ref="common_popup">
<view class="common-popup-content fill-base">
<view class="title" v-if="ptitle">{{ptitle}}</view>
<view class="desc" v-if="pdesc">{{pdesc}}</view>
<image mode="aspectFill" lazy-load class="image" :class="imgSize" :src="info.image" v-if="info.image">
</image>
<view class="name ellipsis-3" v-if="info.name">{{info.name}}</view>
<view class="button">
<block v-for="(item,index) in pbutton" :key="index">
<view @tap.stop="toEmit(index)" class="item-child"
:style="{background: item.type == 'confirm' ? primaryColor : '',color:item.type == 'confirm' ?'white':''}">
{{item.title}}
</view>
</block>
</view>
</view>
</uni-popup>
</template>
<script>
const Types = {
'CANCEL_ORDER': {
title: '取消订单',
desc: '请确认是否取消订单,取消后将无法恢复',
text: '确认取消'
},
'END_ORDER': {
title: '确认收货',
desc: '请确认是否收货,收货后将无法恢复',
text: '确认收货'
},
'HX_ORDER': {
title: '核销订单',
desc: '请确认是否核销订单,核销订单后将无法恢复',
text: '确认核销'
},
'DELETE_ORDER': {
title: '删除订单',
desc: '请确认是否删除订单,删除后将无法恢复',
text: '确认删除'
},
'REFUND_ORDER': {
title: '申请退款',
desc: '请确认是否申请退款',
text: '确认申请'
},
'CANCEL_REFUND_ORDER': {
title: '取消退款',
desc: '请确认是否取消退款',
text: '确认取消'
},
'NO_PASS_REFUND': {
title: '拒绝退款',
desc: '请确认是否拒绝退款',
text: '确认拒绝'
},
'HX_CODE': {
title: '核销码',
desc: '请出示二维码给核销人员',
},
'CHANGE_UP_ITEM': {
title: '上架',
desc: '请确认是否上架',
text: '确认上架'
},
'CHANGE_DOWN_ITEM': {
title: '下架',
desc: '请确认是否下架',
text: '确认下架'
},
'DEL_ITEM': {
title: '删除',
desc: '请确认是否删除此数据,删除后将无法恢复',
text: '确认删除'
},
}
import {
mapState,
} from "vuex"
export default {
components: {},
props: {
maskClick: {
type: Boolean,
default () {
return false
}
},
type: {
type: String,
default () {
return 'CANCEL_ORDER'
}
},
title: {
type: String,
default () {
return ''
}
},
desc: {
type: String,
default () {
return ''
}
},
text: {
type: String,
default () {
return ''
}
},
info: {
type: Object,
default () {
return {}
}
},
button: {
type: Array,
default () {
return [{
title: '取消',
type: 'cancel'
}, {
title: '确定取消',
type: 'confirm'
}]
}
},
imgSize: {
type: String,
default () {
return ''
}
}
},
created() {
this.init();
},
data() {
return {
ptitle: '',
pdesc: '',
pbutton: [],
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
init() {
let {
type,
} = this;
if (Types[type]) {
this.ptitle = this.title || Types[type].title
this.pdesc = this.desc || Types[type].desc
this.pbutton = this.button
if (this.text || Types[type].text) {
this.pbutton[1].title = this.text || Types[type].text
}
}
},
open() {
this.$refs.common_popup.open()
},
close() {
this.$refs.common_popup.close()
},
toEmit(index) {
let {
type
} = this.button[index]
if (type == 'cancel') {
this.close()
} else {
this.$emit(type)
}
}
},
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,69 @@
<template>
<view class="content" :style="{background: bgColor}">
<block v-for="(item,index) in text" :key="index">
<view @click="confirm(item)" class="bottom-view"
:style="{width:(100/ text.length)+ '%',margin:text.length==1?'0 30rpx':index==0?'0 0 0 30rpx': index == text.length -1 ? '0 30rpx 0 0' : '',borderRadius:text.length==1?'88rpx':index==0?'88rpx 0 0 88rpx': index== text.length -1 ? '0 88rpx 88rpx 0' : '',background: item.type == 'confirm'? primaryColor : subColor}">
{{ item.text }}
</view>
</block>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
props: {
text: {
type: Array,
default () {
return [{
text: '保存',
type: 'confirm'
}]
}
},
bgColor: {
type: String,
default () {
return '#fff'
}
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
confirm(item) {
this.$emit(item.type)
}
}
}
</script>
<style scoped lang="scss">
.content {
position: fixed;
bottom: 0rpx;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 128rpx;
z-index: 997;
height: calc(128rpx + env(safe-area-inset-bottom) / 2);
padding-bottom: calc(env(safe-area-inset-bottom) / 2);
.bottom-view {
width: auto;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
color: #FFFFFF;
}
}
</style>

View File

@@ -0,0 +1,79 @@
<template>
<view>
<view class="ui-fixed" :class="{'fixed-top':position=='top','fixed-bottom':position=='bottom'}"
:style="{zIndex:zIndex}">
<slot></slot>
</view>
<view :style="{height:height+'px'}"></view>
</view>
</template>
<script>
export default {
name: 'fixed',
props: {
position: {
type: [String],
default () {
return 'top'
}
},
zIndex: {
type: Number,
default () {
return 999
}
},
refresh: {
type: Boolean,
default () {
return false
}
}
},
data() {
return {
height: uni.getSystemInfoSync().windowWidth * 100 / 750
}
},
mounted() {
this.setHeight();
},
onReady() {
this.setHeight();
},
watch: {
refresh(val) {
console.log(val, "=======val")
if (val) return
this.setHeight()
},
},
methods: {
setHeight() {
var _this = this;
var query = uni.createSelectorQuery().in(this);
query.select('.ui-fixed').boundingClientRect(function(res) {
_this.height = res.height;
}).exec();
}
},
}
</script>
<style>
.ui-fixed {
position: fixed;
left: 0;
width: 100%;
}
.fixed-top {
top: 0;
}
.fixed-bottom {
bottom: 0;
}
</style>

View File

@@ -0,0 +1,62 @@
var inlineTags = {
abbr: true,
b: true,
big: true,
code: true,
del: true,
em: true,
font: true,
i: true,
ins: true,
label: true,
mark: true,
q: true,
s: true,
small: true,
span: true,
strong: true,
u: true
}
export default {
getStyle: function(style, display) {
var res = "";
var reg = getRegExp("float\s*:\s*[^;]*", "i");
if (reg.test(style)) res += reg.exec(style)[0];
reg = getRegExp("margin[^;]*", "gi");
var margin = reg.exec(style);
while (margin) {
res += (';' + margin[0]);
margin = reg.exec(style);
}
reg = getRegExp("display\s*:\s*([^;]*)", "i");
if (reg.test(style) && reg.exec(style)[1] != "flex") res += (';' + reg.exec(style)[0]);
else res += (';display:' + display);
reg = getRegExp("flex\s*:[^;]*", "i");
if (reg.test(style)) res += (';' + reg.exec(style)[0]);
reg = getRegExp("[^;\s]*width[^;]*", "ig");
var width = reg.exec(style);
while (width) {
res += (';' + width[0]);
width = reg.exec(style);
}
return res;
},
setImgStyle: function(item, imgMode) {
if (imgMode == "widthFix")
item.attrs.style = (item.attrs.style || '') + ";height:auto !important";
if (getRegExp("[^-]width[^pev;]+").test(";" + item.attrs.style))
item.attrs.style = (item.attrs.style || '') + ";width:100%";
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp('margin[^;]*', "gi"), "");
return [item];
},
setStyle: function(item) {
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp("width[^;]*?%", "gi"), "width:100%").replace(getRegExp(
'margin[^;]+', "gi"), "");
return [item];
},
notContinue: function(item) {
return !(item.c || inlineTags[item.name] || item["continue"]);
}
}

View File

@@ -0,0 +1,56 @@
var inlineTags = {
abbr: true,
b: true,
big: true,
code: true,
del: true,
em: true,
font: true,
i: true,
ins: true,
label: true,
mark: true,
q: true,
s: true,
small: true,
span: true,
strong: true,
u: true
}
module.exports = {
getStyle: function(style, display) {
var res = "";
var reg = getRegExp("float[^;]+(?![\s\S]*?float)", "i");
if (reg.test(style)) res += reg.exec(style)[0];
reg = getRegExp("margin[^;]+", "gi");
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = getRegExp("display\s*:\s*([^;]*)(?![\s\S]*?display)", "i");
if (reg.test(style) && reg.exec(style)[1] != "flex") res += (';' + reg.exec(style)[0]);
else res += (';display:' + display);
reg = getRegExp("flex[^;]*:[^;]+", "ig");
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = getRegExp("[^;\s]*width[^;]+", "ig");
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
return res;
},
setImgStyle: function(item, imgMode, imgLoad) {
if (imgMode == "widthFix") item.attrs.style = (item.attrs.style || '') + ";height:auto !important";
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp("width[^;]*?%", "gi"), "width:100%").replace(getRegExp(
'margin[^;]+', "gi"), "");
if (!imgLoad) {
delete item.attrs.src;
item.attrs.style += ";width:20px !important;height:20px !important";
}
return [item];
},
setStyle: function(item) {
if (item.attrs.style)
item.attrs.style = item.attrs.style.replace(getRegExp("width[^;]*?%", "gi"), "width:100%").replace(getRegExp(
'margin[^;]+', "gi"), "");
return [item];
},
notContinue: function(item) {
return !(item.c || inlineTags[item.name] || item["continue"]);
}
}

View File

@@ -0,0 +1,622 @@
<!--
parser 主模块组件
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
插件市场https://ext.dcloud.net.cn/plugin?id=805
authorJinYufeng
-->
<template>
<view>
<!--#ifdef H5-->
<slot v-if="!html && !nodes.length"></slot>
<div :id="'rtf' + uid" :style="(selectable ? 'user-select:text;-webkit-user-select:text;' : '') + (showWithAnimation ? ('opacity:0;' + showAnimation) : '')"></div>
<!--#endif-->
<!--#ifndef H5-->
<slot v-if="!html[0].name && !html[0].type && !nodes.length"></slot>
<!--#endif-->
<!--#ifdef MP-ALIPAY-->
<view class="_contain" :style="(selectable ? 'user-select:text;-webkit-user-select:text;' : '') + (showWithAnimation ? ('opacity:0;' + showAnimation) : '')">
<trees :nodes="nodes.length ? nodes : (html[0].name || html[0].type ? html : [])" :imgMode="imgMode" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY || H5-->
<trees class="_contain" :style="'display:block;' + (selectable ? 'user-select:text;-webkit-user-select:text;' : '') + (showWithAnimation ? ('opacity:0;' + showAnimation) : '')"
:nodes="nodes.length ? nodes : (html[0].name || html[0].type ? html : [])" :imgMode="imgMode" :loadVideo="loadVideo" />
<!--#endif-->
</view>
</template>
<script>
// #ifndef H5
import trees from "./trees"
var document; // document 补丁包,详见 https://jin-yufeng.github.io/Parser/#/instructions?id=document
const parseHtmlSync = require('./libs/MpHtmlParser.js').parseHtmlSync;
// #ifdef APP-PLUS
const cache = {};
// #endif
// #ifndef APP-PLUS
const cache = getApp().parserCache = {};
// #endif
const CssHandler = require("./libs/CssHandler.js");
// 散列函数(计算 cache 的 key
const Hash = (str) => {
for (var i = 0, hash = 5381, len = str.length; i < len; i++)
hash += (hash << 5) + str.charCodeAt(i);
return hash;
};
// #endif
// 动画
const showAnimation =
"transition:400ms ease 0ms;transition-property:transform,opacity;transform-origin:50% 50% 0;-webkit-transition:400ms ease 0ms;-webkit-transform:;-webkit-transition-property:transform,opacity;-webkit-transform-origin:50% 50% 0;opacity: 1"
const config = require('./libs/config.js');
// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU || MP-TOUTIAO
// 图片链接去重
const Deduplication = (src) => {
if (src.indexOf("http") != 0) return src;
var newSrc = '';
for (var i = 0; i < src.length; i++) {
newSrc += (Math.random() >= 0.5 ? src[i].toUpperCase() : src[i].toLowerCase());
if (src[i] == '/' && src[i - 1] != '/' && src[i + 1] != '/') break;
}
newSrc += src.substring(i + 1);
return newSrc;
}
// #endif
export default {
name: 'parser',
data() {
return {
// #ifdef APP-PLUS
loadVideo: false,
// #endif
// #ifdef H5
uid: this._uid,
showAnimation: '',
// #endif
// #ifndef H5
showAnimation: {},
controls: {},
// #endif
nodes: []
}
},
// #ifndef H5
components: {
trees
},
// #endif
props: {
'html': {
type: null,
default: null
},
'autocopy': {
type: Boolean,
default: true
},
// #ifndef MP-ALIPAY
'autopause': {
type: Boolean,
default: true
},
// #endif
'autopreview': {
type: Boolean,
default: true
},
'autosetTitle': {
type: Boolean,
default: true
},
'domain': {
type: String,
default: null
},
'imgMode': {
type: String,
default: 'default'
},
// #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
'lazyLoad': {
type: Boolean,
default: false
},
// #endif
'selectable': {
type: Boolean,
default: false
},
'tagStyle': {
type: Object,
default: () => {
return {};
}
},
'showWithAnimation': {
type: Boolean,
default: false
},
'useAnchor': {
type: Boolean,
default: false
},
'useCache': {
type: Boolean,
default: false
}
},
watch: {
html(html) {
this.setContent(html, undefined, true);
}
},
mounted() {
this.imgList = [];
this.imgList.each = function(f) {
for (var i = 0; i < this.length; i++) {
// #ifdef MP-ALIPAY || APP-PLUS
this[i] = f(this[i], i, this) || this[i];
// #endif
// #ifndef MP-ALIPAY || APP-PLUS
var newSrc = f(this[i], i, this);
if (newSrc) {
if (this.includes(newSrc)) this[i] = Deduplication(newSrc);
else this[i] = newSrc;
}
// #endif
}
}
this.setContent(this.html, undefined, true);
},
// #ifdef H5
beforeDestroy() {
if (this._observer) this._observer.disconnect();
},
// #endif
methods: {
// #ifdef H5
setContent(html, options, observed) {
if (typeof options == "object")
for (var key in options) {
key = key.replace(/-(\w)/g, function() {
return arguments[1].toUpperCase();
})
this[key] = options[key];
}
html = html || '';
if (!html) {
if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
return;
}
if (typeof html != 'string') html = this.Dom2Str(html.nodes || html);
// 处理 rpx
if (/[0-9.]*?rpx/.test(html)) {
const rpx = uni.getSystemInfoSync().screenWidth / 750;
html = html.replace(/([0-9.]*?)rpx/g, function() {
return parseFloat(arguments[1]) * rpx + "px";
})
}
// 处理 tag-style 和 userAgentStyles
var style = "<style>";
for (var item in config.userAgentStyles)
style += (item + '{' + config.userAgentStyles[item] + '}');
for (var item in this.tagStyle)
style += (item + '{' + this.tagStyle[item] + '}');
style += "</style>";
html = style + html;
if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
this.rtf = document.createElement('div');
this.rtf.innerHTML = html;
for (var style of this.rtf.getElementsByTagName("style")) {
style.innerHTML = style.innerHTML.replace(/\s*body/g, "#rtf" + this._uid);
style.setAttribute("scoped", "true");
}
// 懒加载
if (this.lazyLoad && IntersectionObserver) {
if (this._observer) this._observer.disconnect();
this._observer = new IntersectionObserver(changes => {
for (var change of changes) {
if (change.isIntersecting) {
change.target.src = change.target.getAttribute("data-src");
change.target.removeAttribute("data-src");
this._observer.unobserve(change.target);
}
}
}, {
rootMargin: "1000px 0px 1000px 0px"
})
}
var component = this;
// 获取标题
var title = this.rtf.getElementsByTagName("title");
if (title.length && this.autosetTitle)
uni.setNavigationBarTitle({
title: title[0].innerText
})
// 图片处理
this.imgList.length = 0;
var imgs = this.rtf.getElementsByTagName("img");
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
img.style.maxWidth = "100%";
img.i = i;
if (this.domain && img.getAttribute("src")[0] == "/") {
if (img.getAttribute("src")[1] == "/")
img.src = (this.domain.includes("://") ? this.domain.split("://")[0] : "http") + ':' + img.getAttribute("src");
else img.src = this.domain + img.getAttribute("src");
}
component.imgList.push(img.src);
if (img.parentElement.nodeName != 'A') {
img.onclick = function() {
if (!this.hasAttribute('ignore')) {
var preview = true;
this.ignore = () => preview = false;
component.$emit('imgtap', this);
if (preview && component.autopreview) {
uni.previewImage({
current: this.i,
urls: component.imgList
});
}
}
}
}
img.onerror = function() {
component.$emit('error', {
source: "img",
target: this
});
}
if (component.lazyLoad && this._observer) {
img.setAttribute("data-src", img.src);
img.removeAttribute("src");
this._observer.observe(img);
}
}
// 链接处理
var links = this.rtf.getElementsByTagName("a");
for (var link of links) {
link.onclick = function(e) {
var jump = true,
href = this.getAttribute("href");
component.$emit('linkpress', {
href,
ignore: () => jump = false
});
if (jump && href) {
if (href[0] == '#') {
if (component.useAnchor) {
component.navigateTo({
id: href.substring(1)
})
}
} else if (href.indexOf("http") == 0 || href.indexOf("//") == 0)
return true;
else {
uni.navigateTo({
url: href
})
}
}
return false;
}
}
// 视频处理
var videos = this.rtf.getElementsByTagName("video");
component.videoContexts = videos;
for (var video of videos) {
video.style.maxWidth = "100%";
video.onerror = function() {
component.$emit('error', {
source: "video",
target: this
});
}
video.onplay = function() {
if (component.autopause) {
for (var video of component.videoContexts) {
if (video != this)
video.pause();
}
}
}
}
// 音频处理
var audios = this.rtf.getElementsByTagName("audios");
for (var audio of audios) {
audio.onerror = function(e) {
component.$emit('error', {
source: "audio",
target: this
});
}
}
document.getElementById("rtf" + this._uid).appendChild(this.rtf);
if (this.showWithAnimation)
this.showAnimation = showAnimation;
if (!observed) this.nodes = [0];
this.$nextTick(() => {
this.$emit("ready", this.rtf.getBoundingClientRect());
})
},
Dom2Str(nodes) {
var str = "";
for (var node of nodes) {
if (node.type == "text")
str += node.text;
else {
str += ('<' + node.name);
for (var attr in node.attrs || {})
str += (' ' + attr + '="' + node.attrs[attr] + '"');
if (!node.children || !node.children.length) str += "/>";
else str += ('>' + this.Dom2Str(node.children) + "</" + node.name + '>');
}
}
return str;
},
getText(whiteSpace = true) {
if (!whiteSpace) return this.rtf.innerText.replace(/\s/g, '');
return this.rtf.innerText;
},
navigateTo(obj) {
if (!obj.id) {
window.scrollTo(0, this.rtf.offsetTop);
return obj.success ? obj.success({
errMsg: "pageScrollTo:ok"
}) : null;
}
var target = document.getElementById(obj.id);
if (!target) return obj.fail ? obj.fail({
errMsg: "Label Not Found"
}) : null;
uni.pageScrollTo({
scrollTop: this.rtf.offsetTop + target.offsetTop,
success: obj.success,
fail: obj.fail
});
},
// #endif
// #ifndef H5
setContent(html, options, observed) {
if (typeof options == "object")
for (var key in options) {
key = key.replace(/-(\w)/g, function() {
return arguments[1].toUpperCase();
})
this[key] = options[key];
}
if (this.showWithAnimation)
this.showAnimation = showAnimation;
if (!html) {
if (observed) return;
else this.nodes = [];
} else if (typeof html == "string") {
var res;
// 缓存读取
if (this.useCache) {
var hash = Hash(html);
if (cache[hash])
res = cache[hash];
else {
res = parseHtmlSync(html, this);
cache[hash] = res;
}
} else res = parseHtmlSync(html, this);
this.nodes = res;
this.$emit('parse', res);
} else if (html.constructor == Array) {
if (!observed) this.nodes = html;
else this.nodes = [];
// 非本插件产生的 array 需要进行一些转换
if (html.length && html[0].PoweredBy != "Parser") {
const Parser = {
_imgNum: 0,
_videoNum: 0,
_audioNum: 0,
_domain: this.domain,
_protocol: this.domain ? (this.domain.includes("://") ? this.domain.split("://")[0] : "http") : undefined,
_STACK: [],
CssHandler: new CssHandler(this.tagStyle)
};
Parser.CssHandler.getStyle('');
const DFS = (nodes) => {
for (var node of nodes) {
if (node.type == "text") continue;
node.attrs = node.attrs || {};
for (var item in node.attrs) {
if (!config.trustAttrs[item]) node.attrs[item] = undefined;
else if (typeof node.attrs[item] != "string") node.attrs[item] = node.attrs[item].toString();
}
config.LabelAttrsHandler(node, Parser);
if (config.blockTags[node.name]) node.name = 'div';
else if (!config.trustTags[node.name]) node.name = 'span';
if (node.children && node.children.length) {
Parser._STACK.push(node);
DFS(node.children);
Parser._STACK.pop();
} else node.children = undefined;
}
}
DFS(html);
this.nodes = html;
}
} else if (typeof html == 'object' && html.nodes) {
this.nodes = html.nodes;
console.warn("Parser 类型错误object 类型已废弃,请直接将 html 设置为 object.nodes array 类型)");
} else {
return this.$emit('error', {
source: "parse",
errMsg: "传入的nodes数组格式不正确应该传入的类型是array实际传入的类型是" + typeof html.nodes
});
}
// #ifdef APP-PLUS
this.loadVideo = false;
// #endif
if (document) this.document = new document("html", this.html || html, this);
this.$nextTick(() => {
this.imgList.length = 0;
this.videoContexts = [];
const getContext = (components) => {
for (let component of components) {
if (component.$options.name == "trees") {
var observered = false;
for (var item of component.nodes) {
if (item.continue) continue;
if (item.name == 'img') {
if (item.attrs.src && item.attrs.i) {
// #ifndef MP-ALIPAY || APP-PLUS
if (this.imgList.indexOf(item.attrs.src) == -1)
this.imgList[item.attrs.i] = item.attrs.src;
else this.imgList[item.attrs.i] = Deduplication(item.attrs.src);
// #endif
// #ifdef MP-ALIPAY || APP-PLUS
this.imgList[item.attrs.i] = item.attrs.src;
// #endif
}
// #ifndef MP-ALIPAY
if (!observered) {
observered = true;
if (this.lazyLoad && uni.createIntersectionObserver) {
if (component._observer) component._observer.disconnect();
component._observer = uni.createIntersectionObserver(component);
component._observer.relativeToViewport({
top: 1000,
bottom: 1000
}).observe('.img', res => {
component.imgLoad = true;
component._observer.disconnect();
component._observer = null;
})
} else
component.imgLoad = true;
}
// #endif
}
// #ifndef MP-ALIPAY
else if (item.name == 'video') {
var context = uni.createVideoContext(item.attrs.id, component);
context.id = item.attrs.id;
this.videoContexts.push(context);
}
// #endif
// #ifdef MP-WEIXIN
else if (item.name == 'audio' && item.attrs.autoplay)
wx.createAudioContext(item.attrs.id, component).play();
// #endif
// 设置标题
else if (item.name == "title") {
if (item.children[0].type == "text" && item.children[0].text && this.autosetTitle)
uni.setNavigationBarTitle({
title: item.children[0].text
})
}
// #ifdef MP-BAIDU || MP-ALIPAY
if (item.attrs && item.attrs.id) {
this.anchors = this.anchors || [];
this.anchors.push({
id: item.attrs.id,
node: component
})
}
// #endif
}
}
if (component.$children.length)
getContext(component.$children)
}
}
// #ifdef MP-TOUTIAO
setTimeout(() => {
// #endif
getContext(this.$children)
uni.createSelectorQuery().in(this).select("._contain").boundingClientRect(res => {
this.$emit("ready", res);
}).exec();
// #ifdef MP-TOUTIAO
}, 200)
// #endif
// #ifdef APP-PLUS
setTimeout(() => {
this.loadVideo = true;
}, 3000);
// #endif
})
},
getText(whiteSpace = true) {
var text = "";
const DFS = (node) => {
if (node.type == "text") return text += node.text;
else {
if (whiteSpace && (((node.name == 'p' || node.name == "div" || node.name == "tr" || node.name == "li" ||
/h[1-6]/.test(node.name)) && text && text[text.length - 1] != '\n') || node.name == "br"))
text += '\n';
for (var child of node.children || [])
DFS(child);
if (whiteSpace && (node.name == 'p' || node.name == "div" || node.name == "tr" || node.name == "li" || /h[1-6]/.test(
node.name)) && text && text[text.length - 1] != '\n')
text += '\n';
else if (whiteSpace && node.name == "td") text += '\t';
}
}
var nodes = ((this.nodes && this.nodes.length) ? this.nodes : (this.html[0] && (this.html[0].name || this.html[0].type) ?
this.html : []));
if (!nodes.length) return "";
for (var node of nodes)
DFS(node);
return text;
},
navigateTo(obj) {
var Scroll = (selector, component) => {
const query = uni.createSelectorQuery().in(component ? component : this);
query.select(selector).boundingClientRect();
query.selectViewport().scrollOffset();
query.exec(res => {
if (!res || !res[0])
return obj.fail ? obj.fail({
errMsg: "Label Not Found"
}) : null;
uni.pageScrollTo({
scrollTop: res[1].scrollTop + res[0].top,
success: obj.success,
fail: obj.fail
})
})
}
if (!obj.id) Scroll("._contain");
else {
// #ifndef MP-BAIDU || MP-ALIPAY
Scroll('._contain >>> #' + obj.id + ', ._contain >>> .' + obj.id);
// #endif
// #ifdef MP-BAIDU || MP-ALIPAY
for (var anchor of this.anchors) {
if (anchor.id == obj.id) {
Scroll("#" + obj.id + ", ." + obj.id, anchor.node);
}
}
// #endif
}
},
// #endif
getVideoContext(id) {
if (!id) return this.videoContexts;
else {
for (var video of this.videoContexts) {
if (video.id == id) return video;
}
}
return null;
}
}
}
</script>
<style>
/* #ifndef MP-BAIDU */
:host {
display: block;
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
/* #endif */
</style>

View File

@@ -0,0 +1,149 @@
/*
解析和匹配 Css 的选择器
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
authorJinYufeng
*/
const userAgentStyles = require("./config.js").userAgentStyles;
class CssHandler {
constructor(tagStyle = {}) {
this.styles = Object.assign({}, tagStyle);
};
getStyle(data) {
var style = '';
data = data.replace(/<[sS][tT][yY][lL][eE][\s\S]*?>([\s\S]*?)<\/[sS][tT][yY][lL][eE][\s\S]*?>/g, function() {
style += arguments[1];
return '';
})
this.styles = new CssParser(style, this.styles).parse();
return data;
};
parseCss(css) {
return new CssParser(css, {}, true).parse();
};
match(name, attrs) {
var tmp, matched = ((tmp = this.styles[name]) ? (tmp + ';') : '');
if (attrs.class) {
var classes = attrs.class.split(' ');
for (var i = 0; i < classes.length; i++)
if (tmp = this.styles['.' + classes[i]])
matched += (tmp + ';');
}
if (tmp = this.styles['#' + attrs.id])
matched += tmp;
return matched;
};
}
module.exports = CssHandler;
function isBlankChar(c) {
return c == ' ' || c == '\u00A0' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
};
class CssParser {
constructor(data, tagStyle, api) {
this.data = data;
this.res = tagStyle;
if (!api)
for (var item in userAgentStyles) {
if (tagStyle[item]) tagStyle[item] = userAgentStyles[item] + ';' + tagStyle[item];
else tagStyle[item] = userAgentStyles[item];
}
this._floor = 0;
this._i = 0;
this._list = [];
this._comma = false;
this._sectionStart = 0;
this._state = this.Space;
};
parse() {
for (; this._i < this.data.length; this._i++)
this._state(this.data[this._i]);
return this.res;
};
// 状态机
Space(c) {
if (c == '.' || c == '#' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
this._sectionStart = this._i;
this._state = this.StyleName;
} else if (c == '/' && this.data[this._i + 1] == '*')
this.Comment();
else if (!isBlankChar(c) && c != ';')
this._state = this.Ignore;
};
Comment() {
this._i = this.data.indexOf("*/", this._i);
if (this._i == -1) this._i = this.data.length;
this._i++;
this._state = this.Space;
};
Ignore(c) {
if (c == '{') this._floor++;
else if (c == '}' && --this._floor <= 0) {
this._list = [];
this._state = this.Space;
}
};
StyleName(c) {
if (isBlankChar(c)) {
this._list.push(this.data.substring(this._sectionStart, this._i));
this._state = this.NameSpace;
} else if (c == '{') {
this._list.push(this.data.substring(this._sectionStart, this._i));
this._floor = 1;
this._sectionStart = this._i + 1;
this.Content();
} else if (c == ',') {
this._list.push(this.data.substring(this._sectionStart, this._i));
this._sectionStart = this._i + 1;
this._comma = true;
} else if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != '.' && c != '#' &&
c != '-' && c != '_')
this._state = this.Ignore;
};
NameSpace(c) {
if (c == '{') {
this._floor = 1;
this._sectionStart = this._i + 1;
this.Content();
} else if (c == ',') {
this._comma = true;
this._sectionStart = this._i + 1;
this._state = this.StyleName;
} else if (!isBlankChar(c)) {
if (this._comma) {
this._state = this.StyleName;
this._sectionStart = this._i;
this._i--;
this._comma = false;
} else this._state = this.Ignore;
}
};
Content() {
this._i = this.data.indexOf('}', this._i);
if (this._i == -1) this._i = this.data.length;
// 去除空白符
var flag = false,
pos, content = this.data.substring(this._sectionStart, this._i);
for (var i = 0; i < content.length; i++) {
if (isBlankChar(content[i])) {
if (!flag) {
pos = i;
flag = true;
}
} else {
if (flag) {
if (pos == 0) content = content.substring(i);
else if (i - pos > 1) content = content.substring(0, pos) + (content[pos - 1] == ';' ? (pos--, '') : ' ') +
content.substring(i);
i = pos;
flag = false;
}
}
}
if (flag) content = content.substring(0, pos);
for (var i = 0; i < this._list.length; i++)
this.res[this._list[i]] = (this.res[this._list[i]] || '') + content;
this._list = [];
this._state = this.Space;
}
}

View File

@@ -0,0 +1,430 @@
/*
将 html 解析为适用于小程序 rich-text 的 DOM 结构
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
authorJinYufeng
*/
const CssHandler = require("./CssHandler.js");
const config = require("./config.js");
var emoji; // 需要使用 emoji 补丁包时将此行改为 const emoji = require("./emoji.js");
function isBlankChar(c) {
return c == ' ' || c == '\u00A0' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
};
class MpHtmlParser {
constructor(data, options = {}, cb) {
this.cb = cb;
this.CssHandler = new CssHandler(options.tagStyle);
this.data = data;
this.DOM = [];
// #ifdef MP-BAIDU || MP-TOUTIAO
this._imgMode = options.imgMode;
// #endif
this._attrName = '';
this._attrValue = '';
this._attrs = {};
this._domain = options.domain;
this._protocol = options.domain ? (options.domain.includes("://") ? this._domain.split("://")[0] : "http") :
undefined;
this._i = 0;
this._sectionStart = 0;
this._state = this.Text;
this._STACK = [];
this._tagName = '';
this._audioNum = 0;
this._imgNum = 0;
this._videoNum = 0;
this._useAnchor = options.useAnchor;
this._whiteSpace = false;
};
parse() {
if (this.CssHandler) this.data = this.CssHandler.getStyle(this.data);
if (emoji) this.data = emoji.parseEmoji(this.data);
// 高亮处理
if (config.highlight)
this.data = this.data.replace(/<[pP][rR][eE]([\s\S]*?)>([\s\S]*?)<\/[pP][rR][eE][\s\S]*?>/g, function() {
return "<pre" + arguments[1] + '>' + config.highlight(arguments[2], "<pre" + arguments[1] + '>') + "</pre>";
})
for (var len = this.data.length; this._i < len; this._i++)
this._state(this.data[this._i]);
if (this._state == this.Text) this.setText();
while (this._STACK.length)
this.popNode(this._STACK.pop());
// #ifdef MP-BAIDU || MP-TOUTIAO
const inlineTags = config.makeMap("abbr,b,big,code,del,em,font,i,ins,label,mark,q,s,small,span,strong,sub,sup,u")
// 将顶层标签的一些样式提取出来给 rich-text
const setContain = function(nodes) {
for (var element of nodes) {
if (element.type == "text")
continue;
if (!element.c) {
var res = "";
var style = element.attrs.style;
var reg = /float[^;]+(?![\s\S]*?float)/i;
if (reg.test(style)) res += reg.exec(style)[0];
reg = /margin[^;]+/gi;
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = /display\s*:\s*([^;]*)(?![\s\S]*?display)/i;
if (reg.test(style) && reg.exec(style)[1] != "flex") res += (';' + reg.exec(style)[0]);
else if (inlineTags[element.name]) res += ";display:inline";
else res += (";display:" + (element.name == 'img' ? 'inline-block' : 'block'));
reg = /flex[^;]*:[^;]+/gi;
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
reg = /[^;\s]*width[^;]+/gi;
if (reg.test(style)) res += (';' + style.match(reg).join(';'));
element.attrs.containStyle = res;
if (/[^-]width[^pev;]+/.test(";" + style))
element.attrs.style += ";width:100%";
let addMargin = "";
if (/margin\s*:/.test(style)) addMargin = ';margin:0';
else if (/margin-top/.test(style)) addMargin = ';margin-top:0';
else if (/margin-bottom/.test(style)) addMargin = ';margin-bottom:0';
element.attrs.style = (element.attrs.style || '').replace(/margin[^;]*/gi, "");
element.attrs.style += addMargin;
} else setContain(element.children);
}
};
setContain(this.DOM);
// #endif
if (this.DOM.length) this.DOM[0].PoweredBy = "Parser";
// console.log(this.DOM)
if (this.cb)
this.cb(this.DOM)
else return this.DOM;
};
// 设置属性
setAttr() {
if (config.trustAttrs[this._attrName])
this._attrs[this._attrName] = (this._attrValue ? this._attrValue : (this._attrName == "src" ? "" : "true"));
this._attrValue = '';
while (isBlankChar(this.data[this._i])) this._i++;
if (this.checkClose()) this.setNode();
else this._state = this.AttrName;
};
// 设置文本节点
setText() {
var text = this.getSelection();
if (text) {
if (!this._whiteSpace) {
// 移除空白符
var flag = false,
has = false,
pos;
for (var i = 0; i < text.length; i++) {
if (isBlankChar(text[i])) {
if (!flag) {
pos = i;
flag = true;
}
} else {
has = true;
if (flag) {
if (i - pos > 1) text = text.substring(0, pos) + ' ' + text.substring(i);
i = pos;
flag = false;
}
}
}
if (flag) text = text.substring(0, pos) + ' ';
if (!text || !has) return;
}
// 检查实体
// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
var entities = {
lt: "<",
gt: ">",
amp: "&",
quot: '"',
apos: "'",
nbsp: "\u00A0",
ensp: "\u2002",
emsp: "\u2003",
ndash: "",
mdash: "—",
middot: "·",
lsquo: "",
rsquo: "",
ldquo: "“",
rdquo: "”",
bull: "•",
hellip: "…",
permil: "‰",
copy: "©",
reg: "®",
trade: "™",
times: "×",
divide: "÷",
cent: "¢",
pound: "£",
yen: "¥",
euro: "€",
sect: "§"
};
// #endif
var i = text.indexOf('&'),
j, decode;
while (i != -1 && i < text.length) {
j = text.indexOf(';', i);
if (j - i >= 2 && j - i <= 7) {
var entity = text.substring(i + 1, j);
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
if (!entity.includes("sp") && !entity.includes("lt") && !entity.includes("gt")) {
decode = true
break;
}
// #endif
// #ifdef MP-BAIDU || MP-ALIPAY || MP-TOUTIAO
if (entities[entity]) text = text.substring(0, i) + entities[entity] + text.substring(j + 1);
// #endif
}
i = text.indexOf('&', i + 1);
}
var slibings = this._STACK.length ? this._STACK[this._STACK.length - 1].children : this.DOM;
if (slibings.length && slibings[slibings.length - 1].type == "text") {
slibings[slibings.length - 1].text += text;
if (decode) slibings[slibings.length - 1].decode = true;
} else
slibings.push({
type: "text",
text,
decode
})
}
};
// 设置元素节点
setNode() {
var slibings = this._STACK.length ? this._STACK[this._STACK.length - 1].children : this.DOM;
var node = {
name: this._tagName.toLowerCase(),
attrs: this._attrs
}
config.LabelAttrsHandler(node, this);
this._attrs = {};
if (this.data[this._i] == '>') {
if (!config.selfClosingTags[this._tagName]) {
if (config.ignoreTags[node.name]) {
var j = this._i;
// 处理要被移除的标签
while (this._i < this.data.length) {
this._i = this.data.indexOf("</", this._i);
if (this._i == -1) return this._i = this.data.length;
this._i += 2;
this._sectionStart = this._i;
while (!isBlankChar(this.data[this._i]) && this.data[this._i] != '>' && this.data[this._i] != '/') this._i++;
if (this.data.substring(this._sectionStart, this._i).toLowerCase() == node.name) {
this._i = this.data.indexOf('>', this._i);
if (this._i == -1) this._i = this.data.length;
else this._sectionStart = this._i + 1;
this._state = this.Text;
// 处理svg
if (node.name == "svg") {
var src = this.data.substring(j, this._i + 1);
if (!node.attrs.xmlns) src = " xmlns=\"http://www.w3.org/2000/svg\"" + src;
this._i = j;
while (this.data[j] != '<') j--;
src = this.data.substring(j, this._i) + src;
this._i = this._sectionStart - 1;
node.name = "img";
node.attrs = {
src: "data:image/svg+xml;utf8," + src.replace(/#/g, "%23"),
ignore: "true"
}
slibings.push(node);
}
break;
}
}
return;
} else this._STACK.push(node);
node.children = [];
}
} else this._i++;
this._sectionStart = this._i + 1;
this._state = this.Text;
if (!config.ignoreTags[node.name]) {
// 检查空白符是否有效
if (node.name == "pre" || (node.attrs.style && node.attrs.style.toLowerCase().includes("white-space") && node.attrs
.style.toLowerCase().includes("pre"))) {
this._whiteSpace = true;
node.pre = true;
}
slibings.push(node);
}
};
// 标签出栈处理
popNode(node) {
// 替换一些标签名
if (config.blockTags[node.name]) node.name = 'div';
else if (!config.trustTags[node.name]) node.name = 'span';
// 空白符处理
if (node.pre) {
this._whiteSpace = false;
node.pre = undefined;
for (var i = 0; i < this._STACK.length; i++)
if (this._STACK[i].pre)
this._whiteSpace = true;
}
// 处理表格的边框
if (node.name == 'table') {
if (node.attrs.border)
node.attrs.style = "border:" + node.attrs.border + "px solid gray;" + (node.attrs.style || '');
if (node.attrs.hasOwnProperty("cellspacing"))
node.attrs.style = "border-spacing:" + node.attrs.cellspacing + "px;" + (node.attrs.style || '');
function setBorder(elem) {
if (elem.name == 'th' || elem.name == 'td') {
if (node.attrs.border)
elem.attrs.style = "border:" + node.attrs.border + "px solid gray;" + (elem.attrs.style || '');
if (node.attrs.hasOwnProperty("cellpadding"))
elem.attrs.style = "padding:" + node.attrs.cellpadding + "px;" + (elem.attrs.style || '');
return;
}
if (elem.type == 'text') return;
for (var i = 0; i < elem.children.length; i++)
setBorder(elem.children[i]);
}
if (node.attrs.border || node.attrs.hasOwnProperty("cellpadding"))
for (var i = 0; i < node.children.length; i++)
setBorder(node.children[i]);
}
// 合并一些不必要的层,减小节点深度
if (node.children.length == 1 && node.name == "div" && node.children[0].name == "div") {
var child = node.children[0];
node.attrs.style = node.attrs.style || '';
child.attrs.style = child.attrs.style || '';
if (node.attrs.style.includes("padding") && (node.attrs.style.includes("margin") || child.attrs.style.includes(
"margin")) && node.attrs.style.includes("display") && child.attrs.style.includes("display") && !(node.attrs.id &&
node.attrs.id) && !(node.attrs.class && child.attrs.class)) {
if (child.attrs.style.includes("padding"))
child.attrs.style = "box-sizing:border-box;" + child.attrs.style;
node.attrs.style = node.attrs.style + ";" + child.attrs.style;
node.attrs.id = (child.attrs.id || '') + (node.attrs.id || '');
node.attrs.class = (child.attrs.class || '') + (node.attrs.class || '');
node.children = child.children;
}
}
// 多层样式处理
if (this.CssHandler.pop)
this.CssHandler.pop(node);
};
// 工具函数
checkClose() {
if (this.data[this._i] == '>' || (this.data[this._i] == '/' && this.data[this._i + 1] == '>'))
return true;
return false;
};
getSelection(trim) {
var str = (this._sectionStart == this._i ? '' : this.data.substring(this._sectionStart, this._i));
while (trim && isBlankChar(this.data[++this._i]));
if (trim) this._i--;
this._sectionStart = this._i + 1;
return str;
};
// 状态机
Text(c) {
if (c == '<') {
var next = this.data[this._i + 1];
if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) {
this.setText();
this._state = this.TagName;
} else if (next == '/') {
this.setText();
this._i++;
next = this.data[this._i + 1];
if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) {
this._sectionStart = this._i + 1;
this._state = this.EndTag;
} else
this._state = this.Comment;
} else if (next == '!') {
this.setText();
this._state = this.Comment;
}
}
};
Comment() {
if (this.data.substring(this._i + 1, this._i + 3) == "--" || this.data.substring(this._i + 1, this._i + 7) ==
"[CDATA[") {
this._i = this.data.indexOf("-->", this._i + 1);
if (this._i == -1) return this._i = this.data.length;
else this._i = this._i + 2;
} else {
this._i = this.data.indexOf(">", this._i + 1);
if (this._i == -1) return this._i = this.data.length;
}
this._sectionStart = this._i + 1;
this._state = this.Text;
};
TagName(c) {
if (isBlankChar(c)) {
this._tagName = this.getSelection(true);
if (this.checkClose()) this.setNode();
else this._state = this.AttrName;
} else if (this.checkClose()) {
this._tagName = this.getSelection();
this.setNode();
}
};
AttrName(c) {
if (isBlankChar(c)) {
this._attrName = this.getSelection(true).toLowerCase();
if (this.data[this._i] == '=') {
while (isBlankChar(this.data[++this._i]));
this._sectionStart = this._i;
this._i--;
this._state = this.AttrValue;
} else this.setAttr();
} else if (c == '=') {
this._attrName = this.getSelection().toLowerCase();
while (isBlankChar(this.data[++this._i]));
this._sectionStart = this._i;
this._i--;
this._state = this.AttrValue;
} else if (this.checkClose()) {
this._attrName = this.getSelection().toLowerCase();
this.setAttr();
}
};
AttrValue(c) {
if (c == '"' || c == "'") {
this._sectionStart++;
if ((this._i = this.data.indexOf(c, this._i + 1)) == -1) return this._i = this.data.length;
} else
for (; !isBlankChar(this.data[this._i] && this.data[this._i] != '/' && this.data[this._i] != '>'); this._i++);
this._attrValue = this.getSelection();
while (this._attrValue.includes("&quot;")) this._attrValue = this._attrValue.replace("&quot;", '');
this.setAttr();
};
EndTag(c) {
if (isBlankChar(c) || c == '>' || c == '/') {
var name = this.getSelection().toLowerCase();
var flag = false;
for (var i = this._STACK.length - 1; i >= 0; i--)
if (this._STACK[i].name == name) {
flag = true;
break;
}
if (flag) {
var node;
while (flag) {
node = this._STACK.pop();
if (node.name == name) flag = false;
this.popNode(node);
}
} else if (name == 'p' || name == "br") {
var slibings = this._STACK.length ? this._STACK[this._STACK.length - 1].children : this.DOM;
var node = {
name
}
slibings.push(node);
}
this._i = this.data.indexOf('>', this._i);
if (this._i == -1) this._i = this.data.length;
else this._state = this.Text;
}
};
};
module.exports = {
parseHtml: (data, options) => new Promise((resolve) => new MpHtmlParser(data, options, resolve).parse()),
parseHtmlSync: (data, options) => new MpHtmlParser(data, options).parse()
};

View File

@@ -0,0 +1,247 @@
// <<<<<<< HEAD
/* 配置文件 */
// =======
/* 配置文件 xxx*/
// >>>>>>> 53c74b30a29ef2e569158438db4f5df4b7480643
function makeMap(str) {
var map = Object.create(null),
list = str.split(',');
for (var item of list)
map[item] = true;
return map;
}
// 信任的属性列表,不在列表中的属性将被移除
const trustAttrs = makeMap(
"align,alt,app-id,appId,"
// #ifdef MP-BAIDU
+
"appid,apid,"
// #endif
+
"author,autoplay,border,cellpadding,cellspacing,class,color,colspan,controls,data-src,dir,face,height,href,id,ignore,loop,muted,name,path,poster,rowspan,size,span,src,start,style,type,lbType,lbtype,"
// #ifdef MP-WEIXIN || MP-QQ
+
"unit-id,unitId,"
// #endif
+
"width,xmlns"
);
// 信任的标签,将保持标签名不变
const trustTags = makeMap(
"a,abbr,ad,audio,b,blockquote,br,code,col,colgroup,dd,del,dl,dt,div,em,fieldset,h1,h2,h3,h4,h5,h6,hr,i,img,ins,label,legend,li,ol,p,q,source,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,tr,title,u,ul,video,iframe"
);
// 块级标签,将被转为 div
const blockTags = makeMap("address,article,aside,body,center,cite,footer,header,html,nav,pre,section");
// 被移除的标签(其中 svg 系列标签会被转为图片)
const ignoreTags = makeMap(
"area,base,basefont,canvas,circle,command,ellipse,embed,frame,head,input,isindex,keygen,line,link,map,meta,param,path,polygon,rect,script,source,svg,textarea,track,use,wbr,"
);
// 只能用 rich-text 显示的标签(其中图片不能预览、不能显示视频、音频等)
const richOnlyTags = makeMap(
"a,ad,audio,colgroup,fieldset,legend,li,ol,sub,sup,table,tbody,td,tfoot,th,thead,tr,ul,video,iframe,");
// 自闭合标签
const selfClosingTags = makeMap(
"area,base,basefont,br,col,circle,ellipse,embed,frame,hr,img,input,isindex,keygen,line,link,meta,param,path,polygon,rect,source,track,use,wbr,"
);
// 默认的标签样式
var userAgentStyles = {
a: "color:#366092;word-break:break-all;padding:1.5px 0 1.5px 0",
address: "font-style:italic",
blockquote: "background-color:#f6f6f6;border-left:3px solid #dbdbdb;color:#6c6c6c;padding:5px 0 5px 10px",
center: "text-align:center",
cite: "font-style:italic",
code: "padding:0 1px 0 1px;margin-left:2px;margin-right:2px;background-color:#f8f8f8;border-radius:3px",
dd: "margin-left:40px",
img: "max-width:100%",
mark: "background-color:yellow",
pre: "font-family:monospace;white-space:pre;overflow:scroll",
s: "text-decoration:line-through",
u: "text-decoration:underline"
};
// #ifndef MP-ALIPAY || H5
const SDKVersion = uni.getSystemInfoSync().SDKVersion;
function versionHigherThan(version = '') {
var v1 = SDKVersion.split('.'),
v2 = version.split('.');
while (v1.length != v2.length)
v1.length < v2.length ? v1.push('0') : v2.push('0');
for (var i = 0; i < v1.length; i++) {
if (v1[i] == v2[i]) continue;
if (parseInt(v1[i]) > parseInt(v2[i])) return true;
return false;
}
return true;
};
// #endif
// #ifdef MP-WEIXIN || MP-QQ
// 版本兼容
if (versionHigherThan("2.7.1")) {
trustTags.bdi = true;
trustTags.bdo = true;
trustTags.caption = true;
trustTags.rt = true;
trustTags.ruby = true;
ignoreTags.rp = true;
trustTags.big = true;
trustTags.small = true;
trustTags.pre = true;
trustTags.iframe = true;
richOnlyTags.bdi = true;
richOnlyTags.bdo = true;
richOnlyTags.caption = true;
richOnlyTags.rt = true;
richOnlyTags.ruby = true;
richOnlyTags.pre = true;
blockTags.pre = undefined;
} else {
blockTags.caption = true;
userAgentStyles.big = "display:inline;font-size:1.2em";
userAgentStyles.small = "display:inline;font-size:0.8em";
}
// #endif
function bubbling(Parser) {
for (var i = Parser._STACK.length - 1; i >= 0; i--) {
if (!richOnlyTags[Parser._STACK[i].name])
Parser._STACK[i].c = 1;
else return false;
}
return true;
}
module.exports = {
// 高亮处理函数
highlight: null,
// 处理标签的属性,需要通过组件递归方式显示的标签需要调用 bubbling(Parser)
LabelAttrsHandler(node, Parser) {
node.attrs.style = Parser.CssHandler.match(node.name, node.attrs, node) + (node.attrs.style || '');
switch (node.name) {
case "ul":
case "ol":
case "li":
case "dd":
case "dl":
case "dt":
case "div":
case "span":
case "em":
case 'p':
let default_p_style = "max-width: 100% !important;display:block;"
if (node.attrs.style) {
node.attrs.style = node.attrs.style.includes('width:') ? default_p_style : node.attrs.style + default_p_style
}
if (node.attrs.align) {
node.attrs.style = "text-align:" + node.attrs.align + ';' + node.attrs.style;
node.attrs.align = undefined;
}
break;
case "img":
if (node.attrs.height) {
node.attrs.height = 'auto'
}
let default_style = "max-width: 100% !important;display:block;"
if (node.attrs.style) {
node.attrs.style = node.attrs.style.includes('height:') ? default_style : node.attrs.style + default_style
}
if (node.attrs["data-src"]) {
node.attrs.src = node.attrs.src || node.attrs["data-src"];
node.attrs["data-src"] = undefined;
}
// #ifdef MP-BAIDU || MP-TOUTIAO
if (Parser._imgMode == "widthFix") node.attrs.style = node.attrs.style + ";height:auto !important;";
// #endif
if (node.attrs.src) {
if (!node.attrs.ignore) {
if (bubbling(Parser)) node.attrs.i = (Parser._imgNum++).toString();
else node.attrs.ignore = "true";
}
if (Parser._domain && node.attrs.src[0] == '/') {
if (node.attrs.src[1] == '/') node.attrs.src = Parser._protocol + ":" + node.attrs.src;
else node.attrs.src = Parser._domain + node.attrs.src;
}
}
break;
case 'a':
case "ad":
bubbling(Parser);
break;
case "font":
if (node.attrs.color) {
node.attrs.style = "color:" + node.attrs.color + ';' + node.attrs.style;
node.attrs.color = undefined;
}
if (node.attrs.face) {
node.attrs.style = "font-family:" + node.attrs.face + ';' + node.attrs.style;
node.attrs.face = undefined;
}
if (node.attrs.size) {
var size = parseInt(node.attrs.size);
if (size < 1) size = 1;
else if (size > 7) size = 7;
var map = ["xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"];
node.attrs.style = "font-size:" + map[size - 1] + ';' + node.attrs.style;
node.attrs.size = undefined;
}
break;
case 'iframe':
case "video":
case "audio":
node.attrs.loop = node.attrs.hasOwnProperty('loop') || false;
node.attrs.controls = node.attrs.hasOwnProperty(
'controls') || true;
node.attrs.autoplay = node.attrs.hasOwnProperty('autoplay') || false;
if (node.attrs.id) Parser['_' + node.name + "Num"]++;
else node.attrs.id = (node.name + (++Parser['_' + node.name + "Num"]));
if (node.name == "video") {
node.attrs.style = node.attrs.style || '';
if (node.attrs.width) {
node.attrs.style = "width:" + parseFloat(node.attrs.width) + (node.attrs.height.includes('%') ? '%' : "px") +
';' + node.attrs.style;
node.attrs.width = undefined;
}
if (node.attrs.height) {
node.attrs.style = "height:" + parseFloat(node.attrs.height) + (node.attrs.height.includes('%') ? '%' : "px") +
';' + node.attrs.style;
node.attrs.height = undefined;
}
if (Parser._videoNum > 3) node.lazyLoad = true;
}
// 新增iframe【create_by_xx】
if (node.name == 'iframe') {
// console.log(node.attrs, "====iframe attrs");
}
node.attrs.source = [];
if (node.attrs.src) node.attrs.source.push(node.attrs.src);
if (!node.attrs.controls && !node.attrs.autoplay)
console.warn("存在没有controls属性的 " + node.name + " 标签,可能导致无法播放", node);
bubbling(Parser);
break;
case "source":
var parent = Parser._STACK[Parser._STACK.length - 1];
if (parent && (parent.name == "video" || parent.name == "audio")) {
parent.attrs.source.push(node.attrs.src);
if (!parent.attrs.src) parent.attrs.src = node.attrs.src;
}
break;
}
if (Parser._domain && node.attrs.style.includes("url"))
node.attrs.style = node.attrs.style.replace(/url\s*\(['"\s]*(\S*?)['"\s]*\)/, function() {
var src = arguments[1];
if (src && src[0] == '/') {
if (src[1] == '/') return "url(" + Parser._protocol + ':' + src + ')';
else return "url(" + Parser._domain + src + ')';
} else return arguments[0];
})
if (!node.attrs.style) node.attrs.style = undefined;
if (Parser._useAnchor && node.attrs.id) bubbling(Parser);
},
trustAttrs,
trustTags,
blockTags,
ignoreTags,
selfClosingTags,
userAgentStyles,
// #ifndef MP-ALIPAY || H5
versionHigherThan,
// #endif
makeMap
}

View File

@@ -0,0 +1,392 @@
<!--
trees 递归显示组件
github地址https://github.com/jin-yufeng/Parser
文档地址https://jin-yufeng.github.io/Parser
插件市场https://ext.dcloud.net.cn/plugin?id=805
authorJinYufeng
-->
<template>
<view style="display: inherit; white-space: inherit;">
<block v-for="(item, index) in nodes" v-bind:key="index">
<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
<block v-if="handler.notContinue(item)">
<!--#endif-->
<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
<block v-if="!item.c">
<!--#endif-->
<!--图片-->
<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
<rich-text v-if="item.name=='img'" :id="item.attrs.id" class="img" :style="'text-indent:0;'+handler.getStyle(item.attrs.style, 'inline-block')"
:nodes='handler.setImgStyle(item, imgMode, imgLoad)' :data-attrs="item.attrs" @tap='previewEvent' />
<!--#endif-->
<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
<rich-text v-if="item.name=='img'" :id="item.attrs.id" :style="'text-indent:0;'+item.attrs.containStyle" :nodes='[item]'
:data-attrs="item.attrs" @tap='previewEvent' />
<!--#endif-->
<!--文本-->
<!--#ifdef MP-WEIXIN || MP-QQ || APP-PLUS-->
<block v-else-if="item.type=='text'">
<text v-if="!item.decode" decode>{{item.text}}</text>
<rich-text v-else style="display:inline-block" :nodes="[item]"></rich-text>
</block>
<!--#endif-->
<!--#ifdef MP-ALIPAY-->
<text v-else-if="item.type=='text'" decode>{{item.text}}</text>
<!--#endif-->
<text v-else-if="item.name=='br'">\n</text>
<!--视频-->
<block v-else-if="item.name=='video'">
<!--#ifdef APP-PLUS-->
<view v-if="(!loadVideo||item.lazyLoad)&&!controls[item.attrs.id].play" :id="item.attrs.id" :class="'_video '+(item.attrs.class||'')"
:style="item.attrs.style||''" @tap="_loadVideo" />
<!--#endif-->
<!--#ifndef APP-PLUS-->
<view v-if="item.lazyLoad&&!controls[item.attrs.id].play" :id="item.attrs.id" :class="'_video '+(item.attrs.class||'')"
:style="item.attrs.style||''" @tap="_loadVideo" />
<!--#endif-->
<video v-else :src="controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index] : item.attrs.src"
:id="item.attrs.id" :loop="item.attrs.loop" :controls="item.attrs.controls" :autoplay="item.attrs.autoplay||(controls[item.attrs.id]&&controls[item.attrs.id].play)"
:unit-id="item.attrs['unit-id']" :class="item.attrs.class" :muted="item.attrs.muted" :style="item.attrs.style||''"
:data-source="item.attrs.source" @play="playEvent" @error="videoError" />
</block>
<!-- iframe create_by_xx-->
<block v-else-if="item.name=='iframe'">
<!-- #ifdef MP-WEIXIN -->
<!-- 腾讯视频 -->
<txv-video :vid="item.attrs.src" :playerid="item.attrs.src" width="100%" height="100%" :controls="true" :autoplay="false"
:isHiddenStop="true" v-if="item.attrs.lbType=='vid' || item.attrs.lbtype=='vid'">
</txv-video>
<!-- #endif -->
</block>
<!--音频-->
<audio v-else-if="item.name=='audio'" :src="controls[item.attrs.id]?item.attrs.source[controls[item.attrs.id].index] : item.attrs.src"
:id="item.attrs.id" :loop="item.attrs.loop" :controls="item.attrs.controls" :poster="item.attrs.poster" :name="item.attrs.name"
:author="item.attrs.author" :class="item.attrs.class" :style="item.attrs.style||''" :data-source="item.attrs.source"
@error="audioError" />
<!--链接-->
<view v-else-if="item.name=='a'" :class="'_a '+(item.attrs.class||'')" :style="item.attrs.style||''" :data-attrs="item.attrs"
hover-class="navigator-hover" :hover-start-time="25" :hover-stay-time="300" @tap="tapEvent">
<trees :nodes="item.children" :imgMode="imgMode" />
</view>
<!--广告-->
<!--#ifdef MP-WEIXIN || MP-QQ-->
<ad v-else-if="item.name=='ad'" :unit-id="item.attrs['unit-id']" :class="item.attrs.class||''" :style="item.attrs.style||''"
@error="adError"></ad>
<!--#endif-->
<!--#ifdef MP-BAIDU-->
<ad v-else-if="item.name=='ad'" :appid="item.attrs.appid" :apid="item.attrs.apid" :type="item.attrs.type" :class="item.attrs.class||''"
:style="item.attrs.style" @error="adError"></ad>
<!--#endif-->
<!--富文本-->
<!--#ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY || APP-PLUS-->
<rich-text v-else :id="item.attrs.id" :class="'__'+item.name" :style="''+handler.getStyle(item.attrs.style, 'block')"
:nodes="handler.setStyle(item)" />
<!--#endif-->
<!--#ifdef MP-BAIDU || MP-TOUTIAO-->
<rich-text v-else :id="item.attrs.id" :style="item.attrs?item.attrs.containStyle:''" :nodes="[item]" />
<!--#endif-->
</block>
<!--#ifdef MP-ALIPAY-->
<view v-else :id="item.attrs.id" :class="'_'+item.name+' '+(item.attrs.class||'')" :style="item.attrs.style||''">
<trees :nodes="item.children" :imgMode="imgMode" />
</view>
<!--#endif-->
<!--#ifndef MP-ALIPAY-->
<trees v-else :class="item.attrs.id+' _'+item.name+' '+(item.attrs.class||'')" :style="item.attrs.style||''" :nodes="item.children"
:imgMode="imgMode" :loadVideo="loadVideo" />
<!--#endif-->
</block>
</view>
</template>
<script module="handler" lang="wxs" src="./handler.wxs"></script>
<script module="handler" lang="sjs" src="./handler.sjs"></script>
<script>
import trees from "./trees"
export default {
components: {
trees
},
name: 'trees',
data() {
return {
controls: {},
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
imgLoad: true
// #endif
}
},
props: {
nodes: {
type: Array,
default: []
},
// #ifdef APP-PLUS
loadVideo: {
type: Boolean,
default: false
},
// #endif
imgMode: {
type: String,
default: "default"
}
},
mounted() {
// 获取顶层组件
this._top = this.$parent;
while (this._top.$options.name != 'parser') {
if (this._top._top) {
this._top = this._top._top;
break;
}
this._top = this._top.$parent;
}
},
// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS
beforeDestroy() {
if (this._observer)
this._observer.disconnect();
},
// #endif
methods: {
// #ifndef MP-ALIPAY
playEvent(e) {
if ((this._top.videoContexts || []).length > 1 && this._top.autopause) {
for (var video of this._top.videoContexts) {
if (video.id == e.currentTarget.id) continue;
video.pause();
}
}
},
// #endif
previewEvent(e) {
var attrs = e.currentTarget.dataset.attrs;
if (!attrs.ignore) {
var preview = true;
this._top.$emit('imgtap', {
id: e.currentTarget.id,
src: attrs.src,
ignore: () => preview = false
})
if (preview && this._top.autopreview) {
var urls = this._top.imgList || [],
current = urls[attrs.i] ? attrs.i : (urls = [attrs.src], 0);
uni.previewImage({
current,
urls
})
}
}
},
tapEvent(e) {
var jump = true,
attrs = e.currentTarget.dataset.attrs;
attrs.ignore = () => jump = false;
this._top.$emit('linkpress', attrs);
if (jump) {
// #ifdef MP
if (attrs['app-id'] || attrs.appId) {
return uni.navigateToMiniProgram({
appId: attrs['app-id'] || attrs.appId,
path: attrs.path || ''
})
}
// #endif
if (attrs.href) {
if (attrs.href[0] == "#") {
if (this._top.useAnchor)
this._top.navigateTo({
id: attrs.href.substring(1)
})
} else if (attrs.href.indexOf("http") == 0 || attrs.href.indexOf("//") == 0) {
if (this._top.autocopy) {
uni.setClipboardData({
data: attrs.href,
success() {
uni.showToast({
title: '链接已复制'
});
}
});
}
} else
uni.navigateTo({
url: attrs.href
})
}
}
},
triggerError(source, target, errMsg, errCode, context) {
this._top.$emit('error', {
source,
target,
errMsg,
errCode,
context
});
},
loadSource(target) {
// console.log(target)
var index = (this.controls[target.id] ? this.controls[target.id].index : 0) + 1;
if (index < target.dataset.source.length) {
this.$set(this.controls[target.id], "index", index);
return true;
}
return false;
},
adError(e) {
this.triggerError("ad", e.currentTarget, "", e.detail.errorCode);
},
videoError(e) {
if (!this.loadSource(e.currentTarget) && this._top)
this.triggerError("video", e.currentTarget, e.detail.errMsg, undefined, uni.createVideoContext(e.currentTarget.id,
this));
},
audioError(e) {
if (!this.loadSource(e.currentTarget))
this.triggerError("audio", e.currentTarget, e.detail.errMsg);
},
_loadVideo(e) {
this.$set(this.controls, e.currentTarget.id, {
play: true,
index: 0
})
}
}
}
</script>
<style>
/* 可以在这里引入自定义的外部样式 */
/* 链接受到点击的hover-class可自定义修改 */
.navigator-hover {
opacity: 0.7;
text-decoration: underline;
}
/* 以下内容不建议修改 */
/* #ifndef MP-BAIDU */
:host {
display: inherit;
float: inherit;
}
/* #endif */
._b,
._strong {
font-weight: bold;
}
._big {
font-size: 1.2em;
}
._small {
font-size: 0.8em;
}
._blockquote,
._div,
._p {
display: block;
}
._code {
font-family: monospace;
}
._del {
text-decoration: line-through;
}
._em,
._i {
font-style: italic;
}
._h1 {
font-size: 2em;
}
._h2 {
font-size: 1.5em;
}
._h3 {
font-size: 1.17em;
}
._h5 {
font-size: 0.67em;
}
._h1,
._h2,
._h3,
._h4,
._h5,
._h6 {
font-weight: bold;
}
._ins {
text-decoration: underline;
}
._q::before {
content: '"';
}
._q::after {
content: '"';
}
._a,
._abbr,
._b,
._big,
._small,
._code,
._del,
._em,
._i,
._ins,
._label,
._q,
._span,
._strong {
display: inline;
}
/* #ifdef MP-WEIXIN || MP-QQ || MP-ALIPAY */
.__sub,
.__sup,
.__bdo,
.__bdi,
.__ruby,
.__rt {
display: inline-block !important;
}
/* #endif */
._video {
background-color: black;
width: 300px;
height: 225px;
display: inline-block;
position: relative;
}
._video::after {
content: '';
border-width: 15px 0 15px 30px;
border-style: solid;
border-color: transparent transparent transparent white;
position: absolute;
left: 50%;
top: 50%;
margin: -15px 0 0 -15px;
}
</style>

View File

@@ -0,0 +1,131 @@
<template>
<view class='load-more'>
<block v-if="!loading&&noMore">
<view class="loadmore__line"></view>
<text class="loadmore__text">{{noMoreText}}</text>
<view class="loadmore__line"></view>
</block>
<block v-if="loading">
<view class="weui-loading"></view>
<text class="loadmore__text">{{loadText}}</text>
</block>
</view>
</template>
<script>
export default {
name: 'load-more',
props: {
loadText: {
type: String,
default () {
return '努力加载中'
}
},
noMoreText: {
type: String,
default () {
return '没有更多了'
}
},
noMore: {
type: Boolean,
default () {
return false
}
},
loading: {
type: Boolean,
default () {
return true
}
}
},
created() {
},
data() {
return {
}
},
methods: {
},
}
</script>
<style>
.load-more {
display: flex;
width: 100%;
height: 90rpx;
justify-content: center;
align-items: center;
}
.loading-icon {
width: 30rpx;
height: 30rpx;
margin-right: 8rpx;
display: inline-block;
vertical-align: middle;
animation: weuiLoading 1s steps(12, end) infinite;
background: url('https://s10.mogucdn.com/mlcdn/c45406/171016_4a61e09hcadd157gadhdeje55e82c_32x32.png') no-repeat;
-webkit-background-size: 100%;
background-size: 100%;
}
.loadmore__text {
margin: 0 8rpx 0 8rpx;
color: #333;
font-size: 28rpx;
}
.loadmore__line {
width: 100rpx;
height: 1rpx;
border-top: 1rpx solid #d2d2d2;
}
/* 加载中动画 */
.weui-loading {
margin: 0 5px;
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
-webkit-animation: a 1s steps(12) infinite;
animation: a 1s steps(12) infinite;
background: transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;
background-size: 100%
}
.weui-loading.weui-loading_transparent {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 100 100'%3E%3Cpath fill='none' d='M0 0h100v100H0z'/%3E%3Crect xmlns='http://www.w3.org/2000/svg' width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.56)' rx='5' ry='5' transform='translate(0 -30)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.5)' rx='5' ry='5' transform='rotate(30 105.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.43)' rx='5' ry='5' transform='rotate(60 75.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.38)' rx='5' ry='5' transform='rotate(90 65 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.32)' rx='5' ry='5' transform='rotate(120 58.66 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.28)' rx='5' ry='5' transform='rotate(150 54.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.25)' rx='5' ry='5' transform='rotate(180 50 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.2)' rx='5' ry='5' transform='rotate(-150 45.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.17)' rx='5' ry='5' transform='rotate(-120 41.34 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.14)' rx='5' ry='5' transform='rotate(-90 35 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.1)' rx='5' ry='5' transform='rotate(-60 24.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.03)' rx='5' ry='5' transform='rotate(-30 -5.98 65)'/%3E%3C/svg%3E")
}
@-webkit-keyframes a {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn)
}
}
@keyframes a {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg)
}
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn)
}
}
</style>

View File

@@ -0,0 +1,107 @@
<template>
<view>
<view class="login-info pd-lg flex-center rel" v-if="isShowLogin">
<view @tap.stop="toClose" class="login-close flex-center radius abs">
<i class="iconfont icon-add c-base"></i>
</view>
<image mode="aspectFill" lazy-load class="logo-img radius" :src="configInfo.app_logo"></image>
<view class="flex-center flex-1 ml-md">
<view class="flex-1">
<view class="f-title c-base max-380 ellipsis">{{`欢迎来到${configInfo.app_text}`}}</view>
<view class="text f-caption">登录后获取更多精彩内容</view>
</view>
<view @tap.stop="toLogin" class="login-btn flex-center f-desc c-base radius"
:style="{background:primaryColor}">去登录</view>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
props: {},
mounted() {
this.init();
},
data() {
return {
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
isShowLogin: state => state.user.isShowLogin
}),
methods: {
...mapActions(['getConfigInfo', 'getUserInfo']),
...mapMutations(['updateUserItem']),
async init() {
},
toClose() {
this.updateUserItem({
key: 'isShowLogin',
val: false
})
},
toLogin() {
let pages = getCurrentPages();
let {
route
} = pages[pages.length - 1]
this.updateUserItem({
key: 'loginPage',
val: `/${route}`
})
this.$util.goUrl({
url: `/pages/login?type=1`
})
}
},
}
</script>
<style scoped lang="scss">
.login-info {
position: fixed;
left: 20rpx;
bottom: 20rpx;
width: 710rpx;
height: 139rpx;
background: rgba(0, 0, 0, 0.8);
border-radius: 16rpx;
.logo-img {
width: 90rpx;
height: 90rpx;
}
.text {
color: #9E9E9E;
}
.login-btn {
width: 150rpx;
height: 54rpx;
}
.login-close {
top: -10rpx;
right: -10rpx;
width: 40rpx;
height: 40rpx;
background: #000;
.iconfont {
font-size: 24rpx;
transform: rotate(45deg);
}
}
}
</style>

View File

@@ -0,0 +1,121 @@
<template>
<view :class="['ui-mask ','center',effect,{'show':show}]" @tap="handleMaskTap" :style='{top:top,background:background}'>
<slot></slot>
</view>
</template>
<script>
export default {
name: 'mask',
props: {
background: {
type: String,
default: "rgba(0, 0, 0, 0.5)"
},
show: {
type: Boolean
},
top: {
type: String,
default () {
return '0'
}
},
effect: {
type: String
},
hideDelay: {
type: Number
},
hideOnTap: {
type: Number,
default () {
return 1
}
},
blur: {
type: String
}
},
created() {
},
data() {
return {
}
},
watch: {
show(newValue, oldValue) {
var _this = this;
if (newValue) {
this.selfShow = false
} else {
if (this.hideDelay) {
setTimeout(function() {
_this.selfShow = false
}, this.hideDelay);
} else {
_this.selfShow = false
}
}
}
},
methods: {
handleMaskTap(e) {
if (this.hideOnTap) {
this.show = false;
this.$emit('hide')
}
}
},
}
</script>
<style>
.ui-mask {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
visibility: hidden;
opacity: 0;
transition: all 0.25s ease-in;
-webkit-backface-visibility: hidden;
z-index: 0;
}
.center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.ui-mask.scale-out {
transition: all 0.25s ease-in;
transform: scale(0);
}
.ui-mask.scale-out.show {
transform: scale(1);
}
.ui-mask.scale-in {
transition: all 0.25s ease-in;
transform: scale(1.5);
}
.ui-mask.scale-in.show {
transform: scale(1);
}
.ui-mask.show {
opacity: 1;
visibility: visible;
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<view class="min-countdown" :class="className" :style="{color:color}">
<rich-text :nodes="time" v-if="type == 1"></rich-text>
<!-- x天x时x分x秒 -->
<view class="flex-y-center" v-if="type == 2">
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``,width:time.d*1>99?'54rpx':''}">
{{time.d}}
</view>
<view class="text" :style="{color:textColor}"></view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">{{time.h}}</view>
<view class="text" :style="{color:textColor}"></view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">
{{time.m}}
</view>
<view class="text" :style="{color:textColor}"></view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">{{time.s}}</view>
<view class="text" :style="{color:textColor}"></view>
</view>
<!-- :: -->
<view class="flex-y-center" v-if="type == 3">
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``,width:time.all_h*1>99?className=='mini'?'48rpx':className=='sm'?'54rpx':'66rpx':''}">
{{time.all_h}}
</view>
<view class="text" :style="{color:textColor}">:</view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">
{{time.m}}
</view>
<view class="text" :style="{color:textColor}">:</view>
<view class="count-tag flex-center" :class="[{'haveColor':borderColor}]"
:style="{background:bgColor,border:borderColor?`1rpx solid ${borderColor}`:``}">
{{time.s}}
</view>
</view>
<view v-if="type==4">{{`${time.all_h}:${time.m}:${time.s}`}}</view>
<view v-if="type==5">{{`${time.all_h}小时${time.m}${time.s}`}}</view>
</view>
</template>
<script>
import {
mapState,
} from 'vuex';
export default {
name: 'min-countdown',
props: {
type: {
type: Number,
default: 1
},
targetTime: {
type: Number,
default: 0
},
format: {
type: String,
default: '{%d}天{%h}小时{%m}分{%s}秒'
},
color: {
type: String,
default: '#39b54a'
},
bgColor: {
type: String,
default: '#fff'
},
textColor: {
type: String,
default: '#39b54a'
},
borderColor: {
type: String,
default: ''
},
className: {
type: String,
default: ''
}
},
data() {
return {
time: '00:00:00'
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
init() {
setTimeout(() => {
this.getTime()
}, 1000)
},
getTime() {
let time = {}
let format = this.format
function formatNumber(num) {
return num > 9 ? `${num}` : `0${num}`
}
const gapTime = Math.ceil((this.targetTime - new Date().getTime()) / 1000)
if (gapTime >= 0) {
time.d = formatNumber(parseInt(gapTime / 86400))
let lastTime = gapTime % 86400;
time.h = formatNumber(parseInt(lastTime / 3600))
lastTime = lastTime % 3600;
time.all_h = time.d * 24 + time.h * 1
time.m = formatNumber(parseInt(lastTime / 60))
time.s = formatNumber(lastTime % 60);
['d', 'h', 'm', 's'].forEach(item => {
const day = time[item].split('');
format = format.replace('{%' + item + '}', time[item])
format = format.replace('{%' + item + '0}', day[0])
format = format.replace('{%' + item + '1}', day[1])
format = format.replace('{%' + item + '2}', day[2] ? day[2] : '0')
})
this.time = this.type == 1 ? format : time
this.init()
} else {
this.$emit('callback')
}
}
},
mounted() {
this.getTime()
}
}
</script>
<style lang="scss">
.min-countdown {
display: inline-flex;
justify-content: center;
align-items: center;
.count-tag {
width: 42rpx;
height: 42rpx;
margin: 0 10rpx;
padding: 0 10rpx;
border-radius: 4rpx;
}
.text {
color: #fff;
font-size: 20rpx;
}
}
.min-countdown.mini {
.count-tag {
width: 36rpx;
height: 29rpx;
border-radius: 2px;
}
}
.min-countdown.sm {
.count-tag {
width: 42rpx;
height: 42rpx;
}
}
.min-countdown.md {
.count-tag {
width: 54rpx;
height: 54rpx;
font-size: 32rpx;
}
.text {
font-size: 28rpx;
}
}
.haveColor {
transform: rotateZ(360deg);
}
</style>

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Arnout Kazemier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,92 @@
# EventEmitter3
[![Version npm](https://img.shields.io/npm/v/eventemitter3.svg?style=flat-square)](https://www.npmjs.com/package/eventemitter3)[![Build Status](https://img.shields.io/travis/primus/eventemitter3/master.svg?style=flat-square)](https://travis-ci.org/primus/eventemitter3)[![Dependencies](https://img.shields.io/david/primus/eventemitter3.svg?style=flat-square)](https://david-dm.org/primus/eventemitter3)[![Coverage Status](https://img.shields.io/coveralls/primus/eventemitter3/master.svg?style=flat-square)](https://coveralls.io/r/primus/eventemitter3?branch=master)[![IRC channel](https://img.shields.io/badge/IRC-irc.freenode.net%23primus-00a8ff.svg?style=flat-square)](https://webchat.freenode.net/?channels=primus)
[![Sauce Test Status](https://saucelabs.com/browser-matrix/eventemitter3.svg)](https://saucelabs.com/u/eventemitter3)
EventEmitter3 is a high performance EventEmitter. It has been micro-optimized
for various of code paths making this, one of, if not the fastest EventEmitter
available for Node.js and browsers. The module is API compatible with the
EventEmitter that ships by default with Node.js but there are some slight
differences:
- Domain support has been removed.
- We do not `throw` an error when you emit an `error` event and nobody is
listening.
- The `newListener` and `removeListener` events have been removed as they
are useful only in some uncommon use-cases.
- The `setMaxListeners`, `getMaxListeners`, `prependListener` and
`prependOnceListener` methods are not available.
- Support for custom context for events so there is no need to use `fn.bind`.
- The `removeListener` method removes all matching listeners, not only the
first.
It's a drop in replacement for existing EventEmitters, but just faster. Free
performance, who wouldn't want that? The EventEmitter is written in EcmaScript 3
so it will work in the oldest browsers and node versions that you need to
support.
## Installation
```bash
$ npm install --save eventemitter3
```
## CDN
Recommended CDN:
```text
https://unpkg.com/eventemitter3@latest/umd/eventemitter3.min.js
```
## Usage
After installation the only thing you need to do is require the module:
```js
var EventEmitter = require('eventemitter3');
```
And you're ready to create your own EventEmitter instances. For the API
documentation, please follow the official Node.js documentation:
http://nodejs.org/api/events.html
### Contextual emits
We've upgraded the API of the `EventEmitter.on`, `EventEmitter.once` and
`EventEmitter.removeListener` to accept an extra argument which is the `context`
or `this` value that should be set for the emitted events. This means you no
longer have the overhead of an event that required `fn.bind` in order to get a
custom `this` value.
```js
var EE = new EventEmitter()
, context = { foo: 'bar' };
function emitted() {
console.log(this === context); // true
}
EE.once('event-name', emitted, context);
EE.on('another-event', emitted, context);
EE.removeListener('another-event', emitted, context);
```
### Tests and benchmarks
This module is well tested. You can run:
- `npm test` to run the tests under Node.js.
- `npm run test-browser` to run the tests in real browsers via Sauce Labs.
We also have a set of benchmarks to compare EventEmitter3 with some available
alternatives. To run the benchmarks run `npm run benchmark`.
Tests and benchmarks are not included in the npm package. If you want to play
with them you have to clone the GitHub repository.
## License
[MIT](LICENSE)

View File

@@ -0,0 +1,67 @@
type EventNames<T extends string | symbol | { [K in string | symbol]: any[] }> = T extends string | symbol ? T : keyof T;
type EventArgs<T extends string | symbol | { [K in string | symbol]: any[] }, K extends EventNames<T>> = T extends string | symbol ? any[] : K extends keyof T ? T[K] : never;
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*/
declare class EventEmitter<EventTypes extends string | symbol | { [K in keyof EventTypes]: any[] } = string | symbol> {
static prefixed: string | boolean;
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*/
eventNames(): Array<EventNames<EventTypes>>;
/**
* Return the listeners registered for a given event.
*/
listeners<T extends EventNames<EventTypes>>(event: T): Array<EventEmitter.ListenerFn<EventArgs<EventTypes, T>>>;
/**
* Return the number of listeners listening to a given event.
*/
listenerCount(event: EventNames<EventTypes>): number;
/**
* Calls each of the listeners registered for a given event.
*/
emit<T extends EventNames<EventTypes>>(event: T, ...args: EventArgs<EventTypes, T>): boolean;
/**
* Add a listener for a given event.
*/
on<T extends EventNames<EventTypes>>(event: T, fn: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any): this;
addListener<T extends EventNames<EventTypes>>(event: T, fn: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any): this;
/**
* Add a one-time listener for a given event.
*/
once<T extends EventNames<EventTypes>>(event: T, fn: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any): this;
/**
* Remove the listeners of a given event.
*/
removeListener<T extends EventNames<EventTypes>>(event: T, fn?: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any, once?: boolean): this;
off<T extends EventNames<EventTypes>>(event: T, fn?: EventEmitter.ListenerFn<EventArgs<EventTypes, T>>, context?: any, once?: boolean): this;
/**
* Remove all listeners, or those of the specified event.
*/
removeAllListeners(event?: EventNames<EventTypes>): this;
}
declare namespace EventEmitter {
export interface ListenerFn<Args extends any[] = any[]> {
(...args: Args): void;
}
export interface EventEmitterStatic {
new<EventTypes extends string | symbol | { [K in keyof EventTypes]: any[] } = string | symbol>(): EventEmitter<EventTypes>;
}
export const EventEmitter: EventEmitterStatic;
}
export = EventEmitter;

View File

@@ -0,0 +1,347 @@
'use strict';
var has = Object.prototype.hasOwnProperty,
prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {} //
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null); //
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once),
evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = [],
events,
name;
if (this._eventsCount === 0) return names;
for (name in events = this._events) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event,
handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event,
listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt],
len = arguments.length,
args,
i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1:
return listeners.fn.call(listeners.context), true;
case 2:
return listeners.fn.call(listeners.context, a1), true;
case 3:
return listeners.fn.call(listeners.context, a1, a2), true;
case 4:
return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5:
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6:
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len - 1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length,
j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1:
listeners[i].fn.call(listeners[i].context);
break;
case 2:
listeners[i].fn.call(listeners[i].context, a1);
break;
case 3:
listeners[i].fn.call(listeners[i].context, a1, a2);
break;
case 4:
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
break;
default:
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
events.push(listeners[i]);
}
} //
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
}; //
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on; //
// Expose the prefix.
//
EventEmitter.prefixed = prefix; //
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter; //
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,84 @@
{
"_from": "eventemitter3@^4.0.0",
"_id": "eventemitter3@4.0.0",
"_inBundle": false,
"_integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==",
"_location": "/eventemitter3",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "eventemitter3@^4.0.0",
"name": "eventemitter3",
"escapedName": "eventemitter3",
"rawSpec": "^4.0.0",
"saveSpec": null,
"fetchSpec": "^4.0.0"
},
"_requiredBy": [
"/widget-ui"
],
"_resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"_shasum": "d65176163887ee59f386d64c82610b696a4a74eb",
"_spec": "eventemitter3@^4.0.0",
"_where": "C:\\Users\\sanfordsun\\WeChatProjects\\minicode-177\\node_modules\\widget-ui",
"author": {
"name": "Arnout Kazemier"
},
"bugs": {
"url": "https://github.com/primus/eventemitter3/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.",
"devDependencies": {
"assume": "~2.2.0",
"browserify": "~16.2.0",
"mocha": "~6.1.0",
"nyc": "~14.1.0",
"pre-commit": "~1.2.0",
"sauce-browsers": "~2.0.0",
"sauce-test": "~1.3.3",
"uglify-js": "~3.6.0"
},
"files": [
"index.js",
"index.d.ts",
"umd"
],
"homepage": "https://github.com/primus/eventemitter3#readme",
"keywords": [
"EventEmitter",
"EventEmitter2",
"EventEmitter3",
"Events",
"addEventListener",
"addListener",
"emit",
"emits",
"emitter",
"event",
"once",
"pub/sub",
"publish",
"reactor",
"subscribe"
],
"license": "MIT",
"main": "index.js",
"name": "eventemitter3",
"repository": {
"type": "git",
"url": "git://github.com/primus/eventemitter3.git"
},
"scripts": {
"benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;",
"browserify": "rm -rf umd && mkdir umd && browserify index.js -s EventEmitter3 -o umd/eventemitter3.js",
"minify": "uglifyjs umd/eventemitter3.js --source-map -cm -o umd/eventemitter3.min.js",
"prepublishOnly": "npm run browserify && npm run minify",
"test": "nyc --reporter=html --reporter=text mocha test/test.js",
"test-browser": "node test/browser.js"
},
"typings": "index.d.ts",
"version": "4.0.0"
}

View File

@@ -0,0 +1,404 @@
(function (f) {
if (typeof exports === "object" && typeof module !== "undefined") {
module.exports = f();
} else if (typeof define === "function" && define.amd) {
define([], f);
} else {
var g;
if (typeof window !== "undefined") {
g = window;
} else if (typeof global !== "undefined") {
g = global;
} else if (typeof self !== "undefined") {
g = self;
} else {
g = this;
}
g.EventEmitter3 = f();
}
})(function () {
var define, module, exports;
return function () {
function r(e, n, t) {
function o(i, f) {
if (!n[i]) {
if (!e[i]) {
var c = "function" == typeof require && require;
if (!f && c) return c(i, !0);
if (u) return u(i, !0);
var a = new Error("Cannot find module '" + i + "'");
throw a.code = "MODULE_NOT_FOUND", a;
}
var p = n[i] = {
exports: {}
};
e[i][0].call(p.exports, function (r) {
var n = e[i][1][r];
return o(n || r);
}, p, p.exports, r, e, n, t);
}
return n[i].exports;
}
for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
return o;
}
return r;
}()({
1: [function (require, module, exports) {
'use strict';
var has = Object.prototype.hasOwnProperty,
prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {} //
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null); //
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = false;
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once),
evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
function EventEmitter() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter.prototype.eventNames = function eventNames() {
var names = [],
events,
name;
if (this._eventsCount === 0) return names;
for (name in events = this._events) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
if (Object.getOwnPropertySymbols) {
return names.concat(Object.getOwnPropertySymbols(events));
}
return names;
};
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
EventEmitter.prototype.listeners = function listeners(event) {
var evt = prefix ? prefix + event : event,
handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
};
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
EventEmitter.prototype.listenerCount = function listenerCount(event) {
var evt = prefix ? prefix + event : event,
listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
};
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt],
len = arguments.length,
args,
i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1:
return listeners.fn.call(listeners.context), true;
case 2:
return listeners.fn.call(listeners.context, a1), true;
case 3:
return listeners.fn.call(listeners.context, a1, a2), true;
case 4:
return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5:
return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6:
return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len - 1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length,
j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1:
listeners[i].fn.call(listeners[i].context);
break;
case 2:
listeners[i].fn.call(listeners[i].context, a1);
break;
case 3:
listeners[i].fn.call(listeners[i].context, a1, a2);
break;
case 4:
listeners[i].fn.call(listeners[i].context, a1, a2, a3);
break;
default:
if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
};
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.on = function on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.once = function once(event, fn, context) {
return addListener(this, event, fn, context, true);
};
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) {
events.push(listeners[i]);
}
} //
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;else clearEvent(this, evt);
}
return this;
};
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
}; //
// Alias methods names because people roll like that.
//
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.addListener = EventEmitter.prototype.on; //
// Expose the prefix.
//
EventEmitter.prefixed = prefix; //
// Allow `EventEmitter` to be imported as module namespace.
//
EventEmitter.EventEmitter = EventEmitter; //
// Expose the module.
//
if ('undefined' !== typeof module) {
module.exports = EventEmitter;
}
}, {}]
}, {}, [1])(1);
});

View File

@@ -0,0 +1,162 @@
!function (e) {
if ("object" == typeof exports && "undefined" != typeof module) module.exports = e();else if ("function" == typeof define && define.amd) define([], e);else {
("undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : this).EventEmitter3 = e();
}
}(function () {
return function i(s, f, c) {
function u(t, e) {
if (!f[t]) {
if (!s[t]) {
var n = "function" == typeof require && require;
if (!e && n) return n(t, !0);
if (a) return a(t, !0);
var r = new Error("Cannot find module '" + t + "'");
throw r.code = "MODULE_NOT_FOUND", r;
}
var o = f[t] = {
exports: {}
};
s[t][0].call(o.exports, function (e) {
return u(s[t][1][e] || e);
}, o, o.exports, i, s, f, c);
}
return f[t].exports;
}
for (var a = "function" == typeof require && require, e = 0; e < c.length; e++) u(c[e]);
return u;
}({
1: [function (e, t, n) {
"use strict";
var r = Object.prototype.hasOwnProperty,
v = "~";
function o() {}
function f(e, t, n) {
this.fn = e, this.context = t, this.once = n || !1;
}
function i(e, t, n, r, o) {
if ("function" != typeof n) throw new TypeError("The listener must be a function");
var i = new f(n, r || e, o),
s = v ? v + t : t;
return e._events[s] ? e._events[s].fn ? e._events[s] = [e._events[s], i] : e._events[s].push(i) : (e._events[s] = i, e._eventsCount++), e;
}
function u(e, t) {
0 == --e._eventsCount ? e._events = new o() : delete e._events[t];
}
function s() {
this._events = new o(), this._eventsCount = 0;
}
Object.create && (o.prototype = Object.create(null), new o().__proto__ || (v = !1)), s.prototype.eventNames = function () {
var e,
t,
n = [];
if (0 === this._eventsCount) return n;
for (t in e = this._events) r.call(e, t) && n.push(v ? t.slice(1) : t);
return Object.getOwnPropertySymbols ? n.concat(Object.getOwnPropertySymbols(e)) : n;
}, s.prototype.listeners = function (e) {
var t = v ? v + e : e,
n = this._events[t];
if (!n) return [];
if (n.fn) return [n.fn];
for (var r = 0, o = n.length, i = new Array(o); r < o; r++) i[r] = n[r].fn;
return i;
}, s.prototype.listenerCount = function (e) {
var t = v ? v + e : e,
n = this._events[t];
return n ? n.fn ? 1 : n.length : 0;
}, s.prototype.emit = function (e, t, n, r, o, i) {
var s = v ? v + e : e;
if (!this._events[s]) return !1;
var f,
c,
u = this._events[s],
a = arguments.length;
if (u.fn) {
switch (u.once && this.removeListener(e, u.fn, void 0, !0), a) {
case 1:
return u.fn.call(u.context), !0;
case 2:
return u.fn.call(u.context, t), !0;
case 3:
return u.fn.call(u.context, t, n), !0;
case 4:
return u.fn.call(u.context, t, n, r), !0;
case 5:
return u.fn.call(u.context, t, n, r, o), !0;
case 6:
return u.fn.call(u.context, t, n, r, o, i), !0;
}
for (c = 1, f = new Array(a - 1); c < a; c++) f[c - 1] = arguments[c];
u.fn.apply(u.context, f);
} else {
var l,
p = u.length;
for (c = 0; c < p; c++) switch (u[c].once && this.removeListener(e, u[c].fn, void 0, !0), a) {
case 1:
u[c].fn.call(u[c].context);
break;
case 2:
u[c].fn.call(u[c].context, t);
break;
case 3:
u[c].fn.call(u[c].context, t, n);
break;
case 4:
u[c].fn.call(u[c].context, t, n, r);
break;
default:
if (!f) for (l = 1, f = new Array(a - 1); l < a; l++) f[l - 1] = arguments[l];
u[c].fn.apply(u[c].context, f);
}
}
return !0;
}, s.prototype.on = function (e, t, n) {
return i(this, e, t, n, !1);
}, s.prototype.once = function (e, t, n) {
return i(this, e, t, n, !0);
}, s.prototype.removeListener = function (e, t, n, r) {
var o = v ? v + e : e;
if (!this._events[o]) return this;
if (!t) return u(this, o), this;
var i = this._events[o];
if (i.fn) i.fn !== t || r && !i.once || n && i.context !== n || u(this, o);else {
for (var s = 0, f = [], c = i.length; s < c; s++) (i[s].fn !== t || r && !i[s].once || n && i[s].context !== n) && f.push(i[s]);
f.length ? this._events[o] = 1 === f.length ? f[0] : f : u(this, o);
}
return this;
}, s.prototype.removeAllListeners = function (e) {
var t;
return e ? (t = v ? v + e : e, this._events[t] && u(this, t)) : (this._events = new o(), this._eventsCount = 0), this;
}, s.prototype.off = s.prototype.removeListener, s.prototype.addListener = s.prototype.on, s.prefixed = v, s.EventEmitter = s, void 0 !== t && (t.exports = s);
}, {}]
}, {}, [1])(1);
});

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,172 @@
import computeLayout from "./css-layout";
import { getDefaultStyle, scalableStyles, layoutAffectedStyles } from "./style";
type LayoutData = {
left: number,
top: number,
width: number,
height: number
};
type LayoutNode = {
id: number,
style: Object,
children: LayoutNode[],
layout?: LayoutData
};
let uuid = 0;
class Element {
public static uuid(): number {
return uuid++;
}
public parent: Element | null = null;
public id: number = Element.uuid();
public style: { [key: string]: any } = {};
public computedStyle: { [key: string]: any } = {};
public lastComputedStyle: { [key: string]: any } = {};
public children: { [key: string]: Element } = {};
public layoutBox: LayoutData = { left: 0, top: 0, width: 0, height: 0 };
constructor(style: { [key: string]: any } = {}) {
// 拷贝一份,防止被外部逻辑修改
style = Object.assign(getDefaultStyle(), style);
this.computedStyle = Object.assign(getDefaultStyle(), style);
this.lastComputedStyle = Object.assign(getDefaultStyle(), style);
Object.keys(style).forEach(key => {
Object.defineProperty(this.style, key, {
configurable: true,
enumerable: true,
get: () => style[key],
set: (value: any) => {
if (value === style[key] || value === undefined) {
return;
}
this.lastComputedStyle = this.computedStyle[key]
style[key] = value
this.computedStyle[key] = value
// 如果设置的是一个可缩放的属性, 计算自己
if (scalableStyles.includes(key) && this.style.scale) {
this.computedStyle[key] = value * this.style.scale
}
// 如果设置的是 scale, 则把所有可缩放的属性计算
if (key === "scale") {
scalableStyles.forEach(prop => {
if (style[prop]) {
this.computedStyle[prop] = style[prop] * value
}
})
}
if (key === "hidden") {
if (value) {
layoutAffectedStyles.forEach((key: string) => {
this.computedStyle[key] = 0;
});
} else {
layoutAffectedStyles.forEach((key: string) => {
this.computedStyle[key] = this.lastComputedStyle[key];
});
}
}
}
})
})
if (this.style.scale) {
scalableStyles.forEach((key: string) => {
if (this.style[key]) {
const computedValue = this.style[key] * this.style.scale;
this.computedStyle[key] = computedValue;
}
});
}
if (style.hidden) {
layoutAffectedStyles.forEach((key: string) => {
this.computedStyle[key] = 0;
});
}
}
getAbsolutePosition(element: Element) {
if (!element) {
return this.getAbsolutePosition(this)
}
if (!element.parent) {
return {
left: 0,
top: 0
}
}
const {left, top} = this.getAbsolutePosition(element.parent)
return {
left: left + element.layoutBox.left,
top: top + element.layoutBox.top
}
}
public add(element: Element) {
element.parent = this;
this.children[element.id] = element;
}
public remove(element?: Element) {
// 删除自己
if (!element) {
Object.keys(this.children).forEach(id => {
const child = this.children[id]
child.remove()
delete this.children[id]
})
} else if (this.children[element.id]) {
// 是自己的子节点才删除
element.remove()
delete this.children[element.id];
}
}
public getNodeTree(): LayoutNode {
return {
id: this.id,
style: this.computedStyle,
children: Object.keys(this.children).map((id: string) => {
const child = this.children[id];
return child.getNodeTree();
})
}
}
public applyLayout(layoutNode: LayoutNode) {
["left", "top", "width", "height"].forEach((key: string) => {
if (layoutNode.layout && typeof layoutNode.layout[key] === "number") {
this.layoutBox[key] = layoutNode.layout[key];
if (this.parent && (key === "left" || key === "top")) {
this.layoutBox[key] += this.parent.layoutBox[key];
}
}
});
layoutNode.children.forEach((child: LayoutNode) => {
this.children[child.id].applyLayout(child);
});
}
layout() {
const nodeTree = this.getNodeTree();
computeLayout(nodeTree);
this.applyLayout(nodeTree);
}
}
export default Element;

View File

@@ -0,0 +1,15 @@
import _EventEmitter from "eventemitter3";
const emitter = new _EventEmitter();
export default class EventEmitter {
public emit(event: string, data?: any) {
emitter.emit(event, data);
}
public on(event: string, callback) {
emitter.on(event, callback);
}
public off(event: string, callback) {
emitter.off(event, callback);
}
}

View File

@@ -0,0 +1,746 @@
module.exports = function () {
var __MODS__ = {};
var __DEFINE__ = function (modId, func, req) {
var m = {
exports: {}
};
__MODS__[modId] = {
status: 0,
func: func,
req: req,
m: m
};
};
var __REQUIRE__ = function (modId, source) {
if (!__MODS__[modId]) return require(source);
if (!__MODS__[modId].status) {
var m = {
exports: {}
};
__MODS__[modId].status = 1;
__MODS__[modId].func(__MODS__[modId].req, m, m.exports);
if (typeof m.exports === "object") {
__MODS__[modId].m.exports.__proto__ = m.exports.__proto__;
Object.keys(m.exports).forEach(function (k) {
__MODS__[modId].m.exports[k] = m.exports[k];
var desp = Object.getOwnPropertyDescriptor(m.exports, k);
if (desp && desp.configurable) Object.defineProperty(m.exports, k, {
set: function (val) {
__MODS__[modId].m.exports[k] = val;
},
get: function () {
return __MODS__[modId].m.exports[k];
}
});
});
if (m.exports.__esModule) Object.defineProperty(__MODS__[modId].m.exports, "__esModule", {
value: true
});
} else {
__MODS__[modId].m.exports = m.exports;
}
}
return __MODS__[modId].m.exports;
};
var __REQUIRE_WILDCARD__ = function (obj) {
if (obj && obj.__esModule) {
return obj;
} else {
var newObj = {};
if (obj != null) {
for (var k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k];
}
}
newObj.default = obj;
return newObj;
}
};
var __REQUIRE_DEFAULT__ = function (obj) {
return obj && obj.__esModule ? obj.default : obj;
};
__DEFINE__(1572960819414, function (require, module, exports) {
!function (t, e) {
if ("object" == typeof exports && "object" == typeof module) module.exports = e();else if ("function" == typeof define && define.amd) define([], e);else {
var o = e();
for (var r in o) ("object" == typeof exports ? exports : t)[r] = o[r];
}
}(this, function () {
return function (t) {
var e = {};
function o(r) {
if (e[r]) return e[r].exports;
var i = e[r] = {
i: r,
l: !1,
exports: {}
};
return t[r].call(i.exports, i, i.exports, o), i.l = !0, i.exports;
}
return o.m = t, o.c = e, o.d = function (t, e, r) {
o.o(t, e) || Object.defineProperty(t, e, {
enumerable: !0,
get: r
});
}, o.r = function (t) {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {
value: "Module"
}), Object.defineProperty(t, "__esModule", {
value: !0
});
}, o.t = function (t, e) {
if (1 & e && (t = o(t)), 8 & e) return t;
if (4 & e && "object" == typeof t && t && t.__esModule) return t;
var r = Object.create(null);
if (o.r(r), Object.defineProperty(r, "default", {
enumerable: !0,
value: t
}), 2 & e && "string" != typeof t) for (var i in t) o.d(r, i, function (e) {
return t[e];
}.bind(null, i));
return r;
}, o.n = function (t) {
var e = t && t.__esModule ? function () {
return t.default;
} : function () {
return t;
};
return o.d(e, "a", e), e;
}, o.o = function (t, e) {
return Object.prototype.hasOwnProperty.call(t, e);
}, o.p = "", o(o.s = 0);
}([function (t, e, o) {
var r = this && this.__importDefault || function (t) {
return t && t.__esModule ? t : {
default: t
};
};
Object.defineProperty(e, "__esModule", {
value: !0
});
var i = r(o(1)),
l = o(2),
n = 0,
a = function () {
function t(e) {
var o = this;
void 0 === e && (e = {}), this.parent = null, this.id = t.uuid(), this.style = {}, this.computedStyle = {}, this.lastComputedStyle = {}, this.children = {}, this.layoutBox = {
left: 0,
top: 0,
width: 0,
height: 0
}, e = Object.assign(l.getDefaultStyle(), e), this.computedStyle = Object.assign(l.getDefaultStyle(), e), this.lastComputedStyle = Object.assign(l.getDefaultStyle(), e), Object.keys(e).forEach(function (t) {
Object.defineProperty(o.style, t, {
configurable: !0,
enumerable: !0,
get: function () {
return e[t];
},
set: function (r) {
r !== e[t] && void 0 !== r && (o.lastComputedStyle = o.computedStyle[t], e[t] = r, o.computedStyle[t] = r, l.scalableStyles.includes(t) && o.style.scale && (o.computedStyle[t] = r * o.style.scale), "scale" === t && l.scalableStyles.forEach(function (t) {
e[t] && (o.computedStyle[t] = e[t] * r);
}), "hidden" === t && (r ? l.layoutAffectedStyles.forEach(function (t) {
o.computedStyle[t] = 0;
}) : l.layoutAffectedStyles.forEach(function (t) {
o.computedStyle[t] = o.lastComputedStyle[t];
})));
}
});
}), this.style.scale && l.scalableStyles.forEach(function (t) {
if (o.style[t]) {
var e = o.style[t] * o.style.scale;
o.computedStyle[t] = e;
}
}), e.hidden && l.layoutAffectedStyles.forEach(function (t) {
o.computedStyle[t] = 0;
});
}
return t.uuid = function () {
return n++;
}, t.prototype.getAbsolutePosition = function (t) {
if (!t) return this.getAbsolutePosition(this);
if (!t.parent) return {
left: 0,
top: 0
};
var e = this.getAbsolutePosition(t.parent),
o = e.left,
r = e.top;
return {
left: o + t.layoutBox.left,
top: r + t.layoutBox.top
};
}, t.prototype.add = function (t) {
t.parent = this, this.children[t.id] = t;
}, t.prototype.remove = function (t) {
var e = this;
t ? this.children[t.id] && (t.remove(), delete this.children[t.id]) : Object.keys(this.children).forEach(function (t) {
e.children[t].remove(), delete e.children[t];
});
}, t.prototype.getNodeTree = function () {
var t = this;
return {
id: this.id,
style: this.computedStyle,
children: Object.keys(this.children).map(function (e) {
return t.children[e].getNodeTree();
})
};
}, t.prototype.applyLayout = function (t) {
var e = this;
["left", "top", "width", "height"].forEach(function (o) {
t.layout && "number" == typeof t.layout[o] && (e.layoutBox[o] = t.layout[o], !e.parent || "left" !== o && "top" !== o || (e.layoutBox[o] += e.parent.layoutBox[o]));
}), t.children.forEach(function (t) {
e.children[t.id].applyLayout(t);
});
}, t.prototype.layout = function () {
var t = this.getNodeTree();
i.default(t), this.applyLayout(t);
}, t;
}();
e.default = a;
}, function (t, e, o) {
o.r(e);
var r = function () {
var t,
e = "inherit",
o = "ltr",
r = "rtl",
i = "row",
l = "row-reverse",
n = "column",
a = "column-reverse",
u = "flex-start",
d = "center",
s = "flex-end",
y = "space-between",
c = "space-around",
f = "flex-start",
h = "center",
p = "flex-end",
g = "stretch",
v = "relative",
m = "absolute",
b = {
row: "left",
"row-reverse": "right",
column: "top",
"column-reverse": "bottom"
},
x = {
row: "right",
"row-reverse": "left",
column: "bottom",
"column-reverse": "top"
},
w = {
row: "left",
"row-reverse": "right",
column: "top",
"column-reverse": "bottom"
},
S = {
row: "width",
"row-reverse": "width",
column: "height",
"column-reverse": "height"
};
function W(t) {
return void 0 === t;
}
function L(t) {
return t === i || t === l;
}
function k(t, e) {
if (void 0 !== t.style.marginStart && L(e)) return t.style.marginStart;
var o = null;
switch (e) {
case "row":
o = t.style.marginLeft;
break;
case "row-reverse":
o = t.style.marginRight;
break;
case "column":
o = t.style.marginTop;
break;
case "column-reverse":
o = t.style.marginBottom;
}
return void 0 !== o ? o : void 0 !== t.style.margin ? t.style.margin : 0;
}
function j(t, e) {
if (void 0 !== t.style.marginEnd && L(e)) return t.style.marginEnd;
var o = null;
switch (e) {
case "row":
o = t.style.marginRight;
break;
case "row-reverse":
o = t.style.marginLeft;
break;
case "column":
o = t.style.marginBottom;
break;
case "column-reverse":
o = t.style.marginTop;
}
return null != o ? o : void 0 !== t.style.margin ? t.style.margin : 0;
}
function B(t, e) {
if (void 0 !== t.style.borderStartWidth && t.style.borderStartWidth >= 0 && L(e)) return t.style.borderStartWidth;
var o = null;
switch (e) {
case "row":
o = t.style.borderLeftWidth;
break;
case "row-reverse":
o = t.style.borderRightWidth;
break;
case "column":
o = t.style.borderTopWidth;
break;
case "column-reverse":
o = t.style.borderBottomWidth;
}
return null != o && o >= 0 ? o : void 0 !== t.style.borderWidth && t.style.borderWidth >= 0 ? t.style.borderWidth : 0;
}
function E(t, e) {
if (void 0 !== t.style.borderEndWidth && t.style.borderEndWidth >= 0 && L(e)) return t.style.borderEndWidth;
var o = null;
switch (e) {
case "row":
o = t.style.borderRightWidth;
break;
case "row-reverse":
o = t.style.borderLeftWidth;
break;
case "column":
o = t.style.borderBottomWidth;
break;
case "column-reverse":
o = t.style.borderTopWidth;
}
return null != o && o >= 0 ? o : void 0 !== t.style.borderWidth && t.style.borderWidth >= 0 ? t.style.borderWidth : 0;
}
function C(t, e) {
return function (t, e) {
if (void 0 !== t.style.paddingStart && t.style.paddingStart >= 0 && L(e)) return t.style.paddingStart;
var o = null;
switch (e) {
case "row":
o = t.style.paddingLeft;
break;
case "row-reverse":
o = t.style.paddingRight;
break;
case "column":
o = t.style.paddingTop;
break;
case "column-reverse":
o = t.style.paddingBottom;
}
return null != o && o >= 0 ? o : void 0 !== t.style.padding && t.style.padding >= 0 ? t.style.padding : 0;
}(t, e) + B(t, e);
}
function T(t, e) {
return function (t, e) {
if (void 0 !== t.style.paddingEnd && t.style.paddingEnd >= 0 && L(e)) return t.style.paddingEnd;
var o = null;
switch (e) {
case "row":
o = t.style.paddingRight;
break;
case "row-reverse":
o = t.style.paddingLeft;
break;
case "column":
o = t.style.paddingBottom;
break;
case "column-reverse":
o = t.style.paddingTop;
}
return null != o && o >= 0 ? o : void 0 !== t.style.padding && t.style.padding >= 0 ? t.style.padding : 0;
}(t, e) + E(t, e);
}
function O(t, e) {
return B(t, e) + E(t, e);
}
function _(t, e) {
return k(t, e) + j(t, e);
}
function R(t, e) {
return C(t, e) + T(t, e);
}
function A(t, e) {
return e.style.alignSelf ? e.style.alignSelf : t.style.alignItems ? t.style.alignItems : "stretch";
}
function P(t, e) {
if (e === r) {
if (t === i) return l;
if (t === l) return i;
}
return t;
}
function D(t, e) {
return function (t) {
return t === n || t === a;
}(t) ? P(i, e) : n;
}
function H(t) {
return t.style.position ? t.style.position : "relative";
}
function M(t) {
return H(t) === v && t.style.flex > 0;
}
function I(t, e) {
return t.layout[S[e]] + _(t, e);
}
function N(t, e) {
return void 0 !== t.style[S[e]] && t.style[S[e]] >= 0;
}
function F(t, e) {
return void 0 !== t.style[e];
}
function q(t, e) {
return void 0 !== t.style[e] ? t.style[e] : 0;
}
function z(t, e, o) {
var r = {
row: t.style.minWidth,
"row-reverse": t.style.minWidth,
column: t.style.minHeight,
"column-reverse": t.style.minHeight
}[e],
i = {
row: t.style.maxWidth,
"row-reverse": t.style.maxWidth,
column: t.style.maxHeight,
"column-reverse": t.style.maxHeight
}[e],
l = o;
return void 0 !== i && i >= 0 && l > i && (l = i), void 0 !== r && r >= 0 && l < r && (l = r), l;
}
function U(t, e) {
return t > e ? t : e;
}
function G(t, e) {
void 0 === t.layout[S[e]] && N(t, e) && (t.layout[S[e]] = U(z(t, e, t.style[S[e]]), R(t, e)));
}
function J(t, e, o) {
e.layout[x[o]] = t.layout[S[o]] - e.layout[S[o]] - e.layout[w[o]];
}
function K(t, e) {
return void 0 !== t.style[b[e]] ? q(t, b[e]) : -q(t, x[e]);
}
function Q(r, E, Q) {
var X = function (t, r) {
var i;
return (i = t.style.direction ? t.style.direction : e) === e && (i = void 0 === r ? o : r), i;
}(r, Q),
Y = P(function (t) {
return t.style.flexDirection ? t.style.flexDirection : n;
}(r), X),
Z = D(Y, X),
$ = P(i, X);
G(r, Y), G(r, Z), r.layout.direction = X, r.layout[b[Y]] += k(r, Y) + K(r, Y), r.layout[x[Y]] += j(r, Y) + K(r, Y), r.layout[b[Z]] += k(r, Z) + K(r, Z), r.layout[x[Z]] += j(r, Z) + K(r, Z);
var tt = r.children.length,
et = R(r, $);
if (function (t) {
return void 0 !== t.style.measure;
}(r)) {
var ot = !W(r.layout[S[$]]),
rt = t;
rt = N(r, $) ? r.style.width : ot ? r.layout[S[$]] : E - _(r, $), rt -= et;
var it = !N(r, $) && !ot,
lt = !N(r, n) && W(r.layout[S[n]]);
if (it || lt) {
var nt = r.style.measure(rt);
it && (r.layout.width = nt.width + et), lt && (r.layout.height = nt.height + R(r, n));
}
if (0 === tt) return;
}
var at,
ut,
dt,
st,
yt = function (t) {
return "wrap" === t.style.flexWrap;
}(r),
ct = function (t) {
return t.style.justifyContent ? t.style.justifyContent : "flex-start";
}(r),
ft = C(r, Y),
ht = C(r, Z),
pt = R(r, Y),
gt = R(r, Z),
vt = !W(r.layout[S[Y]]),
mt = !W(r.layout[S[Z]]),
bt = L(Y),
xt = null,
wt = null,
St = t;
vt && (St = r.layout[S[Y]] - pt);
for (var Wt = 0, Lt = 0, kt = 0, jt = 0, Bt = 0, Et = 0; Lt < tt;) {
var Ct,
Tt = 0,
Ot = 0,
_t = 0,
Rt = 0,
At = vt && ct === u || !vt && ct !== d,
Pt = At ? tt : Wt,
Dt = !0,
Ht = tt,
Mt = null,
It = null,
Nt = ft,
Ft = 0;
for (at = Wt; at < tt; ++at) {
if ((dt = r.children[at]).lineIndex = Et, dt.nextAbsoluteChild = null, dt.nextFlexChild = null, (Xt = A(r, dt)) === g && H(dt) === v && mt && !N(dt, Z)) dt.layout[S[Z]] = U(z(dt, Z, r.layout[S[Z]] - gt - _(dt, Z)), R(dt, Z));else if (H(dt) === m) for (null === xt && (xt = dt), null !== wt && (wt.nextAbsoluteChild = dt), wt = dt, ut = 0; ut < 2; ut++) st = 0 !== ut ? i : n, !W(r.layout[S[st]]) && !N(dt, st) && F(dt, b[st]) && F(dt, x[st]) && (dt.layout[S[st]] = U(z(dt, st, r.layout[S[st]] - R(r, st) - _(dt, st) - q(dt, b[st]) - q(dt, x[st])), R(dt, st)));
var qt = 0;
if (vt && M(dt) ? (Ot++, _t += dt.style.flex, null === Mt && (Mt = dt), null !== It && (It.nextFlexChild = dt), It = dt, qt = R(dt, Y) + _(dt, Y)) : (Ct = t, bt || (Ct = N(r, $) ? r.layout[S[$]] - et : E - _(r, $) - et), 0 === kt && V(dt, Ct, X), H(dt) === v && (Rt++, qt = I(dt, Y))), yt && vt && Tt + qt > St && at !== Wt) {
Rt--, kt = 1;
break;
}
At && (H(dt) !== v || M(dt)) && (At = !1, Pt = at), Dt && (H(dt) !== v || Xt !== g && Xt !== f || W(dt.layout[S[Z]])) && (Dt = !1, Ht = at), At && (dt.layout[w[Y]] += Nt, vt && J(r, dt, Y), Nt += I(dt, Y), Ft = U(Ft, z(dt, Z, I(dt, Z)))), Dt && (dt.layout[w[Z]] += jt + ht, mt && J(r, dt, Z)), kt = 0, Tt += qt, Lt = at + 1;
}
var zt = 0,
Ut = 0,
Gt = 0;
if (Gt = vt ? St - Tt : U(Tt, 0) - Tt, 0 !== Ot) {
var Jt,
Kt,
Qt = Gt / _t;
for (It = Mt; null !== It;) (Jt = Qt * It.style.flex + R(It, Y)) !== (Kt = z(It, Y, Jt)) && (Gt -= Kt, _t -= It.style.flex), It = It.nextFlexChild;
for ((Qt = Gt / _t) < 0 && (Qt = 0), It = Mt; null !== It;) It.layout[S[Y]] = z(It, Y, Qt * It.style.flex + R(It, Y)), Ct = t, N(r, $) ? Ct = r.layout[S[$]] - et : bt || (Ct = E - _(r, $) - et), V(It, Ct, X), dt = It, It = It.nextFlexChild, dt.nextFlexChild = null;
} else ct !== u && (ct === d ? zt = Gt / 2 : ct === s ? zt = Gt : ct === y ? (Gt = U(Gt, 0), Ut = Ot + Rt - 1 != 0 ? Gt / (Ot + Rt - 1) : 0) : ct === c && (zt = (Ut = Gt / (Ot + Rt)) / 2));
for (Nt += zt, at = Pt; at < Lt; ++at) H(dt = r.children[at]) === m && F(dt, b[Y]) ? dt.layout[w[Y]] = q(dt, b[Y]) + B(r, Y) + k(dt, Y) : (dt.layout[w[Y]] += Nt, vt && J(r, dt, Y), H(dt) === v && (Nt += Ut + I(dt, Y), Ft = U(Ft, z(dt, Z, I(dt, Z)))));
var Vt = r.layout[S[Z]];
for (mt || (Vt = U(z(r, Z, Ft + gt), gt)), at = Ht; at < Lt; ++at) if (H(dt = r.children[at]) === m && F(dt, b[Z])) dt.layout[w[Z]] = q(dt, b[Z]) + B(r, Z) + k(dt, Z);else {
var Xt,
Yt = ht;
if (H(dt) === v) if ((Xt = A(r, dt)) === g) W(dt.layout[S[Z]]) && (dt.layout[S[Z]] = U(z(dt, Z, Vt - gt - _(dt, Z)), R(dt, Z)));else if (Xt !== f) {
var Zt = Vt - gt - I(dt, Z);
Yt += Xt === h ? Zt / 2 : Zt;
}
dt.layout[w[Z]] += jt + Yt, mt && J(r, dt, Z);
}
jt += Ft, Bt = U(Bt, Nt), Et += 1, Wt = Lt;
}
if (Et > 1 && mt) {
var $t = r.layout[S[Z]] - gt,
te = $t - jt,
ee = 0,
oe = ht,
re = function (t) {
return t.style.alignContent ? t.style.alignContent : "flex-start";
}(r);
re === p ? oe += te : re === h ? oe += te / 2 : re === g && $t > jt && (ee = te / Et);
var ie = 0;
for (at = 0; at < Et; ++at) {
var le = ie,
ne = 0;
for (ut = le; ut < tt; ++ut) if (H(dt = r.children[ut]) === v) {
if (dt.lineIndex !== at) break;
W(dt.layout[S[Z]]) || (ne = U(ne, dt.layout[S[Z]] + _(dt, Z)));
}
for (ie = ut, ne += ee, ut = le; ut < ie; ++ut) if (H(dt = r.children[ut]) === v) {
var ae = A(r, dt);
if (ae === f) dt.layout[w[Z]] = oe + k(dt, Z);else if (ae === p) dt.layout[w[Z]] = oe + ne - j(dt, Z) - dt.layout[S[Z]];else if (ae === h) {
var ue = dt.layout[S[Z]];
dt.layout[w[Z]] = oe + (ne - ue) / 2;
} else ae === g && (dt.layout[w[Z]] = oe + k(dt, Z));
}
oe += ne;
}
}
var de = !1,
se = !1;
if (vt || (r.layout[S[Y]] = U(z(r, Y, Bt + T(r, Y)), pt), Y !== l && Y !== a || (de = !0)), mt || (r.layout[S[Z]] = U(z(r, Z, jt + gt), gt), Z !== l && Z !== a || (se = !0)), de || se) for (at = 0; at < tt; ++at) dt = r.children[at], de && J(r, dt, Y), se && J(r, dt, Z);
for (wt = xt; null !== wt;) {
for (ut = 0; ut < 2; ut++) st = 0 !== ut ? i : n, !W(r.layout[S[st]]) && !N(wt, st) && F(wt, b[st]) && F(wt, x[st]) && (wt.layout[S[st]] = U(z(wt, st, r.layout[S[st]] - O(r, st) - _(wt, st) - q(wt, b[st]) - q(wt, x[st])), R(wt, st))), F(wt, x[st]) && !F(wt, b[st]) && (wt.layout[b[st]] = r.layout[S[st]] - wt.layout[S[st]] - q(wt, x[st]));
dt = wt, wt = wt.nextAbsoluteChild, dt.nextAbsoluteChild = null;
}
}
function V(t, e, r) {
t.shouldUpdate = !0;
var i = t.style.direction || o;
!t.isDirty && t.lastLayout && t.lastLayout.requestedHeight === t.layout.height && t.lastLayout.requestedWidth === t.layout.width && t.lastLayout.parentMaxWidth === e && t.lastLayout.direction === i ? (t.layout.width = t.lastLayout.width, t.layout.height = t.lastLayout.height, t.layout.top = t.lastLayout.top, t.layout.left = t.lastLayout.left) : (t.lastLayout || (t.lastLayout = {}), t.lastLayout.requestedWidth = t.layout.width, t.lastLayout.requestedHeight = t.layout.height, t.lastLayout.parentMaxWidth = e, t.lastLayout.direction = i, t.children.forEach(function (t) {
t.layout.width = void 0, t.layout.height = void 0, t.layout.top = 0, t.layout.left = 0;
}), Q(t, e, r), t.lastLayout.width = t.layout.width, t.lastLayout.height = t.layout.height, t.lastLayout.top = t.layout.top, t.lastLayout.left = t.layout.left);
}
return {
layoutNodeImpl: Q,
computeLayout: V,
fillNodes: function t(e) {
return e.layout && !e.isDirty || (e.layout = {
width: void 0,
height: void 0,
top: 0,
left: 0,
right: 0,
bottom: 0
}), e.style || (e.style = {}), e.children || (e.children = []), e.children.forEach(t), e;
}
};
}();
e.default = function (t) {
r.fillNodes(t), r.computeLayout(t);
};
}, function (t, e, o) {
Object.defineProperty(e, "__esModule", {
value: !0
});
e.textStyles = ["color", "fontSize", "textAlign", "fontWeight", "lineHeight", "lineBreak"];
e.scalableStyles = ["left", "top", "right", "bottom", "width", "height", "margin", "marginLeft", "marginRight", "marginTop", "marginBottom", "padding", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom", "borderWidth", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth"];
e.layoutAffectedStyles = ["margin", "marginTop", "marginBottom", "marginLeft", "marginRight", "padding", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight", "width", "height"];
e.getDefaultStyle = function () {
return {
left: void 0,
top: void 0,
right: void 0,
bottom: void 0,
width: void 0,
height: void 0,
maxWidth: void 0,
maxHeight: void 0,
minWidth: void 0,
minHeight: void 0,
margin: void 0,
marginLeft: void 0,
marginRight: void 0,
marginTop: void 0,
marginBottom: void 0,
padding: void 0,
paddingLeft: void 0,
paddingRight: void 0,
paddingTop: void 0,
paddingBottom: void 0,
borderWidth: void 0,
flexDirection: void 0,
justifyContent: void 0,
alignItems: void 0,
alignSelf: void 0,
flex: void 0,
flexWrap: void 0,
position: void 0,
hidden: !1,
scale: 1
};
};
}]).default;
});
}, function (modId) {
var map = {};
return __REQUIRE__(map[modId], modId);
});
return __REQUIRE__(1572960819414);
}(); //# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,87 @@
const textStyles: string[] = ["color", "fontSize", "textAlign", "fontWeight", "lineHeight", "lineBreak"];
const scalableStyles: string[] = ["left", "top", "right", "bottom", "width", "height",
"margin", "marginLeft", "marginRight", "marginTop", "marginBottom",
"padding", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
"borderWidth", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth"];
const layoutAffectedStyles: string[] = [
"margin", "marginTop", "marginBottom", "marginLeft", "marginRight",
"padding", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight",
"width", "height"];
type Style = {
left: number,
top: number,
right: number,
bottom: number,
width: number,
height: number,
maxWidth: number,
maxHeight: number,
minWidth: number,
minHeight: number,
margin: number,
marginLeft: number,
marginRight: number,
marginTop: number,
marginBottom: number,
padding: number,
paddingLeft: number,
paddingRight: number,
paddingTop: number,
paddingBottom: number,
borderWidth: number,
borderLeftWidth: number,
borderRightWidth: number,
borderTopWidth: number,
borderBottomWidth: number,
flexDirection: "column" | "row",
justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around",
alignItems: "flex-start" | "center" | "flex-end" | "stretch",
alignSelf: "flex-start" | "center" | "flex-end" | "stretch",
flex: number,
flexWrap: "wrap" | "nowrap",
position: "relative" | "absolute",
hidden: boolean,
scale: number
}
const getDefaultStyle = () => ({
left: undefined,
top: undefined,
right: undefined,
bottom: undefined,
width: undefined,
height: undefined,
maxWidth: undefined,
maxHeight: undefined,
minWidth: undefined,
minHeight: undefined,
margin: undefined,
marginLeft: undefined,
marginRight: undefined,
marginTop: undefined,
marginBottom: undefined,
padding: undefined,
paddingLeft: undefined,
paddingRight: undefined,
paddingTop: undefined,
paddingBottom: undefined,
borderWidth: undefined,
flexDirection: undefined,
justifyContent: undefined,
alignItems: undefined,
alignSelf: undefined,
flex: undefined,
flexWrap: undefined,
position: undefined,
hidden: false,
scale: 1
})
export {
getDefaultStyle, scalableStyles, textStyles, layoutAffectedStyles
}

View File

@@ -0,0 +1,219 @@
class Draw {
constructor(canvas, context) {
this.canvas = canvas;
this.ctx = context;
}
roundRect(x, y, w, h, r, fill = true, stroke = false) {
if (r < 0) return;
const ctx = this.ctx;
ctx.beginPath();
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0);
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2);
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI);
ctx.lineTo(x, y + r);
if (stroke) ctx.stroke();
if (fill) ctx.fill();
}
drawView(box, style) {
const ctx = this.ctx;
const {
left: x,
top: y,
width: w,
height: h
} = box;
const {
borderRadius = 0,
borderWidth = 0,
borderColor,
color = '#000',
backgroundColor = 'transparent'
} = style;
ctx.save(); // 外环
if (borderWidth > 0) {
ctx.fillStyle = borderColor || color;
this.roundRect(x, y, w, h, borderRadius);
} // 内环
ctx.fillStyle = backgroundColor;
const innerWidth = w - 2 * borderWidth;
const innerHeight = h - 2 * borderWidth;
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0;
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius);
ctx.restore();
}
async drawImage(img, box, style) {
await new Promise((resolve, reject) => {
const ctx = this.ctx;
const canvas = this.canvas;
const {
borderRadius = 0
} = style;
const {
left: x,
top: y,
width: w,
height: h
} = box;
ctx.save();
this.roundRect(x, y, w, h, borderRadius, false, false);
ctx.clip();
const Image = canvas.createImage();
Image.onload = () => {
ctx.drawImage(Image, x, y, w, h);
ctx.restore();
resolve();
};
Image.onerror = () => {
reject();
};
Image.src = img;
});
} // eslint-disable-next-line complexity
drawText(text, box, style) {
const ctx = this.ctx;
let {
left: x,
top: y,
width: w,
height: h
} = box;
let {
color = '#000',
lineHeight = '1.4em',
fontSize = 14,
textAlign = 'left',
verticalAlign = 'top',
backgroundColor = 'transparent'
} = style;
if (!text || lineHeight > h) return;
ctx.save();
if (lineHeight) {
// 2em
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize);
}
ctx.textBaseline = 'top';
ctx.font = `${fontSize}px sans-serif`;
ctx.textAlign = textAlign; // 背景色
ctx.fillStyle = backgroundColor;
this.roundRect(x, y, w, h, 0); // 文字颜色
ctx.fillStyle = color; // 水平布局
switch (textAlign) {
case 'left':
break;
case 'center':
x += 0.5 * w;
break;
case 'right':
x += w;
break;
default:
break;
}
const textWidth = ctx.measureText(text).width;
const actualHeight = Math.ceil(textWidth / w) * lineHeight;
let paddingTop = Math.ceil((h - actualHeight) / 2);
if (paddingTop < 0) paddingTop = 0; // 垂直布局
switch (verticalAlign) {
case 'top':
break;
case 'middle':
y += paddingTop;
break;
case 'bottom':
y += 2 * paddingTop;
break;
default:
break;
}
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2); // 不超过一行
if (textWidth <= w) {
ctx.fillText(text, x, y + inlinePaddingTop);
return;
} // 多行文本
const chars = text.split('');
const _y = y; // 逐行绘制
let line = '';
for (const ch of chars) {
const testLine = line + ch;
const testWidth = ctx.measureText(testLine).width;
if (testWidth > w) {
ctx.fillText(line, x, y + inlinePaddingTop);
y += lineHeight;
line = ch;
if (y + lineHeight > _y + h) break;
} else {
line = testLine;
}
} // 避免溢出
if (y + lineHeight <= _y + h) {
ctx.fillText(line, x, y + inlinePaddingTop);
}
ctx.restore();
}
async drawNode(element) {
const {
layoutBox,
computedStyle,
name
} = element;
const {
src,
text
} = element.attributes;
if (name === 'view') {
this.drawView(layoutBox, computedStyle);
} else if (name === 'image') {
await this.drawImage(src, layoutBox, computedStyle);
} else if (name === 'text') {
this.drawText(text, layoutBox, computedStyle);
}
const childs = Object.values(element.children);
for (const child of childs) {
await this.drawNode(child);
}
}
}
module.exports = {
Draw
};

View File

@@ -0,0 +1,34 @@
const $filterNullChildren = children => {
children = deepFlatten(children);
return children.filter(child => child != null);
};
const deepFlatten = arr => {
let flatten = arr => [].concat(...arr);
return flatten(arr.map(x => Array.isArray(x) ? deepFlatten(x) : x));
};
export default ((data, opt) => {
const {
View,
Text,
Image
} = opt;
Object.assign(data, {});
return new View({
style: {},
attr: {
"needRoot": true
},
children: $filterNullChildren([new Canvas({
style: {},
attr: {
"id": "canvas",
"type": "2d",
"style": "width: " + data.width + "px; height: " + data.height + "px;"
},
children: $filterNullChildren([])
})])
});
});

View File

@@ -0,0 +1,109 @@
<template>
<view>
<canvas id="canvas" type="2d" :style="'width: ' + width + 'px; height: ' + height + 'px;'"></canvas>
</view>
</template>
<script>
const xmlParse = require("./xml-parser");
const {
Widget
} = require("./widget");
const {
Draw
} = require("./draw");
export default {
data() {
return {};
},
components: {},
props: {
width: {
type: Number,
default: 400
},
height: {
type: Number,
default: 300
}
},
beforeMount() {
const dpr = wx.getSystemInfoSync().pixelRatio;
const query = this.createSelectorQuery();
this.dpr = dpr;
query.select('#canvas').fields({
node: true,
size: true
}).exec(res => {
console.log(res, "==res");
const canvas = res[0].node;
const ctx = canvas.getContext('2d');
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
ctx.scale(dpr, dpr);
this.ctx = ctx;
this.canvas = canvas;
});
},
methods: {
async renderToCanvas(args) {
const {
wxml,
style
} = args; // 清空画布
const ctx = this.ctx;
const canvas = this.canvas;
if (!ctx || !canvas) {
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'));
}
ctx.clearRect(0, 0, this.width, this.height);
const {
root: xom
} = xmlParse(wxml);
const widget = new Widget(xom, style);
const container = widget.init();
this.boundary = {
top: container.layoutBox.top,
left: container.layoutBox.left,
width: container.computedStyle.width,
height: container.computedStyle.height
};
const draw = new Draw(canvas, ctx);
await draw.drawNode(container);
return Promise.resolve(container);
},
canvasToTempFilePath(args = {}) {
return new Promise((resolve, reject) => {
const {
top,
left,
width,
height
} = this.boundary;
wx.canvasToTempFilePath({
x: left,
y: top,
width,
height,
destWidth: width * this.dpr,
destHeight: height * this.dpr,
canvas: this.canvas,
fileType: args.fileType || 'png',
quality: args.quality || 1,
success: resolve,
fail: reject
});
});
}
}
};
</script>

View File

@@ -0,0 +1,34 @@
const hex = color => {
let result = null;
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
return color; // eslint-disable-next-line no-cond-assign
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
return '#' + result[2].split(',').map((part, index) => {
part = part.trim();
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10);
part = part.toString(16);
if (part.length === 1) {
part = '0' + part;
}
return part;
}).join('');
} else {
return '#00000000';
}
};
const splitLineToCamelCase = str => str.split('-').map((part, index) => {
if (index === 0) {
return part;
}
return part[0].toUpperCase() + part.slice(1);
}).join('');
module.exports = {
hex,
splitLineToCamelCase
};

View File

@@ -0,0 +1,88 @@
const Block = require("@/components/miniprogram_npm/widget-ui/index.js");
const {
splitLineToCamelCase
} = require("./utils.js");
class Element extends Block {
constructor(prop) {
super(prop.style);
this.name = prop.name;
this.attributes = prop.attributes;
}
}
class Widget {
constructor(xom, style) {
this.xom = xom;
this.style = style;
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color'];
}
init() {
this.container = this.create(this.xom);
this.container.layout();
this.inheritStyle(this.container);
return this.container;
} // 继承父节点的样式
inheritStyle(node) {
const parent = node.parent || null;
const children = node.children || {};
const computedStyle = node.computedStyle;
if (parent) {
this.inheritProps.forEach(prop => {
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop];
});
}
Object.values(children).forEach(child => {
this.inheritStyle(child);
});
}
create(node) {
let classNames = (node.attributes.class || '').split(' ');
classNames = classNames.map(item => splitLineToCamelCase(item.trim()));
const style = {};
classNames.forEach(item => {
Object.assign(style, this.style[item] || {});
});
const args = {
name: node.name,
style
};
const attrs = Object.keys(node.attributes);
const attributes = {};
for (const attr of attrs) {
const value = node.attributes[attr];
const CamelAttr = splitLineToCamelCase(attr);
if (value === '' || value === 'true') {
attributes[CamelAttr] = true;
} else if (value === 'false') {
attributes[CamelAttr] = false;
} else {
attributes[CamelAttr] = value;
}
}
attributes.text = node.content;
args.attributes = attributes;
const element = new Element(args);
node.children.forEach(childNode => {
const childElement = this.create(childNode);
element.add(childElement);
});
return element;
}
}
module.exports = {
Widget
};

View File

@@ -0,0 +1,153 @@
/**
* Module dependencies.
*/
/**
* Expose `parse`.
*/
/**
* Parse the given string of `xml`.
*
* @param {String} xml
* @return {Object}
* @api public
*/
function parse(xml) {
xml = xml.trim(); // strip comments
xml = xml.replace(/<!--[\s\S]*?-->/g, '');
return document();
/**
* XML document.
*/
function document() {
return {
declaration: declaration(),
root: tag()
};
}
/**
* Declaration.
*/
function declaration() {
const m = match(/^<\?xml\s*/);
if (!m) return; // tag
const node = {
attributes: {}
}; // attributes
while (!(eos() || is('?>'))) {
const attr = attribute();
if (!attr) return node;
node.attributes[attr.name] = attr.value;
}
match(/\?>\s*/);
return node;
}
/**
* Tag.
*/
function tag() {
const m = match(/^<([\w-:.]+)\s*/);
if (!m) return; // name
const node = {
name: m[1],
attributes: {},
children: []
}; // attributes
while (!(eos() || is('>') || is('?>') || is('/>'))) {
const attr = attribute();
if (!attr) return node;
node.attributes[attr.name] = attr.value;
} // self closing tag
if (match(/^\s*\/>\s*/)) {
return node;
}
match(/\??>\s*/); // content
node.content = content(); // children
let child;
while (child = tag()) {
node.children.push(child);
} // closing
match(/^<\/[\w-:.]+>\s*/);
return node;
}
/**
* Text content.
*/
function content() {
const m = match(/^([^<]*)/);
if (m) return m[1];
return '';
}
/**
* Attribute.
*/
function attribute() {
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/);
if (!m) return;
return {
name: m[1],
value: strip(m[2])
};
}
/**
* Strip quotes from `val`.
*/
function strip(val) {
return val.replace(/^['"]|['"]$/g, '');
}
/**
* Match `re` and advance the string.
*/
function match(re) {
const m = xml.match(re);
if (!m) return;
xml = xml.slice(m[0].length);
return m;
}
/**
* End-of-source.
*/
function eos() {
return xml.length == 0;
}
/**
* Check for `prefix`.
*/
function is(prefix) {
return xml.indexOf(prefix) == 0;
}
}
module.exports = parse;

View File

@@ -0,0 +1,194 @@
<template>
<view class='search-box'
:style='{padding:`${padding}rpx`,margin:`${margin}rpx`,borderRadius:`${radius}rpx`,background:backgroundColor}'>
<view class='search-item' :class="[{'flex-y-center': type == 'text'},{'flex-between': type == 'input'}]"
:style='{borderRadius:`${radius}rpx`,background:searchColor,color:frontColor}'>
<block v-if="type=='text'">
<i class="iconfont icon-search"></i>
<view class='ml-md f-paragraph'>{{placeholder}}</view>
</block>
<block v-if="type=='input'">
<view class="flex-y-center flex-1">
<i class="iconfont icon-search"></i>
<input type='text' class="flex-1 f-paragraph ml-md mr-md" :disabled="disabled"
:placeholder='placeholder'
:placeholder-class='frontColor === "#fff" ? "c-base" : "c-placeholder"' confirm-type="search"
@input="handerInput" :value="keyword" @confirm="confirm" :auto-focus="autofocus"></input>
</view>
<view class=' search-item-btn flex-center radius' :style="{background:primaryColor}" @tap='confirm'
v-if="showbtn">搜索
</view>
</block>
</view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
name: 'search',
props: {
type: {
type: String,
default () {
return 'text'
}
},
placeholder: {
type: String,
default () {
return "请输入关键字进行搜索"
}
},
searchStyle: {
type: String,
default () {
return 'circle'
}
},
textAlign: {
type: String,
default () {
return 'center'
}
},
padding: {
type: Number,
default () {
return 30
}
},
margin: {
type: Number,
default () {
return 0
}
},
radius: {
type: Number,
default () {
return 0
}
},
backgroundColor: {
type: String,
default () {
return '#fff'
}
},
searchColor: {
type: String,
default () {
return '#F6F5FA'
}
},
frontColor: {
type: String,
default () {
return '#888'
}
},
autofocus: {
type: Boolean,
default () {
return false
}
},
disabled: {
type: Boolean,
default () {
return false
}
},
showbtn: {
type: Boolean,
default () {
return false
}
},
focus: {
type: Boolean,
default () {
return false
}
},
keyword: {
type: String,
default () {
return ''
}
}
},
created() {
},
data() {
return {
text: '',
searchImg: 'https://lbqny.migugu.com/admin/public/search.png'
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
confirm(e) {
let val = this.text;
this.$emit("confirm", val)
},
handerInput(e) {
let val = e.detail.value;
this.text = val;
this.$emit("input", val)
}
},
}
</script>
<style>
.search-box {
padding: 16rpx 16rpx;
display: flex;
align-items: center;
background: #efeff5;
}
.search-item {
width: 100%;
height: 70rpx;
background: #ffffff;
border-radius: 30rpx;
padding: 0 0 0 25rpx;
line-height: 1;
font-size: 26rpx;
}
.search-item-btn {
width: 110rpx;
height: 70rpx;
font-size: 26rpx;
font-weight: 400;
color: #FFFFFF;
}
.search-btn {
padding: 0 20rpx 0 40rpx;
}
.flex-1 {
flex: 1;
}
.icon-md {
width: 40rpx;
height: 40rpx;
}
.ml-md {
margin-left: 16rpx;
}
</style>

View File

@@ -0,0 +1,132 @@
<template>
<view>
<scroll-view scroll-x class='tab-list' :scroll-into-view="'tab'+(activeIndex-1)" :scroll-with-animation="true"
:style="{background: bgColor}">
<view class='tab-item text-bold rel' v-for="(item,index) in list" :key="index"
@tap='handerTabChange(index)' :id="'tab'+index"
:style="{width:width,height:height,lineHeight:height,color:index==activeIndex? activeColor : color,fontSize:fontSize}">
{{item.title || item}}
<view class="abs line" :style="{background: activeColor}" v-if="index==activeIndex && haveLine"></view>
<view v-if="item.number" class="item-msg c-base f-icontext abs"
:style="{width: item.number<10 ? '30rpx' : '50rpx',right: msgRight }">
{{item.number < 100 ? item.number : '99+'}}
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
name: 'tab',
props: {
list: {
type: Array,
default () {
return []
}
},
activeIndex: {
type: Number || String,
default () {
return 0
}
},
color: {
type: String,
default () {
return '#333'
}
},
activeColor: {
type: String,
default () {
return '#e73535'
}
},
bgColor: {
type: String,
default () {
return '#fff'
}
},
width: {
type: String,
default () {
return ''
}
},
height: {
type: String,
default () {
return ''
}
},
haveLine: {
type: Boolean,
default () {
return true
}
},
msgRight: {
type: String,
default () {
return '5rpx'
}
},
fontSize: {
type: String,
default () {
return '28rpx'
}
}
},
created() {
},
data() {
return {
}
},
methods: {
handerTabChange(index) {
this.$emit('change', index);
}
},
}
</script>
<style lang="scss">
.tab-list {
white-space: nowrap;
width: 100%;
}
.tab-item {
display: inline-block;
text-align: center;
line-height: 100rpx;
padding: 0 20rpx;
border-color: #fff;
box-sizing: border-box;
.line {
width: 80rpx;
height: 6rpx;
border-radius: 6rpx;
left: 50%;
bottom: 0rpx;
margin-left: -40rpx;
}
}
.item-msg {
width: 30rpx;
height: 30rpx;
line-height: 30rpx;
border-radius: 15rpx 15rpx 15rpx 0;
background: #f12c20;
top: 5rpx;
}
</style>

View File

@@ -0,0 +1,87 @@
<template>
<view class="custom-tabbar fix flex-center" :class="[{'fill-base b-1px-t':configInfo.tabBar[1].name}]">
<block v-if="configInfo.tabBar[1].name">
<view @tap.stop="changeTab(index)" class="flex-center flex-column mt-sm"
:style="{width: 100/configInfo.tabBar.length + '%'}" v-for="(item,index) in configInfo.tabBar"
:key="index">
<image mode="aspectFill" lazy-load class="cover"
:src="cur == index ? item.selected_img : item.default_img"></image>
<view class="text" :style="{color:cur == index ? primaryColor : '#666'}">{{item.name}}</view>
</view>
</block>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from "vuex"
export default {
components: {},
props: {
cur: {
type: String,
default () {
return '0'
}
},
},
data() {
return {}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
}),
created() {
let that = this;
let sysheight = uni.getSystemInfoSync().windowHeight
let configInfo = JSON.parse(JSON.stringify(this.configInfo))
let {
navBarHeight
} = configInfo
const query = uni.createSelectorQuery().in(this);
query.select('.custom-tabbar').boundingClientRect(data => {
let curSysHeight = sysheight - data.height - navBarHeight
configInfo.curSysHeight = curSysHeight
configInfo.tabbarHeight = data.height
this.updateConfigItem({
key: 'configInfo',
val: configInfo
})
}).exec();
},
methods: {
...mapMutations(['updateConfigItem']),
// 点击跳转
changeTab(index) {
this.$emit('change',
index
);
},
},
}
</script>
<style scoped lang="scss">
.custom-tabbar {
height: 98rpx;
bottom: 0;
height: calc(98rpx + env(safe-area-inset-bottom) / 2);
padding-bottom: calc(env(safe-area-inset-bottom) / 2);
.cover {
width: 44rpx;
height: 44rpx;
}
.text {
font-size: 22rpx;
margin-top: 5rpx;
height: 32rpx;
}
}
</style>

View File

@@ -0,0 +1,134 @@
<template>
<view>
<view class='record-box fill-base radius-24'>
<view class='record-item' :class="[{'b-1px-l': index != list.length -1}]" v-for="(item,index) in list"
:key="index">
<view class='c-caption rel'>
<text class="item-tag abs" :class="[{'cur':info.pay_type > item.pay_type -1}]"
:style="{border:`5rpx solid ${primaryColor}`,background: info.pay_type > item.pay_type -1 ? activeColor : ''}"></text>
<view class="item-text f-paragraph c-title flex-y-baseline">
{{item[type]}}
</view>
</view>
<view class="f-caption c-caption">{{item.time_text}}</view>
<image @tap.stop="toPreviewImage(index)" mode="aspectFill" class="item-img mt-sm radius-10"
:src="item.cover">
</image>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'timeline',
props: {
list: {
type: Object,
default () {
return {}
}
},
info: {
type: Object,
default () {
return {}
}
},
type: {
type: String,
default () {
return ''
}
},
activeColor: {
type: String,
default () {
return '#39b54a'
}
},
primaryColor: {
type: String,
default () {
return '#39b54a'
}
}
},
created() {
},
data() {
return {
}
},
methods: {
toPreviewImage(index) {
let {
cover: current
} = this.list[index]
let urls = this.list.map(item => {
return item.cover
})
this.$util.previewImage({
current,
urls
})
},
async toMap(key) {
let {
info
} = this
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: info[`${key}_lat`] * 1,
longitude: info[`${key}_lng`] * 1,
name: info[`${key}_address`],
scale: 28
})
},
},
}
</script>
<style lang="scss">
.record-item {
padding: 0rpx 0rpx 30rpx 40rpx;
margin-left: 13rpx;
.item-tag {
width: 19rpx;
height: 19rpx;
background: #EEF5F2;
border: 5rpx solid #39b54a;
transform: rotateZ(360deg);
border-radius: 50%;
top: 0rpx;
left: -52rpx;
}
.item-text {
line-height: 34rpx;
}
.item-img {
width: 158rpx;
height: 120rpx;
background: #f4f6f8;
}
}
.record-item.b-1px-l::before {
border: 1rpx solid #DBDBDB;
transform: rotateZ(360deg);
}
.record-item:nth-child(5) {
padding-bottom: 0;
}
</style>

View File

@@ -0,0 +1,256 @@
<template>
<view class="uni-navbar" :class="{'uni-navbar-fixed':isFixed,'uni-navbar-shadow':hasShadow}"
:style="{backgroundColor:backgroundColor,height:navBarHeight+'px'}">
<uni-status-bar v-if="insertStatusBar"></uni-status-bar>
<view class="uni-navbar-header" :style="{color:color}">
<view class="uni-navbar-header-btns left" @tap="onClickLeft">
<slot name="left"></slot>
<view v-if="leftIcon || leftText" class="uni-navbar-btn-text"><i class="iconfont" :class="leftIcon"
v-if="leftIcon"></i>{{leftText || ''}}</view>
</view>
<block v-if="!onlyLeft">
<view class="uni-navbar-container" :class="[{'flex-center':title},{'flex-y-center':image}]">
<view v-if="title" class="uni-navbar-container-title ellipsis">{{title}}</view>
<image mode="aspectFill" lazy-load class="seckill-nav" :src="image" v-if="image">
</image>
<!-- 标题插槽 -->
<slot></slot>
</view>
<view class="uni-navbar-header-btns right" @tap="onClickRight">
<view v-if="rightText" class="uni-navbar-btn-text">{{rightText}}</view>
<slot name="right"></slot>
</view>
</block>
</view>
</view>
</template>
<script>
import uniStatusBar from '@/components/uni-status-bar.vue';
export default {
components: {
uniStatusBar
},
props: {
/**
* 标题文字
*/
title: {
type: String,
default: ''
},
/**
* 标题图片
*/
image: {
type: String,
default: ''
},
/**
* 左侧按钮图标
*/
leftIcon: {
type: String,
default: ''
},
/**
* 左侧按钮文本
*/
leftText: {
type: String,
default: ''
},
/**
* 右侧按钮文本
*/
rightText: {
type: String,
default: ''
},
/**
* 是否固定在顶部
*/
fixed: {
type: [Boolean, String],
default: false
},
/**
* 按钮图标和文字颜色
*/
color: {
type: String,
default: '#000'
},
/**
* 背景颜色
*/
backgroundColor: {
type: String,
default: '#FFF'
},
/**
* 是否仅有左侧
*/
onlyLeft: {
type: [Boolean],
default: false
},
/**
* 是否包含状态栏,默认固定在顶部时包含
*/
statusBar: {
type: [Boolean, String],
default: ''
},
/**
* 是否使用阴影,默认根据背景色判断
*/
shadow: {
type: Boolean,
default () {
return false
}
},
},
computed: {
isFixed() {
return String(this.fixed) === 'true'
},
insertStatusBar() {
switch (String(this.statusBar)) {
case 'true':
return true
case 'false':
return false
default:
return this.isFixed
}
},
hasShadow() {
var backgroundColor = this.backgroundColor
switch (this.shadow) {
case true:
return true
case false:
return false
default:
return backgroundColor !== 'transparent' && backgroundColor.indexOf('rgba') < 0
}
}
},
data() {
return {
navBarHeight: uni.getSystemInfoSync().statusBarHeight * 1 + 44
}
},
methods: {
/**
* 左侧按钮点击事件
*/
onClickLeft() {
let {
leftIcon = ''
} = this
if (leftIcon == 'icon-left') {
this.$util.goUrl({
url: 1,
openType: `navigateBack`
})
} else if (leftIcon == 'iconshouye') {
this.$util.goUrl({
url: `/pages/home`,
openType: 'reLaunch'
})
} else {
this.$emit('clickLeft')
this.$emit('click-left')
}
},
/**
* 右侧按钮点击事件
*/
onClickRight() {
this.$emit('clickRight')
this.$emit('click-right')
}
}
}
</script>
<style>
.uni-navbar {
display: block;
position: relative;
width: 100%;
overflow: hidden;
}
.uni-navbar-shadow {
box-shadow: 0 1px 6px #ccc;
}
.uni-navbar.uni-navbar-fixed {
position: fixed;
z-index: 999999999;
}
.uni-navbar-header {
display: flex;
flex-direction: row;
width: 100%;
/* #ifdef MP-BAIDU */
height: 38px;
line-height: 38px;
font-size: 15px;
/* #endif */
/* #ifndef MP-BAIDU */
height: 44px;
line-height: 44px;
font-size: 14px;
/* #endif */
}
.uni-navbar-header-btns {
display: inline-flex;
flex-wrap: nowrap;
flex-shrink: 0;
width: 100px;
}
.uni-navbar-header-btns.left {
padding-left: 30rpx;
}
.uni-navbar-header-btns.right {
padding-right: 30rpx;
}
.uni-navbar-btn-text {
display: flex;
align-items: center;
}
.uni-navbar-btn-text.iconfont {
font-size: 40rpx;
}
.uni-navbar-container {
width: 100%;
margin: 0 5px;
}
.uni-navbar-container-title {
/* #ifdef MP-BAIDU */
height: 38px;
line-height: 38px;
/* #endif */
/* #ifndef MP-BAIDU */
height: 44px;
line-height: 44px;
/* #endif */
font-size: 15px;
max-width: 330rpx;
/* text-align: center; */
/* padding-right: 30px; */
}
</style>

View File

@@ -0,0 +1,196 @@
<template>
<view v-if="showPopup" class="uni-popup">
<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask"
@click="close(true)" />
<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper"
@click="close(true)">
<view class="uni-popup__wrapper-box" @click.stop="clear">
<slot />
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UniPopup',
props: {
// 开启动画
animation: {
type: Boolean,
default: true
},
// 弹出层类型可选值top: 顶部弹出层bottom底部弹出层center全屏弹出层
type: {
type: String,
default: 'center'
},
// 是否开启自定义
custom: {
type: Boolean,
default: false
},
maskClick: {
type: Boolean,
default: true
},
show: {
type: Boolean,
default: true
}
},
data() {
return {
ani: '',
showPopup: false
}
},
watch: {
show(newValue) {
if (newValue) {
this.open()
} else {
this.close()
}
}
},
created() {},
methods: {
clear() {},
open() {
this.$emit('change', {
show: true
})
this.showPopup = true
this.$nextTick(() => {
setTimeout(() => {
this.ani = 'uni-' + this.type
}, 30)
})
},
close(type) {
if (!this.maskClick && type) return
this.$emit('change', {
show: false
})
this.ani = ''
this.$nextTick(() => {
setTimeout(() => {
this.showPopup = false
}, 300)
})
}
}
}
</script>
<style lang="scss">
@charset "UTF-8";
.uni-popup {
position: fixed;
top: 0;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
overflow: hidden
}
.uni-popup__mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 998;
background: rgba(0, 0, 0, .4);
opacity: 0
}
.uni-popup__mask.ani {
transition: all .3s
}
.uni-popup__mask.uni-bottom,
.uni-popup__mask.uni-center,
.uni-popup__mask.uni-top {
opacity: 1
}
.uni-popup__wrapper {
position: absolute;
z-index: 999;
box-sizing: border-box
}
.uni-popup__wrapper.ani {
transition: all .3s
}
.uni-popup__wrapper.top {
top: 0;
left: 0;
width: 100%;
transform: translateY(-100%)
}
.uni-popup__wrapper.bottom {
bottom: 0;
left: 0;
width: 100%;
transform: translateY(100%)
}
.uni-popup__wrapper.center {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
transform: scale(1.2);
opacity: 0
}
.uni-popup__wrapper-box {
position: relative;
box-sizing: border-box
}
.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
/* padding: 30upx; */
// background: #fff
}
.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
position: relative;
/* max-width: 80%;
max-height: 80%; */
overflow-y: scroll;
border-radius: 25rpx;
}
.uni-popup__wrapper.uni-custom.bottom .uni-popup__wrapper-box {
width: 100%;
// max-height: 500px;
overflow-y: scroll;
border-radius: 25rpx 25rpx 0 0;
}
.uni-popup__wrapper.uni-custom.top .uni-popup__wrapper-box {
width: 100%;
// max-height: 500px;
overflow-y: scroll;
border-radius: 0 0 25rpx 25rpx;
}
.uni-popup__wrapper.uni-bottom,
.uni-popup__wrapper.uni-top {
transform: translateY(0)
}
.uni-popup__wrapper.uni-center {
transform: scale(1);
opacity: 1
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<view class="segmented-control" :class="styleType" :style="wrapStyle">
<view v-for="(item, index) in values" class="segmented-control-item" :class="styleType" :key="index"
:style="index === currentIndex ? activeStyle : itemStyle" @click="onClick(index)">
{{item.title}}
</view>
</view>
</template>
<script>
export default {
name: 'uni-segmented-control',
props: {
current: {
type: Number,
default: 0
},
values: {
type: Array,
default () {
return [];
}
},
activeColor: {
type: String,
default: '#007aff'
},
styleType: {
type: String,
default: 'button'
},
haveBorder: {
type: Boolean,
default () {
return false
}
},
lockTap: {
type: Boolean,
default () {
return true
}
}
},
data() {
return {
currentIndex: this.current
}
},
watch: {
current(val) {
if (val !== this.currentIndex) {
this.currentIndex = val;
}
}
},
computed: {
wrapStyle() {
let styleString = '';
switch (this.styleType) {
case 'text':
styleString = `border:0;`;
break;
default:
styleString = this.haveBorder ? `border: 1rpx solid ${this.activeColor};` : ``;
break;
}
return styleString;
},
itemStyle() {
let styleString = '';
switch (this.styleType) {
case 'text':
styleString = `color:#000;border-left:0;`;
break;
default:
styleString = `color:#222;background:#eddbba;border-color:#fff;`;
break;
}
return styleString;
},
activeStyle() {
let styleString = '';
switch (this.styleType) {
case 'text':
styleString = `color:${this.activeColor};border-left:0;border-bottom-style:solid;`;
break;
default:
styleString = `color:#fff;border-color:${this.activeColor};background-color:${this.activeColor}`;
break;
}
return styleString;
}
},
methods: {
onClick(index) {
let {
lockTap,
currentIndex
} = this
console.log(lockTap, currentIndex);
if (currentIndex !== index || !lockTap) {
this.currentIndex = index;
this.$emit('clickItem', index);
}
}
},
}
</script>
<style>
.segmented-control {
display: flex;
flex-direction: row;
justify-content: center;
font-size: 26rpx;
border-radius: 5rpx;
box-sizing: border-box;
margin: 0 auto;
overflow: hidden;
}
.segmented-control.button {
border-radius: 54rpx;
box-sizing: border-box;
}
.segmented-control.text {
border: 0;
border-radius: 0rpx;
}
.segmented-control-item {
flex: 1;
text-align: center;
line-height: 54rpx;
box-sizing: border-box;
}
.segmented-control-item.button {
border-left: 1upx solid;
}
.segmented-control-item.button:first-child {
border-radius: 54rpx 0 0 54rpx;
}
.segmented-control-item.button:last-child {
border-radius: 0 54rpx 54rpx 0;
}
.segmented-control-item.text {
border-left: 0;
}
.segmented-control-item:first-child {
border-left-width: 0;
}
</style>

View File

@@ -0,0 +1,29 @@
<template>
<view class="uni-status-bar" :style="style">
<slot></slot>
</view>
</template>
<script>
export default {
computed: {
style() {
//#ifdef APP-PLUS
return ''
//#endif
//#ifndef APP-PLUS
return `height:${uni.getSystemInfoSync().statusBarHeight}px`
//#endif
}
}
}
</script>
<style>
.uni-status-bar {
display: block;
width: 100%;
height: 20px;
height: var(--status-bar-height);
}
</style>

View File

@@ -0,0 +1,326 @@
<template>
<view
:class="[{'flex-warp':!imgTypeList.includes(imgclass) || !imgclass},{'flex-center flex-column':imgTypeList.includes(imgclass)}]">
<block v-for="(item,index) in imagelist" :key="index">
<view class="rel item-child radius-16" :class="[imgclass,{'margin border': imgsize > 1}]">
<image mode="aspectFill" lazy-load @tap="previewImage(item,imagelist)" class="upload-img radius-16"
:src="item.path" v-if="filetype == 'picture'"></image>
<video :id="`video_${index}`" class="ipload-video radius-16" :loop="false" enable-play-gesture
enable-progress-gesture :show-center-play-btn="true" :controls="true" :src="item.path"
:data-id="item.id" objectFit="cover" :data-index="index" @play="onPlay" @pause="onPause"
@ended="onEnded" @timeupdate="onTimeUpdate" @waiting="onWaiting" @progress="onProgress"
@loadedmetadata="onLoadedMetaData" v-if="filetype == 'video'">
</video>
<block v-if="imgauth">
<block v-if="imgsize>1">
<view @tap="toDel(index)" class="guanbi abs flex-center" :class="[imgclass]"
style="z-index: 1;"><i class="iconfont icon-add rotate-45 c-base"></i></view>
</block>
<block v-else>
<view @tap="chooseImage"
class="flex-center flex-column item-child border upload-item radius-16 abs"
:class="[imgclass]" style="top:0;margin-top:0;background:rgba(0,0,0,0.5);">
<view class="upload-icon flex-center c-base radius-16">
<i class="iconfont icon-camera"></i>
</view>
<view class="f-caption c-base mt-sm">重新上传</view>
</view>
</block>
</block>
</view>
</block>
<view @tap="chooseImage" class="radius-16 flex-center flex-column item-child border rel upload-item fill-body"
:class="[imgclass,{'margin': imgsize > 1}]" v-if="imgauth && imagelist.length < imgsize">
<image mode="aspectFill" lazy-load class="item-child md bg-img abs radius-16"
style="width: 292rpx;height: 200rpx;" :src="bgimg" v-if="bgimg">
</image>
<block v-else>
<view class="upload-icon flex-center c-desc radius-16">
<i class="iconfont icon-camera"></i>
</view>
<view class="f-caption c-caption mt-sm" v-if="text">{{text}}</view>
<view class="cur-imgsize f-caption c-caption" v-if="imgsize>1">{{`${imagelist.length}/${imgsize}`}}
</view>
</block>
</view>
</view>
</template>
<script>
import {
mapState,
mapActions,
} from 'vuex';
export default {
props: {
// 图片列表
imagelist: {
type: Array,
default () {
return []
}
},
// 图片参数名
imgtype: {
type: String,
default () {
return ''
}
},
// 图片张数
imgsize: {
type: Number,
default () {
return 9
}
},
// 上传类型
filetype: {
type: String,
default () {
return 'picture'
}
},
// 图片样式
imgclass: {
type: String,
default () {
return ''
}
},
// 是否有权限
imgauth: {
type: Boolean,
default () {
return true
}
},
// 备注信息
text: {
type: String,
default () {
return ''
}
},
// 默认背景图
bgimg: {
type: String,
default () {
return ''
}
}
},
data() {
return {
imgTypeList: ['md', 'lg'],
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
methods: {
previewImage(current, urls) {
let res_urls = [];
urls = this.$util.deepCopy(urls);
urls.forEach((item, index) => {
res_urls.push(item.path)
})
uni.previewImage({
current: current.path,
urls: res_urls,
})
},
async toDel(index) {
let fileName = this.filetype == 'picture' ? '图片' : '视频'
let [res_del, {
confirm
}] = await uni.showModal({
content: `请确认是否要删除${fileName}`,
})
if (!confirm) return;
this.imagelist.splice(index, 1);
this.$emit('del', {
imgtype: this.imgtype,
imagelist: this.imagelist
});
},
async chooseImage() {
let {
imgtype,
imgsize,
filetype
} = this;
let imagelist = this.$util.deepCopy(this.imagelist)
let is_upload_img = filetype == 'picture'
let chooseModel = is_upload_img ? 'chooseImage' : 'chooseVideo'
let param = {
count: imgsize - imagelist.length * 1,
}
if (is_upload_img) {
param.sizeType = ['compressed']
}
let [res_upload, res_info] = await uni[chooseModel](param)
if (res_upload) return
let {
size = 0,
tempFiles,
tempFilePath = ''
} = res_info
if (filetype == 'video' && size / 1024 / 1024 > 100) {
this.$util.showToast({
title: `上传视频大小超过限制100M`
})
return
}
let filePath = [];
// 格式化图片参数
this.$util.showLoading({
title: "上传中"
});
if (is_upload_img) {
for (let i = 0; i < tempFiles.length; i++) {
let {
attachment_path: path
} = await this.$api.base.uploadFile({
filePath: tempFiles[i].path,
filetype
})
if (imgsize > 1) {
imagelist.push({
path
})
} else {
imagelist = [{
path
}]
}
}
} else {
let path = await this.$api.base.uploadVideo({
filePath: tempFilePath,
filetype
})
console.log(path, "=====video path");
imagelist.push({
path
})
}
this.$util.hideAll()
this.$emit('upload', {
imgtype,
imagelist
});
},
onPlay(e) {},
onPause(e) {},
onEnded(e) {},
onTimeUpdate(e) {},
onWaiting(e) {},
onProgress(e) {},
onLoadedMetaData(e) {},
}
}
</script>
<style lang="scss">
.item-child {
width: 200rpx;
height: 200rpx;
background: #fff;
margin: 20rpx 0;
}
.border {
border: 1rpx dashed #ccc;
transform: rotateZ(360deg);
}
.item-child.bg-img {
top: 0;
margin: 0;
overflow: hidden;
}
.margin {
margin: 20rpx 20rpx 0 0;
}
.item-child:nth-child(3n) {
margin-right: 0rpx;
}
.item-child.sm {
width: 140rpx;
height: 140rpx;
}
.item-child.mini {
width: 196rpx;
height: 196rpx;
}
.item-child.md {
width: 294rpx;
height: 202rpx;
margin-bottom: 20rpx;
overflow: hidden;
}
.item-child.lg {
width: 690rpx;
height: 400rpx;
}
.upload-img {
width: 100%;
height: 100%;
}
.upload-item {
.upload-icon {
// width: 80rpx;
// height: 76rpx;
// background: #FFFFFF;
.iconfont {
font-size: 70rpx;
display: block;
// color: #BBBBBB;
}
}
.cur-imgsize {
line-height: 1.1;
}
}
.upload-item.margin {
margin-bottom: 0;
}
.guanbi {
width: 32rpx;
height: 32rpx;
background: rgba(0, 0, 0, 0.2);
border-radius: 0 10rpx 0 0;
top: 0rpx;
right: 0rpx;
z-index: 1;
.iconfont {
font-size: 28rpx;
}
}
.guanbi.lg {
width: 50rpx;
height: 50rpx;
.iconfont {
font-size: 38rpx;
}
}
</style>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,742 @@
<template>
<view class="w-picker-view">
<picker-view v-if="fields=='year'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='month'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='day'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='hour'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='minute'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
<picker-view v-if="fields=='second'" class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{
years:[],
months:[],
days:[],
hours:[],
minutes:[],
seconds:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:[String,Number],
default:""
},
endYear:{
type:[String,Number],
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
disabledAfter:{//是否禁用当前之后的日期
type:Boolean,
default:false
},
fields:{
type:String,
default:"day"
}
},
watch:{
fields(val){
this.initData();
},
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg,example
switch(this.fields){
case "year":
strReg=/^\d{4}$/;
example="2019";
break;
case "month":
strReg=/^\d{4}-\d{2}$/;
example="2019-02";
break;
case "day":
strReg=/^\d{4}-\d{2}-\d{2}$/;
example="2019-02-01";
break;
case "hour":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}(:\d{2}){1,2}?$/;
example="2019-02-01 18:00:00或2019-02-01 18";
break;
case "minute":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2}){0,1}?$/;
example="2019-02-01 18:06:00或2019-02-01 18:06";
break;
case "second":
strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
example="2019-02-01 18:06:01";
break;
}
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let months=[],days=[],hours=[],minutes=[],seconds=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let hoursLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay||hour*1<curHour||minute*1<curMinute)?60:curSecond+1):60;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<secondsLen;second++){
seconds.push(this.formatNum(second));
}
return{
months,
days,
hours,
minutes,
seconds
}
},
isLeapYear (Year) {
if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0)) {
return true;
} else {
return false;
}
},
getData(dVal){
//用来处理初始化数据
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
//颗粒度禁用当前之后日期仅对year,month,day,hour生效;分钟秒禁用没有意义,
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let hour=dVal[3]*1;
let minute=dVal[4]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let hoursLen=disabledAfter?((year<curYear||month<curMonth||day<curDay)?24:curHour+1):24;
let minutesLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour)?60:curMinute+1):60;
let secondsLen=disabledAfter?((year<curYear||month<curMonth||day<curDay||hour<curHour||minute<curMinute)?60:curSecond+1):60;
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
for(let hour=0;hour<hoursLen;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<minutesLen;minute++){
minutes.push(this.formatNum(minute));
}
// for(let second=0;second<(disabledAfter?curDate.curSecond+1:60);second++){
// seconds.push(this.formatNum(second));
// }
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return {
years,
months,
days,
hours,
minutes,
seconds
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curMinute,
curSecond
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/01");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,hour,minute,second]
}else{
switch(this.fields){
case "year":
dVal=value?[value]:[];
break;
case "month":
dVal=value?value.split("-"):[];
break;
case "day":
dVal=value?value.split("-"):[];
break;
case "hour":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
case "minute":
dVal=value?[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")]:[];
break;
case "second":
dVal=[...value.split(" ")[0].split("-"),...value.split(" ")[1].split(":")];
break;
}
}
}else{
dVal=[year,month,day,hour,minute,second]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],hours=[],minutes=[],seconds=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,hour,minute,second,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
hours=dateData.hours;
minutes=dateData.minutes;
seconds=dateData.seconds;
switch(this.fields){
case "year":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]:(curFlag?[
years.indexOf(curYear+'')
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0
]);
range={years};
year=dVal[0]?dVal[0]:years[0];
result=full=`${year}`;
obj={
year
}
break;
case "month":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth))
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0
]);
range={years,months};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
result=full=`${year+'-'+month}`;
obj={
year,
month
}
break;
case "day":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0
]);
range={years,months,days};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
result=full=`${year+'-'+month+'-'+day}`;
obj={
year,
month,
day
}
break;
case "hour":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0
]);
range={years,months,days,hours};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
obj={
year,
month,
day,
hour
}
break;
case "minute":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0
]);
range={years,months,days,hours,minutes};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
obj={
year,
month,
day,
hour,
minute
}
break;
case "second":
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
hours.indexOf(this.formatNum(curHour)),
minutes.indexOf(this.formatNum(curMinute)),
seconds.indexOf(this.formatNum(curSecond)),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&hours.indexOf(dVal[3])!=-1?hours.indexOf(dVal[3]):0,
dVal[4]&&minutes.indexOf(dVal[4])!=-1?minutes.indexOf(dVal[4]):0,
dVal[5]&&seconds.indexOf(dVal[5])!=-1?seconds.indexOf(dVal[5]):0
]);
range={years,months,days,hours,minutes,seconds};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
hour=dVal[3]?dVal[3]:hours[0];
minute=dVal[4]?dVal[4]:minutes[0];
second=dVal[5]?dVal[5]:seconds[0];
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
default:
range={years,months,days};
break;
}
this.range=range;
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
});
this.$nextTick(()=>{
this.pickVal=pickVal;
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",hour="",minute="",second="";
let result="",full="",obj={};
let months=null,days=null,hours=null,minutes=null,seconds=null;
let disabledAfter=this.disabledAfter;
let leapYear=false,resetData={};
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
hour=(arr[3]||arr[3]==0)?data.hours[arr[3]]||data.hours[data.hours.length-1]:"";
minute=(arr[4]||arr[4]==0)?data.minutes[arr[4]]||data.minutes[data.minutes.length-1]:"";
second=(arr[5]||arr[5]==0)?data.seconds[arr[5]]||data.seconds[data.seconds.length-1]:"";
resetData=this.resetData(year,month,day,hour,minute);//重新拉取当前日期数据;
leapYear=this.isLeapYear(year);//判断是否为闰年;
switch(this.fields){
case "year":
result=full=`${year}`;
obj={
year
};
break;
case "month":
result=full=`${year+'-'+month}`;
if(this.disabledAfter)months=resetData.months;
if(months)this.range.months=months;
obj={
year,
month
}
break;
case "day":
result=full=`${year+'-'+month+'-'+day}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
obj={
year,
month,
day
}
break;
case "hour":
result=`${year+'-'+month+'-'+day+' '+hour}`;
full=`${year+'-'+month+'-'+day+' '+hour+':00:00'}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
obj={
year,
month,
day,
hour
}
break;
case "minute":
full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':00'}`;
result=`${year+'-'+month+'-'+day+' '+hour+':'+minute}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
obj={
year,
month,
day,
hour,
minute
};
break;
case "second":
result=full=`${year+'-'+month+'-'+day+' '+hour+':'+minute+':'+second}`;
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
hours=resetData.hours;
minutes=resetData.minutes;
//seconds=resetData.seconds;
}else{
if(leapYear||(month!=this.checkObj.month)||month==2){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(hours)this.range.hours=hours;
if(minutes)this.range.minutes=minutes;
//if(seconds)this.range.seconds=seconds;
obj={
year,
month,
day,
hour,
minute,
second
}
break;
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,345 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.years" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.months" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.days" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.sections" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
startYear:{
type:String,
default:""
},
endYear:{
type:String,
default:""
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
disabledAfter:{//是否禁用当前之后的日期
type:Boolean,
default:false
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2} [\u4e00-\u9fa5]{2}$/,example;
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let months=[],days=[],sections=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
let daysLen=disabledAfter?((year*1<curYear||month*1<curMonth)?totalDays:curDay):totalDays;
let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
sections=["上午","下午"];
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
if(sectionFlag){
sections=["上午"];
}
return{
months,
days,
sections
}
},
getData(dVal){
//用来处理初始化数据
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonthdays=curDate.curMonthdays;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let defaultDate=this.getDefaultDate();
let startYear=this.getStartDate().getFullYear();
let endYear=this.getEndDate().getFullYear();
let years=[],months=[],days=[],sections=[];
let year=dVal[0]*1;
let month=dVal[1]*1;
let day=dVal[2]*1;
let monthsLen=disabledAfter?(year<curYear?12:curDate.curMonth):12;
let daysLen=disabledAfter?((year<curYear||month<curMonth)?defaultDate.defaultDays:curDay):(curFlag?curMonthdays:defaultDate.defaultDays);
let sectionFlag=disabledAfter?((year*1<curYear||month*1<curMonth||day*1<curDay)==true?false:true):(curHour>12==true?true:false);
for(let year=startYear;year<=(disabledAfter?curYear:endYear);year++){
years.push(year.toString())
}
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
}
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
if(sectionFlag){
sections=["下午"];
}else{
sections=["上午","下午"];
}
return {
years,
months,
days,
sections
}
},
getCurrenDate(){
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth()+1;
let curMonthdays=new Date(curYear,curMonth,0).getDate();
let curDay=curDate.getDate();
let curHour=curDate.getHours();
let curSection="上午";
if(curHour>=12){
curSection="下午";
}
return{
curDate,
curYear,
curMonth,
curMonthdays,
curDay,
curHour,
curSection
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.split(" ")[0].replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getStartDate(){
let start=this.startYear;
let startDate="";
let reg=/-/g;
if(start){
startDate=new Date(start+"/01/01");
}else{
startDate=new Date("1970/01/01");
}
return startDate;
},
getEndDate(){
let end=this.endYear;
let reg=/-/g;
let endDate="";
if(end){
endDate=new Date(end+"/12/31");
}else{
endDate=new Date();
}
return endDate;
},
getDval(){
let value=this.value;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let hour=aDate.getHours();
let section="上午";
if(hour>=12)section="下午";
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[year,month,day,section]
}else{
let v=value.split(" ");
dVal=[...v[0].split("-"),v[1]];
}
}else{
dVal=[year,month,day,section]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let years=[],months=[],days=[],sections=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",year,month,day,section,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let curDate=this.getCurrenDate();
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curMonthdays=curDate.curMonthdays;
let curDay=curDate.curDay;
let curSection=curDate.curSection;
let dateData=[];
dVal=this.getDval();
startDate=this.getStartDate();
endDate=this.getEndDate();
startYear=startDate.getFullYear();
startMonth=startDate.getMonth();
startDay=startDate.getDate();
endYear=endDate.getFullYear();
endMonth=endDate.getMonth();
endDay=endDate.getDate();
dateData=this.getData(dVal);
years=dateData.years;
months=dateData.months;
days=dateData.days;
sections=dateData.sections;
pickVal=disabledAfter?[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
]:(curFlag?[
years.indexOf(curYear+''),
months.indexOf(this.formatNum(curMonth)),
days.indexOf(this.formatNum(curDay)),
sections.indexOf(curSection),
]:[
dVal[0]&&years.indexOf(dVal[0])!=-1?years.indexOf(dVal[0]):0,
dVal[1]&&months.indexOf(dVal[1])!=-1?months.indexOf(dVal[1]):0,
dVal[2]&&days.indexOf(dVal[2])!=-1?days.indexOf(dVal[2]):0,
dVal[3]&&sections.indexOf(dVal[3])!=-1?sections.indexOf(dVal[3]):0
]);
range={years,months,days,sections};
year=dVal[0]?dVal[0]:years[0];
month=dVal[1]?dVal[1]:months[0];
day=dVal[2]?dVal[2]:days[0];
section=dVal[3]?dVal[3]:sections[0];
result=full=`${year+'-'+month+'-'+day+' '+section}`;
obj={
year,
month,
day,
section
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let year="",month="",day="",section="";
let result="",full="",obj={};
let months=null,days=null,sections=null;
let disabledAfter=this.disabledAfter;
year=(arr[0]||arr[0]==0)?data.years[arr[0]]||data.years[data.years.length-1]:"";
month=(arr[1]||arr[1]==0)?data.months[arr[1]]||data.months[data.months.length-1]:"";
day=(arr[2]||arr[2]==0)?data.days[arr[2]]||data.days[data.days.length-1]:"";
section=(arr[3]||arr[3]==0)?data.sections[arr[3]]||data.sections[data.sections.length-1]:"";
result=full=`${year+'-'+month+'-'+day+' '+section}`;
let resetData=this.resetData(year,month,day);
if(this.disabledAfter){
months=resetData.months;
days=resetData.days;
sections=resetData.sections;
}else{
if(year%4==0||(month!=this.checkObj.month)){
days=resetData.days;
}
}
if(months)this.range.months=months;
if(days)this.range.days=days;
if(sections)this.range.sections=sections;
obj={
year,
month,
day,
section
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,274 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column v-for="(group,gIndex) in range" :key="gIndex">
<view class="w-picker-item" v-for="(item,index) in group" :key="index">{{item[nodeKey]}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:[],
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[Array,String],
default:""
},
defaultType:{
type:String,
default:"label"
},
options:{
type:Array,
default(){
return []
}
},
defaultProps:{
type:Object,
default(){
return{
lable:"label",
value:"value",
children:"children"
}
}
},
level:{
//多级联动层级,表示几级联动
type:[Number,String],
default:2
}
},
computed:{
nodeKey(){
return this.defaultProps.label;
},
nodeVal(){
return this.defaultProps.value;
},
nodeChild(){
return this.defaultProps.children;
}
},
watch:{
value(val){
if(this.options.length!=0){
this.initData();
}
},
options(val){
this.initData();
}
},
created() {
if(this.options.length!=0){
this.initData();
}
},
methods:{
getData(){
//用来处理初始化数据
let options=this.options;
let col1={},col2={},col3={},col4={};
let arr1=options,arr2=[],arr3=[],arr4=[];
let col1Index=0,col2Index=0,col3Index=0,col4Index=0;
let a1="",a2="",a3="",a4="";
let dVal=[],obj={};
let value=this.value;
let data=[];
a1=value[0];
a2=value[1];
if(this.level>2){
a3=value[2];
}
if(this.level>3){
a4=value[3];
};
/*第1列*/
col1Index=arr1.findIndex((v)=>{
return v[this.defaultType]==a1
});
col1Index=value?(col1Index!=-1?col1Index:0):0;
col1=arr1[col1Index];
/*第2列*/
arr2=arr1[col1Index][this.nodeChild];
col2Index=arr2.findIndex((v)=>{
return v[this.defaultType]==a2
});
col2Index=value?(col2Index!=-1?col2Index:0):0;
col2=arr2[col2Index];
/*第3列*/
if(this.level>2){
arr3=arr2[col2Index][this.nodeChild];
col3Index=arr3.findIndex((v)=>{
return v[this.defaultType]==a3;
});
col3Index=value?(col3Index!=-1?col3Index:0):0;
col3=arr3[col3Index];
};
/*第4列*/
if(this.level>3){
arr4=arr3[col4Index][this.nodeChild];
col4Index=arr4.findIndex((v)=>{
return v[this.defaultType]==a4;
});
col4Index=value?(col4Index!=-1?col4Index:0):0;
col4=arr4[col4Index];
};
switch(this.level*1){
case 2:
dVal=[col1Index,col2Index];
obj={
col1,
col2
}
data=[arr1,arr2];
break;
case 3:
dVal=[col1Index,col2Index,col3Index];
obj={
col1,
col2,
col3
}
data=[arr1,arr2,arr3];
break;
case 4:
dVal=[col1Index,col2Index,col3Index,col4Index];
obj={
col1,
col2,
col3,
col4
}
data=[arr1,arr2,arr3,arr4];
break
}
return {
data,
dVal,
obj
}
},
initData(){
let dataData=this.getData();
let data=dataData.data;
let arr1=data[0];
let arr2=data[1];
let arr3=data[2]||[];
let arr4=data[3]||[];
let obj=dataData.obj;
let col1=obj.col1,col2=obj.col2,col3=obj.col3||{},col4=obj.col4||{};
let result="",value=[];
let range=[];
switch(this.level){
case 2:
value=[col1[this.nodeVal],col2[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]}`;
range=[arr1,arr2];
break;
case 3:
value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]}`;
range=[arr1,arr2,arr3];
break;
case 4:
value=[col1[this.nodeVal],col2[this.nodeVal],col3[this.nodeVal],col4[this.nodeVal]];
result=`${col1[this.nodeKey]+col2[this.nodeKey]+col3[this.nodeKey]+col4[this.nodeKey]}`;
range=[arr1,arr2,arr3,arr4];
break;
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=dataData.dVal;
});
this.$emit("change",{
result:result,
value:value,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let col1Index=arr[0],col2Index=arr[1],col3Index=arr[2]||0,col4Index=arr[3]||0;
let arr1=[],arr2=[],arr3=[],arr4=[];
let col1,col2,col3,col4,obj={};
let result="",value=[];
arr1=this.options;
arr2=(arr1[col1Index]&&arr1[col1Index][this.nodeChild])||arr1[arr1.length-1][this.nodeChild]||[];
col1=arr1[col1Index]||arr1[arr1.length-1]||{};
col2=arr2[col2Index]||arr2[arr2.length-1]||{};
if(this.level>2){
arr3=(arr2[col2Index]&&arr2[col2Index][this.nodeChild])||arr2[arr2.length-1][this.nodeChild];
col3=arr3[col3Index]||arr3[arr3.length-1]||{};
}
if(this.level>3){
arr4=(arr3[col3Index]&&arr3[col3Index][this.nodeChild])||arr3[arr3.length-1][this.nodeChild]||[];
col4=arr4[col4Index]||arr4[arr4.length-1]||{};
}
switch(this.level){
case 2:
obj={
col1,
col2
}
this.range=[arr1,arr2];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||''];
break;
case 3:
obj={
col1,
col2,
col3
}
this.range=[arr1,arr2,arr3];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||''];
break;
case 4:
obj={
col1,
col2,
col3,
col4
}
this.range=[arr1,arr2,arr3,arr4];
result=`${(col1[this.nodeKey]||'')+(col2[this.nodeKey]||'')+(col3[this.nodeKey]||'')+(col4[this.nodeKey]||'')}`;
value=[col1[this.nodeVal]||'',col2[this.nodeVal]||'',col3[this.nodeVal]||'',col4[this.nodeVal]||''];
break;
}
this.checkObj=obj;
this.pickVal=arr;
this.$emit("change",{
result:result,
value:value,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,344 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fyears" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fmonths" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.fdays" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex1">
<view class="w-picker-item">-</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tyears" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tmonths" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column class="w-picker-flex2">
<view class="w-picker-item" v-for="(item,index) in range.tdays" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array],
default(){
return []
}
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2}$/,example="2020-04-03";
if(!strReg.test(value[0])||!strReg.test(value[1])){
console.log(new Error("请传入与mode匹配的value值["+example+","+example+"]"))
}
return strReg.test(value[0])&&strReg.test(value[1]);
},
resetToData(fmonth,fday,tyear,tmonth){
let range=this.range;
let tmonths=[],tdays=[];
let yearFlag=tyear!=range.tyears[0];
let monthFlag=tyear!=range.tyears[0]||tmonth!=range.tmonths[0];
let ttotal=new Date(tyear,tmonth,0).getDate();
for(let i=yearFlag?1:fmonth*1;i<=12;i++){
tmonths.push(this.formatNum(i))
}
for(let i=monthFlag?1:fday*1;i<=ttotal;i++){
tdays.push(this.formatNum(i))
}
return{
tmonths,
tdays
}
},
resetData(fyear,fmonth,fday,tyear,tmonth){
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
let startYear=this.startYear;
let endYear=this.endYear;
let ftotal=new Date(fyear,fmonth,0).getDate();
let ttotal=new Date(tyear,tmonth,0).getDate();
for(let i=startYear*1;i<=endYear;i++){
fyears.push(this.formatNum(i))
}
for(let i=1;i<=12;i++){
fmonths.push(this.formatNum(i))
}
for(let i=1;i<=ftotal;i++){
fdays.push(this.formatNum(i))
}
for(let i=fyear*1;i<=endYear;i++){
tyears.push(this.formatNum(i))
}
for(let i=fmonth*1;i<=12;i++){
tmonths.push(this.formatNum(i))
}
for(let i=fday*1;i<=ttotal;i++){
tdays.push(this.formatNum(i))
}
return {
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays
}
},
getData(dVal){
let start=this.startYear*1;
let end=this.endYear*1;
let value=dVal;
let flag=this.current;
let aToday=new Date();
let tYear,tMonth,tDay,tHours,tMinutes,tSeconds,pickVal=[];
let initstartDate=new Date(start.toString());
let endDate=new Date(end.toString());
if(start>end){
initstartDate=new Date(end.toString());
endDate=new Date(start.toString());
};
let startYear=initstartDate.getFullYear();
let startMonth=initstartDate.getMonth()+1;
let endYear=endDate.getFullYear();
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[],returnArr=[],startDVal=[],endDVal=[];
let curMonth=flag?value[1]*1:(startDVal[1]*1+1);
let curMonth1=flag?value[5][1]*1:(value[5]*1+1);
let totalDays=new Date(value[0],value[1],0).getDate();
let totalDays1=new Date(value[4],value[5],0).getDate();
for(let s=startYear;s<=endYear;s++){
fyears.push(this.formatNum(s));
};
for(let m=1;m<=12;m++){
fmonths.push(this.formatNum(m));
};
for(let d=1;d<=totalDays;d++){
fdays.push(this.formatNum(d));
};
for(let s=value[0]*1;s<=endYear;s++){
tyears.push(this.formatNum(s));
};
if(value[4]*1>value[0]*1){
for(let m=1;m<=12;m++){
tmonths.push(this.formatNum(m));
};
for(let d=1;d<=totalDays1;d++){
tdays.push(this.formatNum(d));
};
}else{
for(let m=value[1]*1;m<=12;m++){
tmonths.push(this.formatNum(m));
};
for(let d=value[2]*1;d<=totalDays1;d++){
tdays.push(this.formatNum(d));
};
};
pickVal=[
fyears.indexOf(value[0])==-1?0:fyears.indexOf(value[0]),
fmonths.indexOf(value[1])==-1?0:fmonths.indexOf(value[1]),
fdays.indexOf(value[2])==-1?0:fdays.indexOf(value[2]),
0,
tyears.indexOf(value[4])==-1?0:tyears.indexOf(value[4]),
tmonths.indexOf(value[5])==-1?0:tmonths.indexOf(value[5]),
tdays.indexOf(value[6])==-1?0:tdays.indexOf(value[6])
];
return {
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays,
pickVal
}
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let fyear=this.formatNum(aDate.getFullYear());
let fmonth=this.formatNum(aDate.getMonth()+1);
let fday=this.formatNum(aDate.getDate());
let tyear=this.formatNum(aDate.getFullYear());
let tmonth=this.formatNum(aDate.getMonth()+1);
let tday=this.formatNum(aDate.getDate());
if(value&&value.length>0){
let flag=this.checkValue(value);
if(!flag){
dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
}else{
dVal=[...value[0].split("-"),"-",...value[1].split("-")];
}
}else{
dVal=[fyear,fmonth,fday,"-",tyear,tmonth,tday]
}
return dVal;
},
initData(){
let range=[],pickVal=[];
let result="",full="",obj={};
let dVal=this.getDval();
let dateData=this.getData(dVal);
let fyears=[],fmonths=[],fdays=[],tyears=[],tmonths=[],tdays=[];
let fyear,fmonth,fday,tyear,tmonth,tday;
pickVal=dateData.pickVal;
fyears=dateData.fyears;
fmonths=dateData.fmonths;
fdays=dateData.fdays;
tyears=dateData.tyears;
tmonths=dateData.tmonths;
tdays=dateData.tdays;
range={
fyears,
fmonths,
fdays,
tyears,
tmonths,
tdays,
}
fyear=range.fyears[pickVal[0]];
fmonth=range.fmonths[pickVal[1]];
fday=range.fdays[pickVal[2]];
tyear=range.tyears[pickVal[4]];
tmonth=range.tmonths[pickVal[5]];
tday=range.tdays[pickVal[6]];
obj={
fyear,
fmonth,
fday,
tyear,
tmonth,
tday
}
result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:result.split("至"),
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let result="",full="",obj={};
let year="",month="",day="",hour="",minute="",second="",note=[],province,city,area;
let checkObj=this.checkObj;
let days=[],months=[],endYears=[],endMonths=[],endDays=[],startDays=[];
let mode=this.mode;
let col1,col2,col3,d,a,h,m;
let xDate=new Date().getTime();
let range=this.range;
let fyear=range.fyears[arr[0]]||range.fyears[range.fyears.length-1];
let fmonth=range.fmonths[arr[1]]||range.fmonths[range.fmonths.length-1];
let fday=range.fdays[arr[2]]||range.fdays[range.fdays.length-1];
let tyear=range.tyears[arr[4]]||range.tyears[range.tyears.length-1];
let tmonth=range.tmonths[arr[5]]||range.tmonths[range.tmonths.length-1];
let tday=range.tdays[arr[6]]||range.tdays[range.tdays.length-1];
let resetData=this.resetData(fyear,fmonth,fday,tyear,tmonth);
if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth||fday!=checkObj.fday){
arr[4]=0;
arr[5]=0;
arr[6]=0;
range.tyears=resetData.tyears;
range.tmonths=resetData.tmonths;
range.tdays=resetData.tdays;
tyear=range.tyears[0];
checkObj.tyears=range.tyears[0];
tmonth=range.tmonths[0];
checkObj.tmonths=range.tmonths[0];
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
}
if(fyear!=checkObj.fyear||fmonth!=checkObj.fmonth){
range.fdays=resetData.fdays;
};
if(tyear!=checkObj.tyear){
arr[5]=0;
arr[6]=0;
let toData=this.resetToData(fmonth,fday,tyear,tmonth);
range.tmonths=toData.tmonths;
range.tdays=toData.tdays;
tmonth=range.tmonths[0];
checkObj.tmonths=range.tmonths[0];
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
};
if(tmonth!=checkObj.tmonth){
arr[6]=0;
let toData=this.resetToData(fmonth,fday,tyear,tmonth);
range.tdays=toData.tdays;
tday=range.tdays[0];
checkObj.tdays=range.tdays[0];
};
result=`${fyear+'-'+fmonth+'-'+fday+'至'+tyear+'-'+tmonth+'-'+tday}`;
obj={
fyear,fmonth,fday,tyear,tmonth,tday
}
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=arr;
})
this.$emit("change",{
result:result,
value:result.split("至"),
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,183 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.provinces" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.citys" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column v-if="!hideArea">
<view class="w-picker-item" v-for="(item,index) in range.areas" :key="index">{{item.label}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
import areaData from "./areadata/areadata.js"
export default {
data() {
return {
pickVal:[],
range:{
provinces:[],
citys:[],
areas:[]
},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[Array,String],
default:""
},
defaultType:{
type:String,
default:"label"
},
hideArea:{
type:Boolean,
default:false
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
getData(){
//用来处理初始化数据
let provinces=areaData;
let dVal=[];
let value=this.value;
let a1=value[0];//默认值省
let a2=value[1];//默认值市
let a3=value[2];//默认值区、县
let province,city,area;
let provinceIndex=provinces.findIndex((v)=>{
return v[this.defaultType]==a1
});
provinceIndex=value?(provinceIndex!=-1?provinceIndex:0):0;
let citys=provinces[provinceIndex].children;
let cityIndex=citys.findIndex((v)=>{
return v[this.defaultType]==a2
});
cityIndex=value?(cityIndex!=-1?cityIndex:0):0;
let areas=citys[cityIndex].children;
let areaIndex=areas.findIndex((v)=>{
return v[this.defaultType]==a3;
});
areaIndex=value?(areaIndex!=-1?areaIndex:0):0;
dVal=this.hideArea?[provinceIndex,cityIndex]:[provinceIndex,cityIndex,areaIndex];
province=provinces[provinceIndex];
city=citys[cityIndex];
area=areas[areaIndex];
let obj=this.hideArea?{
province,
city
}:{
province,
city,
area
}
return this.hideArea?{
provinces,
citys,
dVal,
obj
}:{
provinces,
citys,
areas,
dVal,
obj
}
},
initData(){
let dataData=this.getData();
let provinces=dataData.provinces;
let citys=dataData.citys;
let areas=this.hideArea?[]:dataData.areas;
let obj=dataData.obj;
let province=obj.province,city=obj.city,area=this.hideArea?{}:obj.area;
let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
this.range=this.hideArea?{
provinces,
citys,
}:{
provinces,
citys,
areas
};
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=dataData.dVal;
});
this.$emit("change",{
result:result,
value:value,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let provinceIndex=arr[0],cityIndex=arr[1],areaIndex=this.hideArea?0:arr[2];
let provinces=areaData;
let citys=(provinces[provinceIndex]&&provinces[provinceIndex].children)||provinces[provinces.length-1].children||[];
let areas=this.hideArea?[]:((citys[cityIndex]&&citys[cityIndex].children)||citys[citys.length-1].children||[]);
let province=provinces[provinceIndex]||provinces[provinces.length-1],
city=citys[cityIndex]||[citys.length-1],
area=this.hideArea?{}:(areas[areaIndex]||[areas.length-1]);
let obj=this.hideArea?{
province,
city
}:{
province,
city,
area
}
if(this.checkObj.province.label!=province.label){
//当省更新的时候需要刷新市、区县的数据;
this.range.citys=citys;
if(!this.hideArea){
this.range.areas=areas;
}
}
if(this.checkObj.city.label!=city.label){
//当市更新的时候需要刷新区县的数据;
if(!this.hideArea){
this.range.areas=areas;
}
}
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=arr;
})
let result=this.hideArea?`${province.label+city.label}`:`${province.label+city.label+area.label}`;
let value=this.hideArea?[province.value,city.value]:[province.value,city.value,area.value];
this.$emit("change",{
result:result,
value:value,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,129 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range" :key="index">{{item[nodeKey]}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
props:{
itemHeight:{
type:String,
default:"44px"
},
options:{
type:[Array,Object],
default(){
return []
}
},
value:{
type:String,
default:""
},
defaultType:{
type:String,
default:"label"
},
defaultProps:{
type:Object,
default(){
return{
label:"label",
value:"value"
}
}
}
},
data() {
return {
pickVal:[]
};
},
computed:{
nodeKey(){
return this.defaultProps.label;
},
nodeValue(){
return this.defaultProps.value;
},
range(){
return this.options
}
},
watch:{
value(val){
if(this.options.length!=0){
this.initData();
}
},
options(val){
this.initData();
}
},
created() {
if(this.options.length!=0){
this.initData();
}
},
methods:{
initData(){
let dVal=this.value||"";
let data=this.range;
let pickVal=[0];
let cur=null;
let label="";
let value,idx;
if(this.defaultType==this.nodeValue){
value=data.find((v)=>v[this.nodeValue]==dVal);
idx=data.findIndex((v)=>v[this.nodeValue]==dVal);
}else{
value=data.find((v)=>v[this.nodeKey]==dVal);
idx=data.findIndex((v)=>v[this.nodeKey]==dVal);
}
pickVal=[idx!=-1?idx:0];
this.$nextTick(()=>{
this.pickVal=pickVal;
});
if(this.defaultType==this.nodeValue){
this.$emit("change",{
result:value?value[this.nodeKey]:data[0][this.nodeKey],
value:dVal||data[0][this.nodeKey],
obj:value?value:data[0]
})
}else{
this.$emit("change",{
result:dVal||data[0][this.nodeKey],
value:value?value[this.nodeValue]:data[0][this.nodeValue],
obj:value?value:data[0]
})
}
},
handlerChange(e){
let arr=[...e.detail.value];
let pickVal=[arr[0]||0];
let data=this.range;
let cur=data[arr[0]];
let label="";
let value="";
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:cur[this.nodeKey],
value:cur[this.nodeValue],
obj:cur
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,250 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.dates" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item.label}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item.label}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
expand:{
type:[Number,String],
default:30
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/,example="2019-12-12 18:05:00或者2019-12-12 18:05";
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curYear=curDate.curYear;
let curMonth=curDate.curMonth;
let curDay=curDate.curDay;
let curHour=curDate.curHour;
let months=[],days=[],sections=[];
let disabledAfter=this.disabledAfter;
let monthsLen=disabledAfter?(year*1<curYear?12:curMonth):12;
let totalDays=new Date(year,month,0).getDate();//计算当月有几天;
for(let month=1;month<=monthsLen;month++){
months.push(this.formatNum(month));
};
for(let day=1;day<=daysLen;day++){
days.push(this.formatNum(day));
}
return{
months,
days,
sections
}
},
getData(dVal){
//用来处理初始化数据
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let dates=[],hours=[],minutes=[];
let curDate=new Date();
let curYear=curDate.getFullYear();
let curMonth=curDate.getMonth();
let curDay=curDate.getDate();
let aDate=new Date(curYear,curMonth,curDay);
for(let i=0;i<this.expand*1;i++){
aDate=new Date(curYear,curMonth,curDay+i);
let year=aDate.getFullYear();
let month=aDate.getMonth()+1;
let day=aDate.getDate();
let label=year+"-"+this.formatNum(month)+"-"+this.formatNum(day);
switch(i){
case 0:
label="今天";
break;
case 1:
label="明天";
break;
case 2:
label="后天";
break
}
dates.push({
label:label,
value:year+"-"+this.formatNum(month)+"-"+this.formatNum(day)
})
};
for(let i=0;i<24;i++){
hours.push({
label:this.formatNum(i),
value:this.formatNum(i)
})
}
for(let i=0;i<60;i++){
minutes.push({
label:this.formatNum(i),
value:this.formatNum(i)
})
}
return {
dates,
hours,
minutes
}
},
getDefaultDate(){
let value=this.value;
let reg=/-/g;
let defaultDate=value?new Date(value.replace(reg,"/")):new Date();
let defaultYear=defaultDate.getFullYear();
let defaultMonth=defaultDate.getMonth()+1;
let defaultDay=defaultDate.getDate();
let defaultDays=new Date(defaultYear,defaultMonth,0).getDate()*1;
return{
defaultDate,
defaultYear,
defaultMonth,
defaultDay,
defaultDays
}
},
getDval(){
let value=this.value;
let dVal=null;
let aDate=new Date();
let year=this.formatNum(aDate.getFullYear());
let month=this.formatNum(aDate.getMonth()+1);
let day=this.formatNum(aDate.getDate());
let date=this.formatNum(year)+"-"+this.formatNum(month)+"-"+this.formatNum(day);
let hour=aDate.getHours();
let minute=aDate.getMinutes();
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[date,hour,minute]
}else{
let v=value.split(" ");
dVal=[v[0],...v[1].split(":")];
}
}else{
dVal=[date,hour,minute]
}
return dVal;
},
initData(){
let startDate,endDate,startYear,endYear,startMonth,endMonth,startDay,endDay;
let dates=[],hours=[],minutes=[];
let dVal=[],pickVal=[];
let value=this.value;
let reg=/-/g;
let range={};
let result="",full="",date,hour,minute,obj={};
let defaultDate=this.getDefaultDate();
let defaultYear=defaultDate.defaultYear;
let defaultMonth=defaultDate.defaultMonth;
let defaultDay=defaultDate.defaultDay;
let defaultDays=defaultDate.defaultDays;
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let dateData=[];
dVal=this.getDval();
dateData=this.getData(dVal);
dates=dateData.dates;
hours=dateData.hours;
minutes=dateData.minutes;
pickVal=[
dates.findIndex(n => n.value == dVal[0])!=-1?dates.findIndex(n => n.value == dVal[0]):0,
hours.findIndex(n => n.value == dVal[1])!=-1?hours.findIndex(n => n.value == dVal[1]):0,
minutes.findIndex(n => n.value == dVal[2])!=-1?minutes.findIndex(n => n.value == dVal[2]):0,
];
range={dates,hours,minutes};
date=dVal[0]?dVal[0]:dates[0].label;
hour=dVal[1]?dVal[1]:hours[0].label;
minute=dVal[2]?dVal[2]:minutes[0].label;
result=full=`${date+' '+hour+':'+minute}`;
obj={
date,
hour,
minute
}
this.range=range;
this.checkObj=obj;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let date="",hour="",minute="";
let result="",full="",obj={};
let disabledAfter=this.disabledAfter;
date=(arr[0]||arr[0]==0)?data.dates[arr[0]]||data.dates[data.dates.length-1]:"";
hour=(arr[1]||arr[1]==0)?data.hours[arr[1]]||data.hours[data.hours.length-1]:"";
minute=(arr[2]||arr[2]==0)?data.minutes[arr[2]]||data.minutes[data.minutes.length-1]:"";
result=full=`${date.label+' '+hour.label+':'+minute.label+':00'}`;
obj={
date,
hour,
minute
}
this.checkObj=obj;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,218 @@
<template>
<view class="w-picker-view">
<picker-view class="d-picker-view" :indicator-style="itemHeight" :value="pickVal" @change="handlerChange">
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.hours" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view class="w-picker-item" v-for="(item,index) in range.minutes" :key="index">{{item}}</view>
</picker-view-column>
<picker-view-column v-if="second">
<view class="w-picker-item" v-for="(item,index) in range.seconds" :key="index">{{item}}</view>
</picker-view-column>
</picker-view>
</view>
</template>
<script>
export default {
data() {
return {
pickVal:[],
range:{},
checkObj:{}
};
},
props:{
itemHeight:{
type:String,
default:"44px"
},
value:{
type:[String,Array,Number],
default:""
},
current:{//是否默认选中当前日期
type:Boolean,
default:false
},
second:{
type:Boolean,
default:true
}
},
watch:{
value(val){
this.initData();
}
},
created() {
this.initData();
},
methods:{
formatNum(n){
return (Number(n)<10?'0'+Number(n):Number(n)+'');
},
checkValue(value){
let strReg=/^\d{2}:\d{2}:\d{2}$/,example="18:00:05";
if(!strReg.test(value)){
console.log(new Error("请传入与modefields匹配的value值例value="+example+""))
}
return strReg.test(value);
},
resetData(year,month,day,hour,minute){
let curDate=this.getCurrenDate();
let curFlag=this.current;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
for(let hour=0;hour<24;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<60;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return{
hours,
minutes,
seconds
}
},
getData(curDate){
//用来处理初始化数据
let hours=[],minutes=[],seconds=[];
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let fields=this.fields;
let curHour=curDate.curHour;
let curMinute=curDate.curMinute;
let curSecond=curDate.curSecond;
for(let hour=0;hour<24;hour++){
hours.push(this.formatNum(hour));
}
for(let minute=0;minute<60;minute++){
minutes.push(this.formatNum(minute));
}
for(let second=0;second<60;second++){
seconds.push(this.formatNum(second));
}
return this.second?{
hours,
minutes,
seconds
}:{
hours,
minutes
}
},
getCurrenDate(){
let curDate=new Date();
let curHour=curDate.getHours();
let curMinute=curDate.getMinutes();
let curSecond=curDate.getSeconds();
return this.second?{
curHour,
curMinute,
curSecond
}:{
curHour,
curMinute,
}
},
getDval(){
let value=this.value;
let fields=this.fields;
let dVal=null;
let aDate=new Date();
let hour=this.formatNum(aDate.getHours());
let minute=this.formatNum(aDate.getMinutes());
let second=this.formatNum(aDate.getSeconds());
if(value){
let flag=this.checkValue(value);
if(!flag){
dVal=[hour,minute,second]
}else{
dVal=value?value.split(":"):[];
}
}else{
dVal=this.second?[hour,minute,second]:[hour,minute]
}
return dVal;
},
initData(){
let curDate=this.getCurrenDate();
let dateData=this.getData(curDate);
let pickVal=[],obj={},full="",result="",hour="",minute="",second="";
let dVal=this.getDval();
let curFlag=this.current;
let disabledAfter=this.disabledAfter;
let hours=dateData.hours;
let minutes=dateData.minutes;
let seconds=dateData.seconds;
let defaultArr=this.second?[
dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0,
dVal[2]&&seconds.indexOf(dVal[2])!=-1?seconds.indexOf(dVal[2]):0
]:[
dVal[0]&&hours.indexOf(dVal[0])!=-1?hours.indexOf(dVal[0]):0,
dVal[1]&&minutes.indexOf(dVal[1])!=-1?minutes.indexOf(dVal[1]):0
];
pickVal=disabledAfter?defaultArr:(curFlag?(this.second?[
hours.indexOf(this.formatNum(curDate.curHour)),
minutes.indexOf(this.formatNum(curDate.curMinute)),
seconds.indexOf(this.formatNum(curDate.curSecond)),
]:[
hours.indexOf(this.formatNum(curDate.curHour)),
minutes.indexOf(this.formatNum(curDate.curMinute))
]):defaultArr);
this.range=dateData;
this.checkObj=obj;
hour=dVal[0]?dVal[0]:hours[0];
minute=dVal[1]?dVal[1]:minutes[0];
if(this.second)second=dVal[2]?dVal[0]:seconds[0];
result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
this.$nextTick(()=>{
this.pickVal=pickVal;
});
this.$emit("change",{
result:result,
value:full,
obj:obj
})
},
handlerChange(e){
let arr=[...e.detail.value];
let data=this.range;
let hour="",minute="",second="",result="",full="",obj={};
hour=(arr[0]||arr[0]==0)?data.hours[arr[0]]||data.hours[data.hours.length-1]:"";
minute=(arr[1]||arr[1]==0)?data.minutes[arr[1]]||data.minutes[data.minutes.length-1]:"";
if(this.second)second=(arr[2]||arr[2]==0)?data.seconds[arr[2]]||data.seconds[data.seconds.length-1]:"";
obj=this.second?{
hour,
minute,
second
}:{
hour,
minute
};
this.checkObj=obj;
result=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute}`;
full=this.second?`${hour+':'+minute+':'+second}`:`${hour+':'+minute+':00'}`;
this.$emit("change",{
result:result,
value:full,
obj:obj
})
}
}
}
</script>
<style lang="scss">
@import "./w-picker.css";
</style>

View File

@@ -0,0 +1,26 @@
.w-picker-flex2{
flex:2;
}
.w-picker-flex1{
flex:1;
}
.w-picker-view {
width: 100%;
height: 476upx;
overflow: hidden;
background-color: rgba(255, 255, 255, 1);
z-index: 666;
}
.d-picker-view{
height: 100%;
}
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}

View File

@@ -0,0 +1,340 @@
<template name="w-picker">
<view class="w-picker" :key="createKey" :data-key="createKey">
<view class="mask" :class="{'visible':visible}" @tap="onCancel" @touchmove.stop.prevent catchtouchmove="true"></view>
<view class="w-picker-cnt" :class="{'visible':visible}">
<view class="w-picker-header" @touchmove.stop.prevent catchtouchmove="true">
<text @tap.stop.prevent="onCancel">取消</text>
<slot></slot>
<text :style="{'color':themeColor}" @tap.stop.prevent="pickerConfirm">确定</text>
</view>
<date-picker
v-if="mode=='date'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:fields="fields"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</date-picker>
<range-picker
v-if="mode=='range'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</range-picker>
<half-picker
v-if="mode=='half'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</half-picker>
<shortterm-picker
v-if="mode=='shortTerm'"
class="w-picker-wrapper"
:startYear="startYear"
:endYear="endYear"
:value="value"
:item-height="itemHeight"
:current="current"
expand="60"
:disabled-after="disabledAfter"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</shortterm-picker>
<time-picker
v-if="mode=='time'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:current="current"
:disabled-after="disabledAfter"
:second="second"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</time-picker>
<selector-picker
v-if="mode=='selector'"
class="w-picker-wrapper"
:value="value"
:item-height="itemHeight"
:options="options"
:default-type="defaultType"
:default-props="defaultProps"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</selector-picker>
<region-picker
v-if="mode=='region'"
class="w-picker-wrapper"
:value="value"
:hide-area="hideArea"
:default-type="defaultType"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</region-picker>
<linkage-picker
v-if="mode=='linkage'"
class="w-picker-wrapper"
:value="value"
:options="options"
:level="level"
:default-type="defaultType"
:default-props="defaultProps"
:item-height="itemHeight"
@change="handlerChange"
@touchstart="touchStart"
@touchend="touchEnd">
</linkage-picker>
</view>
</view>
</template>
<script>
import datePicker from "./date-picker.vue"
import rangePicker from "./range-picker.vue"
import halfPicker from "./half-picker.vue"
import shorttermPicker from "./shortterm-picker.vue"
import timePicker from "./time-picker.vue"
import selectorPicker from "./selector-picker.vue"
import regionPicker from "./region-picker.vue"
import linkagePicker from "./linkage-picker.vue"
export default {
name:"w-picker",
components:{
datePicker,
rangePicker,
halfPicker,
timePicker,
selectorPicker,
shorttermPicker,
regionPicker,
linkagePicker
},
props:{
mode:{
type:String,
default:"date"
},
value:{//默认值
type:[String,Array,Number],
default:""
},
current:{//是否默认显示当前时间,如果是,传的默认值将失效
type:Boolean,
default:false
},
themeColor:{//确认按钮主题颜色
type:String,
default:"#f5a200"
},
fields:{//日期颗粒度:year、month、day、hour、minute、second
type:String,
default:"date"
},
disabledAfter:{//是否禁用当前之后的日期
type:Boolean,
default:false
},
second:{//time-picker是否显示秒
type:Boolean,
default:true
},
options:{//selector,region数据源
type:[Array,Object],
default(){
return []
}
},
defaultProps:{//selector,linkagle字段转换配置
type:Object,
default(){
return{
label:"label",
value:"value",
children:"children"
}
}
},
defaultType:{
type:String,
default:"label"
},
hideArea:{//mode=region时是否隐藏区县列
type:Boolean,
default:false
},
level:{
//多级联动层级,表示几级联动,区间2-4;
type:[Number,String],
default:2
},
timeout:{//是否开启点击延迟,当快速滚动 还没有滚动完毕点击关闭时得到的值是不准确的
type:Boolean,
default:false
},
expand:{//mode=shortterm 默认往后拓展天数
type:[Number,String],
default:30
},
startYear:{
type:[String,Number],
default:1970
},
endYear:{
type:[String,Number],
default:new Date().getFullYear()
},
visible:{
type:Boolean,
default:false
}
},
created() {
this.createKey=Math.random()*1000;
},
data() {
return {
itemHeight:`height: ${uni.upx2px(88)}px;`,
result:{},
confirmFlag:true
};
},
methods:{
touchStart(){
if(this.timeout){
this.confirmFlag=false;
}
},
touchEnd(){
if(this.timeout){
setTimeout(()=>{
this.confirmFlag=true;
},500)
}
},
handlerChange(res){
let _this=this;
this.result={...res};
},
show(){
this.$emit("update:visible",true);
},
hide(){
this.$emit("update:visible",false);
},
onCancel(res){
this.$emit("update:visible",false);
this.$emit("cancel");
},
pickerConfirm(){
if(!this.confirmFlag){
return;
};
this.$emit("confirm",this.result);
this.$emit("update:visible",false);
}
}
}
</script>
<style lang="scss">
.w-picker-item {
text-align: center;
width: 100%;
height: 88upx;
line-height: 88upx;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30upx;
}
.w-picker{
z-index: 888;
.mask {
position: fixed;
z-index: 1000;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
visibility: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.mask.visible{
visibility: visible;
opacity: 1;
}
.w-picker-cnt {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
transition: all 0.3s ease;
transform: translateY(100%);
z-index: 3000;
background-color: #fff;
}
.w-picker-cnt.visible {
transform: translateY(0);
}
.w-picker-header{
display: flex;
align-items: center;
padding: 0 30upx;
height: 88upx;
background-color: #fff;
position: relative;
text-align: center;
font-size: 32upx;
justify-content: space-between;
border-bottom: solid 1px #eee;
.w-picker-btn{
font-size: 30upx;
}
}
.w-picker-hd:after {
content: ' ';
position: absolute;
left: 0;
bottom: 0;
right: 0;
height: 1px;
border-bottom: 1px solid #e5e5e5;
color: #e5e5e5;
transform-origin: 0 100%;
transform: scaleY(0.5);
}
}
</style>

View File

@@ -0,0 +1,324 @@
<template>
<view class="apply-pages" v-if="isLoad">
<view class="fill-base mt-md">
<view class="flex-y-center pt-lg pl-lg">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">农场头像</view>
</view>
<view class="flex-between ml-lg mr-lg pb-sm b-1px-b">
<upload @upload="imgUpload" :imagelist="subForm.cover" imgtype="cover" text="添加封面" :imgsize="1">
</upload>
</view>
<view class="flex-between ml-lg mr-lg">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">农场名称</view>
</view>
<input v-model="subForm.title" type="text" class="item-input flex-1" maxlength="40"
placeholder-class="c-placeholder" :placeholder="rule[1].errorMsg" />
</view>
</view>
<view class="fill-base mt-md">
<view class="ml-lg mr-lg b-1px-b">
<view class="flex-y-center pt-lg pb-md">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">农场描述</view>
</view>
<textarea v-model="subForm.desc" type="text" class="item-textarea flex-1" maxlength="200"
placeholder-class="c-placeholder" :placeholder="rule[2].errorMsg"></textarea>
</view>
<view class="flex-y-center pt-lg pl-lg">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">农场详情图</view>
</view>
<view class="flex-between ml-lg mr-lg pb-lg">
<upload @upload="imgUpload" @del="imgUpload" :imagelist="subForm.imgs" imgtype="imgs" text="添加图片"
:imgsize="9">
</upload>
</view>
</view>
<view class="fill-base mt-md">
<view class="flex-between ml-lg mr-lg b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">联系电话</view>
</view>
<input v-model="subForm.mobile" type="text" class="item-input flex-1" placeholder-class="c-placeholder"
:placeholder="rule[4].errorMsg" />
</view>
<view class="flex-between ml-lg mr-lg b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">农场地址</view>
</view>
<view class="item-input text flex-1">
<view @tap.stop="toChooseLocation" class="flex-y-center">
<view class="flex-1" style="line-height: 1.2;"
:class="[{'c-placeholder':!subForm.address},{'c-title':subForm.address}]">
{{subForm.address || `点击右边图标设置`}}
</view><i class="iconfont icon-dingwei ml-sm" :style="{color:primaryColor}"></i>
</view>
</view>
</view>
<view class="flex-between ml-lg mr-lg b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">姓名</view>
</view>
<input v-model="subForm.user_name" type="text" class="item-input flex-1" maxlength="20"
placeholder-class="c-placeholder" :placeholder="rule[6].errorMsg" />
</view>
<view class="flex-between ml-lg mr-lg b-1px-b">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">手机号</view>
</view>
<input v-model="subForm.phone" type="text" class="item-input flex-1" placeholder-class="c-placeholder"
:placeholder="rule[7].errorMsg" />
</view>
<view class="flex-between ml-lg mr-lg">
<view class="flex-y-center">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text">身份证号</view>
</view>
<input v-model="subForm.idcard_code" type="text" class="item-input flex-1"
placeholder-class="c-placeholder" :placeholder="rule[8].errorMsg" />
</view>
</view>
<view class="fill-base mt-md">
<view class="flex-y-center pt-lg pl-lg">
<i class="iconfont icon-required c-warning"></i>
<view class="item-text" style="width: 400rpx;">身份证正反面照片</view>
</view>
<view class="flex-between" style="width: 626rpx;margin: 0 auto;">
<upload @upload="imgUpload" :imagelist="subForm.idcard_imgs" imgtype="idcard_imgs" imgclass="md"
text="身份证正面照" :imgsize="1" bgimg="/static/image/farm/idcard.png"></upload>
<upload @upload="imgUpload" :imagelist="subForm.idcard_imgs_fan" imgtype="idcard_imgs_fan" imgclass="md"
text="身份证反面照" :imgsize="1" bgimg="/static/image/farm/idcard_fan.png"></upload>
</view>
<view class="flex-between pb-lg f-caption c-caption" style="width: 626rpx;margin: 0 auto;">
<view class="flex-center" style="width: 292rpx;">身份证正面照</view>
<view class="flex-center" style="width: 292rpx;">身份证反面照</view>
</view>
</view>
<view class="space-max-footer"></view>
<auth :needAuth="userInfo && !userInfo.nickname" :must="true" @go="submit">
<fix-bottom-button :text="[{text:options.type==1?'保存':'提交申请',type:'confirm'}]" bgColor="#fff">
</fix-bottom-button>
</auth>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
data() {
return {
isLoad: false,
options: {},
subForm: {
id: 0,
cover: [], // 农场头像
title: '', //农场名称
desc: '', // 简介
imgs: [], // 详情
mobile: '', //联系电话
address: '', //农场地址
lng: '',
lat: '',
user_name: '', //用户名称
phone: '', //手机号
idcard_code: '', //身份证号
idcard_imgs: [], //身份证
idcard_imgs_fan: []
},
rule: [{
name: "cover",
checkType: "isNotNull",
errorMsg: "请上传农场头像"
}, {
name: "title",
checkType: "isNotNull",
errorMsg: "请输入您所拥有的农场名称",
regType: 2
}, {
name: "desc",
checkType: "isNotNull",
errorMsg: "请输入农场描述",
regType: 2
}, {
name: "imgs",
checkType: "isNotNull",
errorMsg: "请上传农场详情图"
},
{
name: "mobile",
checkType: "isAllPhone",
errorMsg: "请输入农场联系电话",
regText: "联系电话"
}, {
name: "address",
checkType: "isNotNull",
errorMsg: "请设置农场地址"
}, {
name: "user_name",
checkType: "isNotNull",
errorMsg: "请输入您的姓名",
regType: 2
},
{
name: "phone",
checkType: "isMobile",
errorMsg: "请输入您的手机号"
}, {
name: "idcard_code",
checkType: "isIdCard",
errorMsg: "请输入您的身份证号码"
},
{
name: "idcard_imgs",
checkType: "isNotNull",
errorMsg: "请上传身份证正面照"
},
{
name: "idcard_imgs_fan",
checkType: "isNotNull",
errorMsg: "请上传身份证反面照"
}
],
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
async onLoad(options) {
let {
type = 0
} = options
this.options = options
uni.setNavigationBarTitle({
title: type == 1 ? `农场管理` : `申请农场主`
})
await this.initIndex()
this.isLoad = true
},
methods: {
...mapActions(['getUserInfo']),
...mapMutations(['updateUserItem']),
async initIndex() {
let data = await this.$api.farmer.farmerInfo()
if (!data.id) return
data.cover = [{
path: data.cover
}]
let arr = ['imgs', 'idcard_imgs']
arr.map(item => {
data[item] = data[item].map(aitem => {
return {
path: aitem
}
})
})
data.idcard_imgs_fan = [data.idcard_imgs[1]]
data.idcard_imgs.splice(1, 2)
for (let key in this.subForm) {
this.subForm[key] = data[key]
}
},
imgUpload(e) {
let {
imagelist,
imgtype
} = e;
this.subForm[imgtype] = imagelist;
},
// 选择地区
async toChooseLocation(e) {
await this.$util.checkAuth({
type: 'userLocation'
})
let [, {
address = '',
longitude,
latitude
} = {}] = await uni.chooseLocation();
if (!address) return
this.subForm.address = address
this.subForm.lng = longitude
this.subForm.lat = latitude
},
//表单验证
validate(param) {
let validate = new this.$util.Validate();
this.rule.map(item => {
let {
name,
} = item
validate.add(param[name], item);
})
let message = validate.start();
console.log(message, "message");
return message;
},
async submit() {
let param = this.$util.deepCopy(this.subForm)
let arr = ['cover', 'idcard_imgs', 'idcard_imgs_fan']
arr.map(item => {
param[item] = param[item].length > 0 ? param[item][0].path : ''
})
let msg = this.validate(param);
if (msg) {
this.$util.showToast({
title: msg
})
return
}
param.imgs = param.imgs.map(item => {
return item.path
})
param.idcard_imgs = [param.idcard_imgs, param.idcard_imgs_fan]
delete param.idcard_imgs_fan
if (this.lockTap) return
this.lockTap = true
this.$util.showLoading()
let {
type = 0
} = this.options
let methodModel = type == 1 ? 'farmerUpdate' : 'applyFarmer'
try {
await this.$api.farmer[methodModel](param)
this.$util.hideAll()
this.$util.showToast({
title: type == 1 ? `修改成功` : `提交成功`
});
setTimeout(() => {
this.$util.back()
this.$util.goUrl({
url: 1,
openType: `navigateBack`
})
}, 1000)
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,160 @@
<template>
<view class="farmer-finance-index rel" v-if="isLoad">
<view class="count-info flex-center flex-column c-base" :style="{background:primaryColor}">
<view class="f-caption">可提余额()</view>
<view class="price mt-sm">{{detail.cash}}</view>
<view class="flex-center btn-list f-paragraph mt-lg pt-lg">
<view @tap.stop="$util.goUrl({url:`/mine/pages/cash-out?type=farmer`})"
class="item-child flex-center fill-base c-title mr-lg radius">提现</view>
<view @tap.stop="$util.goUrl({url:`/farmer/pages/finance/record`})"
class="item-child flex-center c-base radius">交易记录</view>
</view>
</view>
<view class="money-count fill-base flex-center pt-lg pb-lg">
<view class="item-child flex-center flex-column b-1px-r">
<view class="flex-y-baseline f-lg-title">{{detail.total_cash}}</view>
<view class="f-caption c-caption">总金额()</view>
</view>
<view class="item-child flex-center flex-column">
<view class="flex-y-baseline f-lg-title">{{detail.frozen_cash}}</view>
<view class="f-caption c-caption">实际冻结金额()</view>
</view>
</view>
<view class="space-md"></view>
<view class="fill-base pd-lg b-1px-b" v-for="(item,index) in list.data" :key="index">
<view class="flex-between">
<view class="max-446 ellipsis">{{item.order_code}}</view>
<view class="f-title text-bold" :style="{color:index==0 ? primaryColor : '#FF5537'}">
{{item.add==1?'+':'-'}} {{item.price}}
</view>
</view>
<view class="flex-between f-caption c-desc mt-sm">
<view class="c-caption">{{item.create_time_text}}</view>
<view>{{item.type_text}}</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from "vuex"
import tabbar from "@/components/tabbar.vue"
export default {
components: {
tabbar
},
data() {
return {
isLoad: false,
detail: {},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
commonOptions: state => state.user.commonOptions,
userInfo: state => state.user.userInfo,
}),
onLoad() {
this.$util.setNavigationBarColor({
color: '#ffffff',
bg: '#39b54a'
});
this.$util.showLoading()
this.initIndex()
},
onUnload() {
this.$util.back()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
methods: {
...mapMutations([]),
async initIndex() {
this.detail = await this.$api.farmer.farmerFinanceInfo()
this.isLoad = true
this.getList()
this.$util.hideAll()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
param
} = this
let newList = await this.$api.farmer.farmerFinanceList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
handerTabChange(index) {
this.activeIndex = index
// this.$util.showLoading()
// this.param.page = 1
// this.list.data = []
// this.getList()
},
}
}
</script>
<style lang="scss">
.farmer-finance-index {
.count-info {
height: 450rpx;
.price {
font-size: 70rpx;
}
.btn-list {
.item-child {
width: 240rpx;
height: 64rpx;
border: 1rpx solid #fff;
transform: rotateZ(360deg);
}
}
}
.money-count {
.item-child {
width: 50%;
}
}
}
</style>

View File

@@ -0,0 +1,278 @@
<template>
<view class="mine-stored-record">
<fixed>
<view class="fill-base b-1px-b">
<view class="pl-lg pr-lg">
<view class="space-lg"></view>
<uni-segmented-control :current="typeInd" :values="typeList"
@clickItem="handerTabChange($event,'typeInd')" style-type="button" :active-color="primaryColor">
</uni-segmented-control>
</view>
<tab @change="handerTabChange($event,'activeIndex')" :list="tabList"
:lockTap="activeIndex==2&&tabList[2].start_time&&tabList[2].end_time ? false : true"
:activeIndex="activeIndex" :activeColor="primaryColor" :width="100/tabList.length + '%'"
height="100rpx"></tab>
</view>
</fixed>
<view class="fill-base pd-lg b-1px-b" v-for="(item,index) in list.data" :key="index">
<view class="flex-between">
<view class="max-446 ellipsis">{{item.type_text}}</view>
<view class="text-bold" :style="{color:primaryColor}">{{item.add==1?'+':'-'}} {{item.price}}</view>
</view>
<view class="flex-between f-caption c-caption mt-sm">{{item.create_time_text}}</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
<uni-popup ref="show_choose_time" type="top" :custom="true" :maskClick="false">
<view style="height: 184rpx;"></view>
<view class="popup-choose-time fill-base f-paragraph c-desc pt-lg pb-lg radius-bottom-34">
<view class="time-item flex-center pt-lg">
<view @tap.stop="toShowTime('start_time')" class="item-child flex-center flex-column">
<view>开始时间</view>
<view class="mt-sm" :style="{color:tabList[2].start_time ? primaryColor : '#999'}">
{{tabList[2].start_time || '选择时间'}}
</view>
</view>
<view @tap.stop="toShowTime('end_time')" class="item-child flex-center flex-column b-1px-l">
<view>结束时间</view>
<view class="mt-sm" :style="{color:tabList[2].end_time ? primaryColor : '#999'}">
{{tabList[2].end_time || '选择时间'}}
</view>
</view>
</view>
<view class="button-item flex-center">
<view @tap.stop="toConfirm(1)" class="item-child disabled flex-center"> 取消 </view>
<view @tap.stop="toConfirm(2)" class="item-child flex-center"
:style="{background: primaryColor,color:'#fff'}"> 确定
</view>
</view>
</view>
</uni-popup>
<w-picker mode="date" :startYear="startYear" :endYear="endYear" :value="tabList[0].end_time" :current="false"
fields="day" @confirm="onConfirm($event)" :disabled-after="false" ref="day" :themeColor="primaryColor"
:visible.sync="showDate">
</w-picker>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
import uniSegmentedControl from "@/components/uni-segmented-control.vue"
import wPicker from "@/components/w-picker/w-picker.vue";
export default {
components: {
uniSegmentedControl,
wPicker
},
data() {
return {
startYear: '',
endYear: '',
showKey: '',
showDate: false,
typeList: [{
id: 1,
title: '收入记录'
}, {
id: 0,
title: '支出记录'
}],
typeInd: 0,
activeIndex: 0,
tabList: [{
id: 1,
title: '本周',
start_time: '',
end_time: ''
}, {
id: 2,
title: '本月',
start_time: '',
end_time: ''
}, {
id: 3,
title: '自定义',
start_time: '',
end_time: ''
}],
preTime: {
start_time: '',
end_time: ''
},
param: {
page: 1,
start_time: '',
end_time: ''
},
list: {
data: []
},
loading: true,
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
}),
onLoad() {
this.$util.showLoading()
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
let cur_time = new Date(Math.ceil(new Date().getTime()))
const currentDate = new Date()
//返回date是一周中的某一天
const week = currentDate.getDay()
//一天的毫秒数
const millisecond = 1000 * 60 * 60 * 24
//减去的天数
const minusDay = week != 0 ? week - 1 : 6
//本周 周一
const monday = new Date(currentDate.getTime() - minusDay * millisecond)
//本周 周日
const sunday = new Date(monday.getTime() + 6 * millisecond)
this.tabList[0].start_time = this.$util.formatTime(monday, 'YY-M-D')
this.tabList[0].end_time = this.$util.formatTime(sunday, 'YY-M-D')
this.tabList[1].start_time = this.$util.formatTime(cur_time, 'YY-M') + '-01'
this.tabList[1].end_time = this.$util.formatTime(cur_time, 'YY-M-D')
let year = this.$util.formatTime(cur_time, 'YY') * 1
this.startYear = year - 5
this.endYear = year
this.param.page = 1
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
activeIndex,
tabList,
typeList,
typeInd
} = this
let {
start_time,
end_time
} = tabList[activeIndex]
let param = this.$util.deepCopy(this.param)
param.add = typeList[typeInd].id
param.start_time = this.$util.DateToUnix(start_time)
param.end_time = this.$util.DateToUnix(end_time) + 24 * 3600 - 1
let newList = await this.$api.farmer.farmerFinanceList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
handerTabChange(index, key, ind = 2) {
this[key] = index;
if (key == 'activeIndex') {
let methodModel = index == ind ? 'open' : 'close'
this.$refs.show_choose_time[methodModel]()
if (index == ind) return
} else {
this.$refs.show_choose_time.close()
}
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
toShowTime(key, ind = 2) {
if (key == 'end_time' && !this.tabList[ind].start_time) {
this.$util.showToast({
title: `请选择开始时间`
})
return
}
this.showKey = key
this.showDate = true
},
async onConfirm(val, ind = 2) {
let {
start_time,
end_time
} = this.tabList[ind]
let {
showKey
} = this
if ((end_time && showKey == 'start_time' && this.$util.DateToUnix(end_time) < this.$util.DateToUnix(val
.result)) || (
showKey == 'end_time' && this.$util.DateToUnix(start_time) > this.$util.DateToUnix(val.result)
)) {
this.$util.showToast({
title: `结束时间不能小于开始时间`
})
return
}
this.tabList[ind][showKey] = val.result;
if (showKey == 'end_time') {
this.showDate = false
}
},
toConfirm(type, ind = 2) {
let {
start_time = '',
end_time = ''
} = type == 1 ? this.preTime : this.tabList[ind]
if (type == 1) {
this.tabList[ind].start_time = start_time
this.tabList[ind].end_time = end_time
} else {
if (!start_time || !end_time) {
this.$util.showToast({
title: !start_time ? `请选择开始时间` : `请选择结束时间`
})
return
}
this.preTime.start_time = start_time
this.preTime.end_time = end_time
}
this.activeIndex = start_time && end_time ? ind : 0
this.$refs.show_choose_time.close()
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,178 @@
<template>
<view class="farmer-order-claim-list">
<fixed>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
:width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<view @tap.stop="goDetail(index)" class="order-item fill-base mg-big pd-lg radius-16 box-shadow"
v-for="(item,index) in list.data" :key="index">
<view class="flex-between pb-lg">
<view class="f-paragraph c-title"
:style="{color:item.pay_type == 1? subColor: item.pay_type == 2? primaryColor: ''}">
{{statusType[item.pay_type]}}
</view>
<i class="iconfont icon-right"></i>
</view>
<view class="flex-center">
<image mode="aspectFill" lazy-load class="avatar radius-24" :src="item.goods_cover"></image>
<view class="flex-1 ml-lg">
<view class="f-title c-title text-bold ellipsis" style="max-width: 498rpx;"> {{item.goods_name}}
</view>
<view class="f-caption c-caption mt-sm"> 数量{{`${item.num}/${item.unit}`}} </view>
<view class="f-caption c-caption"> 配送周期{{item.send_cycle}} </view>
</view>
</view>
<view class="flex-between f-paragraph mt-md pt-md b-1px-t">
<view class="c-desc max-380 ellipsis">认养编号 {{item.claim_code}}</view>
<view class="flex-y-baseline c-title">总计<view class="f-title c-warning">¥{{item.pay_price}}</view>
</view>
</view>
<view class="flex-between mt-md">
<view></view>
<view @tap.stop="toTel(index)" class="common-btn flex-center f-caption c-base radius-4 ml-lg"
:style="{background:primaryColor}">
联系买家
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
activeIndex: 0,
tabList: [{
id: 0,
title: '全部'
}, {
id: 2,
title: '认养中',
number: 0
}, {
id: 3,
title: '配送中',
number: 0
}, {
id: 7,
title: '已完成'
}],
statusType: {
'-1': '已取消',
1: '待支付',
2: '认养中',
3: '配送中',
7: '已完成',
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
let {
tab = 0,
} = options
this.options = options
this.activeIndex = tab
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
param,
tabList,
activeIndex
} = this
param.pay_type = tabList[activeIndex].id
let newList = await this.$api.farmer.claimOrderList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.tabList[1].number = newList.claim_ing
this.tabList[2].number = newList.send_ing
this.$util.hideAll()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
toTel(index) {
let {
mobile: url
} = this.list.data[index].address_info
this.$util.goUrl({
url,
openType: 'call'
})
},
goDetail(index) {
let {
id
} = this.list.data[index]
let url = `/claim/pages/order/detail?id=${id}&farmer=1`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,366 @@
<template>
<view class="farmer-order-distribution-detail" v-if="detail.id">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="scrollTop < 20 ?``:primaryColor" leftIcon="icon-left" title="配送订单详情">
</uni-nav-bar>
<view :style="{height:`${configInfo.navBarHeight}px`}"></view>
<image mode="aspectFill" lazy-load class="common-bg abs" src="https://lbqny.migugu.com/admin/farm/bg-cash.png"></image>
<view class="pl-lg pr-lg c-base" style="height: 120rpx;">
<view class="f-sm-title text-bold pt-md">{{statusType[detail.pay_type]}}</view>
<view class="f-caption">
<block v-if="detail.pay_type == -1">已退配送费 ¥{{detail.pay_price}}</block>
<block v-if="detail.pay_type == 2">请尽快为买家配送该商品</block>
</view>
</view>
<view class="order-text-info ml-md mr-md pd-lg fill-base f-paragraph c-title box-shadow radius-24"
v-if="detail.send_type == 2">
<view @tap.stop="toMap" class="flex-warp">
<i class="iconfont icon-dingwei mr-sm" style="font-size: 28rpx;margin-top: 6rpx;"></i>
<view class="flex-1">{{detail.address}}</view>
</view>
<view @tap.stop="$util.goUrl({url:detail.mobile,openType:'call'})" class="flex-y-baseline c-caption"
style="margin:5rpx 0 0 38rpx;">{{detail.user_name}}
<view class="ml-lg">
{{detail.mobile}}
</view>
</view>
</view>
<view class="order-text-info mt-md ml-md mr-md pl-lg pr-lg fill-base f-paragraph c-title box-shadow radius-24">
<view class="flex-between pt-lg pb-lg">
<view class="text c-desc">配送单号</view>
<view class="flex-y-center">
<view class="max-350 ellipsis">{{detail.order_code}}</view>
<view @tap="$util.goUrl( {url:`${detail.order_code}`,openType:'copy'})"
class="copy-btn flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-t">
<view class="text c-desc">{{detail.send_type == 1 ? '自提时间':'送货时间'}}</view>
<view>{{detail.time_text}}</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-t">
<view class="text c-desc">配送方式</view>
<view>{{sendType[detail.send_type]}}</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-t" v-if="detail.send_type == 1">
<view class="text c-desc">自提人</view>
<view class="flex-y-center">
<view class="max-350">{{`${detail.user_name} ${detail.mobile}`}}</view>
<view @tap="$util.goUrl( {url:`${detail.mobile}`,openType:'call'})"
class="copy-btn flex-center fill-body" :style="{color:primaryColor}">拨打</view>
</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-t" v-if="detail.pay_type > 2 && detail.send_type == 2">
<view class="text c-desc">发货时间</view>
<view>{{detail.send_time}}</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-t" v-if="detail.pay_type == 7">
<view class="text c-desc">{{detail.send_type == 1 ? '取货时间':'收货时间'}}</view>
<view>{{detail.receiving_time}}</view>
</view>
<view class="pt-lg pb-lg b-1px-t" v-if="detail.text">
<view class="text c-desc">备注</view>
<view class="mt-sm">
<text decode="emsp" style="word-break:break-all;">{{detail.text}}</text>
</view>
</view>
</view>
<view class="mt-md ml-md mr-md pd-lg fill-base f-paragraph c-title box-shadow radius-24">
<view class="order-item flex-center">
<image mode="aspectFill" lazy-load class="avatar box-shadow-mini radius-24"
:src="detail[orderText[options.type]].goods_cover"></image>
<view class="flex-1 ml-lg">
<view class="f-title c-title text-bold max-490 ellipsis">
{{detail[orderText[options.type]].goods_name}}
</view>
<block v-if="options.type == 1">
<view class="f-caption c-caption mt-sm">
数量{{`${detail[orderText[options.type]].num}/${detail[orderText[options.type]].unit}`}}
</view>
<view class="f-caption c-caption"> 配送周期{{detail[orderText[options.type]].send_cycle}}
</view>
</block>
<block v-if="options.type == 2">
<view class="f-caption c-caption mt-sm">租赁期限{{detail[orderText[options.type]].end_time}} 到期
</view>
<view class="f-caption c-caption"> 租赁面积{{detail[orderText[options.type]].area}} </view>
</block>
</view>
</view>
</view>
<view class="fill-base mt-md ml-md mr-md pl-lg pr-lg f-paragraph c-title box-shadow radius-24"
v-if="options.type == 1">
<view class="pt-lg pb-lg f-title text-bold b-1px-b">认养收获</view>
<view class="pt-lg pb-lg">
<view class="order-seed-item flex-center">
<image mode="aspectFill" lazy-load classload class="seed-img radius-10"
:src="detail.claim_order.harvest_cover"></image>
<view class="flex-1 ml-lg f-paragraph c-title ellipsis">{{detail.claim_order.harvest_text}}</view>
</view>
</view>
</view>
<view class="mt-md ml-md mr-md pl-lg pr-lg fill-base f-paragraph c-title box-shadow radius-24"
v-if="options.type == 2 && detail.land_order.seed.length > 0">
<view class="pt-lg pb-lg f-title text-bold b-1px-b">种子管理</view>
<view class="pt-lg pb-lg">
<view class="order-seed-item flex-center" :class="[{'mt-md':index!=0}]"
v-for="(item,index) in detail.land_order.seed" :key="index">
<image mode="aspectFill" lazy-load classload class="seed-img radius-10" :src="item.imgs"></image>
<view class="flex-1 ml-lg c-title">
<view class="flex-between">
<view class="f-paragraph max-380 ellipsis">{{item.title}}</view>
<view class="f-caption">{{item.area}}</view>
</view>
<view class="flex-between f-caption c-caption">
<view class="c-warning">¥{{item.seed_price}}</view>
<view>x{{item.num}}</view>
</view>
</view>
</view>
</view>
</view>
<view class="order-text-info mt-md ml-md mr-md pl-lg pr-lg fill-base f-paragraph c-title box-shadow radius-24"
v-if="detail.send_type == 2">
<view class="flex-between pt-lg pb-lg">
<view class="text">配送费</view>
<view>¥{{detail.pay_price}}</view>
</view>
<view class="flex-between pt-lg pb-lg b-1px-t">
<view class="text">实付金额</view>
<view class="c-warning text-bold">¥{{detail.pay_price}}</view>
</view>
</view>
<view class="order-text-info mt-md ml-md mr-md pd-lg fill-base f-caption c-title box-shadow radius-24">
<view class="flex-warp pt-sm">
<view class="text c-caption">订单类型</view>
<view class="flex-1">{{orderType[options.type]}}</view>
</view>
<block v-if="detail.send_type == 2">
<view class="flex-warp pt-md">
<view class="text c-caption">付款单号</view>
<view class="flex-1 flex-y-center">{{detail.transaction_id}}
<view @tap="$util.goUrl( {url:`${detail.transaction_id}`,openType:'copy'})"
class="copy-btn sm flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
<view class="flex-warp pt-md" v-if="detail.pay_type == -1">
<view class="text c-caption">退款单号</view>
<view class="flex-1 flex-y-center">{{detail.refund_code}}
<view @tap="$util.goUrl( {url:`${detail.refund_code}`,openType:'copy'})"
class="copy-btn sm flex-center fill-body" :style="{color:primaryColor}">复制</view>
</view>
</view>
</block>
<view class="flex-warp pt-md">
<view class="text c-caption">下单时间</view>
<view class="flex-1">{{detail.create_time}}</view>
</view>
<block v-if="detail.send_type == 2">
<view class="flex-warp pt-md" v-if="detail.pay_time">
<view class="text c-caption">支付时间</view>
<view class="flex-1">{{detail.pay_time}}</view>
</view>
<view class="flex-warp pt-md" v-if="detail.pay_type == -1">
<view class="text c-caption">退款时间</view>
<view class="flex-1">{{detail.refund_time}}</view>
</view>
<view class="flex-warp pt-md">
<view class="text c-caption">支付方式</view>
<view class="flex-1">{{payType[detail.pay_model]}}</view>
</view>
</block>
</view>
<view :class="[{'space-max-footer':detail.pay_type == 2},{'space-footer':detail.pay_type != 2}]"></view>
<block v-if="detail.pay_type == 2">
<fix-bottom-button @confirm="toChangeOrder('confirm_get_item')" :text="[{type:'confirm',text: '确认取货'}]"
v-if="detail.send_type == 1">
</fix-bottom-button>
<fix-bottom-button @confirm="toChangeOrder('confirm_send_item')" :text="[{type:'confirm',text: '立即发货'}]"
v-if="detail.send_type == 2">
</fix-bottom-button>
</block>
<common-popup @confirm="confirmChangeOrder" ref="confirm_get_item" title="确认取货" desc="请确认是否取货,确认后将无法恢复"
text="确认取货" :info="popupInfo">
</common-popup>
<common-popup @confirm="confirmChangeOrder" ref="confirm_send_item" title="立即发货" desc="请确认是否发货,确认后将无法恢复"
text="确认发货" :info="popupInfo">
</common-popup>
</view>
</template>
<script>
import {
mapState,
mapActions
} from "vuex"
export default {
components: {},
data() {
return {
scrollTop: 0,
options: {},
detail: {},
payType: {
1: '微信支付',
2: '余额支付',
3: '支付宝支付'
},
statusType: {
'-1': '已取消',
2: '待配送',
3: '配送中',
7: '已完成',
},
orderType: {
1: '认养订单',
2: '土地订单'
},
orderText: {
1: 'claim_order',
2: 'land_order'
},
sendType: {
1: '自提',
2: '快递'
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.options = options
this.$util.showLoading()
this.initIndex()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
methods: {
...mapActions(['getConfigInfo']),
async initIndex() {
let {
id,
type
} = this.options
let methodModel = type == 1 ? 'claim' : 'land'
let data = await this.$api[methodModel].sendOrderInfo({
id
})
this.detail = data
this.$util.hideAll()
},
initRefresh() {
this.initIndex()
},
async toMap() {
let {
lat,
lng,
address
} = this.detail
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: lat * 1,
longitude: lng * 1,
name: address,
scale: 28
})
},
// 确认取货/开始配送/配送到家
async toChangeOrder(key) {
let {
id,
order_code,
} = this.detail
this.popupInfo = {
id,
name: `订单编号:${order_code}`,
image: '',
refs_key: key
}
this.$refs[key].open()
},
async confirmChangeOrder() {
let {
id,
refs_key
} = this.popupInfo
if (this.lockTap) return;
this.lockTap = true;
this.$util.showLoading()
let methodModel = {
'confirm_get_item': {
method: 'sendOrderPickup',
msg: '取货成功'
},
'confirm_send_item': {
method: 'sendOrderSend',
msg: '发货成功'
}
}
let {
method,
msg,
} = methodModel[refs_key]
try {
await this.$api.farmer[method]({
id
})
this.$util.hideAll()
this.$util.showToast({
title: msg
})
this.initRefresh()
this.$util.back()
this.lockTap = false
this.$refs[refs_key].close()
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
},
}
}
</script>
<style lang="scss">
.farmer-order-distribution-detail {
.order-seed-item {
.seed-img {
width: 80rpx;
height: 80rpx;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1);
}
}
}
</style>

View File

@@ -0,0 +1,282 @@
<template>
<view class="farmer-order-distribution-list">
<fixed>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
:width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<view @tap.stop="goDetail(index)"
class="order-item fill-base mg-big pd-lg f-paragraph c-title radius-16 box-shadow"
v-for="(item,index) in list.data" :key="index">
<view class="flex-between pb-lg b-1px-b">
<view class="c-desc max-500 ellipsis">配送单号{{item.order_code}}</view>
<view :style="{color:item.pay_type == 2? subColor: item.pay_type == 3? primaryColor: ''}">
{{statusType[item.pay_type]}}
</view>
</view>
<view class="flex-warp mt-lg">
<view class="text-item c-desc">订单类型</view>
<view class="flex-1">{{orderType[item.type]}}</view>
</view>
<view class="flex-warp mt-md">
<view class="text-item c-desc">{{item.send_type == 1 ? '自提时间':'送货时间'}}</view>
<view class="flex-1">{{item.time_text}}</view>
</view>
<view class="flex-warp mt-md">
<view class="text-item c-desc">配送方式</view>
<view class="flex-1">{{sendType[item.send_type]}}</view>
</view>
<view class="flex-warp mt-md" v-if="item.send_type == 2">
<view class="text-item c-desc">收货地址</view>
<view class="flex-1">{{item.address}}</view>
</view>
<view class="flex-warp mt-md">
<view class="text-item c-desc">{{item.send_type==1?'自提人':'收货人'}}</view>
<view class="flex-1">{{`${item.user_name} ${item.mobile}`}}</view>
</view>
<view class="flex-warp pt-lg" v-if="item.pay_type > 2 && item.send_type == 2">
<view class="text-item c-desc">发货时间</view>
<view class="flex-1">{{item.send_time}}</view>
</view>
<view class="flex-warp pt-lg" v-if="item.pay_type == 7">
<view class="text-item c-desc">{{item.send_type == 1 ? '取货时间':'收货时间'}}</view>
<view class="flex-1">{{item.receiving_time}}</view>
</view>
<view class="mt-md" v-if="item.text">
<view class="text-item c-desc">备注</view>
<view class="mt-sm">
<text decode="emsp" style="word-break:break-all;">{{item.text}}</text>
</view>
</view>
<block v-if="item.send_type == 2">
<view class="flex-warp pt-lg">
<view class="text-item c-desc">付款单号</view>
<view class="flex-1">{{item.transaction_id}}</view>
</view>
<view class="flex-warp pt-lg" v-if="item.pay_type == -1">
<view class="text-item c-desc">退款单号</view>
<view class="flex-1">{{item.refund_code}}</view>
</view>
<view class="flex-warp mt-md">
<view class="text-item c-desc">配送费</view>
<view class="flex-1 flex-y-center c-warning">
<view>¥{{item.pay_price}}</view>
<view class="ml-sm" v-if="item.pay_type == -1">(已退)</view>
</view>
</view>
</block>
<view class="flex-between mt-md" v-if="item.pay_type == 2">
<view></view>
<view @tap.stop="toChangeOrder(index,item.send_type==1?'confirm_get_item':'confirm_send_item')"
class="common-btn flex-center f-caption c-base radius-4 ml-lg" :style="{background:primaryColor}">
{{item.send_type == 1?'确认取货':'立即发货'}}
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
<common-popup @confirm="confirmChangeOrder" ref="confirm_get_item" title="确认取货" desc="请确认是否取货,确认后将无法恢复"
text="确认取货" :info="popupInfo">
</common-popup>
<common-popup @confirm="confirmChangeOrder" ref="confirm_send_item" title="立即发货" desc="请确认是否发货,确认后将无法恢复"
text="确认发货" :info="popupInfo">
</common-popup>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
activeIndex: 0,
tabList: [{
id: 0,
title: '全部'
}, {
id: 2,
title: '待配送',
number: 0
}, {
id: 3,
title: '配送中',
number: 0
}, {
id: 7,
title: '已完成'
}, {
id: -1,
title: '已取消'
}],
statusType: {
'-1': '已取消',
2: '待配送',
3: '配送中',
7: '已完成',
},
orderType: {
1: '认养订单',
2: '土地订单'
},
sendType: {
1: '自提',
2: '快递'
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
let {
tab = 0
} = options
this.activeIndex = tab
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
param,
tabList,
activeIndex
} = this
param.pay_type = tabList[activeIndex].id
let newList = await this.$api.farmer.farmerSendOrderList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.tabList[1].number = newList.no_send
this.tabList[2].number = newList.send_ing
this.$util.hideAll()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
// 确认取货/开始配送/配送到家
async toChangeOrder(index, key) {
let {
id,
order_code,
} = this.list.data[index]
this.popupInfo = {
id,
name: `订单编号:${order_code}`,
image: '',
index,
refs_key: key
}
this.$refs[key].open()
},
async confirmChangeOrder() {
let {
id,
index,
refs_key
} = this.popupInfo
if (this.lockTap) return;
this.lockTap = true;
this.$util.showLoading()
let methodModel = {
'confirm_get_item': {
method: 'sendOrderPickup',
msg: '取货成功'
},
'confirm_send_item': {
method: 'sendOrderSend',
msg: '发货成功'
}
}
let {
method,
msg,
} = methodModel[refs_key]
try {
await this.$api.farmer[method]({
id
})
this.$util.hideAll()
this.$util.showToast({
title: msg
})
this.lockTap = false
this.$refs[refs_key].close()
this.initRefresh()
} catch (e) {
setTimeout(() => {
this.lockTap = false
this.$util.hideAll()
}, 2000)
}
},
// 订单详情
goDetail(index) {
let {
id,
type
} = this.list.data[index]
let url = `/farmer/pages/order/distribution/detail?id=${id}&type=${type}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,174 @@
<template>
<view class="farmer-order-land-list">
<fixed>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
:width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<view @tap.stop="goDetail(index)" class="order-item fill-base mg-big pd-lg radius-16 box-shadow"
v-for="(item,index) in list.data" :key="index">
<view class="flex-between pb-lg">
<view class="f-paragraph c-title"
:style="{color:item.pay_type == 1? subColor: item.pay_type == 2? primaryColor: ''}">
{{statusType[item.pay_type]}}
</view>
<i class="iconfont icon-right"></i>
</view>
<view class="flex-center" :class="[{'mt-lg':index!=0}]">
<image mode="aspectFill" lazy-load class="avatar radius-24" :src="item.goods_cover"></image>
<view class="flex-1 ml-lg">
<view class="f-title c-title text-bold ellipsis" style="max-width: 498rpx;"> {{item.goods_name}}
</view>
<view class="f-caption c-caption mt-sm"> 租赁期限{{item.end_time}} 到期 </view>
<view class="f-caption c-caption"> 租赁面积{{item.area}} </view>
</view>
</view>
<view class="flex-between mt-md">
<view></view>
<view @tap.stop="toTel(index)" class="common-btn flex-center f-caption c-base radius-4 ml-lg"
:style="{background:primaryColor}">
联系买家
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
activeIndex: 0,
tabList: [{
id: 0,
title: '全部'
}, {
id: 1,
title: '待支付',
number: 0
}, {
id: 2,
title: '租赁中',
number: 0
}, {
id: 7,
title: '已完成'
}],
statusType: {
'-1': '已取消',
1: '待支付',
2: '租赁中',
7: '已完成',
},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
let {
tab = 0
} = options
this.activeIndex = tab
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
param,
tabList,
activeIndex
} = this
param.pay_type = tabList[activeIndex].id
let newList = await this.$api.farmer.landOrderList(param);
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.tabList[1].number = newList.count.no_pay_count
this.tabList[2].number = newList.count.rent_count
this.$util.hideAll()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
toTel(index) {
let {
mobile: url
} = this.list.data[index].address_info
this.$util.goUrl({
url,
openType: 'call'
})
},
// 订单详情
goDetail(index) {
let {
id
} = this.list.data[index]
let url = `/land/pages/order/detail?id=${id}&farmer=1`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,94 @@
<template>
<view class="home-article" v-if="isLoad">
<fixed>
<view>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
height="100rpx"></tab>
</view>
</fixed>
<view class="mg-md pd-md fill-base box-shadow radius-24" v-if="tabList.length > 0">
<parser :html="tabList[activeIndex].text" @linkpress="linkpress" show-with-animation lazy-load>加载中...
</parser>
</view>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
import parser from "@/components/jyf-Parser/index"
export default {
components: {
parser
},
data() {
return {
isLoad: false,
options: {},
tabList: [],
activeIndex: 0,
id: 0
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
methods: {
async initIndex() {
let data = await this.$api.home.aboutUsList();
let {
id = 0
} = this
let ind = data.findIndex(item => {
item.id == id
})
this.activeIndex = ind == -1 ? 0 : ind
this.tabList = data
this.isLoad = true
this.$util.hideAll()
},
initRefresh() {
this.initIndex()
},
handerTabChange(index) {
this.activeIndex = index
this.id = this.tabList[index].id
},
linkpress(res) {
console.log("linkpress", res);
// #ifdef APP-PLUS
this.$util.goUrl({
url: res.href,
openType: 'web'
})
// #endif
},
}
}
</script>
<style lang="scss">
.home-article {
.article-title {
font-size: 44rpx;
line-height: 65rpx;
}
}
</style>

View File

@@ -0,0 +1,119 @@
<template>
<view class="home-article" v-if="detail.id">
<view class="space-md"></view>
<view class="pd-lg">
<view class="article-title c-title">{{detail.title}}</view>
<view class="f-paragraph c-title mt-md">
发布时间{{detail[options.type ==1 ? 'create_time':'create_time_text']}}</view>
</view>
<view class="mt-md pd-lg">
<parser :html="detail[options.type ==1 ? 'text':'content']" @linkpress="linkpress" show-with-animation
lazy-load>加载中...</parser>
</view>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
import parser from "@/components/jyf-Parser/index"
export default {
components: {
parser
},
data() {
return {
options: {},
detail: {},
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
let {
type = 1
} = options
options.type = type * 1
this.$util.showLoading()
this.options = options
uni.setNavigationBarTitle({
title: type == 1 ? '新闻动态' : '文章详情'
})
this.initIndex()
},
onShareAppMessage(e) {
let {
id: pid
} = this.userInfo
let {
id,
title,
} = this.detail
let {
type
} = this.options
let path = `/home/pages/article?id=${id}&pid=${pid}&type=${type}`
this.$util.log(path)
return {
title,
imageUrl: '',
path,
}
},
methods: {
initIndex() {
this.getDetail()
},
initRefresh() {
this.initIndex()
},
linkpress(res) {
console.log("linkpress", res);
// #ifdef APP-PLUS
this.$util.goUrl({
url: res.href,
openType: 'web'
})
// #endif
},
async getDetail() {
let {
id,
type = 1
} = this.options
let methodKey = type == 3 ? 'mine' : 'home'
let methodModel = type === 1 ? 'articleInfo' : type == 3 ? 'operateInfoInfo' : 'welfareColumnInfo'
this.detail = await this.$api[methodKey][methodModel](type === 1 ? {
article_id: id
} : {
id
});
this.$util.hideAll()
if (type !== 3) return
let {
number
} = this.$util.getPage(-1).tabList[1]
this.$util.getPage(-1).tabList[1].number = number - 1
}
}
}
</script>
<style lang="scss">
page {
background: #fff
}
.home-article {
.article-title {
font-size: 44rpx;
line-height: 65rpx;
}
}
</style>

View File

@@ -0,0 +1,525 @@
<template>
<view class="land-order-detail" v-if="detail.id">
<fixed>
<uni-nav-bar :fixed="false"
:shadow="false" :statusBar="true" color="#fff" backgroundColor="none"
:leftIcon="options.pid?'iconshouye':'icon-left'" title="农场详情">
</uni-nav-bar>
<image mode="aspectFill" lazy-load class="common-bg abs"
:style="{height: `${configInfo.navBarHeight + 104}px`}" src="https://lbqny.migugu.com/admin/farm/bg-cash.png"></image>
<view class="common-top-img"></view>
<view class="common-top-info rel fill-base">
<view class="top-box abs">
<view @tap.stop="$util.goUrl({url:`/home/pages/farm/info?id=${options.id}`})"
class="farm-item fill-base flex-center ml-md mr-md pd-lg box-shadow radius-24">
<image mode="aspectFill" lazy-load class="cover box-shadow radius-24" :src="detail.cover">
</image>
<view class="flex-1 ml-lg">
<view class="flex-y-center f-title c-title mt-sm mb-sm">
<view class="max-title ellipsis">{{detail.title}}</view>
<view class="status-btn flex-center c-caption ml-sm radius-10"
:class="[{'fill-space':detail.is_open == 0},{'fill-body':detail.is_open == 1}]"
:style="{color:detail.is_open == 1?primaryColor:''}">
{{detail.is_open == 1?'营业中':'闭店中'}}
</view>
</view>
<view class="flex-between">
<view class="star-fill-info rel">
<view class="flex-warp star rel">
<view class="item-star flex-center" v-for="(aitem,aindex) in 5" :key="aindex">
<i class="iconfont icon-star-bold-fill"></i>
</view>
</view>
<view class="star-fill abs" :style="{width: detail.star_percent}">
<view class="flex-warp">
<view class="item-star flex-center" v-for="(aitem,aindex) in 5" :key="aindex">
<i class="iconfont icon-star-bold-fill icon-font-color"
:style="{backgroundImage: '-webkit-linear-gradient(90deg, #FDCD47, #FFC000)'}"></i>
</view>
</view>
</view>
</view>
<i class="iconfont icon-right"></i>
</view>
<view @tap.stop="toMap" class="flex-y-center f-caption c-caption mt-sm"><i
class="iconfont icon-dingwei mr-sm"></i>
<view class="addr-text ellipsis">{{detail.address}}</view>
</view>
</view>
</view>
</view>
</view>
<view class="space-sm fill-base"></view>
<tab @change="handerTabChange" :list="tabList" :activeIndex="activeIndex" :activeColor="primaryColor"
:width="100/tabList.length + '%'" height="100rpx"></tab>
<view class="b-1px-b"></view>
</fixed>
<block v-if="tabList[activeIndex].id == 1">
<view class="flex-warp">
<view @tap.stop="goDetail(index)" class="goods-item fill-base mt-md ml-md box-shadow radius-16"
v-for="(item,index) in list.data" :key="index">
<image mode="aspectFill" lazy-load class="cover" :src="item.cover"></image>
<view class="pd-md">
<view class="f-paragraph c-title ellipsis">{{item.goods_name}}</view>
<view class="flex-y-baseline">
<view class="f-title c-warning">¥{{item.show_price}}</view>
<view class="f-caption c-caption text-delete ml-sm">¥{{item.show_init_price}}</view>
</view>
<view class="f-caption c-caption">已售 {{item.all_sale_count}}</view>
</view>
</view>
</view>
</block>
<block v-if="tabList[activeIndex].id == 2">
<view @tap.stop="goDetail(index)" class="land-item fill-base mt-md ml-md mr-md box-shadow radius-16"
v-for="(item,index) in list.data" :key="index">
<image mode="aspectFill" lazy-load class="cover" :src="item.cover"></image>
<view class="pd-lg">
<view class="f-title c-title">{{item.title}}</view>
<view class="f-caption c-caption ellipsis-2">
<text decode="emsp" style="word-break:break-all;">{{item.desc}}</text>
</view>
<view class="flex-between mt-md">
<view class="flex-y-baseline">
<view class="f-lg-title c-warning">¥{{item.min_price}}</view>
<view class="f-caption c-caption ml-sm"></view>
</view>
<view class="common-btn flex-center f-paragraph c-base radius-4"
:style="{background:primaryColor}">
立即租赁</view>
</view>
</view>
</view>
</block>
<block v-if="tabList[activeIndex].id == 3">
<view @tap.stop="goDetail(index)" class="claim-item fill-base mt-md ml-md mr-md box-shadow radius-24"
v-for="(item,index) in list.data" :key="index">
<view class="flex-center pd-lg">
<image mode="aspectFill" lazy-load class="cover fill-body radius-24" :src="item.cover"></image>
<view class="flex-1 ml-lg max-380">
<view class="f-title c-title ellipsis-2">{{item.title}}</view>
<view class="f-paragraph c-title">{{`${item.start_time} ${item.end_time}`}}</view>
<view class="flex-between mt-sm mb-md">
<view class="line-item rel">
<view class="cur abs" :style="{width: item.precent}"></view>
</view>
<view class="c-caption" style="font-size: 20rpx;">已认养{{item.precent}}</view>
</view>
<view class="count-list flex-warp">
<view class="count-item">
<view class="flex-y-center f-caption c-caption">
<view class="dot radius"></view>品种
</view>
<view class="f-paragraph c-title ellipsis">{{item.breed}}</view>
</view>
<view class="count-item">
<view class="flex-y-center f-caption c-caption">
<view class="dot radius"></view>周期
</view>
<view class="f-paragraph c-title ellipsis">{{item.cycle}}</view>
</view>
<view class="count-item">
<view class="flex-y-center f-caption c-caption">
<view class="dot radius"></view>产量
</view>
<view class="f-paragraph c-title">{{item.output}}kg</view>
</view>
</view>
</view>
</view>
<view class="flex-between ml-lg mr-lg pt-lg pb-lg b-1px-t">
<view class="flex-y-baseline">
<view class="f-lg-title c-warning">¥{{item.price}}</view>
<view class="f-caption c-caption ml-sm">/{{item.unit}}</view>
</view>
<view class="common-btn flex-center f-paragraph c-base radius-4" :style="{background:primaryColor}">
立即认养</view>
</view>
<view class="flex-y-center pl-lg pr-lg pb-lg">
<view class="flex-warp">
<block v-for="(item,index) in item.count.user_list" :key="index">
<view class="avatar-group" v-if="index<6">
<image mode="aspectFill" lazy-load class="abs avatar fill-body radius"
:src="item.avatarUrl"></image>
</view>
</block>
</view>
<view class="flex-y-center f-caption c-title pr-lg" :class="[{'ml-lg':item.count.user_count>0}]"
style="height: 48rpx;">
已有{{item.count.user_count}}人参与</view>
</view>
</view>
</block>
<block v-if="tabList[activeIndex].id == 4">
<view class="flex-warp ml-md mr-md">
<view @tap.stop="goDetail(index)" class="monitor-item rel fill-base box-shadow radius-16"
:class="[{'mr-md':index%2==0}]" v-for="(item,index) in list.data" :key="index">
<view class="live-item flex-center abs">
<image mode="aspectFill" lazy-load class="live-img" src="/static/image/home/live.png"></image>
<view class="f-icontext c-base">LIVE</view>
</view>
<view class="view-item flex-center abs c-base">
<i class="iconfont icon-chakan mr-sm"></i>
<view class="f-icontext">{{item.iv || 0}}</view>
</view>
<image mode="aspectFill" lazy-load class="cover" :src="item.cover"></image>
<view class="content">
<view class="f-paragraph c-title ellipsis">{{item.title}}</view>
<view class="flex-y-center f-caption c-caption"><i class="iconfont icon-dingwei mr-sm"></i>
<view class="max-300 ellipsis">{{item.address}}</view>
</view>
</view>
</view>
</view>
</block>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapActions
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
detail: {},
activeIndex: 0,
tabList: [{
id: 1,
title: '商品'
}, {
id: 2,
title: '土地',
}, {
id: 3,
title: '认养',
}, {
id: 4,
title: '监控',
}],
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
onShareAppMessage(e) {
let {
id: pid
} = this.userInfo
let {
id,
title,
cover: imageUrl
} = this.detail
let path = `/home/pages/farm/detail?id=${id}&pid=${pid}`
this.$util.log(path)
return {
title,
imageUrl,
path,
}
},
methods: {
...mapActions(['getConfigInfo']),
async initIndex() {
let {
id
} = this.options
let data = await this.$api.home.farmerInfo({
id
})
data.is_open = 1
data.star_percent = (data.star * 1 / 5 * 100).toFixed(2) + '%'
this.detail = data
this.getList()
this.$util.hideAll()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
handerTabChange(index) {
this.activeIndex = index
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
async getList() {
let {
list: oldList,
tabList,
activeIndex
} = this
let param = this.$util.deepCopy(this.param)
let {
id: farmer_id
} = this.options
let {
id
} = tabList[activeIndex]
if (id === 4) {
param.sort = 1
}
param = Object.assign({}, param, {
farmer_id,
});
let method = {
1: {
key: 'shop',
model: 'goodsList'
},
2: {
key: 'land',
model: 'landList'
},
3: {
key: 'claim',
model: 'claimList'
},
4: {
key: 'home',
model: 'monitorList'
}
}
let {
key,
model
} = method[id]
let newList = await this.$api[key][model](param)
if (id == 3) {
newList.data.map(item => {
let {
stock,
sale_num
} = item
item.precent = (sale_num / (stock + sale_num) * 100).toFixed(2) + '%'
})
}
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
async toMap() {
let {
lat,
lng,
address
} = this.detail
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: lat * 1,
longitude: lng * 1,
name: address,
scale: 28
})
},
goDetail(index) {
let {
id
} = this.list.data[index]
let {
id: type
} = this.tabList[this.activeIndex]
let arr = {
1: '/shop/pages/detail',
2: '/land/pages/detail',
3: '/claim/pages/detail',
4: '/home/pages/monitor/detail',
}
let url = `${arr[type]}?id=${id}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.land-order-detail {
.farm-item {
.cover {
width: 160rpx;
height: 160rpx;
}
.icon-dingwei,
.icon-right {
font-size: 24rpx;
}
.status-btn {
width: 80rpx;
height: 36rpx;
font-size: 20rpx;
}
.max-title {
max-width: 370rpx;
}
.addr-text {
max-width: 420rpx;
}
}
.goods-item {
width: 345rpx;
.cover {
width: 345rpx;
height: 345rpx;
border-radius: 15rpx 15rpx 0 0;
}
}
.land-item {
.cover {
width: 100%;
height: 345rpx;
border-radius: 15rpx 15rpx 0 0;
}
}
.claim-item {
.cover {
width: 240rpx;
height: 240rpx;
}
.line-item {
width: 230rpx;
height: 15rpx;
background: rgba(112, 152, 64, 0.2);
border-radius: 8rpx;
.cur {
top: 0;
left: 0;
height: 15rpx;
background: linear-gradient(0deg, #709840 0%, #91C84E 100%);
border-radius: 8rpx;
}
}
.count-list {
.count-item {
width: 33.33%;
.dot {
width: 10rpx;
height: 10rpx;
background: #B3D465;
margin-right: 10rpx;
}
}
}
}
.monitor-item {
width: 345rpx;
margin-top: 24rpx;
.live-item {
top: 10rpx;
left: 10rpx;
width: 109rpx;
height: 38rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 18rpx;
.live-img {
top: 0;
width: 38rpx;
height: 38rpx;
}
.f-icontext {
margin-left: 10rpx;
width: 61rpx;
}
}
.view-item {
top: 0;
right: 0;
width: 137rpx;
height: 51rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 0 15rpx 0 0;
}
.cover {
width: 345rpx;
height: 190rpx;
border-radius: 15rpx 15rpx 0 0;
}
.content {
padding: 24rpx;
.iconfont {
font-size: 26rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,188 @@
<template>
<view class="home-farm-info" v-if="detail.id">
<uni-nav-bar :fixed="true" :shadow="false" :statusBar="true" color="#fff"
:backgroundColor="scrollTop < 20 ?``:primaryColor" leftIcon="icon-left" title="商户信息">
</uni-nav-bar>
<view :style="{height:`${configInfo.navBarHeight}px`}"></view>
<image mode="aspectFill" lazy-load class="common-bg abs" :style="{height: `${configInfo.navBarHeight + 104}px`}"
src="https://lbqny.migugu.com/admin/farm/bg-cash.png"></image>
<view class="common-top-img"></view>
<view class="common-top-info rel">
<view class="top-box abs">
<view class="farm-item fill-base flex-center ml-md mr-md pd-lg box-shadow radius-24">
<image mode="aspectFill" lazy-load class="cover box-shadow-mini radius-24" :src="detail.cover">
</image>
<view class="flex-1 ml-lg">
<view class="f-title c-title ellipsis" style="max-width: 460rpx;">{{detail.title}}</view>
<view class="f-caption c-caption mt-sm">浏览量 {{detail.iv || 0}}</view>
</view>
</view>
</view>
</view>
<view class="space-md"></view>
<view class="fill-base pt-sm pl-lg pr-lg pb-lg">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="f-title text-bold flex-1">店铺描述</view>
</view>
<view class="f-paragraph c-desc">
<text decode="emsp" style="word-break:break-all;">{{detail.desc}}</text>
</view>
</view>
<view class="info-list fill-base pl-lg pb-lg">
<image mode="aspectFill" @tap.stop="$util.previewImage({current:item,urls:detail.imgs})"
class="info-img fill-body radius-16"
:class="[{'mr-md':index!=detail.imgs.length},{'mr-lg': index == detail.imgs.length}]" :src="item"
v-for="(item,index) in detail.imgs" :key="index"></image>
</view>
<view class="space-md"></view>
<view class="fill-base pt-sm pl-lg pr-lg pb-lg">
<view class="common-nav-title flex-center c-title">
<view class="common-line" :style="{background:primaryColor}"></view>
<view class="f-title text-bold flex-1">基础信息</view>
</view>
<view class="f-paragraph c-desc mt-sm">
<view @tap.stop="$util.goUrl({url:detail.mobile,openType:'call'})" class="flex-between">
<view>联系电话</view>
<view>{{detail.mobile}}</view>
</view>
<view @tap.stop="toMap" class="flex-warp mt-md">
<view>店铺地址</view>
<view class="flex-1 ml-lg pl-lg">{{detail.address}}</view>
</view>
<!-- <view class="flex-between mt-md">
<view>开店时间</view>
<view>{{detail.create_time}}</view>
</view> -->
<view class="flex-between mt-md">
<view>是否营业</view>
<view>{{openType[detail.is_open]}}</view>
</view>
</view>
</view>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
export default {
components: {},
data() {
return {
scrollTop: 0,
options: {},
detail: {},
openType: {
0: '闭店中',
1: '营业中'
}
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.options = options
this.initIndex()
},
onPageScroll(e) {
this.scrollTop = e.scrollTop
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
methods: {
async initIndex() {
let {
id
} = this.options
let data = await this.$api.home.farmerInfo({
id
})
data.is_open = 1
data.star_percent = (data.star * 1 / 5 * 100).toFixed(2) + '%'
data.iv = data.iv*1+1
this.detail = data
},
initRefresh() {
this.initIndex()
},
async toMap() {
let {
lat,
lng,
address
} = this.detail
await this.$util.checkAuth({
type: 'userLocation'
})
await uni.getLocation({
type: 'gcj02',
})
await uni.openLocation({
latitude: lat * 1,
longitude: lng * 1,
name: address,
scale: 28
})
},
}
}
</script>
<style lang="scss">
.home-farm-info {
.farm-item {
.cover {
width: 160rpx;
height: 160rpx;
}
.icon-dingwei,
.icon-right {
font-size: 24rpx;
}
.status-btn {
width: 80rpx;
height: 36rpx;
font-size: 20rpx;
}
.max-title {
max-width: 370rpx;
}
.addr-text {
max-width: 420rpx;
}
}
.info-list {
white-space: nowrap;
width: 100%;
overflow: auto;
.info-img {
width: 200rpx;
height: 200rpx;
display: inline-block;
}
}
}
</style>

View File

@@ -0,0 +1,225 @@
<template>
<view class="home-farm-list">
<fixed>
<view @tap.stop="toChooseLocation"
class="fill-base flex-center pt-lg pb-lg pl-md pr-md f-paragraph c-title">
<view class="flex-1 flex-warp">
<i class="iconfont icon-dingwei mr-sm" style="margin-top: 3rpx;" :style="{color:primaryColor}"></i>
<view class="max-580 ellipsis">{{location.address || '定位中...'}}</view>
</view>
<i class="iconfont icon-right" style="font-size: 28rpx;"></i>
</view>
</fixed>
<view @tap.stop="goDetail(index)"
class="farm-item fill-base flex-center mt-md ml-md mr-md pd-lg box-shadow radius-24"
v-for="(item,index) in list.data" :key="index">
<image mode="aspectFill" lazy-load class="cover radius-24" :src="item.cover"></image>
<view class="flex-1 ml-lg">
<view class="flex-y-center f-title c-title mt-sm mb-sm">
<view class="max-title ellipsis">{{item.title}}</view>
<view class="status-btn flex-center c-caption ml-sm radius-10"
:class="[{'fill-space':item.is_open==0},{'fill-body':item.is_open==1}]"
:style="{color:item.is_open==1?primaryColor:''}">
{{item.is_open==1?'营业中':'闭店中'}}
</view>
</view>
<view class="star-fill-info rel">
<view class="flex-warp star rel">
<view class="item-star flex-center" v-for="(aitem,aindex) in 5" :key="aindex">
<i class="iconfont icon-star-bold-fill"></i>
</view>
</view>
<view class="star-fill abs" :style="{width: item.star_percent}">
<view class="flex-warp">
<view class="item-star flex-center" v-for="(aitem,aindex) in 5" :key="aindex">
<i class="iconfont icon-star-bold-fill icon-font-color"
:style="{backgroundImage: '-webkit-linear-gradient(90deg, #FDCD47, #FFC000)'}"></i>
</view>
</view>
</view>
</view>
<view class="flex-y-center f-caption c-caption mt-sm"><i class="iconfont icon-dingwei mr-sm"></i>
<view class="addr-text ellipsis">{{item.address}}</view>
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<abnor @confirm="$util.checkAuth({ type: 'userLocation' })" :tip="[{ text: '定位失败,请开启地理位置授权后重试~', color: 0 }]"
:button="[{ text: '开启定位' , type: 'confirm' }]" btnSize="" v-if="!loading && !location.lng"> </abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
options: {},
param: {
page: 1,
},
list: {
data: []
},
loading: true,
popupInfo: {},
lockTap: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
location: state => state.user.location,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
...mapActions(['getShopCarList']),
...mapMutations(['updateUserItem']),
async initIndex() {
let {
location
} = this
if (!location.lat) {
location = await this.$util.getBmapLocation()
this.updateUserItem({
key: 'location',
val: location
})
}
if (!this.location.lat) {
this.loading = false
this.$util.hideAll()
return
}
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
async getList() {
let {
list: oldList,
param,
location
} = this
let {
lat,
lng
} = location
param = Object.assign({}, param, {
lat,
lng,
});
let newList = await this.$api.home.farmerList(param);
newList.data.map(item => {
item.is_open = 1
item.star_percent = (item.star * 1 / 5 * 100).toFixed(2) + '%'
})
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
// 选择地区
async toChooseLocation(e) {
await this.$util.checkAuth({
type: 'userLocation'
})
let [, {
address = '',
longitude: lng = 0,
latitude: lat = 0
} = {}] = await uni.chooseLocation();
if (!address) return
let location = {
lng,
lat,
address
}
this.updateUserItem({
key: 'location',
val: location
})
this.param.page = 1
this.getList()
this.$util.back()
},
// 详情
async goDetail(index) {
let {
id
} = this.list.data[index]
let url = `/home/pages/farm/detail?id=${id}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.home-farm-list {
.farm-item {
.cover {
width: 160rpx;
height: 160rpx;
}
.icon-dingwei {
font-size: 24rpx;
}
.status-btn {
width: 80rpx;
height: 36rpx;
font-size: 20rpx;
}
.max-title {
max-width: 370rpx;
}
.addr-text {
max-width: 420rpx;
}
}
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<view class="home-monitor-detail" v-if="url">
<web-view :src="url"></web-view>
</view>
</template>
<script>
import {
mapState,
} from "vuex"
import siteInfo from '../../../siteinfo.js';
export default {
components: {},
data() {
return {
options: {},
url: ''
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
configInfo: state => state.config.configInfo,
userInfo: state => state.user.userInfo,
}),
onLoad(options) {
this.$util.showLoading()
this.options = options
this.initIndex()
},
methods: {
initIndex() {
let {
id
} = this.options
let {
siteroot,
uniacid
} = siteInfo
let href = siteroot.split('/index.php')[0]
this.url = `${href}/demo/index.html?${id}&${uniacid}`
// this.url = `${href}/monitor/index.html?${id}&${uniacid}`
this.$util.hideAll()
}
}
}
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,253 @@
<template>
<view class="home-monitor-list" v-if="isLoad">
<fixed>
<view class="fill-base flex-between pt-md pb-md b-1px-b">
<view class="pl-md pr-md" style="width: 650rpx;">
<search type="input" @input="toSearch" textAlign="left" placeholder="搜索农场名称" :padding="0"
:radius="35">
</search>
</view>
<view @tap.stop="toShowRank" class="flex-center c-caption b-1px-l" style="width: 100rpx;">
<i class="iconfont icon-down-bold" :class="[{'rotate-180':show_rank_item}]"></i>
</view>
</view>
</fixed>
<uni-popup @change="popupChange" ref="rank_item" type="top" :custom="true">
<view style="height: 100rpx;"></view>
<view class="pd-lg fill-base">
<view @tap.stop="handerTabChange(index,'rankInd')" class="f-paragraph c-title"
:class="[{'mt-md':index!=0}]" :style="{color:index==rankInd?primaryColor:''}"
v-for="(item,index) in rankList" :key="index">
{{item.title}}
</view>
</view>
</uni-popup>
<view class="flex-warp ml-md mr-md">
<view @tap.stop="goDetail(index)" class="monitor-item fill-base box-shadow radius-16"
:class="[{'mr-md':index%2==0}]" v-for="(item,index) in list.data" :key="index">
<image mode="aspectFill" lazy-load class="cover" :src="item.cover"></image>
<view class="content">
<view class="f-paragraph c-title ellipsis">{{item.title}}</view>
<view class="flex-y-center f-caption c-caption"><i class="iconfont icon-dingwei mr-sm"></i>
<view class="max-300 ellipsis">{{item.address}}</view>
</view>
<view class="flex-y-center f-caption c-caption"><i class="iconfont icon-eyeopen mr-sm"></i>
<view class="max-300 ellipsis">{{item.iv || 0}}</view>
</view>
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
isLoad: false,
options: {},
rankInd: 0,
rankList: [{
id: 1,
title: '综合排序'
}, {
id: 2,
title: '距离最近'
}, {
id: 3,
title: '观看最多'
}],
activeIndex: 0,
tabList: [],
param: {
page: 1,
},
list: {
data: []
},
loading: true,
lockTap: false,
show_rank_item: false
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
userInfo: state => state.user.userInfo,
location: state => state.user.location,
}),
async onLoad() {
if (this.isLoad) return
this.$util.showLoading()
this.initIndex()
},
async onShow() {
if (!this.isLoad || (this.location.lat && this.rankInd != 1)) return
let [err, result] = await uni.getSetting()
if (err || !result.authSetting[`scope.userLocation`]) return
this.initIndex()
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
...mapMutations(['updateUserItem']),
async initIndex() {
await this.getLocation()
let cate = await this.$api.claim.claimCateList({
type: 1
})
cate.unshift({
id: 0,
title: '全部'
})
this.tabList = cate
this.isLoad = true
this.param.page = 1
this.getList()
},
initRefresh() {
this.initIndex()
},
async getLocation() {
let {
location
} = this
if (!location.lat) {
location = await this.$util.getBmapLocation()
this.updateUserItem({
key: 'location',
val: location
})
}
},
handerTabChange(index, type) {
this[type] = index
if (type == 'rankInd') {
this.$refs.rank_item.close()
}
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
toShowRank() {
let {
show_rank_item,
} = this
if (this.lockTap) return
this.lockTap = true
setTimeout(() => {
let methodModel = show_rank_item ? 'close' : 'open'
this.$refs.rank_item[methodModel]()
}, 500)
},
popupChange(e) {
let {
show
} = e
this.show_rank_item = show
setTimeout(() => {
this.lockTap = false
}, 200)
},
toSearch(val) {
this.loading = true
this.param.page = 1
this.param.title = val
this.getList()
},
async getList() {
let {
list: oldList,
param,
rankList,
rankInd
} = this
let {
id: sort
} = rankList[rankInd]
if (sort == 2 && !this.location.lat) {
await this.getLocation()
return
}
let {
lng = 0,
lat = 0
} = this.location
param = Object.assign({}, param, {
sort,
});
if (sort === 2) {
param.lat = lat
param.lng = lng
}
let newList = await this.$api.home.farmerList(param)
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
async goDetail(index) {
let {
id
} = this.list.data[index]
let url = `/home/pages/monitor/search?id=${id}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.home-monitor-list {
.monitor-item {
width: 345rpx;
margin-top: 24rpx;
.cover {
width: 345rpx;
height: 190rpx;
border-radius: 15rpx 15rpx 0 0;
}
.content {
padding: 24rpx;
.iconfont {
font-size: 26rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<view class="home-monitor-list" v-if="isLoad">
<fixed>
<view class="fill-base pt-big pb-big pl-md pr-md">
<search @input="toSearch" type="input" :padding="0" :radius="70" placeholder="搜索监控名称"></search>
</view>
</fixed>
<view class="flex-warp ml-md mr-md">
<view @tap.stop="goDetail(index)" class="monitor-item rel fill-base box-shadow radius-16"
:class="[{'mr-md':index%2==0}]" v-for="(item,index) in list.data" :key="index">
<view class="live-item flex-center abs">
<image mode="aspectFill" lazy-load class="live-img" src="/static/image/home/live.png"></image>
<view class="f-icontext c-base">LIVE</view>
</view>
<view class="view-item flex-center abs c-base">
<i class="iconfont icon-chakan mr-sm"></i>
<view class="f-icontext">{{item.iv || 0}}</view>
</view>
<image mode="aspectFill" lazy-load class="cover" :src="item.cover"></image>
<view class="content">
<view class="f-paragraph c-title ellipsis">{{item.title}}</view>
<view class="flex-y-center f-caption c-caption"><i class="iconfont icon-dingwei mr-sm"></i>
<view class="max-300 ellipsis">{{item.address}}</view>
</view>
</view>
</view>
</view>
<load-more :noMore="list.current_page>=list.last_page&&list.data.length>0" :loading="loading" v-if="loading">
</load-more>
<abnor v-if="!loading&&list.data.length<=0&&list.current_page==1"></abnor>
<view class="space-footer"></view>
</view>
</template>
<script>
import {
mapState,
mapActions,
mapMutations
} from "vuex"
export default {
components: {},
data() {
return {
isLoad: false,
options: {},
rankInd: 0,
rankList: [{
id: 1,
title: '综合排序'
}, {
id: 2,
title: '距离最近'
}, {
id: 3,
title: '观看最多'
}],
activeIndex: 0,
tabList: [],
param: {
page: 1,
},
list: {
data: []
},
loading: true,
lockTap: false,
}
},
computed: mapState({
primaryColor: state => state.config.configInfo.primaryColor,
subColor: state => state.config.configInfo.subColor,
userInfo: state => state.user.userInfo,
location: state => state.user.location,
}),
async onLoad(options) {
this.$util.showLoading()
this.options = options
await this.initIndex()
this.isLoad = true
},
onPullDownRefresh() {
// #ifndef APP-PLUS
uni.showNavigationBarLoading()
// #endif
this.initRefresh()
uni.stopPullDownRefresh()
},
onReachBottom() {
if (this.list.current_page >= this.list.last_page || this.loading) return
this.param.page = this.param.page + 1
this.loading = true
this.getList()
},
methods: {
...mapActions(['getConfigInfo']),
...mapMutations(['updateUserItem']),
async initIndex() {
this.getList()
},
initRefresh() {
this.param.page = 1
this.initIndex()
},
toSearch(val) {
this.loading = true
this.param.page = 1
this.param.title = val
this.getList()
},
handerTabChange(index, type) {
this[type] = index
if (type == 'rankInd') {
this.$refs.rank_item.close()
}
this.$util.showLoading()
this.param.page = 1
this.list.data = []
this.getList()
},
async getList() {
let {
list: oldList,
param,
} = this
let {
id = 0,
} = this.options
param.farmer_id = id
let newList = await this.$api.home.monitorList(param)
if (this.param.page == 1) {
this.list = newList
} else {
newList.data = oldList.data.concat(newList.data)
this.list = newList
}
this.loading = false
this.$util.hideAll()
},
async goDetail(index) {
let {
id
} = this.list.data[index]
let url = `/home/pages/monitor/detail?id=${id}`
this.$util.goUrl({
url
})
}
}
}
</script>
<style lang="scss">
.home-monitor-list {
.monitor-item {
width: 345rpx;
margin-top: 24rpx;
.live-item {
top: 10rpx;
left: 10rpx;
width: 109rpx;
height: 38rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 18rpx;
.live-img {
top: 0;
width: 38rpx;
height: 38rpx;
}
.f-icontext {
margin-left: 10rpx;
width: 61rpx;
}
}
.view-item {
top: 0;
right: 0;
width: 137rpx;
height: 51rpx;
background: rgba(0, 0, 0, 0.3);
border-radius: 0 15rpx 0 0;
}
.cover {
width: 345rpx;
height: 190rpx;
border-radius: 15rpx 15rpx 0 0;
}
.content {
padding: 24rpx;
.iconfont {
font-size: 26rpx;
}
}
}
}
</style>

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