diff --git a/api/address.js b/api/address.js index 845d525..50022aa 100644 --- a/api/address.js +++ b/api/address.js @@ -21,4 +21,12 @@ export const villageListApi = (data) => { } export const brigadeListApi = (data) => { return request.get('/brigade', data); +} + +export const getMassageCode = (data) => { + return request.get('/user/user/send_sms', data); +} + +export const setPayPassword = (data) => { + return request.post('/user/user/set_payPassword', data); } \ No newline at end of file diff --git a/api/user.js b/api/user.js index f8dbe79..2dcaa73 100644 --- a/api/user.js +++ b/api/user.js @@ -120,4 +120,16 @@ export const getStoreByPhone = (data) => { export const getStoreInfo = (data) => { return request.get('/config', data); +} + +export const getUserLabel = (data) => { + return request.get('/user_label/UserLabel/lists', data); +} + +export const getUserShip = (data) => { + return request.get('/user_ship/UserShip/lists', data); +} + +export const getCreateLists = (data) => { + return request.get('/store/store/create_lists', data); } \ No newline at end of file diff --git a/components/goodPopup.vue b/components/goodPopup.vue index 3075fd9..d8cd2bb 100644 --- a/components/goodPopup.vue +++ b/components/goodPopup.vue @@ -1,159 +1,165 @@ <template> - <up-popup :show="show" closeable round="10" @close="close" :safeAreaInsetBottom="false"> - <view class="good-popup"> - <view class="head-title"> - {{datas.is_bulk ? '称重商品' : '计件商品'}} - </view> - <view class="row"> - <view>商品名称</view> - <view>{{datas.name || datas.goods_name || datas.store_name}}</view> - </view> - <view class="row"> - <view>商品单位</view> - <view>{{datas.unit_name}}</view> - </view> - <view class="row"> - <view>商品价格</view> - <view>¥ {{datas.price || datas.sell}}</view> - </view> - <view class="row"> - <view>小计</view> - <view style="color: #F55726;" v-if="+datas.cart_num<+datas.batch"> - {{`${datas.batch}${datas.unit_name}起批`}} - </view> - <view style="color: #F55726;" v-else>¥ {{subtotal}}</view> - </view> - <view v-if="datas.is_bulk" class="row"> - <view>购买重量<text style="color: #F55726;">*</text></view> - <view style="flex: 1;"> - <up-input v-model="datas.cart_num" :cursorSpacing='120' type="number" border="none" - placeholder="请输入购买重量" inputAlign="right"></up-input> - </view> - </view> - <view v-else class="row"> - <view>购买数量<text style="color: #F55726;">*</text></view> - <view style="flex: 1;"> - <up-input v-model="datas.cart_num" :cursorSpacing='120' type="number" border="none" - placeholder="请输入购买数量" inputAlign="right"> - <template #suffix> - <span style="color: #20b128;">{{datas.unit_name}}</span> - </template> - </up-input> - </view> - </view> - <view class="row" style="padding-top: 30px;padding-bottom: 30rpx;"> - <view style="width: 30%;margin-right: 30rpx;"> - <up-button @click="close" color="#f7f7f7"><text style="color: #333;">取消</text></up-button> - </view> - <view style="flex: 1;"> - <up-button @click="change" color="#20b128">确定</up-button> - </view> - </view> - </view> - </up-popup> + <up-popup :show="show" closeable round="10" @close="close" :safeAreaInsetBottom="false"> + <view class="good-popup"> + <view class="head-title"> + {{ datas.is_bulk ? '称重商品' : '计件商品' }} + </view> + <view class="row"> + <view>商品名称</view> + <view>{{ datas.name || datas.goods_name || datas.store_name }}</view> + </view> + <view class="row"> + <view>商品单位</view> + <view>{{ datas.unit_name }}</view> + </view> + <view class="row"> + <view>商品价格</view> + <view>¥ {{ datas.price || datas.sell }} / {{ datas.unit_name }}</view> + </view> + <view class="row" v-if="datas.batch > 0"> + <view>起批量</view> + <view>{{ datas.batch }}{{ datas.unit_name }}起批 </view> + </view> + + <view class="row"> + <view>小计</view> + <view style="color: #F55726;" v-if="+datas.cart_num < +datas.batch"> + {{ `${datas.batch}${datas.unit_name}起批` }} + </view> + <view style="color: #F55726;" v-else>¥ {{ subtotal }}</view> + </view> + <view v-if="datas.is_bulk" class="row"> + <view>购买重量<text style="color: #F55726;">*</text></view> + <view style="flex: 1;"> + <up-input v-model="datas.cart_num" :cursorSpacing='120' type="digit" border="none" placeholder="请输入购买重量" + inputAlign="right"></up-input> + </view> + </view> + <view v-else class="row"> + <view>购买数量<text style="color: #F55726;">*</text></view> + <view style="flex: 1;"> + <up-input v-model="datas.cart_num" :cursorSpacing='120' type="digit" border="none" placeholder="请输入购买数量" + inputAlign="right"> + <template #suffix> + <span style="color: #20b128;">{{ datas.unit_name }}</span> + </template> + </up-input> + </view> + </view> + <view class="row" style="padding-top: 30px;padding-bottom: 30rpx;"> + <view style="width: 30%;margin-right: 30rpx;"> + <up-button @click="close" color="#f7f7f7"><text style="color: #333;">取消</text></up-button> + </view> + <view style="flex: 1;"> + <up-button @click="change" color="#20b128">确定</up-button> + </view> + </view> + </view> + </up-popup> </template> <script setup> - import { - computed, - ref - } from "vue" - import { - toast - } from "../uni_modules/uview-plus"; +import { + computed, + ref +} from "vue" +import { + toast +} from "../uni_modules/uview-plus"; - const props = defineProps({ - show: { - type: Boolean, - default: false - }, - }) +const props = defineProps({ + show: { + type: Boolean, + default: false + }, +}) - const datas = ref({ - cart_num: '' - }); - const setData = (e) => { - datas.value = e; - if (Number(e.batch) > 0) datas.value.cart_num = Number(e.batch).toFixed(0); - else datas.value.cart_num = ''; - } +const datas = ref({ + cart_num: '' +}); +const setData = (e) => { + datas.value = e; + if (Number(e.batch) > 0) datas.value.cart_num = e.batch; + else datas.value.cart_num = ''; - const emit = defineEmits(['close', 'change']); - const close = () => { - emit('close'); - } +} - const change = () => { - if (+datas.value.cart_num < +datas.value.batch) return uni.$u.toast( - `购买数量不能小于${datas.value.batch}${datas.value.unit_name}`); - if (subtotal.value <= 0) { - uni.$u.toast('金额不可小于等于0'); - datas.value.cart_num = ''; - return; - } - emit('change', datas.value); - } +const emit = defineEmits(['close', 'change']); +const close = () => { + emit('close'); +} - const subtotal = computed(() => { - let num = +datas.value.cart_num || 0; - let sell = +datas.value.sell || +datas.value.price; - return Number(num * sell * 100 / 100).toFixed(2) - }) +const change = () => { + if (+datas.value.cart_num < +datas.value.batch) return uni.$u.toast( + `购买数量不能小于${datas.value.batch}${datas.value.unit_name}`); + if (subtotal.value <= 0) { + uni.$u.toast('金额不可小于等于0'); + datas.value.cart_num = ''; + return; + } + emit('change', datas.value); +} - defineExpose({ - setData - }) +const subtotal = computed(() => { + let num = +datas.value.cart_num || 0; + let sell = +datas.value.sell || +datas.value.price; + return Number(num * sell * 100 / 100).toFixed(2) +}) + +defineExpose({ + setData +}) </script> <style scoped lang="scss"> - .good-popup { - padding: 30rpx; +.good-popup { + padding: 30rpx; - .head-title { - font-weight: bold; - text-align: center; - margin-bottom: 20rpx; - } + .head-title { + font-weight: bold; + text-align: center; + margin-bottom: 20rpx; + } - .row { - display: flex; - align-items: center; - justify-content: space-between; - padding-bottom: 20rpx; - border-bottom: 1rpx solid #f6f6f6; - margin-bottom: 20rpx; + .row { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 20rpx; + border-bottom: 1rpx solid #f6f6f6; + margin-bottom: 20rpx; - &:last-child { - border-bottom: none; - margin-bottom: 0; - } + &:last-child { + border-bottom: none; + margin-bottom: 0; + } - .content { - .top { - display: flex; + .content { + .top { + display: flex; - view { - margin-right: 20rpx; - } - } + view { + margin-right: 20rpx; + } + } - .bottom {} - } + .bottom {} + } - image { - width: 40rpx; - height: 40rpx; - flex-shrink: 0; - } - } - } + image { + width: 40rpx; + height: 40rpx; + flex-shrink: 0; + } + } +} - @keyframes disappear { - to { - opacity: 0; - /* 渐隐 */ - transform: scale(0); - /* 缩小 */ - } - } +@keyframes disappear { + to { + opacity: 0; + /* 渐隐 */ + transform: scale(0); + /* 缩小 */ + } +} </style> \ No newline at end of file diff --git a/config/app.js b/config/app.js index 85facca..ab2ac5f 100644 --- a/config/app.js +++ b/config/app.js @@ -1,8 +1,8 @@ let BASE_URL import store from "@/store/user.js" // 环境 -// let env = "dev" -let env = "prod" +let env = "dev" +// let env = "prod" // let env = "liu"; switch (env) { diff --git a/pageQuota/vipUser/index.vue b/pageQuota/vipUser/index.vue index bb1994f..1efe700 100644 --- a/pageQuota/vipUser/index.vue +++ b/pageQuota/vipUser/index.vue @@ -1,17 +1,24 @@ <template> <view class="" v-if="!STORE_INFO.id"> - <up-modal :show="showModa" title="选择门店" @confirm="confirmStore" confirmColor='#20B128'> + <up-modal :show="showModa" title="门店信息" @confirm="confirmStore" confirmColor='#20B128'> <view class="slot-content"> - <up-input v-model="storePhone" border="none" prefixIcon="phone" placeholder="请输入门店手机号" - :customStyle="{background:'#F3F3F3',padding:'20rpx','border-radius':'30rpx'}" - :placeholderStyle="{color:'#444444'}" :prefixIconStyle="{'margin-right':'40rpx'}"></up-input> + + <up-form labelPosition="left" label-width="100rpx"> + <up-form-item label="手机号" borderBottom> + <up-input v-model="storePhone" placeholder="请输入门店手机号"></up-input> + </up-form-item> + <up-form-item label="角色" borderBottom> + <uni-data-select v-model="Role" :localdata="range" :clear='false'></uni-data-select> + </up-form-item> + </up-form> + </view> </up-modal> </view> <view v-else> <view class="tabs"> - <text @click="currentTab=1" :class="{actText:currentTab==1}">开通行业会员</text> + <text @click="currentTab=1" :class="{actText:currentTab==1}">开通{{Role==1?'行业会员':'商户'}} </text> <text @click="currentTab=2,getCount(),getLists()" :class="{actText:currentTab==2}"> 已开通列表</text> <view class="lines" :class="{actLine:currentTab==2}" /> </view> @@ -20,7 +27,7 @@ <block v-if='currentTab==1'> <view class="card card1"> <view class="card1-tit"> - 行业会员开通报备 + {{Role==1?'行业会员':'商户'}}开通报备 </view> <up-form labelPosition="left" :model="formData" :borderBottom='false'> <up-form-item label="" prop="userInfo.name"> @@ -30,7 +37,7 @@ :prefixIconStyle="{'margin-right':'40rpx'}"></up-input> </up-form-item> <up-form-item label="" prop="userInfo.name"> - <up-input v-model="formData.mobile" border="none" prefixIcon="account" placeholder="请输入电话号码" + <up-input v-model="formData.mobile" border="none" prefixIcon="phone" placeholder="请输入电话号码" :customStyle="{background:'#F3F3F3',padding:'20rpx','border-radius':'30rpx'}" :placeholderStyle="{color:'#444444'}" :prefixIconStyle="{'margin-right':'40rpx'}"></up-input> @@ -38,12 +45,24 @@ <up-form-item label="" prop="userInfo.name"> <view @click="showPop=true" style="width: 100%;"> <up-input style="pointer-events: none" v-model="formData.address" border="none" - prefixIcon="account" readonly placeholder="请选择地址" :customStyle="{background:'#F3F3F3',padding:'20rpx', + prefixIcon="map" readonly placeholder="点击选择地址" :customStyle="{background:'#F3F3F3',padding:'20rpx', 'border-radius':'30rpx' }" :placeholderStyle="{color:'#444444'}" :prefixIconStyle="{'margin-right':'40rpx'}" suffixIcon='arrow-down'></up-input> </view> </up-form-item> + + <up-form-item label="" prop="userInfo.name" v-if='Role==1'> + <view @click="showPop1=true" style="width: 100%;"> + <up-input style="pointer-events: none" v-model="formData.label_name" border="none" + prefixIcon="man-add" readonly placeholder="点击选择用户身份" :customStyle="{background:'#F3F3F3',padding:'20rpx', + 'border-radius':'30rpx' + }" :placeholderStyle="{color:'#444444'}" :prefixIconStyle="{'margin-right':'40rpx'}" + suffixIcon='arrow-down'></up-input> + </view> + </up-form-item> + + </up-form> <view class="store-info"> 报备人:{{STORE_INFO.name}} @@ -54,7 +73,7 @@ shape="circle" color="#50C758"></up-button> --> <view style='width: 710rpx;height: 100rpx;text-align: center;line-height: 100rpx;text-align: center;color: white;background-color: #33B83A;border-radius: 50rpx;font-size:40rpx ;'> - 完成并收款 + {{Role==1?'完成并收款':'完成'}} </view> </view> @@ -65,7 +84,7 @@ <view class="vip-card"> <text>当前已开通:</text> <up-count-to :startVal="0" :endVal="count"></up-count-to> - <text>位行业会员</text> + <text>位{{Role==1?'行业会员':'商户'}}会员</text> </view> <view class="table"> @@ -73,19 +92,23 @@ <!-- 表头行 --> <uni-tr> <uni-th width="20" align="center">序号</uni-th> - <uni-th width="50" align="center">行业会员</uni-th> - <uni-th width="50" align="center">经营资金</uni-th> - <uni-th width="50" align="center">开通时间</uni-th> + <uni-th width="50" align="center" v-if='Role==1'>行业会员</uni-th> + <uni-th width="50" align="center" v-if='Role==1'>经营资金</uni-th> + <uni-th width="50" align="center" v-if='Role==4'>开通时间</uni-th> + <uni-th width="50" align="center" v-if='Role==4'>商户</uni-th> + <uni-th width="50" align="center" v-if='Role==1'>角色</uni-th> <uni-th width="50" align="center">状态</uni-th> </uni-tr> <!-- 表格数据行 --> <uni-tr v-for="(item,index) in lists" :key="item.order_id"> <uni-td align="center">{{index+1}}</uni-td> - <uni-td style="font-size: 20rpx;" align="center">{{item.real_name}}</uni-td> - <uni-td style="font-size: 20rpx;" align="center">{{item.price}}</uni-td> - <uni-td style="font-size: 20rpx;" align="center">{{item.create_time}}</uni-td> + <uni-td style="font-size: 20rpx;" align="center" v-if='Role==1'>{{item.real_name}}</uni-td> + <uni-td style="font-size: 20rpx;" align="center" v-if='Role==1'>{{item.price}}</uni-td> + <uni-td style="font-size: 20rpx;" align="center" v-if='Role==4'>{{item.create_time}}</uni-td> + <uni-td style="font-size: 20rpx;" align="center" v-if='Role==4'>{{item.nickname}}</uni-td> + <uni-td style="font-size: 20rpx;" align="center" v-if='Role==1'>{{item.label_name}}</uni-td> <uni-td style="font-size: 20rpx;" align="center"> - <view v-if="item.paid ==1">已开通</view> + <view v-if="item.paid ==1 ||Role==4">已开通</view> <view v-else @click="upadtaStatus(item)" style="color:#33B83A ;">未开通,查询</view> </uni-td> </uni-tr> @@ -139,6 +162,9 @@ </view> </view> </up-popup> + <up-picker :show="showPop1" :columns="columns" @confirm='conformRole' @close="showPop1=false" @open="showPop1=true" + keyName='label_name' confirmColor='#33B83A'></up-picker> + </template> <script setup> import { @@ -158,13 +184,21 @@ rechargeCountApi, rechargeListsApi, updataOrderApi, - getStoreByPhone + getStoreByPhone, + getUserLabel, + getUserShip, + getCreateLists } from "@/api/user.js" import { - onPullDownRefresh + onPullDownRefresh, + onLoad } from "@dcloudio/uni-app" + const showPop1 = ref(false) + const Role = ref('') + const range = ref({}) + const columns = ref([]) const showModa = ref(true) const storePhone = ref('') const confirmStore = () => { @@ -174,11 +208,11 @@ for (let key in res.data) { STORE_INFO[key] = res.data[key] } + setPhoneOneDay() }).catch(err => { uni.$u.toast('未查到店铺信息,请检查手机号码') }) - } // 用户选择的门店信息 @@ -187,6 +221,30 @@ }) + const setPhoneOneDay = () => { + if (uni.getStorageSync('VIP_PHONE')) return; + const currentDate = new Date(); + const nextDay = new Date(currentDate); + nextDay.setDate(currentDate.getDate() + 1); + uni.setStorageSync('VIP_PHONE', JSON.stringify({ + time: nextDay, + phone: storePhone.value + })); + } + + const getPhoneOneDay = () => { + if (uni.getStorageSync('VIP_PHONE')) { + let data = JSON.parse(uni.getStorageSync('VIP_PHONE')) + if (new Date() > data.time) { + uni.removeStorageSync('VIP_PHONE'); + } else { + storePhone.value = data.phone + } + } + } + + + const currentTab = ref(1) const formData = reactive({ store_id: STORE_INFO.id, @@ -199,9 +257,16 @@ brigade: "", real_name: "", auth_code: "", - address: "" + address: "", + label_name: "", + label_id: "" }) + const conformRole = (e) => { + formData.label_name = e.value[0].label_name + formData.label_id = e.value[0].label_id + showPop1.value = false + } // 地址选择 const showPop = ref(false) @@ -306,29 +371,51 @@ if (!formData.real_name) return uni.$u.toast('请填写真实姓名'); if (!formData.mobile) return uni.$u.toast('请填写电话号码'); formData.store_id = STORE_INFO.id - uni.scanCode({ - success: function(res) { - formData.auth_code = res.result - vipRechargeApi(formData).then(res => { - uni.$u.toast('操作成功'); - currentTab.value = 2 - formData.real_name = '' - formData.mobile = '' - formData.address = '' - tabsList.forEach(item => { - item.name = '请选择' - }) + if (Role.value == 1) { + uni.scanCode({ + success: function(res) { + formData.auth_code = res.result + formData.recharge_type = 'INDUSTRYMEMBERS' + vipRechargeApi(formData).then(res => { + uni.$u.toast('操作成功'); + currentTab.value = 2 + formData.real_name = '' + formData.mobile = '' + formData.address = '' + formData.label_name = '' + tabsList.forEach(item => { + item.name = '请选择' + }) + }) + getCount() + getLists() + } + }); + } else { + delete formData.recharge_type + delete formData.auth_code + vipRechargeApi(formData).then(res => { + uni.$u.toast('操作成功'); + currentTab.value = 2 + formData.real_name = '' + formData.mobile = '' + formData.address = '' + formData.label_name = '' + tabsList.forEach(item => { + item.name = '请选择' }) - getCount() getLists() - } - }); + getCount() + }) + } + } // 邀请用户数 const count = ref(0) const getCount = async () => { + if (Role.value == 4) return; let res = await rechargeCountApi({ store_id: STORE_INFO.id }) @@ -338,11 +425,21 @@ // 邀请列表 const lists = ref([]) const getLists = async () => { - let res = await rechargeListsApi({ - store_id: STORE_INFO.id, - recharge_type: "INDUSTRYMEMBERS" - }) - lists.value = res.data.lists + if (Role.value == 1) { + let res = await rechargeListsApi({ + store_id: STORE_INFO.id, + recharge_type: "INDUSTRYMEMBERS" + }) + lists.value = res.data.lists + } else { + let res = await getCreateLists({ + store_id: STORE_INFO.id, + }) + lists.value = res.data.lists + count.value = res.data.count + + } + } getCount() @@ -354,10 +451,24 @@ order_no: item.order_id, recharge: 1 }) - - } + onLoad(() => { + getUserShip().then(res => { + console.log(res) + + range.value = res.data.lists.map(item => { + return { + value: item.id, + text: item.title + } + }) + }) + getUserLabel().then(res => { + columns.value = [res.data.lists] + }) + getPhoneOneDay() + }) onPullDownRefresh(() => { getCount() @@ -493,4 +604,8 @@ .uni-table-td { padding: 10rpx 0 !important; } + + .slot-content { + padding-bottom: 50rpx; + } </style> \ No newline at end of file diff --git a/pages.json b/pages.json index ed62957..49114dc 100644 --- a/pages.json +++ b/pages.json @@ -161,6 +161,13 @@ "navigationBarTitleText": "确认订单", "enablePullDownRefresh": false } + }, + { + "path": "setPayPassword/index", + "style": { + "navigationBarTitleText": "设置密码", + "enablePullDownRefresh": false + } } ] }, @@ -228,6 +235,8 @@ "enablePullDownRefresh": true } } + + ] } ], diff --git a/pages/index/index.vue b/pages/index/index.vue index e5bd703..1f2fcfd 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -145,12 +145,17 @@ <text>{{item.spec}}</text> </view> </view> - <view style="display: flex;" v-if='userStore?.userInfo?.user_ship==1'> - <view class="price" style="margin-right: 10rpx;">¥{{item.vip_price}}</view> - <text class='price'>会员价</text> + <view style="display: flex;" v-if='item.batch>0'> + <view>{{item.batch}}{{item.unit_name }}起卖</view> + </view> + <view style="display: flex;align-items: center;" v-if='userStore?.userInfo?.user_ship==1'> + <text class='price' style="font-size: 24rpx;">会员价¥</text> + <text class="price" style="margin-right: 10rpx;">{{item.vip_price}} + </text> + <text class='price' style="font-size: 24rpx;">/{{item.unit_name}}</text> </view> <view class="price-btn"> - <view class="price" style="font-size: 24rpx;">¥{{item.price}}</view> + <view class="price" style="font-size: 24rpx;">¥{{item.price}}/{{item.unit_name}}</view> <view class="btn"> <u--icon name="plus-circle-fill" size="20" color="#20b128"></u--icon> </view> diff --git a/pages/login/login.vue b/pages/login/login.vue index 35ce671..1f7d2df 100644 --- a/pages/login/login.vue +++ b/pages/login/login.vue @@ -16,10 +16,10 @@ <up-button @click="weixinLogin" color="#20B128" size="large"><up-icon name="weixin-fill" color="#fff" size="28"></up-icon>微信快捷登录</up-button> </view> - <!-- <view class="btn"> + <view class="btn" v-if="config.ENV=='dev'"> <up-button @click="navgo('/pages/login/test')" color="#20B128" size="large"><up-icon name="weixin-fill" color="#fff" size="28"></up-icon>账号登录</up-button> - </view> --> + </view> <!-- <view class="btn"> <up-button @click="officialCode" color="#20B128" size="large"><up-icon name="weixin-fill" color="#fff" size="28"></up-icon>公众号授权</up-button> @@ -96,6 +96,9 @@ import useUserStore from "@/store/user.js" import bindPhone from "@/components/bindPhone.vue" import modal from "@/components/modal.vue" + import { + config + } from "@/config/app.js" const showOfficial = ref(false); diff --git a/pages/my/my.vue b/pages/my/my.vue index 91f413a..7109249 100644 --- a/pages/my/my.vue +++ b/pages/my/my.vue @@ -108,7 +108,7 @@ <view class="card"> <up-cell-group> <up-cell title="我的地址" :isLink="true" url="/pagesOrder/addressList/addressList"></up-cell> - <!-- <up-cell title="支付密码" :isLink="true" url="/pagesOrder/setPayPassword/index"></up-cell> --> + <up-cell title="支付密码" :isLink="true" url="/pagesOrder/setPayPassword/index"></up-cell> </up-cell-group> </view> diff --git a/pagesOrder/setPayPassword/index.vue b/pagesOrder/setPayPassword/index.vue index ef8854e..c113ac9 100644 --- a/pagesOrder/setPayPassword/index.vue +++ b/pagesOrder/setPayPassword/index.vue @@ -1,38 +1,119 @@ <template> <view class='card'> - <up-input v-model="code" placeholder="手机号" border="none"></up-input> - <up-line color="#D3E3FD" style="margin: 30rpx 0;"></up-line> + <up-input v-model="form.phone" placeholder="手机号" border="none" readonly></up-input> + <view style="margin: 30rpx 0;"> + <up-line color="#D3E3FD"></up-line> + </view> <view style="display: flex;justify-content: space-between;align-items: center;"> - <up-input v-model="code" placeholder="验证码" border="none"></up-input> + <up-input v-model="form.code" placeholder="验证码" border="none" type='number'></up-input> <view class="code-btn"> - <up-line color="grey" direction="col" length="30rpx" style="margin: 0 20rpx;"></up-line> - <text class='btn-text'>获取验证码</text> + <view style="margin: 0 20rpx;"> + <up-line color="grey" direction="col" length="30rpx"></up-line> + </view> + <text class='btn-text' style="color: grey;" v-if='cutDown'>重新获取({{cutDown}})</text> + <text @click="getCode" class='btn-text' v-else> {{flag?'获取验证码':'重新获取' }} </text> </view> </view> </view> <view class='card'> - <up-input v-model="code" placeholder="请输入你的密码" border="none" type='password' readonly></up-input> - <up-line color="#D3E3FD" style="margin: 30rpx 0;"></up-line> - <up-input v-model="code" placeholder="请再次输入你的密码" border="none" type='password' readonly></up-input> + <view @click="showKeyBorad=true,type=1"> + <up-input style="pointer-events: none;" type='password' v-model="form.password" placeholder="请输入你的密码" + border="none" readonly></up-input> + </view> + <view style="margin: 30rpx 0;"> + <up-line color="#D3E3FD"></up-line> + </view> + <view @click="showKeyBorad=true,type=2"> + <up-input style="pointer-events: none;" type='password' v-model="form.rePassword" placeholder="请再次输入你的密码" + border="none" readonly></up-input> + </view> </view> <view class='submit-btn'> <up-button color="#20B128" shape="circle" @click="submit">确认</up-button> </view> - + <up-keyboard dotDisabled safeAreaInsetBottom ref="uKeyboard" @close="showKeyBorad=false" mode="number" + :tooltip="false" :show="showKeyBorad" @change='onKetDown' @backspace='backspaceFn' /> </template> <script setup> + import useUserStore from "@/store/user"; import { - ref + getMassageCode, + setPayPassword + } from "@/api/address.js" + import { + ref, + reactive } from "vue" + const userInfo = useUserStore().userInfo; + const form = reactive({ + phone: userInfo.mobile, //手机号 + code: "", //验证码 + password: "", //6位数密码 + rePassword: "" //确认密码 + }) - - + // 获取验证码 + const cutDown = ref(0) + const flag = ref(true) const code = ref('') - const submit = () => { + const checkPhone = (phone) => { + const regex = /^1[3-9]\d{9}$/; + return regex.test(phone) ? true : false + } + const getCode = async () => { + if (!checkPhone(form.phone)) return uni.$u.toast('请输入正确的手机号') + await getMassageCode() + flag.value = false + cutDown.value = 60 + let timer = setInterval(() => { + cutDown.value-- + if (cutDown.value <= 0) clearInterval(timer) + }, 1000) + } + // 获取验证码结束 + + // 键盘事件 + const showKeyBorad = ref(false) + const type = ref(1) // 1 输入密码 2 再次输入密码 + + const onKetDown = (e) => { + if (type.value == 1) { + form.password.length < 6 ? + form.password += e : + showKeyBorad.value = false + } else { + form.rePassword.length < 6 ? + form.rePassword += e : + showKeyBorad.value = false + } + } + + const backspaceFn = () => { + if (type.value == 1) { + form.password = form.password.slice(0, -1); + } else { + form.rePassword = form.rePassword.slice(0, -1); + } + } + + // 键盘事件结束 + + + + const submit = async () => { + if (!form.code) return uni.$u.toast('请输入验证码'); + if (form.password !== form.rePassword) return uni.$u.toast('两次密码不一致'); + await setPayPassword({ + ...form + }) + uni.$u.toast('设置成功') + setTimeout(() => { + uni.navigateBack() + }, 1000) } </script> diff --git a/pagesOrder/settle/settle.vue b/pagesOrder/settle/settle.vue index cbd5c67..4dc095a 100644 --- a/pagesOrder/settle/settle.vue +++ b/pagesOrder/settle/settle.vue @@ -67,7 +67,7 @@ <view class="row" v-if="[4,5,6].includes(userInfo.user_ship)" style="color: red;"> <view>优惠减免</view> <view> - <text>-¥</text>{{ c_price(orderInfo.preferential_amount, 0) }}<text>.{{ c_price(orderInfo.preferential_amount, 1) }}</text> + <text>-¥</text>{{ c_price(orderInfo.activity_price, 0) }}<text>.{{ c_price(orderInfo.activity_price, 1) }}</text> </view> </view> <view class="row" v-if="userInfo.user_ship==1 "> @@ -93,11 +93,12 @@ <view class="row" v-if="userInfo.user_ship == 4 || userInfo.user_ship == 5 || userInfo.user_ship == 6 || userInfo.user_ship == 1"> - <!-- <view class="row"> --> + <!-- <view class="row"> --> <view class="icon-text"> <image src="@/static/icon/YEZF.png" style="width:40rpx;height: 40rpx;" /> <text style="margin-left: 20rpx;font-size: 26rpx;">余额支付</text> - <text style="margin-left: 20rpx;font-size: 22rpx;color: #FFB76D;">( 可用¥{{userInfo.now_money}} )</text> + <text style="margin-left: 20rpx;font-size: 22rpx;color: #FFB76D;">( 可用¥{{userInfo.now_money}} + )</text> </view> <view class="icon" @click="onChoosePaytype(3)"> <image v-if="pay_type == 3" src="@/static/icon/check.png" /> @@ -110,7 +111,8 @@ <view class="icon-text"> <image src="@/static/icon/cgkzf.png" style="width:40rpx;height: 40rpx;" /> <text style="margin-left: 20rpx;font-size: 26rpx;">采购款支付</text> - <text style="margin-left: 20rpx;font-size: 22rpx;color: #1296DB;">( 可用¥{{userInfo.purchase_funds}} )</text> + <text style="margin-left: 20rpx;font-size: 22rpx;color: #1296DB;">( 可用¥{{userInfo.purchase_funds}} + )</text> </view> <view class="icon" @click="onChoosePaytype(18)"> <image v-if="pay_type == 18" src="@/static/icon/check.png" /> @@ -152,8 +154,11 @@ @change="changeShop" @search="searchShop" /> <modal title="尚未设置收货地址" content="您还没有添加收货地址,请点击添加" cancleText="添加地址" confirmText="继续支付" :show="toastAddressShow" @close="addAddress" @change="goPay" /> - <!-- <modal title="是否要拨打电话" :content="`即将拨打电话${phone}`" cancleText="取消" confirmText="拨打" :show="callShow" @close="callShow = false" - @change="onCall" /> --> + <ZyPasswordboard v-if='passwordBoardVisible' v-model:visible="passwordBoardVisible" v-bind="passwordBoardProps" + @close='closeKeyBord' /> + <up-modal :show="showModal" title="您还没设置密码" :closeOnClickOverlay="true" zoom confirmText='去设置' showCancelButton + @close='showModal=false' @cancel='showModal=false' @confirm="navgo('/pagesOrder/setPayPassword/index')" + confirmColor='#27B52F' cancelText='取消'></up-modal> </view> </template> @@ -175,17 +180,16 @@ checkOrderApi } from "@/api/cart.js"; import { + userInfoApi, addressListsApi, merchantListApi } from "@/api/user.js"; import { createOrderApi } from "@/api/order.js"; + import ZyPasswordboard from '@/uni_modules/zy-passwordboard/components/zy-passwordboard/zy-passwordboard.vue'; const userInfo = useUserStore().userInfo; - - console.log(userInfo) - // 用户选择的门店信息 let STORE_INFO = uni.getStorageSync('STORE_INFO'); if (STORE_INFO) @@ -297,14 +301,6 @@ const searchShop = (e) => { getMerchantList(e) } - - // 拨打电话 - // const callShow = ref(false) - // const phone = ref(''); - // const callphone = (e) => { - // callShow.value = true; - // phone.value = e; - // } const onCall = (e) => { uni.makePhoneCall({ phoneNumber: e, @@ -357,10 +353,28 @@ const pay_type = ref('7'); - const createOrder = () => { - if (!pay_type.value) return uni.$u.toast('请选择支付方式'); + + // 支付密码 + const passwordBoardVisible = ref(false); + + const passwordBoardProps = { + title: '输入支付密码', + onComplete(value) { + password.value = value + passwordBoardVisible.value = false + payFn() + } + }; + + const closeKeyBord = () => { + password.value = '' + } + + const password = ref('') + const payFn = () => { let shareInfo = uni.getStorageSync('SHARE_INFO'); createOrderApi({ + password: password.value, spread_uid: (shareInfo && shareInfo.uid) ? shareInfo.uid : '', cart_id: cartStore.cartList, address_id: addressInfo.value.address_id, @@ -423,6 +437,24 @@ }) } + const showModal = ref(false) + const navgo = (url) => { + showModal.value &&= false + uni.navigateTo({ + url + }) + } + + const createOrder = async () => { + if (!pay_type.value) return uni.$u.toast('请选择支付方式'); + if (pay_type.value == 3 || pay_type.value == 18) { + let res = await userInfoApi() + return res.data.pay_password ? passwordBoardVisible.value = true : showModal.value = true + } + payFn() + } + + const c_price = (price, index = 0) => { price = price + ''; return price.split('.')[index] || (index ? '00' : '0'); diff --git a/uni_modules/uni-data-select/changelog.md b/uni_modules/uni-data-select/changelog.md new file mode 100644 index 0000000..016e3d2 --- /dev/null +++ b/uni_modules/uni-data-select/changelog.md @@ -0,0 +1,39 @@ +## 1.0.8(2024-03-28) +- 修复 在vue2下:style动态绑定导致编译失败的bug +## 1.0.7(2024-01-20) +- 修复 长文本回显超过容器的bug,超过容器部分显示省略号 +## 1.0.6(2023-04-12) +- 修复 微信小程序点击时会改变背景颜色的 bug +## 1.0.5(2023-02-03) +- 修复 禁用时会显示清空按钮 +## 1.0.4(2023-02-02) +- 优化 查询条件短期内多次变更只查询最后一次变更后的结果 +- 调整 内部缓存键名调整为 uni-data-select-lastSelectedValue +## 1.0.3(2023-01-16) +- 修复 不关联服务空间报错的问题 +## 1.0.2(2023-01-14) +- 新增 属性 `format` 可用于格式化显示选项内容 +## 1.0.1(2022-12-06) +- 修复 当where变化时,数据不会自动更新的问题 +## 0.1.9(2022-09-05) +- 修复 微信小程序下拉框出现后选择会点击到蒙板后面的输入框 +## 0.1.8(2022-08-29) +- 修复 点击的位置不准确 +## 0.1.7(2022-08-12) +- 新增 支持 disabled 属性 +## 0.1.6(2022-07-06) +- 修复 pc端宽度异常的bug +## 0.1.5 +- 修复 pc端宽度异常的bug +## 0.1.4(2022-07-05) +- 优化 显示样式 +## 0.1.3(2022-06-02) +- 修复 localdata 赋值不生效的 bug +- 新增 支持 uni.scss 修改颜色 +- 新增 支持选项禁用(数据选项设置 disabled: true 即禁用) +## 0.1.2(2022-05-08) +- 修复 当 value 为 0 时选择不生效的 bug +## 0.1.1(2022-05-07) +- 新增 记住上次的选项(仅 collection 存在时有效) +## 0.1.0(2022-04-22) +- 初始化 diff --git a/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue b/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue new file mode 100644 index 0000000..edab65a --- /dev/null +++ b/uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue @@ -0,0 +1,562 @@ +<template> + <view class="uni-stat__select"> + <span v-if="label" class="uni-label-text hide-on-phone">{{label + ':'}}</span> + <view class="uni-stat-box" :class="{'uni-stat__actived': current}"> + <view class="uni-select" :class="{'uni-select--disabled':disabled}"> + <view class="uni-select__input-box" @click="toggleSelector"> + <view v-if="current" class="uni-select__input-text">{{textShow}}</view> + <view v-else class="uni-select__input-text uni-select__input-placeholder">{{typePlaceholder}}</view> + <view v-if="current && clear && !disabled" @click.stop="clearVal"> + <uni-icons type="clear" color="#c0c4cc" size="24" /> + </view> + <view v-else> + <uni-icons :type="showSelector? 'top' : 'bottom'" size="14" color="#999" /> + </view> + </view> + <view class="uni-select--mask" v-if="showSelector" @click="toggleSelector" /> + <view class="uni-select__selector" :style="getOffsetByPlacement" v-if="showSelector"> + <view :class="placement=='bottom'?'uni-popper__arrow_bottom':'uni-popper__arrow_top'"></view> + <scroll-view scroll-y="true" class="uni-select__selector-scroll"> + <view class="uni-select__selector-empty" v-if="mixinDatacomResData.length === 0"> + <text>{{emptyTips}}</text> + </view> + <view v-else class="uni-select__selector-item" v-for="(item,index) in mixinDatacomResData" :key="index" + @click="change(item)"> + <text :class="{'uni-select__selector__disabled': item.disable}">{{formatItemName(item)}}</text> + </view> + </scroll-view> + </view> + </view> + </view> + </view> +</template> + +<script> + /** + * DataChecklist 数据选择器 + * @description 通过数据渲染的下拉框组件 + * @tutorial https://uniapp.dcloud.io/component/uniui/uni-data-select + * @property {String} value 默认值 + * @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}] + * @property {Boolean} clear 是否可以清空已选项 + * @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效 + * @property {String} label 左侧标题 + * @property {String} placeholder 输入框的提示文字 + * @property {Boolean} disabled 是否禁用 + * @property {String} placement 弹出位置 + * @value top 顶部弹出 + * @value bottom 底部弹出(default) + * @event {Function} change 选中发生变化触发 + */ + + export default { + name: "uni-data-select", + mixins: [uniCloud.mixinDatacom || {}], + props: { + localdata: { + type: Array, + default () { + return [] + } + }, + value: { + type: [String, Number], + default: '' + }, + modelValue: { + type: [String, Number], + default: '' + }, + label: { + type: String, + default: '' + }, + placeholder: { + type: String, + default: '请选择' + }, + emptyTips: { + type: String, + default: '无选项' + }, + clear: { + type: Boolean, + default: true + }, + defItem: { + type: Number, + default: 0 + }, + disabled: { + type: Boolean, + default: false + }, + // 格式化输出 用法 field="_id as value, version as text, uni_platform as label" format="{label} - {text}" + format: { + type: String, + default: '' + }, + placement: { + type: String, + default: 'bottom' + } + }, + data() { + return { + showSelector: false, + current: '', + mixinDatacomResData: [], + apps: [], + channels: [], + cacheKey: "uni-data-select-lastSelectedValue", + }; + }, + created() { + this.debounceGet = this.debounce(() => { + this.query(); + }, 300); + if (this.collection && !this.localdata.length) { + this.debounceGet(); + } + }, + computed: { + typePlaceholder() { + const text = { + 'opendb-stat-app-versions': '版本', + 'opendb-app-channels': '渠道', + 'opendb-app-list': '应用' + } + const common = this.placeholder + const placeholder = text[this.collection] + return placeholder ? + common + placeholder : + common + }, + valueCom() { + // #ifdef VUE3 + return this.modelValue; + // #endif + // #ifndef VUE3 + return this.value; + // #endif + }, + textShow() { + // 长文本显示 + let text = this.current; + if (text.length > 10) { + return text.slice(0, 25) + '...'; + } + return text; + }, + getOffsetByPlacement() { + switch (this.placement) { + case 'top': + return "bottom:calc(100% + 12px);"; + case 'bottom': + return "top:calc(100% + 12px);"; + } + } + }, + + watch: { + localdata: { + immediate: true, + handler(val, old) { + if (Array.isArray(val) && old !== val) { + this.mixinDatacomResData = val + } + } + }, + valueCom(val, old) { + this.initDefVal() + }, + mixinDatacomResData: { + immediate: true, + handler(val) { + if (val.length) { + this.initDefVal() + } + } + }, + + }, + methods: { + debounce(fn, time = 100) { + let timer = null + return function(...args) { + if (timer) clearTimeout(timer) + timer = setTimeout(() => { + fn.apply(this, args) + }, time) + } + }, + // 执行数据库查询 + query() { + this.mixinDatacomEasyGet(); + }, + // 监听查询条件变更事件 + onMixinDatacomPropsChange() { + if (this.collection) { + this.debounceGet(); + } + }, + initDefVal() { + let defValue = '' + if ((this.valueCom || this.valueCom === 0) && !this.isDisabled(this.valueCom)) { + defValue = this.valueCom + } else { + let strogeValue + if (this.collection) { + strogeValue = this.getCache() + } + if (strogeValue || strogeValue === 0) { + defValue = strogeValue + } else { + let defItem = '' + if (this.defItem > 0 && this.defItem <= this.mixinDatacomResData.length) { + defItem = this.mixinDatacomResData[this.defItem - 1].value + } + defValue = defItem + } + if (defValue || defValue === 0) { + this.emit(defValue) + } + } + const def = this.mixinDatacomResData.find(item => item.value === defValue) + this.current = def ? this.formatItemName(def) : '' + }, + + /** + * @param {[String, Number]} value + * 判断用户给的 value 是否同时为禁用状态 + */ + isDisabled(value) { + let isDisabled = false; + + this.mixinDatacomResData.forEach(item => { + if (item.value === value) { + isDisabled = item.disable + } + }) + + return isDisabled; + }, + + clearVal() { + this.emit('') + if (this.collection) { + this.removeCache() + } + }, + change(item) { + if (!item.disable) { + this.showSelector = false + this.current = this.formatItemName(item) + this.emit(item.value) + } + }, + emit(val) { + this.$emit('input', val) + this.$emit('update:modelValue', val) + this.$emit('change', val) + if (this.collection) { + this.setCache(val); + } + }, + toggleSelector() { + if (this.disabled) { + return + } + + this.showSelector = !this.showSelector + }, + formatItemName(item) { + let { + text, + value, + channel_code + } = item + channel_code = channel_code ? `(${channel_code})` : '' + + if (this.format) { + // 格式化输出 + let str = ""; + str = this.format; + for (let key in item) { + str = str.replace(new RegExp(`{${key}}`, "g"), item[key]); + } + return str; + } else { + return this.collection.indexOf('app-list') > 0 ? + `${text}(${value})` : + ( + text ? + text : + `未命名${channel_code}` + ) + } + }, + // 获取当前加载的数据 + getLoadData() { + return this.mixinDatacomResData; + }, + // 获取当前缓存key + getCurrentCacheKey() { + return this.collection; + }, + // 获取缓存 + getCache(name = this.getCurrentCacheKey()) { + let cacheData = uni.getStorageSync(this.cacheKey) || {}; + return cacheData[name]; + }, + // 设置缓存 + setCache(value, name = this.getCurrentCacheKey()) { + let cacheData = uni.getStorageSync(this.cacheKey) || {}; + cacheData[name] = value; + uni.setStorageSync(this.cacheKey, cacheData); + }, + // 删除缓存 + removeCache(name = this.getCurrentCacheKey()) { + let cacheData = uni.getStorageSync(this.cacheKey) || {}; + delete cacheData[name]; + uni.setStorageSync(this.cacheKey, cacheData); + }, + } + } +</script> + +<style lang="scss"> + $uni-base-color: #6a6a6a !default; + $uni-main-color: #333 !default; + $uni-secondary-color: #909399 !default; + $uni-border-3: #e5e5e5; + + /* #ifndef APP-NVUE */ + @media screen and (max-width: 500px) { + .hide-on-phone { + display: none; + } + } + + /* #endif */ + .uni-stat__select { + display: flex; + align-items: center; + // padding: 15px; + /* #ifdef H5 */ + cursor: pointer; + /* #endif */ + width: 100%; + flex: 1; + box-sizing: border-box; + } + + .uni-stat-box { + width: 100%; + flex: 1; + } + + .uni-stat__actived { + width: 100%; + flex: 1; + // outline: 1px solid #2979ff; + } + + .uni-label-text { + font-size: 14px; + font-weight: bold; + color: $uni-base-color; + margin: auto 0; + margin-right: 5px; + } + + .uni-select { + font-size: 14px; + border: 1px solid $uni-border-3; + box-sizing: border-box; + border-radius: 4px; + padding: 0 5px; + padding-left: 10px; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + user-select: none; + /* #endif */ + flex-direction: row; + align-items: center; + border-bottom: solid 1px $uni-border-3; + width: 100%; + flex: 1; + height: 35px; + + &--disabled { + background-color: #f5f7fa; + cursor: not-allowed; + } + } + + .uni-select__label { + font-size: 16px; + // line-height: 22px; + height: 35px; + padding-right: 10px; + color: $uni-secondary-color; + } + + .uni-select__input-box { + height: 35px; + position: relative; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; + flex-direction: row; + align-items: center; + } + + .uni-select__input { + flex: 1; + font-size: 14px; + height: 22px; + line-height: 22px; + } + + .uni-select__input-plac { + font-size: 14px; + color: $uni-secondary-color; + } + + .uni-select__selector { + /* #ifndef APP-NVUE */ + box-sizing: border-box; + /* #endif */ + position: absolute; + left: 0; + width: 100%; + background-color: #FFFFFF; + border: 1px solid #EBEEF5; + border-radius: 6px; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); + z-index: 3; + padding: 4px 0; + } + + .uni-select__selector-scroll { + /* #ifndef APP-NVUE */ + max-height: 200px; + box-sizing: border-box; + /* #endif */ + } + + /* #ifdef H5 */ + @media (min-width: 768px) { + .uni-select__selector-scroll { + max-height: 600px; + } + } + + /* #endif */ + + .uni-select__selector-empty, + .uni-select__selector-item { + /* #ifndef APP-NVUE */ + display: flex; + cursor: pointer; + /* #endif */ + line-height: 35px; + font-size: 14px; + text-align: center; + /* border-bottom: solid 1px $uni-border-3; */ + padding: 0px 10px; + } + + .uni-select__selector-item:hover { + background-color: #f9f9f9; + } + + .uni-select__selector-empty:last-child, + .uni-select__selector-item:last-child { + /* #ifndef APP-NVUE */ + border-bottom: none; + /* #endif */ + } + + .uni-select__selector__disabled { + opacity: 0.4; + cursor: default; + } + + /* picker 弹出层通用的指示小三角 */ + .uni-popper__arrow_bottom, + .uni-popper__arrow_bottom::after, + .uni-popper__arrow_top, + .uni-popper__arrow_top::after, + { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + border-width: 6px; + } + + .uni-popper__arrow_bottom { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + top: -6px; + left: 10%; + margin-right: 3px; + border-top-width: 0; + border-bottom-color: #EBEEF5; + } + + .uni-popper__arrow_bottom::after { + content: " "; + top: 1px; + margin-left: -6px; + border-top-width: 0; + border-bottom-color: #fff; + } + + .uni-popper__arrow_top { + filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03)); + bottom: -6px; + left: 10%; + margin-right: 3px; + border-bottom-width: 0; + border-top-color: #EBEEF5; + } + + .uni-popper__arrow_top::after { + content: " "; + bottom: 1px; + margin-left: -6px; + border-bottom-width: 0; + border-top-color: #fff; + } + + + .uni-select__input-text { + // width: 280px; + width: 100%; + color: $uni-main-color; + white-space: nowrap; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + overflow: hidden; + } + + .uni-select__input-placeholder { + color: $uni-base-color; + font-size: 12px; + } + + .uni-select--mask { + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + z-index: 2; + } +</style> diff --git a/uni_modules/uni-data-select/package.json b/uni_modules/uni-data-select/package.json new file mode 100644 index 0000000..5864594 --- /dev/null +++ b/uni_modules/uni-data-select/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-data-select", + "displayName": "uni-data-select 下拉框选择器", + "version": "1.0.8", + "description": "通过数据驱动的下拉框选择器", + "keywords": [ + "uni-ui", + "select", + "uni-data-select", + "下拉框", + "下拉选" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.1.1" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-load-more"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-data-select/readme.md b/uni_modules/uni-data-select/readme.md new file mode 100644 index 0000000..eb58de3 --- /dev/null +++ b/uni_modules/uni-data-select/readme.md @@ -0,0 +1,8 @@ +## DataSelect 下拉框选择器 +> **组件名:uni-data-select** +> 代码块: `uDataSelect` + +当选项过多时,使用下拉菜单展示并选择内容 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-data-select) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-load-more/changelog.md b/uni_modules/uni-load-more/changelog.md new file mode 100644 index 0000000..8f03f1d --- /dev/null +++ b/uni_modules/uni-load-more/changelog.md @@ -0,0 +1,19 @@ +## 1.3.3(2022-01-20) +- 新增 showText属性 ,是否显示文本 +## 1.3.2(2022-01-19) +- 修复 nvue 平台下不显示文本的bug +## 1.3.1(2022-01-19) +- 修复 微信小程序平台样式选择器报警告的问题 +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more) +## 1.2.1(2021-08-24) +- 新增 支持国际化 +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.8(2021-05-12) +- 新增 组件示例地址 +## 1.1.7(2021-03-30) +- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug +## 1.1.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json new file mode 100644 index 0000000..a4f14a5 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "Pull up to show more", + "uni-load-more.contentrefresh": "loading...", + "uni-load-more.contentnomore": "No more data" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json new file mode 100644 index 0000000..f15d510 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉显示更多", + "uni-load-more.contentrefresh": "正在加载...", + "uni-load-more.contentnomore": "没有更多数据了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json new file mode 100644 index 0000000..a255c6d --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉顯示更多", + "uni-load-more.contentrefresh": "正在加載...", + "uni-load-more.contentnomore": "沒有更多數據了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue new file mode 100644 index 0000000..e5eff4d --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue @@ -0,0 +1,399 @@ +<template> + <view class="uni-load-more" @click="onClick"> + <!-- #ifdef APP-NVUE --> + <loading-indicator v-if="!webviewHide && status === 'loading' && showIcon" + :style="{color: color,width:iconSize+'px',height:iconSize+'px'}" :animating="true" + class="uni-load-more__img uni-load-more__img--nvue"></loading-indicator> + <!-- #endif --> + <!-- #ifdef H5 --> + <svg width="24" height="24" viewBox="25 25 50 50" + v-if="!webviewHide && (iconType==='circle' || iconType==='auto' && platform === 'android') && status === 'loading' && showIcon" + :style="{width:iconSize+'px',height:iconSize+'px'}" + class="uni-load-more__img uni-load-more__img--android-H5"> + <circle cx="50" cy="50" r="20" fill="none" :style="{color:color}" :stroke-width="3"></circle> + </svg> + <!-- #endif --> + <!-- #ifndef APP-NVUE || H5 --> + <view + v-if="!webviewHide && (iconType==='circle' || iconType==='auto' && platform === 'android') && status === 'loading' && showIcon" + :style="{width:iconSize+'px',height:iconSize+'px'}" + class="uni-load-more__img uni-load-more__img--android-MP"> + <view class="uni-load-more__img-icon" :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> + <view class="uni-load-more__img-icon" :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> + <view class="uni-load-more__img-icon" :style="{borderTopColor:color,borderTopWidth:iconSize/12}"></view> + </view> + <!-- #endif --> + <!-- #ifndef APP-NVUE --> + <view v-else-if="!webviewHide && status === 'loading' && showIcon" + :style="{width:iconSize+'px',height:iconSize+'px'}" class="uni-load-more__img uni-load-more__img--ios-H5"> + <image :src="imgBase64" mode="widthFix"></image> + </view> + <!-- #endif --> + <text v-if="showText" class="uni-load-more__text" + :style="{color: color}">{{ status === 'more' ? contentdownText : status === 'loading' ? contentrefreshText : contentnomoreText }}</text> + </view> +</template> + +<script> + let platform + setTimeout(() => { + platform = uni.getSystemInfoSync().platform + }, 16) + + import { + initVueI18n + } from '@dcloudio/uni-i18n' + import messages from './i18n/index.js' + const { + t + } = initVueI18n(messages) + + /** + * LoadMore 加载更多 + * @description 用于列表中,做滚动加载使用,展示 loading 的各种状态 + * @tutorial https://ext.dcloud.net.cn/plugin?id=29 + * @property {String} status = [more|loading|noMore] loading 的状态 + * @value more loading前 + * @value loading loading中 + * @value noMore 没有更多了 + * @property {Number} iconSize 指定图标大小 + * @property {Boolean} iconSize = [true|false] 是否显示 loading 图标 + * @property {String} iconType = [snow|circle|auto] 指定图标样式 + * @value snow ios雪花加载样式 + * @value circle 安卓唤醒加载样式 + * @value auto 根据平台自动选择加载样式 + * @property {String} color 图标和文字颜色 + * @property {Object} contentText 各状态文字说明,值为:{contentdown: "上拉显示更多",contentrefresh: "正在加载...",contentnomore: "没有更多数据了"} + * @event {Function} clickLoadMore 点击加载更多时触发 + */ + export default { + name: 'UniLoadMore', + emits: ['clickLoadMore'], + props: { + status: { + // 上拉的状态:more-loading前;loading-loading中;noMore-没有更多了 + type: String, + default: 'more' + }, + showIcon: { + type: Boolean, + default: true + }, + iconType: { + type: String, + default: 'auto' + }, + iconSize: { + type: Number, + default: 24 + }, + color: { + type: String, + default: '#777777' + }, + contentText: { + type: Object, + default () { + return { + contentdown: '', + contentrefresh: '', + contentnomore: '' + } + } + }, + showText: { + type: Boolean, + default: true + } + }, + data() { + return { + webviewHide: false, + platform: platform, + imgBase64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzlBMzU3OTlEOUM0MTFFOUI0NTZDNERBQURBQzI4RkUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzlBMzU3OUFEOUM0MTFFOUI0NTZDNERBQURBQzI4RkUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDOUEzNTc5N0Q5QzQxMUU5QjQ1NkM0REFBREFDMjhGRSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDOUEzNTc5OEQ5QzQxMUU5QjQ1NkM0REFBREFDMjhGRSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pt+ALSwAAA6CSURBVHja1FsLkFZV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' + } + }, + computed: { + iconSnowWidth() { + return (Math.floor(this.iconSize / 24) || 1) * 2 + }, + contentdownText() { + return this.contentText.contentdown || t("uni-load-more.contentdown") + }, + contentrefreshText() { + return this.contentText.contentrefresh || t("uni-load-more.contentrefresh") + }, + contentnomoreText() { + return this.contentText.contentnomore || t("uni-load-more.contentnomore") + } + }, + mounted() { + // #ifdef APP-PLUS + var pages = getCurrentPages(); + var page = pages[pages.length - 1]; + var currentWebview = page.$getAppWebview(); + currentWebview.addEventListener('hide', () => { + this.webviewHide = true + }) + currentWebview.addEventListener('show', () => { + this.webviewHide = false + }) + // #endif + }, + methods: { + onClick() { + this.$emit('clickLoadMore', { + detail: { + status: this.status, + } + }) + } + } + } +</script> + +<style lang="scss" > + .uni-load-more { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: row; + height: 40px; + align-items: center; + justify-content: center; + } + + .uni-load-more__text { + font-size: 14px; + margin-left: 8px; + } + + .uni-load-more__img { + width: 24px; + height: 24px; + // margin-right: 8px; + } + + .uni-load-more__img--nvue { + color: #666666; + } + + .uni-load-more__img--android, + .uni-load-more__img--ios { + width: 24px; + height: 24px; + transform: rotate(0deg); + } + + /* #ifndef APP-NVUE */ + .uni-load-more__img--android { + animation: loading-ios 1s 0s linear infinite; + } + + @keyframes loading-android { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + .uni-load-more__img--ios-H5 { + position: relative; + animation: loading-ios-H5 1s 0s step-end infinite; + } + + .uni-load-more__img--ios-H5 image { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + } + + @keyframes loading-ios-H5 { + 0% { + transform: rotate(0deg); + } + + 8% { + transform: rotate(30deg); + } + + 16% { + transform: rotate(60deg); + } + + 24% { + transform: rotate(90deg); + } + + 32% { + transform: rotate(120deg); + } + + 40% { + transform: rotate(150deg); + } + + 48% { + transform: rotate(180deg); + } + + 56% { + transform: rotate(210deg); + } + + 64% { + transform: rotate(240deg); + } + + 73% { + transform: rotate(270deg); + } + + 82% { + transform: rotate(300deg); + } + + 91% { + transform: rotate(330deg); + } + + 100% { + transform: rotate(360deg); + } + } + + /* #endif */ + + /* #ifdef H5 */ + .uni-load-more__img--android-H5 { + animation: loading-android-H5-rotate 2s linear infinite; + transform-origin: center center; + } + + .uni-load-more__img--android-H5 circle { + display: inline-block; + animation: loading-android-H5-dash 1.5s ease-in-out infinite; + stroke: currentColor; + stroke-linecap: round; + } + + @keyframes loading-android-H5-rotate { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-H5-dash { + 0% { + stroke-dasharray: 1, 200; + stroke-dashoffset: 0; + } + + 50% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -40; + } + + 100% { + stroke-dasharray: 90, 150; + stroke-dashoffset: -120; + } + } + + /* #endif */ + + /* #ifndef APP-NVUE || H5 */ + .uni-load-more__img--android-MP { + position: relative; + width: 24px; + height: 24px; + transform: rotate(0deg); + animation: loading-ios 1s 0s ease infinite; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + border-radius: 50%; + border: solid 2px transparent; + border-top: solid 2px #777777; + transform-origin: center; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon:nth-child(1) { + animation: loading-android-MP-1 1s 0s linear infinite; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon:nth-child(2) { + animation: loading-android-MP-2 1s 0s linear infinite; + } + + .uni-load-more__img--android-MP .uni-load-more__img-icon:nth-child(3) { + animation: loading-android-MP-3 1s 0s linear infinite; + } + + @keyframes loading-android { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-MP-1 { + 0% { + transform: rotate(0deg); + } + + 50% { + transform: rotate(90deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-MP-2 { + 0% { + transform: rotate(0deg); + } + + 50% { + transform: rotate(180deg); + } + + 100% { + transform: rotate(360deg); + } + } + + @keyframes loading-android-MP-3 { + 0% { + transform: rotate(0deg); + } + + 50% { + transform: rotate(270deg); + } + + 100% { + transform: rotate(360deg); + } + } + + /* #endif */ +</style> diff --git a/uni_modules/uni-load-more/package.json b/uni_modules/uni-load-more/package.json new file mode 100644 index 0000000..2fa6f04 --- /dev/null +++ b/uni_modules/uni-load-more/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-load-more", + "displayName": "uni-load-more 加载更多", + "version": "1.3.3", + "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。", + "keywords": [ + "uni-ui", + "uniui", + "加载更多", + "load-more" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-load-more/readme.md b/uni_modules/uni-load-more/readme.md new file mode 100644 index 0000000..54dc1fa --- /dev/null +++ b/uni_modules/uni-load-more/readme.md @@ -0,0 +1,14 @@ + + +### LoadMore 加载更多 +> **组件名:uni-load-more** +> 代码块: `uLoadMore` + + +用于列表中,做滚动加载使用,展示 loading 的各种状态。 + + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-transition/changelog.md b/uni_modules/uni-transition/changelog.md new file mode 100644 index 0000000..faaf336 --- /dev/null +++ b/uni_modules/uni-transition/changelog.md @@ -0,0 +1,24 @@ +## 1.3.3(2024-04-23) +- 修复 当元素会受变量影响自动隐藏的bug +## 1.3.2(2023-05-04) +- 修复 NVUE 平台报错的问题 +## 1.3.1(2021-11-23) +- 修复 init 方法初始化问题 +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition) +## 1.2.1(2021-09-27) +- 修复 init 方法不生效的 Bug +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.1(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的 Bug +## 1.1.0(2021-04-22) +- 新增 通过方法自定义动画 +- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 +- 优化 动画触发逻辑,使动画更流畅 +- 优化 支持单独的动画类型 +- 优化 文档示例 +## 1.0.2(2021-02-05) +- 调整为 uni_modules 目录规范 diff --git a/uni_modules/uni-transition/components/uni-transition/createAnimation.js b/uni_modules/uni-transition/components/uni-transition/createAnimation.js new file mode 100644 index 0000000..8f89b18 --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/createAnimation.js @@ -0,0 +1,131 @@ +// const defaultOption = { +// duration: 300, +// timingFunction: 'linear', +// delay: 0, +// transformOrigin: '50% 50% 0' +// } +// #ifdef APP-NVUE +const nvueAnimation = uni.requireNativePlugin('animation') +// #endif +class MPAnimation { + constructor(options, _this) { + this.options = options + // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误 + this.animation = uni.createAnimation({ + ...options + }) + this.currentStepAnimates = {} + this.next = 0 + this.$ = _this + + } + + _nvuePushAnimates(type, args) { + let aniObj = this.currentStepAnimates[this.next] + let styles = {} + if (!aniObj) { + styles = { + styles: {}, + config: {} + } + } else { + styles = aniObj + } + if (animateTypes1.includes(type)) { + if (!styles.styles.transform) { + styles.styles.transform = '' + } + let unit = '' + if(type === 'rotate'){ + unit = 'deg' + } + styles.styles.transform += `${type}(${args+unit}) ` + } else { + styles.styles[type] = `${args}` + } + this.currentStepAnimates[this.next] = styles + } + _animateRun(styles = {}, config = {}) { + let ref = this.$.$refs['ani'].ref + if (!ref) return + return new Promise((resolve, reject) => { + nvueAnimation.transition(ref, { + styles, + ...config + }, res => { + resolve() + }) + }) + } + + _nvueNextAnimate(animates, step = 0, fn) { + let obj = animates[step] + if (obj) { + let { + styles, + config + } = obj + this._animateRun(styles, config).then(() => { + step += 1 + this._nvueNextAnimate(animates, step, fn) + }) + } else { + this.currentStepAnimates = {} + typeof fn === 'function' && fn() + this.isEnd = true + } + } + + step(config = {}) { + // #ifndef APP-NVUE + this.animation.step(config) + // #endif + // #ifdef APP-NVUE + this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) + this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin + this.next++ + // #endif + return this + } + + run(fn) { + // #ifndef APP-NVUE + this.$.animationData = this.animation.export() + this.$.timer = setTimeout(() => { + typeof fn === 'function' && fn() + }, this.$.durationTime) + // #endif + // #ifdef APP-NVUE + this.isEnd = false + let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref + if(!ref) return + this._nvueNextAnimate(this.currentStepAnimates, 0, fn) + this.next = 0 + // #endif + } +} + + +const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', + 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', + 'translateZ' +] +const animateTypes2 = ['opacity', 'backgroundColor'] +const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom'] +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + MPAnimation.prototype[type] = function(...args) { + // #ifndef APP-NVUE + this.animation[type](...args) + // #endif + // #ifdef APP-NVUE + this._nvuePushAnimates(type, args) + // #endif + return this + } +}) + +export function createAnimation(option, _this) { + if(!_this) return + clearTimeout(_this.timer) + return new MPAnimation(option, _this) +} diff --git a/uni_modules/uni-transition/components/uni-transition/uni-transition.vue b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue new file mode 100644 index 0000000..f3ddd1f --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue @@ -0,0 +1,286 @@ +<template> + <!-- #ifndef APP-NVUE --> + <view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> + <!-- #endif --> + <!-- #ifdef APP-NVUE --> + <view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view> + <!-- #endif --> +</template> + +<script> +import { createAnimation } from './createAnimation' + +/** + * Transition 过渡动画 + * @description 简单过渡动画组件 + * @tutorial https://ext.dcloud.net.cn/plugin?id=985 + * @property {Boolean} show = [false|true] 控制组件显示或隐藏 + * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型 + * @value fade 渐隐渐出过渡 + * @value slide-top 由上至下过渡 + * @value slide-right 由右至左过渡 + * @value slide-bottom 由下至上过渡 + * @value slide-left 由左至右过渡 + * @value zoom-in 由小到大过渡 + * @value zoom-out 由大到小过渡 + * @property {Number} duration 过渡动画持续时间 + * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red` + */ +export default { + name: 'uniTransition', + emits:['click','change'], + props: { + show: { + type: Boolean, + default: false + }, + modeClass: { + type: [Array, String], + default() { + return 'fade' + } + }, + duration: { + type: Number, + default: 300 + }, + styles: { + type: Object, + default() { + return {} + } + }, + customClass:{ + type: String, + default: '' + }, + onceRender:{ + type:Boolean, + default:false + }, + }, + data() { + return { + isShow: false, + transform: '', + opacity: 1, + animationData: {}, + durationTime: 300, + config: {} + } + }, + watch: { + show: { + handler(newVal) { + if (newVal) { + this.open() + } else { + // 避免上来就执行 close,导致动画错乱 + if (this.isShow) { + this.close() + } + } + }, + immediate: true + } + }, + computed: { + // 生成样式数据 + stylesObject() { + let styles = { + ...this.styles, + 'transition-duration': this.duration / 1000 + 's' + } + let transform = '' + for (let i in styles) { + let line = this.toLine(i) + transform += line + ':' + styles[i] + ';' + } + return transform + }, + // 初始化动画条件 + transformStyles() { + return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject + } + }, + created() { + // 动画默认配置 + this.config = { + duration: this.duration, + timingFunction: 'ease', + transformOrigin: '50% 50%', + delay: 0 + } + this.durationTime = this.duration + }, + methods: { + /** + * ref 触发 初始化动画 + */ + init(obj = {}) { + if (obj.duration) { + this.durationTime = obj.duration + } + this.animation = createAnimation(Object.assign(this.config, obj),this) + }, + /** + * 点击组件触发回调 + */ + onClick() { + this.$emit('click', { + detail: this.isShow + }) + }, + /** + * ref 触发 动画分组 + * @param {Object} obj + */ + step(obj, config = {}) { + if (!this.animation) return + for (let i in obj) { + try { + if(typeof obj[i] === 'object'){ + this.animation[i](...obj[i]) + }else{ + this.animation[i](obj[i]) + } + } catch (e) { + console.error(`方法 ${i} 不存在`) + } + } + this.animation.step(config) + return this + }, + /** + * ref 触发 执行动画 + */ + run(fn) { + if (!this.animation) return + this.animation.run(fn) + }, + // 开始过度动画 + open() { + clearTimeout(this.timer) + this.transform = '' + this.isShow = true + let { opacity, transform } = this.styleInit(false) + if (typeof opacity !== 'undefined') { + this.opacity = opacity + } + this.transform = transform + // 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常 + this.$nextTick(() => { + // TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器 + this.timer = setTimeout(() => { + this.animation = createAnimation(this.config, this) + this.tranfromInit(false).step() + this.animation.run() + this.$emit('change', { + detail: this.isShow + }) + }, 20) + }) + }, + // 关闭过度动画 + close(type) { + if (!this.animation) return + this.tranfromInit(true) + .step() + .run(() => { + this.isShow = false + this.animationData = null + this.animation = null + let { opacity, transform } = this.styleInit(false) + this.opacity = opacity || 1 + this.transform = transform + this.$emit('change', { + detail: this.isShow + }) + }) + }, + // 处理动画开始前的默认样式 + styleInit(type) { + let styles = { + transform: '' + } + let buildStyle = (type, mode) => { + if (mode === 'fade') { + styles.opacity = this.animationType(type)[mode] + } else { + styles.transform += this.animationType(type)[mode] + ' ' + } + } + if (typeof this.modeClass === 'string') { + buildStyle(type, this.modeClass) + } else { + this.modeClass.forEach(mode => { + buildStyle(type, mode) + }) + } + return styles + }, + // 处理内置组合动画 + tranfromInit(type) { + let buildTranfrom = (type, mode) => { + let aniNum = null + if (mode === 'fade') { + aniNum = type ? 0 : 1 + } else { + aniNum = type ? '-100%' : '0' + if (mode === 'zoom-in') { + aniNum = type ? 0.8 : 1 + } + if (mode === 'zoom-out') { + aniNum = type ? 1.2 : 1 + } + if (mode === 'slide-right') { + aniNum = type ? '100%' : '0' + } + if (mode === 'slide-bottom') { + aniNum = type ? '100%' : '0' + } + } + this.animation[this.animationMode()[mode]](aniNum) + } + if (typeof this.modeClass === 'string') { + buildTranfrom(type, this.modeClass) + } else { + this.modeClass.forEach(mode => { + buildTranfrom(type, mode) + }) + } + + return this.animation + }, + animationType(type) { + return { + fade: type ? 0 : 1, + 'slide-top': `translateY(${type ? '0' : '-100%'})`, + 'slide-right': `translateX(${type ? '0' : '100%'})`, + 'slide-bottom': `translateY(${type ? '0' : '100%'})`, + 'slide-left': `translateX(${type ? '0' : '-100%'})`, + 'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`, + 'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})` + } + }, + // 内置动画类型与实际动画对应字典 + animationMode() { + return { + fade: 'opacity', + 'slide-top': 'translateY', + 'slide-right': 'translateX', + 'slide-bottom': 'translateY', + 'slide-left': 'translateX', + 'zoom-in': 'scale', + 'zoom-out': 'scale' + } + }, + // 驼峰转中横线 + toLine(name) { + return name.replace(/([A-Z])/g, '-$1').toLowerCase() + } + } +} +</script> + +<style></style> diff --git a/uni_modules/uni-transition/package.json b/uni_modules/uni-transition/package.json new file mode 100644 index 0000000..d5c20e1 --- /dev/null +++ b/uni_modules/uni-transition/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-transition", + "displayName": "uni-transition 过渡动画", + "version": "1.3.3", + "description": "元素的简单过渡动画", + "keywords": [ + "uni-ui", + "uniui", + "动画", + "过渡", + "过渡动画" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-transition/readme.md b/uni_modules/uni-transition/readme.md new file mode 100644 index 0000000..2f8a77e --- /dev/null +++ b/uni_modules/uni-transition/readme.md @@ -0,0 +1,11 @@ + + +## Transition 过渡动画 +> **组件名:uni-transition** +> 代码块: `uTransition` + + +元素过渡动画 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/zy-passwordboard/changelog.md b/uni_modules/zy-passwordboard/changelog.md new file mode 100644 index 0000000..8b3259c --- /dev/null +++ b/uni_modules/zy-passwordboard/changelog.md @@ -0,0 +1,4 @@ +## 2022.0513.0001(2022-05-13) +小程序样式不支持 *,调整样式配置 +## 2022.0513.0000(2022-05-13) +初建 diff --git a/uni_modules/zy-passwordboard/components/zy-passwordboard/zy-passwordboard.vue b/uni_modules/zy-passwordboard/components/zy-passwordboard/zy-passwordboard.vue new file mode 100644 index 0000000..b9e3317 --- /dev/null +++ b/uni_modules/zy-passwordboard/components/zy-passwordboard/zy-passwordboard.vue @@ -0,0 +1,179 @@ +<script setup lang="ts"> + import { defineProps, defineEmits, ref, watch, computed } from 'vue'; + const props = defineProps({ + visible: { type: Boolean, default: () => true }, + random: { type: Boolean, default: () => true }, + num: { type: Number, default: () => 6 }, + title: { type: String } + }); + const emits = defineEmits<{ + (event : 'update:visible', value : boolean); + (event : 'complete', value : string); + (event : 'close'); + }>(); + const tplInputs = computed(() => { + return Array(props.num).fill(0); + }); + const refKeys = ref([]); + const makeKeys = () => { + refKeys.value = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].sort(() => (props.random ? 0.5 - Math.random() : 0)).concat('删除'); + refKeys.value.splice(9, 0, '清空'); + }; + watch( + () => props.visible, + newValue => { + if (newValue) { + makeKeys(); + } + }, + { immediate: true } + ); + const refValue = ref([]); + const handleKeyClick = (key : string | number) => { + switch (key.toLowerCase()) { + case '清空': + refValue.value = []; + break; + case '删除': + refValue.value.pop(); + break; + default: + if (refValue.value.length < props.num) { + refValue.value.push(key); + } + break; + } + if (refValue.value.length === props.num) { + emits('complete', refValue.value.join('')); + } + }; + + const handleClose = $event => { + handleKeyClick('c'); + emits('update:visible', false); + emits('close'); + }; +</script> + +<template> + <uni-transition :show="props.visible"> + <view class="zy-passwordboard" @click="handleClose"> + <uni-transition :show="props.visible"> + <view class="zy-passwordboard__board" @click.stop> + <uni-transition :mode-class="['slide-bottom']" :show="props.visible"> + <view class="title" v-if="props.title">{{ props.title }}</view> + <view class="inputs"> + <view class="input" v-for="(input, inputi) in tplInputs" :key="inputi"><text class="text" + :class="{ filled: refValue[inputi] }"></text></view> + </view> + <view class="keys"> + <view class="key" v-for="key in refKeys" :key="key"> + <text class="text" :class="[`key-${key}`]" @click="handleKeyClick(key)">{{ key }}</text> + </view> + </view> + </uni-transition> + </view> + </uni-transition> + </view> + </uni-transition> +</template> + +<style lang="scss"> + view, + text { + box-sizing: border-box; + } + + .zy-passwordboard { + box-sizing: border-box; + position: fixed; + z-index: 999; + top: 0; + bottom: 0; + bottom: constant(safe-area-inset-bottom); + bottom: env(safe-area-inset-bottom); + left: 0; + right: 0; + background-color: var(--bg-color, rgba(0, 0, 0, 0.3)); + + &__board { + position: absolute; + left: 0; + right: 0; + bottom: 0; + // padding: 10rpx; + background-color: white; + backdrop-filter: blur(2rpx); + border-radius: 30rpx 30rpx 0 0; + + .title { + padding: 20rpx; + text-align: center; + color: #000000; + font-size: 35rpx; + padding-bottom: 0; + } + + .inputs { + display: flex; + justify-content: center; + padding-top: 40rpx; + margin-bottom: 40rpx; + + + .input { + padding: 10rpx; + + .text { + display: flex; + align-items: center; + justify-content: center; + width: 80rpx; + height: 80rpx; + background-color: #ffffff; + border-radius: 20rpx; + background-color: #E6E6E6; + + &.filled:before { + content: ''; + width: 30%; + height: 30%; + border-radius: 50%; + background-color: #000000; + } + } + } + } + + .keys { + width: 101vw; + display: flex; + flex-wrap: wrap; + background-color: white; + + .key { + flex: 0 1 33%; + padding: 10rpx; + border: 1px solid #E6E6E6; + + .text { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + padding: 20rpx 10rpx; + background-color: #ffffff; + border-radius: 20rpx; + font-size: 34rpx; + font-weight: bold; + + &.key- { + opacity: 1; + } + } + } + } + } + } +</style> \ No newline at end of file diff --git a/uni_modules/zy-passwordboard/package.json b/uni_modules/zy-passwordboard/package.json new file mode 100644 index 0000000..43c7c07 --- /dev/null +++ b/uni_modules/zy-passwordboard/package.json @@ -0,0 +1,89 @@ +{ + "id": "zy-passwordboard", + "displayName": "zy-passwordboard", + "version": "2022.0513.0001", + "description": "密码弹框", + "keywords": [ + "zy-passwordboard", + "密码弹框", + "支付密码" + ], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [ + "uni-transition" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "Vue": { + "vue2": "u", + "vue3": "y" + }, + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u", + "小红书": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/zy-passwordboard/readme.md b/uni_modules/zy-passwordboard/readme.md new file mode 100644 index 0000000..fd9b299 --- /dev/null +++ b/uni_modules/zy-passwordboard/readme.md @@ -0,0 +1,45 @@ +# zy-passwordboard +密码弹框 + +## 兼容说明 +仅基于H5进行了开发,未进行全面测试,后续用到再补 + +## 属性 +|属性名 |类型 |说明 | 默认值 | +|:-- |:-- |:-- |:-- | +|visible|Boolean|是否显示(支持 v-model:visible) | `false` | +|random |Boolean|是否随机排位 | `true` | +|num |Number |密码位数,过多显示会有问题 | `6` | +|title |String |显示标题 | -- | + +## 事件 +|事件名 |说明 | +|:-- |:-- | +| complete | 输入完毕 | +| colse | 关闭 | + +## 样式说明 +未使用 `scoped`, 通过顶层样式名`.zy-passwordboard`就可以进行调整。 + +## 使用案例 + +``` vue +<script setup lang="ts"> +import { ref } from 'vue'; +import ZyPasswordboard from '@/uni_modules/zy-passwordboard/components/zy-passwordboard/zy-passwordboard.vue'; +const passwordBoardVisible = ref(false); +const handleItemClick = () => { + passwordBoardVisible.value = true; +}; +const passwordBoardProps = { + title: '输入支付密码', + onComplete(value) { + console.log(value); + } +}; +</script> +<template> + <button @click="handleItemClick">开</button> + <ZyPasswordboard v-model:visible="passwordBoardVisible" v-bind="passwordBoardProps" /> +</template> +```