Vue3 + TS + Sass Micro program Based on UNI-App
Cheong Soars against the wind 鴍
Project background
Realize wechat applets based on UNI-App, using the framework vue3+ TS + SASS
Why custom picker?
Native applets do not support custom styles
This custom component supports:
Custom data Custom style linkage
Ideas:
Select scrolling is implemented using picker-View, a scroll selector embedded in the page by applets. Using picker-view-column in a picker-view automatically sets the height of the child node to the height of the picker-View check box and does not show it in the page
Picker – view is introduced:
attribute | type | mandatory | instructions | Minimum version |
---|---|---|---|---|
value | Array.<number> | no | The numbers in the array indicate the number of items (subscripts starting from 0) selected by the picker-View-column in the picker-View. If the number is greater than the optional length of the picker-view-column, the last item is selected. | 1.0.0 |
indicator-style | string | no | Sets the style of the checkbox in the middle of the selector | 1.0.0 |
indicator-class | string | no | Sets the class name of the box in the middle of the selector | 1.1.0 |
mask-style | string | no | Sets the style of the mask | 1.5.0 |
mask-class | string | no | Set the mask class name to 1.5.0 | |
bindchange | eventhandle | no | The change event, event.detail = {value}, is emitted while scrolling through the selection. Value is an array, indicating what item is currently selected for the picker-view-column in picker-view (subscripts start at 0) | 1.0.0 |
bindpickstart | eventhandle | no | Trigger event when scroll selection begins | 2.3.1 |
bindpickend | eventhandle | no | Raises the event when the scrolling selection ends | 2.3.1 |
Implementation:
1. Page Layout:
<template>
<view :class="['fh-full-box', isOpen ? 'fh-cur' : '']">
<view class="fh-picker">
<view class="fh-picker-header" :style="pickerHeaderStyle">
<view @click="cancle">
<text :style="cancelStyle">{{ cancelText }}</text>
</view>
<text :style="titleStyle">{{ titleText }}</text>
<view @click="sure">
<text :style="sureStyle">{{ sureText }}</text>
</view>
</view>
<picker-view
:value="value"
class="fh-picker-content"
@pickstart="pickStart"
@change="pickChange"
@pickend="pickEnd"
:indicator-style="indicatorStyle"
>
<picker-view-column
v-for="(items, index) in columnsData"
:key="index"
>
<view v-for="(item, index) in items" :key="index">
<text class="fh-line">{{
isUseKeywordOfShow ? item[keyWordsOfShow] : item
}}</text>
</view>
</picker-view-column>
</picker-view>
</view>
</view>
</template>
Copy the code
Style:
Since the component is introduced to overwrite the clicks on the page that will affect the normal page, pointer-events is used. The pointer-events attribute, which is used to specify under which circumstances an element can be a mouse event, has many values, but for browsers only two values are available: auto and None. So auto and None auto — the effect is the same as if the pointer-events attribute was not defined, and the mouse does not penetrate the current layer. None — The element will never be supported as a target for mouse events: This CSS3 property is supported by Firefox 3.6+, Chrome 2.0+, and Safari 4.0+, not by IE6/7/8/9, and supported by Opera in SVG but not HTML.
<style lang="scss" scoped>
.fh-full-box {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 9999;
background: rgba(0.0.0.0.4);
transition: all 0.4 s ease-in-out 0;
pointer-events: none;
opacity: 0;
.fh-picker {
position: absolute;
left: 0;
bottom: -470rpx;
display: flex;
flex-direction: column;
width: 100%;
height: 470rpx;
background: #ffffff;
transition: all 0.4 s ease-in-out 0;
}
.fh-picker-header {
height: 20%;
box-sizing: border-box;
padding: 0 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #eeeeee;
view {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
text {
font-size: 36rpx; }}}.fh-picker-content {
flex-grow: 1;
view {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
text {
font-size: 36rpx; }}.fh-line {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; }}}.fh-full-box.fh-cur {
opacity: 1;
pointer-events: auto;
.fh-picker {
bottom: 0;
}
}
</style>
Copy the code
Implementation logic:
Receive props from the parent component
props: {
scrollType: {
// "link": Scroll linkage"normal": scroll independent type: String, value:"normal",}, titleText: {// titleText type: String,}, cancelText: {// cancel button text type: String,}, sureText: {// confirm button text type: String,}, listData: {// Data source type: Array, default: () => [],}, defaultPickData: {// Default selection type: Array, default: () => [],}, pickerHeaderStyle: String, // String, // title bar unstyle text titleStyle: String, // title bar titleStyle view indicatorStyle: {// box style type: String, default:"height:48px;"}, keyWordsOfShow: {// Display key fields type: String, default:"name"Type: Boolean, default: false,},}, isShowPicker: {// whether to show pop-upsCopy the code
Define reactive data:
const DATA = reactive<BOOTOMPOPDATA>({
columnsData: []./ / array
value: [].// Select value
isOpen: false.// Whether to display picker
isUseKeywordOfShow: false.// Whether to display by keyword
scrollEnd: true.// Whether scrolling ends
tempValue: [].// Select the index of the data
});
// Use ts to define the interface constraint data
type dataARR = Array<string | number | object>;
export interface BOOTOMPOPDATA {
columnsData: dataARR,
value: dataARR,
isOpen: boolean.isUseKeywordOfShow: boolean.scrollEnd: boolean.tempValue: dataARR,
}
Copy the code
Some related button action events:
/ * * *@description: Close popover */
function closePicker() {
DATA.isOpen = false;
}
/ * * *@description: Open popover */
function openPicker() {
// Save to open again, select the selected content saved last time
setDefault();
DATA.isOpen = true;
}
/ * * *@description: Raises the event */ when the scroll selection begins
function pickStart() {
DATA.scrollEnd = false;
}
/ * * *@description: Raises the event */ when the scrolling selection ends
function pickEnd() {
DATA.scrollEnd = true;
}
/ * * *@description: Click the cancel button */
function cancle() {
closePicker();
context.emit("cancleFn");
}
/ * * *@description: Click ok */
function sure() {
const { scrollEnd, tempValue } = DATA;
if(! scrollEnd)return;
const backData = getBackDataFromValue(tempValue);
closePicker();
context.emit("sureFn", {
choosedData: backData,
choosedIndexArr: tempValue,
});
}
Copy the code
Data initialization is performed by determining the scrollType
function setDefault() {
const { scrollType } = props;
const { tempValue } = DATA;
let listData: any[] = props.listData;
let defaultPickData: any[] = props.defaultPickData;
switch (scrollType) {
case "normal":
if (isPlainObject(listData[0] [0])) {
DATA.isUseKeywordOfShow = true;
}
if (
Array.isArray(defaultPickData) &&
defaultPickData.length > 0
) {
DATA.tempValue = defaultPickData;
}
DATA.columnsData = listData;
DATA.value = defaultPickData;
break;
case "link":
let columnsData = [];
if (
Array.isArray(defaultPickData) &&
defaultPickData.length > 0
) {
if (defaultPickData.every((v) = > isPlainObject(v))) {
const key = Object.keys(defaultPickData[0[])0];
const arr: any[] = [];
getIndexByIdOfObject(
listData,
defaultPickData,
key,
arr
);
defaultPickData = arr;
}
let tempI = 0;
do {
columnsData.push(getColumnData(listData));
listData =
listData[defaultPickData[tempI]].children;
tempI++;
} while (listData);
} else {
do {
tempValue.push(0);
columnsData.push(getColumnData(listData));
listData = listData[0].children;
} while (listData);
}
DATA.tempValue = defaultPickData;
DATA.isUseKeywordOfShow = true;
DATA.columnsData = columnsData;
DATA.value = defaultPickData;
break; }}/ * * *@description: Gets the index */ by ID
function getIndexByIdOfObject(
listData: any[],
idArr: any[],
key: string,
arr: any[]
) :any {
if (!Array.isArray(listData)) return;
for (let i = 0, len = listData.length; i < len; i++) {
if (listData[i][key] === idArr[arr.length][key]) {
arr.push(i);
returngetIndexByIdOfObject( listData[i].children, idArr, key, arr ); }}}/ * * *@description: Filter data without children */
function getColumnData(arr: Array<object>) {
return arr.map((v) = > fomateObj(v));
}
function fomateObj(o: any) {
const temp: any = {};
for (const k ino) { k ! = ="children" && (temp[k] = o[k]);
}
return temp;
}
Copy the code
The change event is triggered when the scrollselection is performed. Determine whether linkage processing is performed according to scrollType
/ * * *@description: Raises the change event */ while scrolling through the selection
function pickChange(e: any) {
const { scrollType } = props;
const { tempValue } = DATA;
let val = e.detail.value;
switch (scrollType) {
case "normal":
DATA.tempValue = val.concat();
DATA.value = val.concat();
break;
case "link":
const tempArray: any[] = [];
val = validate(val);
if (val.length > 1) {
val.slice(0, val.length - 1).reduce(
(t: any, c: any) = > {
let v;
if (t.length < c || t.length == c) {
v = t[t.length - 1].children;
} else {
v = t[c].children;
}
tempArray.push(getColumnData(v));
return v;
},
props.listData
);
//
var columnsData = [DATA.columnsData[0], ...tempArray];
// Set the value association
var compareIndex = getScrollCompareIndex(
tempValue,
val
);
if (compareIndex > -1) {
let tempI = 1;
while(val[compareIndex + tempI] ! = =undefined) {
val[compareIndex + tempI] = 0;
tempI++;
}
}
DATA.tempValue = val.concat();
DATA.columnsData = columnsData;
DATA.value = val;
break; }}}/ * * *@description: linkage: checks whether the sub-index is 0 when scrolling. If it is not, returns for processing */
function getScrollCompareIndex(arr1: any[], arr2: any[]) {
let tempIndex = -1;
for (let i = 0, len = arr1.length; i < len; i++) {
if(arr1[i] ! == arr2[i]) { tempIndex = i;break; }}return tempIndex;
}
/ * * *@descriptionWhen the first column is changed, the subscripts of all child elements are changed to 0 */
function validate(val: number[]) {
const { tempValue } = DATA;
const len = tempValue.length;
for (let i = 0; i < len; i++) {
if(tempValue[i] ! = val[i] && val[i] ==0) {
const arr = val.splice(i + 1, len - 1);
for (let k = 0; k < arr.length; k++) {
val.push(0); }}}return val;
}
Copy the code
IsPlainObject ()
/ * * *@description: Gets the data type */
function _typeof(obj: any) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
/ * * *@description: Checks whether object */ is used
export function isPlainObject(obj: any) {
return _typeof(obj) === 'object';
}
Copy the code
Use:
Parameters that
name | type | required | default | description |
---|---|---|---|---|
isShowPicker | Boolean | is | false | Show hidden picker |
listData | Array | is | [] | The data source |
scrollType | String | no | “normal” | |
cancelText | String | no | “” | Cancel button copy |
sureText | String | no | “” | Confirm button copy |
sureStyle | String | no | “” | Confirm button style |
cancelStyle | String | no | “” | Cancel button style |
titleText | String | no | “” | Headline copy |
titleStyle | String | no | “” | Heading styles |
pickerHeaderStyle | String | no | “” | Title bar style |
defaultPickData | Array | no | [] | Default selection data |
keyWordsOfShow | String | no | “name” | When each member of the listData is an array of objects, keyWordsOfShow is the object’s key and its value is displayed. Or the key to display when picker=’link’ |
cancleFn | Funciton | no | “” | Click to cancel the triggered event |
sureFn | Funciton | no | “” | Click ok to trigger the event |
Introduce the component in the page
import bottomPop from "@/components/bottomPop/bottomPop.vue"; <bottom-pop :isShowPicker="showPicker2" :listData="listData2" scrollType="normal" cancelText=" cancelText "sureText=" confirm" SureStyle ="color:green" cancelStyle="color:red" titleText=" multiple non-linkage "titleStyle="color:orange" pickerHeaderStyle="height:160rpx;" :defaultPickData="defaultPickData2" keyWordsOfShow="str" @cancleFn="cancleCallBack2" @sureFn="sureCallBack2" ></bottom-pop>Copy the code
Click OK to cancel the event
function sureCallBack2(e: any) {
console.log("Hit OK");
}
function cancleCallBack2() {
console.log("Click close");
}
Copy the code