Commit e417bbca by zhangzhen

首页细节优化,添加车牌页面新增

parent 607ebae9
...@@ -158,6 +158,20 @@ ...@@ -158,6 +158,20 @@
} }
], ],
"subPackages": [
{
"root": "setting",
"pages": [
{
"path": "license-plate-number/list",
"style": {
"navigationBarTitleText": "我的车辆",
"navigationStyle": "default"
}
}
]
}
],
"globalStyle": { "globalStyle": {
"navigationStyle": "default", "navigationStyle": "default",
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",
......
<template> <template>
<view class="home" @touchstart="onTouchStart" @touchend="onTouchEnd"> <view class="home" @touchstart="onTouchStart" @touchend="onTouchEnd">
<f-navbar title="凑角" :isShowTransparentTitle="false" :isShowLeft="false" fontColor="#ffffff" :fontSize="46" <f-navbar :title="'凑角-'+storeInfo.name" :isShowTransparentTitle="false" :isShowLeft="false" fontColor="#ffffff" :fontSize="38"
bgColor="#e40583" :scrollTop="scrollTop" navbarType='5'></f-navbar> bgColor="#e40583" :scrollTop="scrollTop" navbarType='5'></f-navbar>
<scroll-view :enable-flex="true" class="scroll-view" scroll-y="true" @scroll="onScroll"> <scroll-view :enable-flex="true" class="scroll-view" scroll-y="true" @scroll="onScroll">
<view class="content-box"> <view class="content-box">
......
...@@ -58,6 +58,14 @@ ...@@ -58,6 +58,14 @@
</view> </view>
<view class="main"> <view class="main">
<view class="cu-list menu sm-border"> <view class="cu-list menu sm-border">
<view class="cu-item arrow" @tap="onHandle(6)">
<view class="content">
<image :src="assetsPath+'/my_list_3.png'" class="png" mode="aspectFit"></image>
<text class="text-title">我的车辆</text>
</view>
</view>
<view class="cu-item arrow" @tap="onHandle(1)"> <view class="cu-item arrow" @tap="onHandle(1)">
<view class="content"> <view class="content">
<image :src="assetsPath+'/my_list_1.png'" class="png" mode="aspectFit"></image> <image :src="assetsPath+'/my_list_1.png'" class="png" mode="aspectFit"></image>
...@@ -317,6 +325,10 @@ ...@@ -317,6 +325,10 @@
uni.navigateTo({ uni.navigateTo({
url:'/pages/deviceManage/index' url:'/pages/deviceManage/index'
}) })
}else if(val === 6){
uni.navigateTo({
url:'/setting/license-plate-number/list'
})
} }
}, },
onClose(){ onClose(){
......
<template>
<view class="license-plate-number">
<view>
<u-swipe-action>
<u-swipe-action-item v-for="(item,k) in licensePlateNumber" :key="k" :index="k" :name="k" :options="options" @click="onClick">
<!-- <view class="swipe-action u-border-top u-border-bottom"> -->
<xm-cell special label="车牌号" :value="item.name"></xm-cell>
<!-- </view> -->
</u-swipe-action-item>
</u-swipe-action>
</view>
<view v-if="licensePlateNumber && licensePlateNumber.length <= 0" class="empty-box">
<u-empty text="暂未绑定车牌号" textColor='#C1C1C1' width="120"
:icon="listBlankImage">
</u-empty>
</view>
<view class="foot-box">
<view class="btn-box">
<button class="cu-btn block bg-blue lg round " @tap="onShowKeyboard('xmKeyboard')">新增</button>
</view>
</view>
<uni-popup ref="popupConfirm" type="dialog">
<uni-popup-dialog
type="warn"
mode="base"
:content="content"
:duration="2000"
:before-close="true"
@close="close"
@confirm="confirm"
></uni-popup-dialog>
</uni-popup>
<xm-keyboard-v2 ref="xmKeyboard" :initValue="initValue" :cursor="true" :vibration="true" @confirm="onSave"></xm-keyboard-v2>
</view>
</template>
<script>
import config from "@/config/index.config"
export default {
data() {
return {
initValue:'',
content:"请确认解除车牌号绑定",
listBlankImage:config.assetsPath+'/no_data_icon.png',
licensePlateNumber: [{
id: '1',
name: '鄂A 66666'
}],
options: [{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}],
deleteIndex:'',
};
},
onLoad() {
},
methods: {
onClick(e){
console.log(e,909090)
this.deleteIndex = e.index
this.$refs.popupConfirm.open();
},
close(){
this.$refs.popupConfirm.close()
},
confirm(){
this.licensePlateNumber = this.licensePlateNumber.filter((item,index) => index!= this.deleteIndex);
this.close()
uni.showToast({
icon:"none",
title:"解绑成功"
})
},
onShowKeyboard(ref) {
this.$refs.xmKeyboard.toShow()
},
onSave(e) {
console.log(e)
let obj = {
name: e
}
this.licensePlateNumber.push(obj)
this.$refs.xmKeyboard.inputClear()
}
}
}
</script>
<style>
page {
background-color: #f1f1f1;
}
</style>
<style lang="scss">
.license-plate-number {
display: flex;
flex-direction: column;
}
.foot-box {
position: fixed;
left: 0;
bottom: 0;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
height: 120upx;
border-radius: 12upx 12upx;
box-shadow: 0 -4upx 8upx rgba(0, 0, 0, 0.13);
background-color: #ffffff;
.btn-box {
width: 80%;
}
}
.u-page {
padding: 0;
}
.u-demo-block__title {
padding: 10px 0 2px 15px;
}
.swipe-action {
&__content {
padding: 25rpx 0;
&__text {
font-size: 15px;
color: $u-main-color;
padding-left: 30rpx;
}
}
}
</style>
\ No newline at end of file
## 1.1.1(2023-06-23)
1. 添加示例项目
2. 欢迎添加QQ: 707200833 直接进行反馈
## 1.1.0(2023-06-21)
写在开始, 此版本算是重做了, 但还是保留了 1.0.2 的版本, 所以插件体积包会比较大, 使用时请按需导入.
> 组件说明
| 组件名称 | 组件说明 |
| ----------------- | ---------------------------------------- |
| -- 新版本 -- | |
| xm-keyboard-v2 | 1.1.0 新版本 |
| xm-keyboard-box | 纯键盘组件 |
| xm-keyboard-input | 输入框组件 |
| xm-popup | 弹出层组件 |
| xm-square | 正方形组件,高度可以按照宽度比例进行显示 |
| xm-cell | 单元格组件 |
| -- 旧版本 -- | |
| xm-keyboard | 1.0.2 保留版本 |
## 1.0.2(2022-12-19)
修复默认值错误
## 1.0.1(2022-12-19)
#### 新增
1. 新增配置 maxAutoClose, 输入达到最大长度后是否自动关闭键盘
2. 新增配置 safeSize, 距离底部的安全距离, 自动匹配失效时可手动配置
#### Bug fixes
- 修复当页面滚动时键盘没有吸底的问题
#### 优化
- 调整为 uni_modules 模式
- 预留键盘底部空间, 优化显示
- 新增默认字体设置
- 按键后顶部进行放大显示
## 1.0.0(2022-01-05)
1. 支持车牌号省份输入
2. 支持数字+字母输入
3. 支持按键震动反馈
4. 支持虚拟键盘显示输入内容
5. 支持默认内容回显
6. 支持是否显示遮罩
7. 支持输入长度限制
8. 支持禁用键盘按键
\ No newline at end of file
<template>
<view class="xm-cell" :class="{
'xm-cell-clickable': clickable,
'xm-cell-border': border,
}" @click.stop="$emit('show', $event)">
<view class="xm-cell-label">
<view v-if="label">{{ label }}</view>
<slot v-else name="label"></slot>
</view>
<view class="xm-cell-value">
<view v-if="value" :class="{ 'xm-cell-special': special }">{{ valueFormat(value) }}</view>
<slot v-else></slot>
</view>
<view v-if="arrow" class="xm-cell-arrow"></view>
</view>
</template>
<script>
export default {
name: 'xm-cell',
emits: ['show'],
props: {
label: {
type: String,
default: ''
},
value: {
type: String,
default: ''
},
// 是否显示右箭头
arrow: {
type: Boolean,
default: true
},
// 是否显示点击效果
clickable: {
type: Boolean,
default: true
},
border: {
type: Boolean,
default: true
},
special: {
type: Boolean,
default: false
}
},
methods: {
valueFormat(value){
if(!value){
return ''
}
if(!this.special){
return value;
}
return [ value.substring(0, 2), value.substring(2) ].filter(x => x).join('·')
}
}
}
</script>
<style lang="scss" scoped>
.xm-cell {
position: relative;
flex: 1;
width: 100%;
display: flex;
box-sizing: border-box;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 10px 22px 10px 16px;
background-color: #FFF;
font-size: 14px;
height: 48px;
&-label {
color: #323233;
}
&-value {
color: #969799;
}
&-arrow {
height: 20px;
width: 20px;
border-width: 3px 3px 0 0;
border-style: solid;
transform: rotate(45deg) scale(0.5);
border-radius: 2px;
flex-shrink: 0;
margin-left: auto;
box-sizing: border-box;
transform-origin: center center;
margin-right: -3px;
border-color: #969799;
position: absolute;
right: 10px;
}
&-clickable:active {
background-color: rgba(0, 0, 0, 0.03) !important;
}
&-border::after {
position: absolute;
box-sizing: border-box;
content: ' ';
pointer-events: none;
right: 16px;
bottom: 0;
left: 16px;
border-bottom: 1px solid #ebedf0;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
&-special {
font-family: '微软雅黑';
display: inline-block;
padding: 4px 6px;
background-color: #0081ff;
color: #FFFFFF;
letter-spacing: 2px;
text-align: center;
border-radius: 2px;
}
}
</style>
\ No newline at end of file
<template>
<view class="xm-keyboard-box">
<view class="xm-keyboard-box-line" v-for="(line, li) in lines[mode]" :key="li" :style="{
marginLeft: diffSize(line.diff) + 'px',
marginRight: diffSize(line.diff) / -1 + 'px',
}">
<view v-for="(item, index) in line.list" :key="index" class="xm-keyboard-box-item" :class="{
'xm-keyboard-box-item-empty': item == '',
}" :style="{
width: btnWidth + 'px',
height: btnHeight + 'px'
}" @click="toClick(item)">
{{ item }}
</view>
</view>
<view class="xm-keyboard-box-line xm-keyboard-box-toolbar">
<view v-if="showCancelBtn" class="xm-keyboard-box-item xm-keyboard-box-btn xm-keyboard-box-btn-cancel" :style="{
marginRight: btnWidth / ratio + 'px',
height: btnHeight + 'px'
}" @click="toCancel()">取消</view>
<view class="xm-keyboard-box-item xm-keyboard-box-btn xm-keyboard-box-btn-clear" :style="{
marginRight: btnWidth / ratio + 'px',
height: btnHeight + 'px'
}" @click="toClear()">清空</view>
<view class="xm-keyboard-box-item xm-keyboard-box-btn-over" :style="{
marginRight: btnWidth / ratio + 'px',
height: btnHeight + 'px'
}" @click="toConfirm()">完成</view>
</view>
<view v-if="showChangeBtn" class="xm-keyboard-box-item xm-keyboard-box-btn xm-keyboard-box-btn-change" :style="{
width: handlerWidth + 'px',
height: btnHeight + 'px',
bottom: 'calc(20px + '+btnHeight+'px)'
}" @click="changeMode()">
<i class="iconxmk2 icon-xm-k2-jianpan" style="font-size: 24px;"></i>
</view>
<view class="xm-keyboard-box-item xm-keyboard-box-btn xm-keyboard-box-btn-del" :style="{
width: handlerWidth + 'px',
height: btnHeight + 'px',
bottom: 'calc(20px + '+btnHeight+'px)'
}" @click="toDel()">
<i class="iconxmk2 icon-xm-k2-backspace" style="font-size: 24px;"></i>
</view>
</view>
</template>
<script>
export default {
name: 'xm-keyboard-box',
emits: ['add', 'del', 'confirm', 'cancel', 'clear'],
props: {
// 是否开启震动效果
vibration: {
type: Boolean,
default: false
},
// 是否显示切换按钮
showChangeBtn: {
type: Boolean,
default: true
},
// 是否显示取消按钮
showCancelBtn: {
type: Boolean,
default: true
}
},
data() {
return {
mode: 0,
ratio: 7,
max: 10,
gutter: 10,
btnWidth: 10,
btnHeight: 10,
handlerWidth: 10,
lines: [
[{
list: ["京", "沪", "浙", "苏", "粤", "鲁", "晋", "冀", "豫", "川"],
diff: 0,
},
{
list: ["渝", "辽", "吉", "黑", "皖", "鄂", "津", "贵", "云", "桂"],
diff: 0,
},
{
list: ["琼", "青", "新", "藏", "蒙", "宁", "甘", "陕", "闽", "赣"],
diff: 0,
},
{
list: ["湘", "使", "领", "警", "学", "港", "澳", "", "", ""],
diff: 3,
},
],
[{
list: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
diff: 0,
},
{
list: ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
diff: 0,
},
{
list: ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ''],
diff: 1,
},
{
list: ['Z', 'X', 'C', 'V', 'B', 'N', 'M', '', '', ''],
diff: 3,
},
],
],
}
},
methods: {
diffSize(pos) {
if (pos == 0) {
return 0
}
return (pos * this.btnWidth + pos * this.btnWidth / this.ratio) / 2
},
changeMode(v) {
this.mode = v == void 0 ? (this.mode == 0 ? 1 : 0) : v
},
toClick(item) {
if (item === '') {
return;
}
this.toEmit('add', item);
},
toDel() {
this.toEmit('del');
},
toCancel(){
this.toEmit('cancel');
},
toConfirm() {
this.toEmit('confirm')
},
toClear() {
this.toEmit('clear')
},
toEmit(type, params){
this.toVibration();
this.$emit(type, params);
},
toVibration() {
if (this.vibration && uni.vibrateShort) {
uni.vibrateShort();
}
}
},
mounted() {
const {
windowWidth
} = uni.getSystemInfoSync();
let _width = (windowWidth - this.gutter * 2) * this.ratio / (this.max * this.ratio + this.max - 1)
this.btnWidth = _width.toFixed(2)
this.btnHeight = (_width / 3 * 4).toFixed(2)
this.handlerWidth = (_width * 1.5 + _width / (this.ratio * 2)).toFixed(2)
}
}
</script>
<style lang="scss" scoped>
@import url(../../styles/iconfont/iconfont.css);
.xm-keyboard-box {
$gutter: 10px;
background-color: #d4d5d9;
padding: $gutter;
position: relative;
.xm-flex {
display: flex;
align-items: center;
}
&-line {
@extend .xm-flex;
justify-content: space-between;
margin-bottom: $gutter;
&:last-child {
margin-bottom: 0;
}
}
&-item {
background-color: #FCFFFF;
@extend .xm-flex;
justify-content: center;
border-radius: 4px;
box-shadow: 0px 2px 2px #999;
position: relative;
&:active {
background-color: rgba(0, 0, 0, 0.1);
}
&-empty {
background-color: unset;
box-shadow: unset;
&:active {
background-color: unset;
}
}
}
&-btn {
background-color: #b6bcc4;
&:active {
background-color: rgba(182, 188, 196, 0.8);
}
}
&-btn-del {
position: absolute;
right: $gutter;
}
&-btn-change {
position: absolute;
left: $gutter;
}
&-btn-over {
// position: absolute;
background-color: #f37b1d;
color: #fff;
&:active {
background-color: rgba(243, 123, 29, 0.8);
}
}
&-toolbar {
margin-bottom: 0;
.xm-keyboard-box-item {
width: 100%;
&:last-child {
margin-right: 0 !important;
}
}
}
}
</style>
\ No newline at end of file
<template>
<view class="xm-keyboard-input">
<view v-for="(item, index) in values" :key="index" class="xm-keyboard-input-item" :class="{
'xm-cur': cur === index,
'xm-show-pointer': showPointer
}" :style="{
marginLeft: (18 - max) + 'px'
}" @click="changeCur(index)">
<xm-square height="120%">
<view class="xm-keyboard-input-cnt" :class="{
'xm-cursor': cursor && cur === index && !item
}">
{{ item }}
</view>
</xm-square>
</view>
</view>
</template>
<script>
export default {
name: 'xm-keyboard-input',
emits: ['change'],
props: {
initValue: {
type: String,
default: ''
},
cursor: {
type: Boolean,
default: false,
},
max: {
type: Number,
default: 8,
},
showPointer: {
type: Boolean,
default: true,
}
},
data() {
return {
cur: 0,
values: []
}
},
methods: {
changeCur(index) {
this.cur = index;
this.toChange();
},
toChange(){
this.$emit('change', this.cur)
},
toAdd(v){
this.values[this.cur] = v;
// #ifdef VUE2
this.$set(this.values, this.cur, v);
// #endif
if(this.cur < this.max - 1){
this.cur ++;
this.toChange()
}
},
toDel(){
if(this.cur == (this.max - 1) && this.values[this.cur]){
this.values[this.cur] = ''
return ;
}
if(this.cur <= 0){
this.cur = 0;
}else{
this.cur --
this.toChange()
}
if(this.values[this.cur]){
this.values[this.cur] = ''
}
},
toClear(){
this.cur = 0;
this.initValues();
this.toChange()
},
changeValue(v){
let max = Math.min(v.length, this.max);
for (let i = 0; i < max; i++) {
this.values[i] = v.charAt(i)
}
let cur = this.values.findIndex(x => !x)
this.cur = cur === -1 ? this.max - 1 : cur;
this.toChange()
},
initValues(){
let vs = [];
vs.length = this.max;
vs.fill('');
this.values = vs;
}
},
mounted() {
this.initValues();
this.changeValue(this.initValue || '')
}
}
</script>
<style scoped lang="scss">
.xm-keyboard-input {
display: flex;
$theme: #007AFF;
$themeBackground: rgba(0,73,255,0.03);
@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
100% {
opacity: 0;
}
}
&-item {
width: 100%;
height: 100%;
box-sizing: border-box;
border: 1px solid #CCC;
border-radius: 2px;
&:first-child{
margin-left: 0;
}
&.xm-show-pointer:nth-child(8){
border-style: solid;
}
&.xm-show-pointer:nth-child(2){
position: relative;
margin-right: 5px;
&::after{
content: ' ';
position: absolute;
right: -12px;
top: calc(50% - 3px);
display: flex;
align-items: center;
width: 6px;
height: 6px;
background-color: #CCC;
border-radius: 100%;
}
}
&.xm-cur {
border-color: $theme;
background-color: $themeBackground;
transition: 0.1s;
}
.xm-cursor {
&::after {
color: $theme;
content: '|';
animation: blink 1s infinite;
}
}
}
&-cnt {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
}
</style>
\ No newline at end of file
<template>
<view class="xm-keyboard-v2">
<xm-popup :show="isShow" @close="toCancel" background="#d4d5d9" :showContent="showContent">
<view style="background-color: #FFF;">
<view class="xm-flex xm-title" v-if="title">
{{ title }}
</view>
<view style="padding: 10px;">
<xm-keyboard-input ref="keyboardInput" @change="inputChange" :cursor="cursor"
:show-pointer="type == 'plate'" :max="max"
></xm-keyboard-input>
</view>
</view>
<xm-keyboard-box ref="keyboardBox" :show-change-btn="type == 'plate'" :show-cancel-btn="!showContent"
@add="inputAdd" @del="inputDel" @clear="inputClear" @cancel="toCancel" @confirm="toConfirm"
></xm-keyboard-box>
</xm-popup>
</view>
</template>
<script>
export default {
name: 'xm-keyboard-v2',
emits: ['confirm', 'cancel'],
props: {
title: {
type: String,
default: ''
},
cursor: {
type: Boolean,
default: false
},
vibration: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'plate'
},
max: {
type: Number,
default: 8,
},
showContent: {
type: Boolean,
default: false
},
},
data() {
return {
isShow: false,
value: '',
}
},
methods: {
toShow(value) {
this.value = value || '';
this.isShow = true;
this.$refs.keyboardInput.changeValue(this.value);
},
toConfirm(){
this.isShow = false;
let value = this.$refs.keyboardInput.values.join('')
this.$emit('confirm', value);
},
toCancel(){
this.isShow = false;
this.$emit('cancel');
},
inputChange(index){
if(this.type == 'plate'){
this.$refs.keyboardBox.changeMode(index == 0 ? 0 : 1);
}else{
this.$refs.keyboardBox.changeMode(1);
}
},
inputAdd(v){
this.$refs.keyboardInput.toAdd(v);
},
inputDel(){
this.$refs.keyboardInput.toDel();
},
inputClear(){
this.$refs.keyboardInput.toClear();
}
},
}
</script>
<style lang="scss" scoped>
.xm-keyboard-v2 {
.xm-flex{
display: flex;
align-items: center;
justify-content: center;
}
.xm-title{
padding: 20px 0 10px 0;
font-weight: bold;
}
}
</style>
\ No newline at end of file
<template>
<view class="xm-keyboard">
<view class="xm-content" v-if="show">
<view class="xm-mask" v-if="mask" @tap="close"></view>
<view class="xm-box" :class="anim?'xm-animation':''">
<view class="xm-toolbar">
<view class="xm-toolbar-left">
<view class="keyboard-input" @tap.stop="changeCur()">
<text v-for="(item, index) in input" :key="index" :class="cur === index ? 'cur abs':''" @tap.stop="changeCur(index)">{{ item }}</text>
<text v-if="input.length === 0" class="cur"></text>
</view>
<view v-if="input.length" class="xm-tips">({{ input.length }}位)</view>
</view>
<view class="xm-toolbar-right">
<view v-show="exchange" class="xm-toolbar-item" v-for="(item, index) in typeList" :key="index" @tap="changeType(item, index)">
{{ item.name }}
</view>
<image class="xm-icon" @tap="close" style="margin-left: 20upx;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAXNJREFUWEftlzEvBUEUhb/3R0SLp6IgUdC+QoWETqWTSESh8RQaEqFTiQYJhUZPp6Gk9Ac0/gE5L3NfxmbXu2uHjWSn3Myc883ZmTszLWpurZr9yQPoAgvAWGK4J+AWkH6/ZQE+EpsWyfV9Y4CdQHcPGG1KnmGgAywCu5ZEDPAITAAbwFFK50hrGbgAnoG2vscAFv8coBR+q5lPz7sBaBL4Fwnsh727BVyX2BpXwBAwlRlTehcchtognSUnhMxVcFTQJqsCaPw5sBKEBkGYuYqN+r6kAJDGKbA6ACI2nwdec35Z6V8Qa5wAawUQsfkM8F6wXioBSPMYWM9AxOa9Gv9Nqwwg7QNgM5g8ANPxAfMXAPLYA7aDmRaa9wKTJAGb5B0wAowDb84akRTA6fmlWwPQJFCYgF1KVW7PfrK6HGNmAe2c3EupXct15N4Alw7BMl1Gwwmph0nutVxitT5MbDZKQme5t7J5U3A9zbxiyfrV/jr+BLxqdyHzSmvBAAAAAElFTkSuQmCC" mode="widthFix"></image>
</view>
</view>
<view class="xm-main" :class="['xm-mode-' + typeInfo.mode, ]" :style="{ paddingBottom: safeBottom + 'px' }">
<view class="xm-main-left">
<view class="xm-line" v-for="(lines, lineIndex) in list" :key="lineIndex">
<view class="xm-btn" v-for="(item, index) in lines" :key="index" :class="disable.indexOf(item)!==-1?'disable':''" @tap="keyPress(item)"
:style="{
height: keyWidth * proportion + 'px',
maxWidth: keyWidth + 'px',
width: keyWidth + 'px',
marginRight: minWidth + 'px'
}">
{{ item }}
<view class="high-item" v-if="highItem == item">{{ item }}</view>
</view>
</view>
</view>
<view class="xm-main-right" :style="{ bottom: safeBottom + 'px' }">
<view class="xm-line">
<view class="xm-btn2" v-for="(bar,index) in bars" :key="index" @tap="toHandler(bar)"
:style="{
backgroundColor: bar.bgColor,
color: bar.color,
maxWidth: keyWidth * (typeInfo.mode == 1 ? 1.5:1) + 'px',
width: keyWidth * (typeInfo.mode == 1 ? 1.5:1) + 'px',
height: (typeInfo.mode == 1 ? (keyWidth * proportion + 'px') : '100%'),
maxHeight: typeInfo.mode == 1 ? '':'unset',
marginTop: typeInfo.mode == 1 ? '':((index != 0 ? 0 : 20) + 'upx')
}"
>
<text v-if="bar.text">{{ bar.text }}</text>
<image v-if="bar.img" :src="bar.img" mode="widthFix" class="xm-icon2"></image>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
/**
* show: 是否展示键盘
* mask: 是否展示遮罩
* anim: 是否开启弹出动画
* defaultValue: 键盘默认数据
* exchange: 是否展示切换工具栏
* mode: 键盘类型(0:车牌, 1:数字+字母, 2:数字, 3:字母)
* maxLength: 输入最大长度
*
* @close: 键盘收起
* @change: 值变化
*/
export default {
name: "xm-keyboard",
props: {
show: {
required: true,
},
mask: {
required: false,
default: true
},
anim: {
required: false,
default: true
},
defaultValue: {
required: false,
default: () => '',
},
exchange: {
required: false,
default: true
},
mode: {
required: false,
default: 1,
},
maxLength: {
required: false,
default: -1,
},
disable: {
required: false,
default: () => ('IO')
},
maxAutoClose: {
required: false,
default: false,
},
safeSize: {
required: false,
default: 10,
}
},
model: {
prop: 'show',
event: 'close'
},
watch: {
show(n, o) {
this.toShow(n, o);
},
mode(n, o){
this.type = n;
}
},
data() {
const delPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAh5JREFUWEftl7+LU0EQx7/fQMS/QBCutBFEsL3uEAtByc6DiL84IxbKiaiFtpeUanMqihbK+eO0COxsYSloY60gWKugFjami6YYWX1P312Se3dJvGdxW73H7s73s7Ozs7NEyY0l62MTYE0eqNfrW3q93lGSewDsHmPbPpH8COCm9/5LtFMIkCTJlJndB7AvFX45KoCZTZHcAeA9gHOq+mxVgFqtNl2pVF4B+GZmrWq1utRut7+OChDnOef2A7hB8nu3250eCiAiOwG8i7RmJiGEN+MI5+eKyGEAT+OiBgIkSbLdzD7HlZM84L2PXphoE5HXv+yvtNpoNLZ2Op0PALYBOBj3aaLKqTHn3DzJZh+AiLwFsAvACVV9+C/E01joBxCR5wD2mtmFEML1InHnXCOEsDhoXJIkc97728Ns9HlARJ4AOGJmzRBCq0hcRI4BeAygraqHVgTZVQCXSJ4dBrEMQEQWAJwnueC9v1gknvU7566QvJyHEJE7AE4DuKaqsW9g+wMgIrMAHpBc9N6fXKt4DuIWybkIAeAHgOiZu6p6ZjVbeYAXcaCqzqxXPBsvIo8AHE//l1Q1+x5q8n8AaJKcZ4lb8Bsg+qikIPwLkEJkx7AVQmgWxcMEjuFygBRivYnoVAjh3oiJqB8ghdioVDwYYAMvo8EA0QulXse55FJeQZJBlFqSZRClFqUZRKlleVFCGre/8F0wrkDR/E2An6wZQ4Kdpz9AAAAAAElFTkSuQmCC'
return {
input: [],
cur: 0,
// 设备的宽度
width: 0,
// 设备的高度
height: 0,
// 按钮与空白的比例
ratio: 7,
// 当前键盘类型
type: 0,
typeList: [
{ name: '地区', max: 10, list: [
"京沪浙苏粤鲁晋冀豫川".split(''),
"渝辽吉黑皖鄂津贵云桂".split(''),
"琼青新藏蒙宁甘陕闽赣".split(''),
"湘使领警学港澳".split(''),
], mode: 1 },
{ name: '英文', max: 10, list: [
'1234567890'.split(''),
'QWERTYUIOP'.split(''),
'ASDFGHJKL'.split(''),
'ZXCVBNM'.split(''),
], mode: 1 },
// { name: '数字', max: 3, list: [
// "123",
// "456",
// "789",
// ".0x",
// ], mode: 2 },
// { name: '字母', max: 10, list: [
// 'QWERTYUIOP',
// 'ASDFGHJKL',
// 'ZXCVBNM',
// ], mode: 1 },
],
bars: [
{ fun: 'close', text: '完成', bgColor: '#f37b1d', color: '#ffffff' },
{ fun: 'del', img: delPng },
],
// 安全底部距离
safeBottom: 0,
// 正在点击的键盘
highItem: '',
};
},
computed: {
//按钮 高:宽 占比
proportion(){
return 4 / 3;
},
minWidth(){
return this.width / ((this.max + this.typeInfo.mode - 1) * this.ratio + this.max + 1);
},
keyWidth() {
return this.minWidth * this.ratio;
},
typeInfo(){
return this.typeList[this.type];
},
list(){
return this.typeInfo.list;
},
max(){
return this.typeInfo.max;
}
},
methods: {
toShow(n, o){
if(n){
this.input = this.defaultValue.split('')
this.cur = this.input.length - 1;
this.type = this.mode;
}
},
close() {
this.$emit('close', false)
this.input.length = 0;
},
del(){
this.downEvent()
let v = this.input.splice(this.cur, 1);
this.emitChange(v, false);
},
keyPress(v){
if(this.maxLength > 0 && this.input.length >= this.maxLength){
return ;
}
if(this.disable.indexOf(v) !== -1){
return ;
}
this.downEvent(v)
this.input.splice(this.cur + 1, 0, v);
this.emitChange(v);
if(this.maxLength > 0 && this.input.length >= this.maxLength){
if(this.maxAutoClose != void 0 && this.maxAutoClose != false){
this.close();
}
}
},
changeCur(index){
this.cur = index || this.input.length - 1;
},
emitChange(v = '', add = true){
if(add){
this.cur ++;
}else{
this.cur --;
if(this.cur < 0){
this.cur = 0;
}
}
this.$emit('change', { text: this.input.join(''), v, add })
},
changeType(item, index){
this.type = index;
},
toHandler(bar){
bar.fun && this[bar.fun] && this[bar.fun]();
},
downEvent(item = ''){
uni.vibrateShort && uni.vibrateShort({});
if(item){
this.highItem = item;
clearTimeout(this.highCid)
this.highCid = setTimeout(() => {
this.highItem = '';
}, 200)
}else{
this.highItem = '';
}
}
},
mounted() {
const { windowWidth, windowHeight, safeAreaInsets } = uni.getSystemInfoSync();
this.width = windowWidth;
this.height = windowHeight;
this.safeBottom = safeAreaInsets.bottom || this.safeSize
this.toShow(this.show);
},
}
</script>
<style lang="scss" scoped>
.xm-keyboard {
$xm-border-color: #D4D4D4;
$xm-shadow-color: rgba(0, 0, 0, 0.1);
$xm-mask-bg-color: rgba(0, 0, 0, 0.4);
$xm-box-bg-color: #FFFFFF;
font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
@keyframes xm-up {
0% { opacity: 0; transform: translateY(100%) }
100% { opacity: 1; transform: translateY(0) }
}
.xm-fixed-b {
position: fixed;
left: 0;
right: 0;
bottom: 0;
}
.xm-flex {
display: flex;
align-items: center;
}
.xm-icon {
width: 32upx;
height: 32upx;
}
.xm-icon2 {
width: 48upx;
height: 48upx;
}
.xm-animation{
animation-duration: .2s;
animation-timing-function: ease-out;
animation-fill-mode: both;
animation-name: xm-up;
}
.xm-content {
}
.xm-mask {
@extend .xm-fixed-b;
z-index: 992;
top: 0;
background-color: $xm-mask-bg-color;
}
.xm-box {
@extend .xm-fixed-b;
z-index: 993;
min-height: 100px;
width: 100%;
background-color: $xm-box-bg-color;
box-shadow: 0 0 10upx $xm-shadow-color;
border-top: 2upx solid $xm-border-color;
flex-direction: column;
}
.xm-toolbar {
@extend .xm-flex;
justify-content: space-between;
padding: 10upx 20upx;
border-bottom: 2upx solid #E0E1E2;
&-left {
@extend .xm-flex;
}
&-right {
position: relative;
@extend .xm-flex;
&::before {
content: '|';
position: absolute;
top: -4upx;
bottom: 0;
left: -30upx;
color: #DEDEDE;
}
}
&-item{
margin-left: 20upx;
font-size: 24upx;
color: #999;
border: 2upx solid $xm-border-color;
padding: 4upx 8upx;
border-radius: 4upx;
&:first-child{
margin-left: 0;
}
}
}
.xm-main {
background-color: #D4D5DA;
padding: 20upx 10upx;
.xm-line {
@extend .xm-flex;
justify-content: center;
padding-top: 20upx;
&:first-child{
padding-top: 0;
}
}
.xm-common-btn{
@extend .xm-flex;
justify-content: center;
box-shadow: 0 0 10upx rgba(0, 0, 0, 0.1);
border-radius: 6upx;
max-height: 120upx;
}
.xm-btn {
@extend .xm-common-btn;
background-color: #FCFFFF;
position: relative;
&:last-child {
margin-right: 0 !important;
}
// &:active:not(.disable){
// transform: scale(1.8, 1.8);
// }
&.disable{
background-color: #BDBEC3;
}
}
.high-item{
position: absolute;
width: 140%;
height: 140%;
top: calc(-140% - 10px);
@extend .xm-common-btn;
background-color: #FCFFFF;
font-size: 18px;
font-weight: bold;
&::after{
content: ' ';
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 10px solid #FCFFFF;
position: absolute;
bottom: -10px;
}
}
.xm-btn2{
@extend .xm-common-btn;
background-color: #BDBEC3;
&:active{
transform: scale(1.2, 1.2);
}
}
}
.xm-mode-1{
.xm-main-left{
.xm-btn{
z-index: 2;
}
}
.xm-main-right{
@extend .xm-fixed-b;
left: 10upx;
right: 10upx;
bottom: 20upx;
z-index: 1;
.xm-line{
justify-content: space-between;
}
}
}
.xm-mode-2{
display: flex;
justify-content: space-between;
.xm-main-right{
.xm-line{
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
flex-flow: column-reverse;
}
}
}
.keyboard-input{
display: inline-block;
min-width: 200upx;
border-bottom: 1px solid #CCC;
padding: 0 6px;
@keyframes blink{
0%{ opacity: 0; }
50%{ opacity: 1; }
100%{ opacity: 0; }
}
.cur:after{
&.abs{
position: absolute;
}
color: #39B54A;
content: '|';
animation: blink 1s infinite;
}
}
.xm-tips{
display: inline-block;
margin-left: 20upx;
color: #8799a3;
font-size: 20upx;
}
}
</style>
<template>
<view class="xm-popup-wrap" :class="{
'xm-popup-wrap-show': show,
'xm-popup-wrap-show-content': showContent
}" :style="{
zIndex: zIndex,
background: maskBackground
}" @tap.stop="toClose" @touchmove.stop.prevent="stop" :animation="animationData">
<view class="xm-popup-cnt" :class="{
'xm-popup-cnt-show-content': showContent,
}" :style="{
borderTopLeftRadius: radius + 'px',
borderTopRightRadius: radius + 'px',
background: background
}" @tap.stop="stop">
<slot></slot>
</view>
</view>
</template>
<script>
export default {
name: 'xm-popup',
emits: ['close'],
props: {
// 是否显示弹出层
show: {
type: Boolean,
default: false
},
// 弹出层内容背景颜色
background: {
type: String,
default: '#FFFFFF'
},
// 弹出层内容圆角
radius: {
type: [Number, String],
default: 0
},
// 弹出层的z-index
zIndex: {
type: [Number, String],
default: 1992
},
// 点击遮罩, 是否可关闭
maskClosable: {
type: Boolean,
default: true
},
// 遮罩的背景色
maskBackground: {
type: String,
default: 'rgba(0,0,0,0.6)'
},
// 是否直接显示键盘
showContent: {
type: Boolean,
default: false
},
},
data() {
return {
isShow: false,
animation: {},
animationData: {}
}
},
methods: {
toClose(e) {
if (!this.maskClosable) return;
this.$emit('close', {});
},
stop(e) {
},
},
};
</script>
<style scoped lang="scss">
.xm-popup {
&-wrap {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1001;
display: none;
flex-direction: row;
align-items: flex-end;
justify-content: center;
&-show {
display: flex;
}
&-show-content{
display: flex;
position: unset;
background-color: unset !important;
}
}
&-cnt {
width: 100%;
min-height: 20px;
overflow: hidden;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
flex: 1;
&-show-content{
padding-bottom: 0;
}
}
}
</style>
\ No newline at end of file
<template>
<view class="xm-square" :style="{ paddingBottom: height }">
<view class="xm-square-box">
<slot></slot>
</view>
</view>
</template>
<script>
export default {
props: {
height: {
type: String,
default: '100%'
},
}
}
</script>
<style scoped lang="scss">
.xm-square {
width: 100%;
height: 0;
position: relative;
&-box {
position: absolute;
width: 100%;
height: 100%;
}
}
</style>
\ No newline at end of file
{
"id": "xm-keyboard",
"displayName": "xm-keyboard 自定义车牌号 车架号键盘",
"version": "1.1.1",
"description": "一个简单自由的车牌号自定义键盘",
"keywords": [
"keyboard",
"自定义键盘",
"车牌号",
"车架号"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "component-vue",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"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",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}
\ No newline at end of file
# xm-keyboard 介绍
xm-keyboard是一款虚拟键盘, 支持自定义拓展键盘内容。
插件已按照easycom处理, 支持直接使用
## 如何使用(v1.1.0使用说明)
## xm-keyboard-v2
> xm-keyboard-v2, 是对多个组件的聚合处理,也可以自行结合实现
```vue
<template>
<view>
<xm-cell special label="【v2】车牌号" :value="value" @show="showKeyboard('xmKeyboard')"></xm-cell>
// 虚拟键盘载体
<xm-keyboard-v2 ref="xmKeyboard" @confirm="(v) => value = v"></xm-keyboard-v2>
</view>
</template>
<script>
export default {
data(){
return {
value: '京A11223'
}
},
methods: {
showKeyboard(ref){
this.$refs[ref].toShow(this.value)
},
}
}
</script>
```
## 属性说明
| 名称 | 类型 | 是否必填 | 默认值 | 可选值 | 说明 |
| ----------- | ------- | -------- | ------- | ----------- | ------------------------- |
| title | string | 否 | '' | | 显示键盘标题 |
| cursor | boolean | 否 | false | true、false | 是否显示光标 |
| vibration | boolean | 否 | false | true、false | 是否开启震动 |
| type | string | 否 | 'plate' | | plate: 做了车牌号特殊处理 |
| max | number | 否 | 8 | | 输入内容的最大长度 |
| showContent | boolean | 否 | false | true、false | 是否直接显示键盘 |
| | | | | | |
## 事件说明
| 名称 | 参数 | 说明 |
| -------- | -------------- | ---------------------------------- |
| @confirm | function(data) | 键盘数据变化回调, data:输入的内容 |
| @cancel | function() | 键盘关闭回调 |
## xm-keyboard-box
> 键盘界面
```vue
<template>
<view>
<xm-keyboard-box></xm-keyboard-v2>
</view>
</template>
<script>
export default {
}
</script>
```
## 属性说明
| 名称 | 类型 | 是否必填 | 默认值 | 可选值 | 说明 |
| ------------- | ------- | -------- | ------ | ----------- | ---------------- |
| vibration | boolean | 否 | false | true、false | 是否开启震动 |
| showChangeBtn | boolean | 否 | false | true、false | 是否显示切换按钮 |
| showCancelBtn | boolean | 否 | false | true、false | 是否显示取消按钮 |
## 事件说明
| 名称 | 参数 | 说明 |
| -------- | -------------- | ---------------------------------- |
| @add | function(data) | 键盘数据变化回调, data:输入的内容 |
| @del | function() | 删除按钮点击回调 |
| @confirm | function() | 确认按钮点击回调 |
| @cancel | function() | 取消按钮点击回调 |
| @clear | function() | 清空按钮点击回调 |
## xm-keyboard-input
> 键盘输入框界面
```vue
<template>
<view>
<xm-keyboard-input></xm-keyboard-input>
</view>
</template>
<script>
export default {
}
</script>
```
## 属性说明
| 名称 | 类型 | 是否必填 | 默认值 | 可选值 | 说明 |
| ----------- | ------- | -------- | ------ | ----------- | ------------------------------- |
| initValue | string | 否 | '' | | 默认输入值 |
| cursor | boolean | 否 | false | true、false | 是否显示光标 |
| max | number | 否 | 8 | | 键盘输入框数量 |
| showPointer | boolean | 否 | true | true、false | 默认第二位 和 第三位之间 有个点 |
## 事件说明
| 名称 | 参数 | 说明 |
| -------- | -------------- | ---------------------------------- |
| @add | function(data) | 键盘数据变化回调, data:输入的内容 |
| @del | function() | 删除按钮点击回调 |
| @confirm | function() | 确认按钮点击回调 |
| @cancel | function() | 取消按钮点击回调 |
| @clear | function() | 清空按钮点击回调 |
## 如何使用(v1.0.2使用说明)
```vue
<xm-keyboard :show="show"></xm-keyboard>
```
## 属性说明
| 名称 | 类型 | 是否必填 | 默认值 | 可选值 | 说明 |
| ------------ | ------- | -------- | ------ | ----------- | ------------------------------------ |
| show | boolean | 是 | false | true、false | 是否展示键盘 |
| mask | boolean | 否 | true | true、false | 是否显示遮罩 |
| anim | boolean | 否 | true | true、false | 是否显示弹起动画 |
| defaultValue | string | 否 | '' | | 默认值 |
| exchange | boolean | 否 | true | true、false | 是否显示切换键盘按钮 |
| mode | number | 否 | 1 | 0、1 | 0:地区键盘,1:字母数字键盘,可拓展 |
| maxLength | number | 否 | -1 | | 限制输入长度 |
| disable | string | 否 | IO | | 禁用哪些按键 |
| maxAutoClose | boolean | 否 | false | true、false | 达到最大长度后是否自动关闭键盘 |
| safeSize | number | 否 | 10 | | 距离底部的安全距离, 自动匹配失效时可手动配置 |
## 事件说明
| 名称 | 参数 | 说明 |
| ------- | -------------- | ------------------------------------------------------------ |
| @change | function(data) | 键盘数据变化回调, data({ text: '', v: '', add: true }) text:输入的值, v:本次变化的值, add:添加还是删除 |
| @close | function(show) | 键盘关闭回调, show: 是否显示 |
## 快速体验
![](http://cdn.faysunshine.com/tc/202212211030790.png)
\ No newline at end of file
@font-face {
font-family: "iconxmk2";
/* Project id 4043845 */
src: url('//at.alicdn.com/t/c/font_4043845_qt9udeb66gq.woff2?t=1682651615161') format('woff2'),
url('//at.alicdn.com/t/c/font_4043845_qt9udeb66gq.woff?t=1682651615161') format('woff'),
url('//at.alicdn.com/t/c/font_4043845_qt9udeb66gq.ttf?t=1682651615161') format('truetype');
}
.iconxmk2 {
font-family: "iconxmk2" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-xm-k2-backspace:before {
content: "\e611";
}
.icon-xm-k2-jianpan:before {
content: "\e661";
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment