提交 9b611903 编写于 作者: A ailululu

fix: 合并

registry=https://registry.npmjs.org
engine-strict=true
{"design":"vcgLzZ0ctHqnqRhSMmxbHFPKHOk=","international":"48VFbFFji5XeQPSYVbEkhUXs9jw=","intro":"NzilpMOPGLKCi3NAacAUHKA5PM8=","joinus":"xe5bD6hZaxe0Xg0m4zCFnTb5JKY=","start":"hWB+R788ekxKa3Y8TItd8OEYnyE=","theme":"ul//vB9gvub+r0tmKQ0utENYGGY="}
\ No newline at end of file
{"mdToVue":"YyHHl228Lm/wkXcldAK2nCQ7hw8="}
\ No newline at end of file
{"actionsheet":"SN660NxsmHTOHWFlINRs7jDhbZE=","address":"iHsjhF1bj84NZaSpwxTagjWqKXg=","avatar":"y2VelTNQJSIjf6PIaWoOgw9Gtn0=","backtop":"5esPro9iIge+l/AK9WCGuKj2w+0=","badge":"q/qbw2iIqkAu1XVYj6P84ZpLkmA=","button":"GYwiDlXmvJ1/F/9OCMyfEMQAaHQ=","buttongroup":"0PPL1IcUOwm1/2HHLS+9sHV9hy0=","calendar":"AjUcH9tq6urCsSisqescxn1lDQQ=","cell":"003K+OtyrnfD3Xp+iKwww4TgLOU=","checkbox":"c3p1oMREjDB47L0lekU1oQ/SIow=","checkboxgroup":"8JAFnlglXs/Vvk4CKyVBJZhoW5Y=","circleprogress":"tOmupi4UEPm4eujy/G3fui1lZ6g=","collapse":"Rrq4ZmLOzc5ML2OylkiYgeHFqAE=","collapseitem":"bIYV2APMhMSzgUOletPoN2NH0O0=","countdown":"WhCuH/CMoQ0MDIX0r7Bjx6vGdA4=","countup":"OR83kgR2VKfojQPbClGPM3Ngpuk=","datepicker":"mjWEWXg/y2fHrztdGDZeHmhzzy8=","dialog":"rN467zkvytz9kBYVoJWKEfnwO0E=","drag":"a/g1S6oPcGQ7jWOU0tBoMuva7lE=","elevator":"RGvv/8Rz7kMwu4CrghLksT+xhL4=","fixednav":"aEfnIniyK9mJdMYIXGG22WBo+fs=","flex":"ETZ1qXDxBPWs08KQLsHn3Iuu/Y4=","gesture":"PBbFWobVtXuQUOSfH5sKXO6BMUY=","grid":"iaGTmOs1G9eNqyC1lBqE1fagu34=","icon":"fHoLf8lzpNW+3GKK+F14W+hkJJU=","imagepicker":"/0N7Ank9UkpyesX0FKOMWzjmIzk=","infiniteloading":"mtrAo7QHcOdbrMVLkJY9rlSY5F4=","lazyload":"q4xqYYdxKRaLFYZNK4UsUCYSuKI=","leftslip":"SKqTwaBp9LzMkhvi2D5tJVcadqI=","luckdraw":"8+br83CrNH5b2FollPTce/nS0o4=","luckycard":"Kyx3QEZfmrUPpxQURhKIUvRBvds=","menu":"7jMIDPF5FNvO3ut6/a4tGfIN0BE=","navbar":"xzsOD2ncNhlqKNc4RN9Lg/zQcpA=","noticebar":"rbBxY5yZJYz5oN1UxB3BIK/fTkw=","notify":"O//laT9S82N9vr9w1aFhlGeuTb0=","numberkeyboard":"6/xKShUUccdtXI82jwZatBq+Uoo=","picker":"dqfVdQJE8qK2S65X4NSCxBlckVk=","popup":"T8P+IcpvFfdn2Z+UBQy5qhRh5Zc=","price":"u7YwMSY/7UT/XALwYFVNfVTinCU=","progress":"tEvkvDjVyhlOXIxcpn396B4lwRw=","qart":"D5eDoFh0ubkeMe9BQzxikGHXlfA=","radio":"8NkHih7Ivh2sMP0m2fnqq2LrsgI=","range":"yZG8zvo3sTtOyZoUWVJjCNA3RlU=","rate":"kS5hKsqQEHER7cQ8qLDHxxyXuus=","scroller":"0CM3W1JpKpNrAkLtzTROAfvj6uY=","searchbar":"slMI0Q3QtW4Hzj7h6cg5hRjgv3M=","shortpassword":"SRHWK8nGfmZrOAfCJhvexPVfmeU=","sidenavbar":"4TMDNODGQE3baqKWrABucyCTqTc=","signature":"ZthW9l4aMug9yI1EYf9sh83CcJA=","basic":"veGPriZOTXGYFz7PN8dfB9kDjps=","slider":"+xSl16wSTJBXxsVZBBGYtsm0w58=","stepper":"NKcQrenccs4NaLmXAUTyKeq3Az4=","steps":"ebxJQJsGMqPCb+u5OnyjJd9+26w=","swiper":"jQ++ds25JDnnnoUgolsLwwwFWW4=","switch":"5YXBEcQdGjh4NbX+cpeHjNzdaQg=","tab":"tVSBBNfWhX5OwA344VWpm0yOQIQ=","tabbar":"cJnhoJlsFvSU6044RSDapUKTvu4=","tabselect":"ir3gYqITddW2sih2StbfgWG4yAk=","textbox":"i9dvcE5hEa1M0iQD71ENTdOgxks=","textinput":"vcKYI9BiGGFhtWNlyiICwh2fqBk=","timeline":"Cx/lWgB07bw2ZoK38ZzkY/ieC1Y=","timelineitem":"iaGTmOs1G9eNqyC1lBqE1fagu34=","toast":"q3RmqgV4zSnj2O/ZMbTg+Pu7j7o=","uploader":"aDl79WlOuvemC6bBbNZ3TfbxLj8=","video":"qwYf75g05dMlqhkhOn3NtLfTehw="}
\ No newline at end of file
......@@ -5,9 +5,9 @@ const inquirer = require('inquirer');
const path = require('path');
const fs = require('fs');
const config =require("../src/config");
const demoModel=require("./demo")
const nav=config.nav;
const config = require('../src/config');
const demoModel = require('./demo');
const nav = config.nav;
var newCpt = {
version: '3.0.0',
......@@ -27,18 +27,18 @@ function init() {
name: 'name',
message: '组件英文名(每个单词的首字母都大写,如TextBox):',
validate(value) {
let repeat = false;
for (var i = 0; i < nav.length; i++) {
for (var j = 0; j < nav[i].packages.length; j++) {
if (nav[i].packages[j].name === value) {
repeat = true;
}
let repeat = false;
for (var i = 0; i < nav.length; i++) {
for (var j = 0; j < nav[i].packages.length; j++) {
if (nav[i].packages[j].name === value) {
repeat = true;
}
}
}
if (repeat) {
return '该组件名已存在!';
}
if (repeat) {
return '该组件名已存在!';
}
const pass = value && value.match(/^[A-Z]/);
if (pass) {
return true;
......@@ -76,18 +76,19 @@ function init() {
return '输入有误!请输入选项前编号';
}
},
{
type: 'input',
name: 'sort',
message: '请选择组件分类(输入编号):1布局组件,2操作反馈,3基础组件,4导航组件,5数据录入,6业务组件',
validate(value) {
const pass = /^[1-6]$/.test(value);
if (pass) {
return true;
}
return '输入有误!请输入选项前编号';
{
type: 'input',
name: 'sort',
message:
'请选择组件分类(输入编号):1布局组件,2操作反馈,3基础组件,4导航组件,5数据录入,6业务组件',
validate(value) {
const pass = /^[1-6]$/.test(value);
if (pass) {
return true;
}
},
return '输入有误!请输入选项前编号';
}
},
// {
// type: 'confirm',
// name: 'showDemo',
......@@ -148,7 +149,7 @@ function createIndexJs() {
// }
// fs.writeFile(filePath, content, (err) => {
// if (err) throw err;
resolve(`生成index.js文件成功`);
resolve(`生成index.js文件成功`);
// });
});
}
......@@ -187,19 +188,19 @@ function createDemo() {
function addToPackageJson() {
return new Promise((resolve, reject) => {
let sort=newCpt.sort;
newCpt.sort=nav[sort-1].packages.length+1;
nav[sort-1].packages.push(newCpt);
config.nav=nav;
// conf.packages.push(newCpt);
const dirPath = path.join(__dirname, `../`);
const filePath = path.join(dirPath, `src/config.js`);
var tempfile="module.exports = "+JSON.stringify(config, null, 2)+";"
fs.writeFile(filePath, tempfile, (err) => {
if (err) throw err;
resolve(`修改config.json文件成功`);
});
let sort = newCpt.sort;
newCpt.sort = nav[sort - 1].packages.length + 1;
nav[sort - 1].packages.push(newCpt);
config.nav = nav;
// conf.packages.push(newCpt);
const dirPath = path.join(__dirname, `../`);
const filePath = path.join(dirPath, `src/config.js`);
var tempfile = 'module.exports = ' + JSON.stringify(config, null, 2) + ';';
fs.writeFile(filePath, tempfile, err => {
if (err) throw err;
resolve(`修改config.json文件成功`);
});
});
}
function createScss() {
......@@ -251,7 +252,7 @@ function createNew() {
return createDoc();
})
.then(() => {
return addToPackageJson();
return addToPackageJson();
})
.then(() => {
console.log('组件模板生成完毕,请开始你的表演~');
......
......@@ -5,8 +5,8 @@ var demoModel = function(nameLc) {
<div class="demo">
<h2>基础用法</h2>
<nut-cell>
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail" txt="test txt"></nut-temp>
<${nameLc} ></${nameLc}>
<${nameLc} ></${nameLc}>
</nut-cell>
</div>
</template>
......@@ -23,7 +23,7 @@ var demoModel = function(nameLc) {
</script>
<style lang="scss" scoped>
.nut-temp {
.demo{
}
</style>
`,
......
......@@ -20,7 +20,7 @@ module.exports = {
path: '/'
},
{
name: '/',
name: 'intro',
cName: '组件',
path: '/'
},
......@@ -96,6 +96,33 @@ module.exports = {
show: false,
desc: '折叠面板-item',
author: 'Ymm0008'
},
{
name: 'Layout',
sort: 4,
cName: '布局',
type: 'component',
show: true,
desc: '简便地创建布局',
author: 'undo'
},
{
name: 'col',
sort: 5,
cName: '布局-Col',
type: 'component',
show: false,
desc: '布局组件Col',
author: 'undo'
},
{
name: 'row',
sort: 6,
cName: '布局-Row',
type: 'component',
show: false,
desc: '布局组件Row',
author: 'undo'
}
]
},
......@@ -110,6 +137,34 @@ module.exports = {
show: true,
desc: '较长页面快捷回到顶部',
author: 'liqiong43'
},
{
name: 'ActionSheet',
sort: '1',
cName: '动作面板',
type: 'component',
show: true,
desc: '底部弹出动作菜单面板',
author: 'zhangyufei'
},
{
name: 'Toast',
sort: '1',
cName: '吐司',
type: 'component',
show: true,
desc: '轻提示',
author: 'undo'
},
{
version: '3.0.0',
name: 'Notify',
type: 'component',
cName: '消息提示',
desc: '在页面顶部展示消息提示,支持函数调用和组件调用两种方式',
sort: 4,
show: true,
author: 'wangyue217'
}
]
},
......@@ -187,6 +242,96 @@ module.exports = {
show: true,
desc: '头像',
author: 'ailululu'
},
{
name: 'Popup',
sort: 8,
cName: '弹出层',
type: 'component',
show: true,
desc:
'弹出层容器,用于展示弹窗、信息提示等内容,支持多个弹出层叠加展示',
author: 'szg2008',
version: '3.0.0'
},
{
name: 'Dialog',
type: 'component',
cName: '对话框',
desc:
'模态对话框,在浮层中显示,引导用户进行相关操作,支持图片对话框。',
sort: 8,
show: true,
author: 'dsj'
},
{
version: '3.0.0',
name: 'Radio',
type: 'component',
cName: '单选按钮',
desc: '单选按钮',
sort: 9,
show: true,
author: 'Ymm0008'
},
{
version: '3.0.0',
name: 'RadioGroup',
type: 'component',
cName: '单选按钮组',
desc: '单选按钮组',
sort: 10,
show: false,
author: 'Ymm0008'
},
{
version: '3.0.0',
name: 'CheckboxGroup',
type: 'component',
cName: '多选按钮组',
desc: '多选按钮组',
sort: 11,
show: false,
author: 'Ymm0008'
},
{
version: '3.0.0',
name: 'OverLay',
type: 'component',
cName: '遮罩层',
desc: '创建一个遮罩层,通常用于阻止用户进行其他操作',
sort: 14,
show: true,
author: 'szg2008'
},
{
version: '3.0.0',
name: 'InfiniteLoading',
type: 'component',
cName: '滚动加载',
desc: '列表滚动到底部自动加载更多数据',
sort: 15,
show: true,
author: 'yangxiaolu'
},
{
version: '3.0.0',
name: 'Range',
type: 'component',
cName: '区间选择器',
desc: '滑动输入条,用于在给定的范围内选择一个值。',
sort: 16,
show: true,
author: 'Jerry'
},
{
name: 'PullRefresh',
type: 'component',
cName: '下拉刷新',
desc: '下拉刷新',
sort: 16,
show: true,
author: 'yangxiaolu3'
}
]
},
......@@ -210,6 +355,34 @@ module.exports = {
show: true,
desc: '标签组件',
author: 'zhenyulei'
},
{
name: 'menu',
sort: 2,
cName: '菜单组件',
type: 'component',
show: true,
desc: '下拉菜单组件',
author: 'vickyYE'
},
{
name: 'tabbar',
sort: 2,
cName: '标签栏组件',
type: 'component',
show: true,
desc: '标签栏组件',
author: 'Drjingfubo'
},
{
version: '3.0.0',
name: 'NoticeBar',
type: 'component',
cName: '公告栏',
desc: '用于循环播放展示一组消息通知',
sort: 5,
show: false,
author: 'wangyue92'
}
]
},
......@@ -233,6 +406,46 @@ module.exports = {
show: true,
desc: '输入框组件',
author: 'gxx158'
},
{
version: '3.0.0',
name: 'Switch',
type: 'component',
cName: '开关组件',
desc: '用来打开或关闭选项',
sort: 3,
show: true,
author: 'zongyue3'
},
{
version: '3.0.0',
name: 'Rate',
sort: 4,
cName: '评分',
type: 'component',
show: true,
desc: '评分组件',
author: 'undo'
},
{
version: '3.0.0',
name: 'Calendar',
type: 'component',
cName: '日历',
desc: '日历组件',
sort: 5,
show: true,
author: ''
},
{
version: '3.0.0',
name: 'ShortPassword',
type: 'component',
cName: '短密码',
desc: '短密码组件',
sort: 6,
show: true,
author: 'Drjingfubo'
}
]
},
......
<template>
<div class="demo full">
<h2>基础用法</h2>
<nut-cell @click="switchActionSheet('dialogShow')">点击出现输出框</nut-cell>
<nut-cell>您输入的密码是:{{ state.value }}</nut-cell>
<h2>展示按钮</h2>
<nut-cell @click="switchActionSheet('dialogShow1')"
>点击出现输出框</nut-cell
>
<nut-cell>您输入的密码是:{{ state.value1 }}</nut-cell>
<h2>自定义长度</h2>
<nut-cell @click="switchActionSheet('dialogShow2')"
>点击出现输出框</nut-cell
>
<nut-cell>您输入的密码是:{{ state.value2 }}</nut-cell>
<h2>出现提示信息</h2>
<nut-cell @click="switchActionSheet('dialogShow3')"
>点击出现输出框</nut-cell
>
<nut-cell>您输入的密码是:{{ state.value3 }}</nut-cell>
<nut-shortpassword
v-model:value="state.value"
v-model:is-visible="state.dialogShow"
>
</nut-shortpassword>
<nut-shortpassword
v-model:value="state.value1"
v-model:is-visible="state.dialogShow1"
:no-button="false"
@sureClick="sureClick"
>
</nut-shortpassword>
<nut-shortpassword
v-model:value="state.value2"
v-model:is-visible="state.dialogShow2"
:length="5"
>
</nut-shortpassword>
<nut-shortpassword
v-model:value="state.value3"
v-model:is-visible="state.dialogShow3"
:errorMsg="state.errorMsg"
>
</nut-shortpassword>
</div>
</template>
<script lang="ts">
import { ref, reactive, toRefs, watch } from 'vue';
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('shortpassword');
export default createDemo({
setup() {
const state = reactive({
dialogShow: false,
dialogShow1: false,
dialogShow2: false,
dialogShow3: false,
value: '',
value1: '',
value2: '',
value3: '',
errorMsg: ''
});
// 方法
function switchActionSheet(param) {
state[`${param}`] = !state[`${param}`];
}
function sureClick() {
state.dialogShow1 = false;
}
watch(
() => state.value3,
val => {
if (val.length == 6) {
state.errorMsg = '请输入正确密码';
}
}
);
return {
state,
switchActionSheet,
sureClick
};
}
});
</script>
<style lang="scss" scoped></style>
# ShortPassword 短密码组件
### 介绍
XXXXXX
### 安装
``` javascript
import { createApp } from 'vue';
import { ShortPassword } from '@nutui/nutui';
const app = createApp();
app.use(ShortPassword);
```
## 代码示例
### 基础用法
``` html
<div @click="switchActionSheet('dialogShow')" >点击出现输出框</div>
<nut-shortpassword
v-model:value="state.value"
v-model:is-visible="state.dialogShow"
>
</nut-shortpassword>
```
``` javascript
setup() {
const state = reactive({
dialogShow: false,
value: '',
});
return {
state,
};
},
// ...
```
### 展示按钮
``` html
<div @click="switchActionSheet('dialogShow')" >点击出现输出框</div>
<nut-shortpassword
v-model:value="state.value"
v-model:is-visible="state.dialogShow"
:no-button="false"
@sureClick='sureClick'
>
</nut-shortpassword>
```
``` javascript
setup() {
const state = reactive({
dialogShow: false,
value: '',
});
function switchActionSheet() {
state.dialogShow = !state.dialogShow
}
function sureClick() {
state.dialogShow = false
}
return {
state,
switchActionSheet,
sureClick
};
},
```
### 自定义长度
``` html
<div @click="switchActionSheet('dialogShow')" >点击出现输出框</div>
<nut-shortpassword
v-model:value="state.value"
v-model:is-visible="state.dialogShow"
:length=5
>
</nut-shortpassword>
```
``` javascript
setup() {
const state = reactive({
dialogShow: false,
value: '',
});
function switchActionSheet() {
state.dialogShow = !state.dialogShow
}
return {
state,
switchActionSheet
};
},
```
### 出现提示信息
``` html
<div @click="switchActionSheet('dialogShow')" >点击出现输出框</div>
<nut-shortpassword
v-model:value="state.value"
v-model:is-visible="state.dialogShow"
:errorMsg = state.errorMsg
>
</nut-shortpassword>
```
``` javascript
setup() {
const state = reactive({
dialogShow: false,
value: '',
errorMsg:''
});
function switchActionSheet() {
state.dialogShow = !state.dialogShow
}
watch(
() => state.value3,
val => {
if (val.length == 6) {
state.errorMsg = '请输入正确密码'
}
}
);
return {
state,
switchActionSheet
};
},
```
### Prop
| 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | -----
| isVisible | 是否展示短密码框| Booble | false
| value | 密码值 | string | ''
| noButton | 是否隐藏底部按钮 |Booble|true|
| length | 密码长度 |string||Number|6|
| errorMsg | 错误信息提示 |string|''|
### Event
| 事件名称 | 说明 | 回调参数
|----- | ----- | -----
| input | 输入密码时触发事件 | --
| sureClick | 点击确实时触发事件 | --
.nut-shortpsd-subtitle {
display: inline-block;
margin-bottom: 24px;
}
.nut-input-w {
padding: 24px 0 10px 0;
text-align: center;
position: relative;
input {
width: 247px;
height: 41px;
background: rgba(245, 245, 245, 1);
border-radius: 4px;
}
.nut-input-real {
padding: 0 12px;
}
}
.psd-blink {
display: inline-block;
}
.nut-shortpsd-fake {
width: 100%;
height: 41px;
margin: 0 auto;
background: rgba(245, 245, 245, 1);
border-radius: 4px;
border: 1px solid #ddd;
display: flex;
justify-content: space-between;
cursor: pointer;
position: absolute;
left: 0;
top: 29%;
z-index: 0;
}
.nut-shortpsd-li {
width: 16.5%;
display: flex;
justify-content: center;
align-items: center;
}
.nut-shortpsd-icon {
height: 6px;
width: 6px;
border-radius: 50%;
background: #000;
display: inline-block;
}
.nut-shortpsd-message {
margin-top: 9px;
margin-left: 20px;
display: flex;
justify-content: space-between;
width: 247px;
.nut-shortpsd-error {
line-height: 10px;
font-family: PingFangSC-Regular;
font-size: 10px;
color: rgba(242, 39, 12, 1);
}
.nut-shortpsd-forget {
line-height: 12px;
font-family: PingFangSC-Regular;
font-size: 12px;
color: rgba(128, 128, 128, 1);
}
}
.nut-dialog-content {
padding-bottom: 0;
}
<template>
<view>
<nut-dialog
title="请输入密码"
:visible="dialogShow"
@ok-btn-click="sureClick"
@cancel-btn-click="dialogShow = false"
@close="close"
:noFooter="noButton"
>
<view class="nut-shortpsd-subtitle">您使用了虚拟资产,请进行验证</view>
<view class="nut-input-w">
<input
ref="realpwd"
class="nut-input-real"
type="number"
maxlength="6"
v-model="realInput"
@input="changeValue"
/>
<view class="nut-shortpsd-fake" @click="focus">
<view class="nut-shortpsd-li"
><view class="nut-shortpsd-icon" v-if="realInput.length > 0"></view
></view>
<view class="nut-shortpsd-li"
><view class="nut-shortpsd-icon" v-if="realInput.length > 1"></view
></view>
<view class="nut-shortpsd-li"
><view class="nut-shortpsd-icon" v-if="realInput.length > 2"></view
></view>
<view class="nut-shortpsd-li"
><view class="nut-shortpsd-icon" v-if="realInput.length > 3"></view
></view>
<view v-if="length >= 5" class="nut-shortpsd-li"
><view class="nut-shortpsd-icon" v-if="realInput.length > 4"></view
></view>
<view v-if="length == 6" class="nut-shortpsd-li"
><view class="nut-shortpsd-icon" v-if="realInput.length > 5"></view
></view>
</view>
</view>
<view class="nut-shortpsd-message">
<view class="nut-shortpsd-error">{{ errorMsg }}</view>
<view class="nut-shortpsd-forget">
<nut-icon class="icon" size="11px" name="tips"></nut-icon
>忘记密码</view
>
</view>
</nut-dialog>
</view>
</template>
<script lang="ts">
import { ref, onMounted, watch, reactive, watchEffect } from 'vue';
import { createComponent } from '@/utils/create';
const { create } = createComponent('shortpassword');
export default create({
props: {
isVisible: {
type: Boolean,
default: false
},
value: {
type: String,
default: ''
},
errorMsg: {
type: String,
default: ''
},
noButton: {
type: Boolean,
default: true
},
length: {
type: String || Number,
default: 6
}
},
setup(props, { emit }) {
const dialogShow = ref(false);
const realInput = ref(props.value);
const realpwd = ref();
// 方法
function sureClick() {
emit('sureClick');
}
function focus() {
realpwd.value.focus();
}
function changeValue(e: Event) {
const input = e.target as HTMLInputElement;
let val = input.value;
if (val.length > Number(props.length)) {
val = val.slice(0, Number(props.length));
}
if (realInput.value.length > Number(props.length)) {
realInput.value = realInput.value.slice(0, Number(props.length));
}
emit('input', val);
emit('update:value', val);
}
function close() {
emit('update:isVisible', false);
}
watch(
() => props.isVisible,
val => {
if (val) {
dialogShow.value = true;
} else {
dialogShow.value = false;
}
}
);
return {
dialogShow,
sureClick,
realInput,
realpwd,
focus,
changeValue,
close
};
}
});
</script>
<style lang="scss">
@import 'index.scss';
</style>
<template>
<div class="demo-list demo">
<h4>基本用法(选择类)</h4>
<div>
<nut-cell
:showIcon="true"
:isLink="true"
@click="switchActionSheet('isVisible1')"
>
<span><label>基础用法</label></span>
<div class="selected-option">{{ state.val1 }}</div>
</nut-cell>
<nut-cell
:showIcon="true"
:isLink="true"
@click="switchActionSheet('isVisible2')"
>
<span><label>展示取消按钮</label></span>
<div class="selected-option">{{ state.val2 }}</div>
</nut-cell>
<div>
<nut-cell :isLink="true" @click="switchActionSheet('isVisible3')">
<span><label>展示描述信息</label></span>
</nut-cell>
</div>
<h4>选项状态</h4>
<nut-cell :isLink="true" @click="switchActionSheet('isVisible4')">
<span><label>选项状态</label></span>
<!-- <div class="selected-option">打开</div> -->
</nut-cell>
<h4>自定义面板</h4>
<nut-cell :isLink="true" @click="switchActionSheet('isVisible5')">
<span><label>自定义内容</label></span>
<!-- <div class="selected-option">打开</div> -->
</nut-cell>
</div>
<!-- demo 基础用法 -->
<nut-actionsheet
:is-visible="state.isVisible1"
:menu-items="menuItemsOne"
@choose="chooseItem"
></nut-actionsheet>
<!-- demo(带取消按钮) -->
<nut-actionsheet
:is-visible="state.isVisible2"
cancelTxt="取消"
:menu-items="menuItemsOne"
@choose="chooseItemTwo"
></nut-actionsheet>
<nut-actionsheet
:is-visible="state.isVisible3"
:description="state.desc"
:menu-items="menuItemsTwo"
cancelTxt="取消"
>
</nut-actionsheet>
<!-- demo 选项状态-->
<nut-actionsheet
:is-visible="state.isVisible4"
cancelTxt="取消"
:menu-items="menuItemsThree"
></nut-actionsheet>
<!-- demo 自定义 -->
<nut-actionsheet :is-visible="state.isVisible5" cancelTxt="取消">
<div class="custom-wrap"><span>自定义</span></div>
</nut-actionsheet>
</div>
</template>
<script lang="ts">
import { reactive } from 'vue';
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('actionsheet');
export default createDemo({
props: {},
setup() {
const state = reactive({
isVisible1: false,
isVisible2: false,
isVisible3: false,
isVisible4: false,
isVisible5: false,
val1: '',
val2: '',
val3: '',
desc: '这是一段描述信息'
});
const menuItemsOne = [
{
name: '选项一',
value: 0
},
{
name: '选项二',
value: 1
},
{
name: '选项三',
value: 2
}
];
const menuItemsTwo = [
{
name: '选项一',
value: 0
},
{
name: '选项二',
value: 1
},
{
name: '选项三',
subname: '描述信息',
value: 2
}
];
const menuItemsThree = [
{
name: '着色选项',
color: '#ee0a24',
value: 0
},
{
name: '禁用选项',
disable: true,
value: 1
}
];
const switchActionSheet = param => {
state[`${param}`] = !state[`${param}`];
// console.log(state[`${param}`], '2');
};
const chooseItem = itemParams => {
console.log(itemParams, 'itemParams');
state.val1 = itemParams.name;
};
function chooseItemTwo(itemParams) {
state.val2 = itemParams.name;
}
return {
state,
menuItemsOne,
menuItemsTwo,
menuItemsThree,
chooseItem,
chooseItemTwo,
switchActionSheet
};
}
});
</script>
<style lang="scss" scoped>
.demo {
height: 100%;
background: #f7f8fa;
overflow-x: hidden;
overflow-y: auto;
padding: 0 25px;
padding-top: 57px;
h4 {
height: 56px;
line-height: 56px;
font-size: 14px;
color: #909ca4;
}
}
.custom-wrap {
padding: 110px 0;
text-align: center;
}
.nut-cell {
justify-content: space-between;
}
</style>
# ActionSheet 动作面板
从底部弹出的动作菜单面板。
## 基本用法(选择类)
默认
```html
<div @click.native="switchActionSheet">
<span class="title"><label>性别</label></span>
<span class="selected-option">{{sex}}</span>
</div>
<nut-actionsheet :is-visible="isVisible"
@close="switchActionSheet"
:menu-items="menuItems"
@choose="chooseItem"
></nut-actionsheet>
```
```javascript
export default {
data() {
return {
sex: '请选择',
isVisible: false,
menuItems: [
{
'name': '',
'value': 0
},
{
'name': '',
'value': 1
}
]
};
},
methods: {
switchActionSheet() {
this.isVisible = !this.isVisible;
},
chooseItem(itemParams) {
this.sex = itemParams.name;
}
}
};
```
带取消按钮
```html
<div @click.native="switchActionSheet">
<span class="title"><label>性别</label></span>
<span class="selected-option">{{sex}}</span>
</div>
<nut-actionsheet :is-visible="isVisible"
@close="switchActionSheet"
:menu-items="menuItems"
@choose="chooseItem"
cancelTxt="取消"
></nut-actionsheet>
```
```javascript
export default {
data() {
return {
sex: '请选择',
isVisible: false,
menuItems: [
{
'name': '',
'value': 0
},
{
'name': '',
'value': 1
}
]
};
},
methods: {
switchActionSheet() {
this.isVisible = !this.isVisible;
},
chooseItem(itemParams) {
this.sex = itemParams.name;
}
}
};
```
高亮已选项
```html
<div @click.native="switchActionSheet">
<span class="title"><label>性别</label></span>
<span class="selected-option">{{sex}}</span>
</div>
<nut-actionsheet :is-visible="isVisible"
@close="switchActionSheet"
:menu-items="menuItems"
@choose="chooseItem"
:chooseTagValue="sex"
></nut-actionsheet>
```
```javascript
export default {
data() {
return {
sex: '请选择',
isVisible: false,
menuItems: [
{
'name': '',
'value': 0
},
{
'name': '',
'value': 1
}
]
};
},
methods: {
switchActionSheet() {
this.isVisible = !this.isVisible;
},
chooseItem(itemParams) {
this.sex = itemParams.name;
}
}
};
```
设置禁用状态
```html
<div @click.native="switchActionSheet">
<span class="title"><label>性别</label></span>
<span class="selected-option">{{sex}}</span>
</div>
<nut-actionsheet :is-visible="isVisible"
@close="switchActionSheet"
:menu-items="menuItems"
@choose="chooseItem"
:chooseTagValue="sex"
></nut-actionsheet>
```
```javascript
export default {
data() {
return {
sex: '请选择',
isVisible: false,
menuItems: [
{
'name': '',
'value': 0,
'disable': false
},
{
'name': '',
'value': 1,
'disable': true
}
]
};
},
methods: {
switchActionSheet() {
this.isVisible = !this.isVisible;
},
chooseItem(itemParams) {
this.sex = itemParams.name;
}
}
};
```
## 提示
```html
<div @click.native="switchActionSheet">
<span class="title"><label>我就列表测试数据</label></span>
<span class="sub-title">我是描述~~~~</span>
<div class="selected-option">删除本条</div>
</div>
<nut-actionsheet :is-visible="isVisible"
:menu-items="menuItems4"
chooseTagValue="确定"
cancelTxt="取消"
@close="switchActionSheet"
@choose="choose"
>
<span slot="title"><label>确定删除吗?</label></span>
<span slot="sub-title">删除之后不能,描述信息,删除之后不能,描述信息</span>
</nut-actionsheet>
```
```javascript
export default {
data() {
return {
sex: '请选择',
isVisible: false,
menuItems: [
{
'name': '确定'
}
]
};
},
methods: {
switchActionSheet() {
this.isVisible = !this.isVisible;
},
choose(itemParams) {
}
}
};
```
## 自定义内容
```html
<div @click.native="switchActionSheet">
<span class="title"><label>内容自定义</label></span>
<div class="selected-option">打开</div>
</div>
<nut-actionsheet :is-visible="isVisible"
@close="switchActionSheet"
>
<div slot="custom" class="custom-wrap"><span>自定义</span></div>
</nut-actionsheet>
```
```javascript
export default {
data() {
return {
isVisible: false
}
},
methods: {
switchActionSheet() {
this.isVisible = !this.isVisible;
}
}
};
```
## Prop
| 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | -----
| is-animation | 是否开启动画 | Boolean | true
| is-lock-bg-scroll | 是否锁定背景滚动 | Boolean | false
| is-visible | 是否可见 | Boolean | false
| is-show-mask | 是否显示背景遮罩 | Boolean | true
| is-click-choose-close | 是否点击列表项后立即关闭 | Boolean | true
| is-click-close-mask | 是否点击mask蒙层关闭 | Boolean | true
| cancel-txt | 取消文案 | String | '取消'
| choose-tag-value | 已选值,如果填写,高亮显示 | String | -
| menu-items | 列表项 | Array | [ ]
| option-tag | 设置列表项展示使用参数 | String | 'name'
## Slot
| 名称 | 说明
|----- | -----
| custom | 自定义内容
| title | 自定义标题
| subTitle | 自定义副标题
## Event
| 字段 | 说明 | 回调参数
|----- | ----- | -----
| choose | 选择之后触发 | 选中列表项
| close | 关闭时触发 | 无
| cancel | 点击取消文案时触发 | 无
\ No newline at end of file
.nut-actionsheet-panel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
width: 100%;
max-height: 80%;
overflow: auto;
z-index: $zindex-actionsheet;
background-color: $body-background;
}
.nut-actionsheet-modal {
padding: 10px;
margin: 0;
text-align: center;
background-color: #fff;
border-bottom: 1px solid $light-color;
.nut-actionsheet-title,
.nut-actionsheet-sub-title {
padding: 5px 0;
}
.nut-actionsheet-title {
font-size: $font-size-base;
color: $title-color;
}
.nut-actionsheet-sub-title {
font-size: $font-size-small;
color: $title-color;
margin-inline-start: 0px;
}
}
.nut-actionsheet-menu {
list-style: none;
padding: 0;
margin: 0;
}
.nut-actionsheet-cancel,
.nut-actionsheet-item {
// height: 24px;
padding: 10px;
line-height: 24px;
font-size: $font-size-base;
color: $title-color;
text-align: center;
background-color: #fff;
}
.desc {
font-size: 14px;
color: #999;
}
.subdesc {
display: block;
font-size: 12px;
color: #999;
}
.nut-actionsheet-item {
border-bottom: 1px solid $light-color;
cursor: pointer;
}
.nut-actionsheet-item-active {
color: $primary-color;
}
.nut-actionsheet-item-disabled {
color: #e1e1e1;
cursor: not-allowed;
}
.nut-actionsheet-cancel {
margin-top: 5px;
border-top: 1px solid $light-color;
}
<template>
<view class="nut-actionsheet">
<nut-popup
v-model:show="state.maskIsVisible"
position="bottom"
round
@click-overlay="closeMask"
>
<view class="nut-actionsheet-panel">
<view class="nut-actionsheet-custom">
<slot name="custom"></slot>
</view>
<dl
class="nut-actionsheet-modal"
v-if="$slots.title || $slots.subTitle"
>
<dt class="nut-actionsheet-title"><slot name="title"></slot></dt>
<dd class="nut-actionsheet-sub-title"
><slot name="sub-title"></slot
></dd>
</dl>
<ul class="nut-actionsheet-menu">
<li class="nut-actionsheet-item desc" v-if="description">{{
description
}}</li>
<li
class="nut-actionsheet-item"
:class="{
'nut-actionsheet-item-disabled': item.disable
}"
:style="{ color: isHighlight(item) }"
v-for="(item, index) of menuItems"
:key="index"
@click="chooseItem(item, index)"
>{{ item[optionTag]
}}<view class="subdesc">{{ item.subname }}</view></li
>
</ul>
<div
class="nut-actionsheet-cancel"
v-if="cancelTxt"
@click="cancelActionSheet"
>{{ cancelTxt }}</div
>
</view>
</nut-popup>
</view>
</template>
<script>
import Popup from '@/packages/popup/index.vue';
import { createComponent } from '@/utils/create';
import { watch, reactive } from 'vue';
const { create } = createComponent('actionsheet');
export default create({
props: {
isVisible: {
type: Boolean,
default: false
},
isClickChooseClose: {
type: Boolean,
default: true
},
cancelTxt: {
type: String,
default: ''
},
optionTag: {
type: String,
default: 'name'
},
chooseTagValue: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
subname: {
type: String,
default: ''
},
menuItems: {
type: Array,
default: () => []
}
},
emits: ['click', 'close', 'cancel', 'choose'],
setup(props, { emit }) {
console.log(props.isVisible, 'props.isVisible');
// state
const state = reactive({
maskIsVisible: false
});
// methods
const isHighlight = item => {
return item.color;
};
const closeActionSheet = () => {
state.maskIsVisible = false;
// console.log(state.maskIsVisible, 'mask');
emit('close');
};
const cancelActionSheet = () => {
closeActionSheet();
emit('cancel');
};
const chooseItem = (item, index) => {
if (!item.disable) {
closeActionSheet();
emit('choose', item, index);
}
};
const closeMask = () => {
state.maskIsVisible = false;
};
watch(
() => props.isVisible,
() => {
console.log(props.isVisible, 'val');
state.maskIsVisible = true;
}
// val => {
// console.log(val, 'val');
// if (val) {
// state.maskIsVisible = true;
// }
// }
);
return {
isHighlight,
closeActionSheet,
cancelActionSheet,
chooseItem,
closeMask,
state
};
}
});
</script>
<style lang="scss">
@import 'index.scss';
</style>
......@@ -3,10 +3,26 @@
<h2>默认用法</h2>
<!-- <p>内置"small","normal","large"三种尺寸规格</p> -->
<nut-cell>
<nut-avatar size="80" bg-icon bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"></nut-avatar>
<nut-avatar size="large" bg-icon bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"></nut-avatar>
<nut-avatar size="normal" bg-icon bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"></nut-avatar>
<nut-avatar size="small" bg-icon bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"></nut-avatar>
<nut-avatar
size="80"
bg-icon
bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"
></nut-avatar>
<nut-avatar
size="large"
bg-icon
bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"
></nut-avatar>
<nut-avatar
size="normal"
bg-icon
bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"
></nut-avatar>
<nut-avatar
size="small"
bg-icon
bg-image="https://img12.360buyimg.com/imagetools/jfs/t1/143702/31/16654/116794/5fc6f541Edebf8a57/4138097748889987.png"
></nut-avatar>
</nut-cell>
<!-- <h2>修改形状类型</h2>
<nut-cell>
......@@ -20,7 +36,10 @@
</nut-cell>
<h2>修改背景图片</h2>
<nut-cell>
<nut-avatar bg-icon bg-image="https://img30.360buyimg.com/uba/jfs/t1/84318/29/2102/10483/5d0704c1Eb767fa74/fc456b03fdd6cbab.png"></nut-avatar>
<nut-avatar
bg-icon
bg-image="https://img30.360buyimg.com/uba/jfs/t1/84318/29/2102/10483/5d0704c1Eb767fa74/fc456b03fdd6cbab.png"
></nut-avatar>
</nut-cell>
<h2>可以修改头像的内容</h2>
<nut-cell>
......
<template>
<view :style="styles" :class="['nut-avatar', 'avatar-' + size, 'avatar-' + shape]" @click="activeAvatar">
<view
:style="styles"
:class="['nut-avatar', 'avatar-' + size, 'avatar-' + shape]"
@click="activeAvatar"
>
<i class="icon" :style="iconStyles"></i
><!--不使用icon组件,1:icon组件没有扩展维护;2:修改该图片不方便-->
<view class="text" v-if="isShowText"><slot></slot></view>
......@@ -40,8 +44,18 @@ export default create({
};
const styles = computed(() => {
return {
width: size.value == 'large' || size.value == 'middle' || size.value == 'small' ? '' : `${size.value}px`,
height: size.value == 'large' || size.value == 'middle' || size.value == 'small' ? '' : `${size.value}px`,
width:
size.value == 'large' ||
size.value == 'middle' ||
size.value == 'small'
? ''
: `${size.value}px`,
height:
size.value == 'large' ||
size.value == 'middle' ||
size.value == 'small'
? ''
: `${size.value}px`,
backgroundImage: bgImage.value ? `url(${bgImage.value})` : null,
backgroundColor: `${bgColor.value}`
};
......
<template>
<div class="demo" id="elId" ref="scroll">
<div class="text-data">我是测试数据1</div>
<div class="text-data">我是测试数据2</div>
<div class="text-data">我是测试数据3</div>
<div class="text-data">我是测试数据4</div>
<div class="text-data">我是测试数据5</div>
<div class="text-data">我是测试数据6</div>
<div class="text-data">我是测试数据7</div>
<div class="text-data">我是测试数据8</div>
<div class="text-data">我是测试数据9</div>
<div class="text-data">我是测试数据10</div>
<div class="text-data">我是测试数据11</div>
<div class="text-data">我是测试数据12</div>
<div class="text-data">我是测试数据13</div>
<div class="text-data">我是测试数据14</div>
<div class="text-data">我是测试数据15</div>
<div class="text-data">我是测试数据16</div>
<div class="text-data">我是测试数据17</div>
<div class="text-data">我是测试数据18</div>
<div class="text-data">我是测试数据19</div>
<div class="text-data">我是测试数据20</div>
<div class="text-data">我是测试数据21</div>
<div class="text-data">我是测试数据22</div>
<div class="text-data">我是测试数据23</div>
<div class="text-data">我是测试数据24</div>
<nut-backtop @click="handleClick" elId="elId" :distance="100" :bottom="90"
><view></view></nut-backtop
>
<nut-backtop @click="handleClick" elId="elId" :distance="200"></nut-backtop>
</div>
</template>
<script>
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('backtop');
export default createDemo({
setup(props, { emit }) {
const handleClick = () => {
console.log('触发回到顶部');
};
return {
handleClick
};
}
});
</script>
<style scoped></style>
# BackTop 回到顶部
### 介绍
提供较长的页面快捷回到顶部功能。
### 安装
```javascript
import { createApp } from 'vue';
import { BackTop } from '@nutui/nutui';
const app = createApp();
app.use(BackTop);
```
### 代码实例
### 基本用法
```html
<nut-backtop elId="elId" ></nut-backtop>
```
### 设置出现位置
```html
<nut-backtop :distance="200" ></nut-backtop>
```
### 自定义样式
```html
<nut-backtop @click="handleClick" elId="elId" :distance="100" :bottom="90" ><div></div></nut-backtop>
```
### 设置样式
<nut-backtop>
</nut-backtop>
### click事件
```html
<nut-backtop @click="handleClick" ></nut-backtop>
```
```html
<script>
export default createDemo({
setup(props, { emit }) {
const handleClick = () => {
console.log('触发回到顶部');
};
return {
handleClick
};
}
});
</script>
```
### API
### Prop
| 字段 | 说明 | 类型 | 默认值 |
|-----------------|------------------------------------------------------------------------------------------------|---------|---------|
| elId | 获取监听元素的父级元素 | String | - |
| bottom | 距离页面底部距离 | Number | - |
| right | 距离页面右侧距离 | Number | |
| distance | 页面垂直滚动多高后出现 | Number | - |
| zIndex | 设置组件页面层级 | Number | - |
### Event
| 名称 | 说明 | 回调参数 |
|-------|----------|-------------|
| click | 按钮点击时触发事件 | event:Event |
\ No newline at end of file
#app .demo {
padding: 0 0;
padding-top: 57px;
z-index: 1;
height: 100%;
// scroll-behavior: smooth;
.text-data {
margin: 0 auto;
margin-top: 15px;
margin-bottom: 20px;
padding-left: 16px;
display: flex;
align-items: center;
width: 342px;
height: 46px;
background: rgba(255, 255, 255, 1);
border-radius: 7px;
box-shadow: 0px 1px 7px 0px rgba(237, 238, 241, 1);
line-height: 19px;
font-family: PingFangSC-Medium;
font-size: 13px;
color: rgba(102, 102, 102, 1);
}
}
.nut-backtop {
display: none;
position: fixed;
bottom: 20px;
right: 20px;
z-index: 111;
&.show {
display: block;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(224, 224, 224, 1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
&.show :active {
background: rgba(0, 0, 0, 0.1);
}
&-main {
transition: all 0.2s ease-in-out;
}
}
<template>
<div
:class="['nut-backtop', { show: backTop }]"
:style="{
right: styleRight,
bottom: styleBottom,
'z-index': zIndex
}"
@click="click"
>
<slot>
<nut-icon size="19px" class="nut-backtop-main" name="top"></nut-icon>
</slot>
</div>
</template>
<script lang="ts">
import { computed, ref, onMounted, onUnmounted } from 'vue';
import { createComponent } from '@/utils/create';
const { create } = createComponent('backtop');
export default create({
props: {
//距离页面底部
bottom: {
type: Number,
default: 20
},
//距离页面右侧
right: {
type: Number,
default: 10
},
///获取容器ID
elId: {
type: String,
default: ''
},
//页面垂直滚动多高后出现
distance: {
type: Number,
default: 200
},
//设置层级
zIndex: {
type: Number,
default: 1111
}
},
emits: ['click'],
components: {},
setup(props, { emit }) {
const styleBottom = computed(() => `${props.bottom}px`);
const styleRight = computed(() => `${props.right}px`);
// const styleDistance = computed(() => `${props.distance}px`);
const zIndex = computed(() => `${props.zIndex}px`);
//默认图标不出现
const backTop = ref(false);
let scrollEl: Window | HTMLElement = window;
const elId = ref(props.elId);
function scrollListener() {
//滚动条偏移量
//Window
if (scrollEl instanceof Window) {
const scrollTop =
scrollEl.pageYOffset !== undefined ? scrollEl.pageYOffset : '';
backTop.value = scrollTop >= props.distance;
//DOM
} else if (scrollEl instanceof HTMLElement) {
const scrollTop = scrollEl.scrollTop;
backTop.value = scrollTop >= props.distance;
}
}
//点击按钮返回顶部
function scroll(y = 0) {
if (scrollEl instanceof Window) {
window.scrollTo(0, y);
} else if (scrollEl instanceof HTMLElement) {
scrollEl.scrollTop = y;
}
}
//监听页面滚动事件
function addEventListener() {
scrollEl.addEventListener('scroll', scrollListener, true);
}
//解绑监听页面滚动事件
function removeEventListener() {
scrollEl.removeEventListener('scroll', scrollListener, true);
}
// function deactivated() {
// keepAlive.value = true;
// removeEventListener();
// }
onUnmounted(() => {
removeEventListener();
});
//点击回到顶部
function click() {
scroll();
emit('click');
}
function init() {
//重新获取容器id
const _scrollEl = document.getElementById(elId.value);
if (elId.value && _scrollEl) {
scrollEl = _scrollEl;
_scrollEl.style.scrollBehavior = 'smooth';
}
addEventListener();
scrollListener();
}
onMounted(() => {
init();
});
return {
backTop,
scrollEl,
click,
styleBottom,
styleRight,
zIndex
};
}
});
</script>
<style lang="scss">
@import 'index.scss';
</style>
......@@ -30,12 +30,21 @@
<h2>加载状态</h2>
<h2>图标按钮</h2>
<div class="demo-button-row2">
<nut-button
shape="square"
plain
type="primary"
icon="star-fill"
></nut-button>
<nut-button shape="square" type="primary" icon="star">收藏</nut-button>
</div>
<h2>按钮尺寸</h2>
<div class="demo-button-row2">
<div class="demo-button-row2">
<nut-button size="large" type="primary">大号按钮</nut-button>
</div>
<nut-button size="large" type="primary" style="margin-bottom: 10px"
>大号按钮</nut-button
>
<nut-button type="primary">普通按钮</nut-button>
<nut-button size="small" type="primary">小型按钮</nut-button>
</div>
......@@ -68,9 +77,11 @@ export default createDemo({
<style lang="scss" scoped>
.demo-button-row {
margin-bottom: 20px;
margin-left: 15px;
}
.demo-button-row2 {
margin-bottom: 10px;
margin-left: 15px;
}
.nut-button {
margin-right: 15px;
......
......@@ -13,6 +13,9 @@
-webkit-appearance: none;
user-select: none;
touch-action: manipulation;
.text {
margin-left: 5px;
}
&::before {
position: absolute;
top: 50%;
......
<template>
<view :class="classes" :style="getStyle" @click="handleClick">
<!-- <i class="nut-icon-loading" v-if="loading"></i> -->
<!-- <i :class="icon" v-if="icon && !loading"></i> -->
<slot></slot>
<nut-icon class="nut-icon-loading" v-if="loading"></nut-icon>
<nut-icon :class="icon" v-if="icon && !loading" :name="icon"></nut-icon>
<view :class="{ text: icon || loading }" v-if="$slots.default"
><slot></slot
></view>
</view>
</template>
......@@ -11,7 +13,13 @@ import { PropType, CSSProperties, toRefs, computed } from 'vue';
import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('button');
export type ButtonType = 'default' | 'primary' | 'info' | 'success' | 'warning' | 'danger';
export type ButtonType =
| 'default'
| 'primary'
| 'info'
| 'success'
| 'warning'
| 'danger';
export type ButtonSize = 'large' | 'normal' | 'small';
export type ButtonShape = 'square' | 'round';
......@@ -45,6 +53,10 @@ export default create({
block: {
type: Boolean,
default: false
},
icon: {
type: String,
default: ''
}
},
components: {},
......@@ -52,7 +64,16 @@ export default create({
setup(props, { emit, slots }) {
// setup内部只能访问这4个属性,值得注意的是props必须在上面声明才能在这里取到
console.log('props', props, 'emit', emit, 'slots', slots);
const { type, size, shape, disabled, loading, color, plain, block } = toRefs(props);
const {
type,
size,
shape,
disabled,
loading,
color,
plain,
block
} = toRefs(props);
// console.log("type", type, "size", size);
const handleClick = (event: MouseEvent) => {
......@@ -90,6 +111,8 @@ export default create({
}
return style;
} else {
return {};
}
});
......
<template>
<div class="demo">
<h2>基础用法</h2>
<div>
<nut-cell
:showIcon="true"
title="选择单个日期"
:desc="date ? `${date} ${dateWeek}` : '请选择'"
@click="openSwitch('isVisible')"
>
</nut-cell>
<nut-calendar
:is-visible="isVisible"
:default-value="date"
@close="closeSwitch('isVisible')"
@choose="setChooseValue"
:start-date="`2019-10-11`"
:end-date="`2022-11-11`"
>
</nut-calendar>
</div>
<div>
<nut-cell
:showIcon="true"
title="选择日期区间"
:desc="date1 ? `${date1[0]}至${date1[1]}` : '请选择'"
@click="openSwitch('isVisible1')"
>
</nut-cell>
<nut-calendar
:is-visible="isVisible1"
:default-value="date1"
type="range"
:start-date="`2019-12-22`"
:end-date="`2021-01-08`"
@close="closeSwitch('isVisible1')"
@choose="setChooseValue1"
>
</nut-calendar>
</div>
<h2>自定义日历-自动回填</h2>
<div>
<nut-cell
:showIcon="true"
title="选择日期"
:desc="date3 ? date3 : '请选择'"
@click="openSwitch('isVisible3')"
>
</nut-cell>
<nut-calendar
:is-visible="isVisible3"
@close="closeSwitch('isVisible3')"
@choose="setChooseValue3"
:default-value="date3"
:start-date="null"
:end-date="null"
:is-auto-back-fill="true"
>
</nut-calendar>
</div>
<h2>平铺展示</h2>
<div class="test-calendar-wrapper">
<nut-calendar
:poppable="false"
:default-value="date2"
:is-auto-back-fill="true"
@choose="setChooseValue2"
>
</nut-calendar>
</div>
</div>
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('calendar');
import Utils from '@/utils/date';
interface TestCalendarState {
isVisible: boolean;
date: string;
dateWeek: string;
isVisible1: boolean;
date1: string[];
date2: string;
isVisible3: boolean;
date3: string;
}
export default createDemo({
props: {},
setup() {
const state: TestCalendarState = reactive({
isVisible: false,
date: '',
dateWeek: '',
isVisible1: false,
date1: ['2019-12-23', '2019-12-26'],
date2: '2020-07-08',
isVisible3: false,
date3: ''
});
const openSwitch = param => {
state[`${param}`] = true;
};
const closeSwitch = param => {
state[`${param}`] = false;
};
const setChooseValue = param => {
state.date = param[3];
state.dateWeek = param[4];
};
const setChooseValue1 = param => {
state.date1 = [...[param[0][3], param[1][3]]];
};
const setChooseValue2 = param => {
state.date2 = param[3];
console.log(state.date2);
};
const setChooseValue3 = param => {
state.date3 = param[3];
};
return {
...toRefs(state),
openSwitch,
closeSwitch,
setChooseValue,
setChooseValue1,
setChooseValue2,
setChooseValue3
};
}
});
</script>
<style lang="scss" scoped>
.test-calendar-wrapper {
display: flex;
width: 100%;
height: 613px;
overflow: hidden;
}
</style>
# calendar 组件
### 介绍
日历,可平铺/弹窗展示
### 安装
```javascript
import { createApp } from 'vue';
import { Calendar } from '@nutui/nutui';
const app = createApp();
app.use(Calendar);
```
## 代码演示
### 基础用法
```html
<nut-cell
:showIcon="true"
title="选择单个日期"
:desc="date ? `${date} ${dateWeek}` : '请选择'"
@click="openSwitch('isVisible')"
>
</nut-cell>
<nut-calendar
:is-visible="isVisible"
:default-value="date"
@close="closeSwitch('isVisible')"
@choose="setChooseValue"
:start-date="`2019-10-11`"
:end-date="`2022-11-11`"
>
</nut-calendar>
```
```javascript
setup() {
const state: TestCalendarState = reactive({
isVisible: false,
date: '',
dateWeek: ''
});
const openSwitch = param => {
state[`${param}`] = true;
};
const closeSwitch = param => {
state[`${param}`] = false;
};
const setChooseValue = param => {
state.date = param[3];
state.dateWeek = param[4];
};
return {
...toRefs(state),
openSwitch,
closeSwitch,
setChooseValue
};
}
```
### 区间选择
```html
<nut-cell
:showIcon="true"
title="选择日期区间"
:desc="date ? `${date[0]}至${date[1]}` : '请选择'"
@click="openSwitch('isVisible')"
>
</nut-cell>
<nut-calendar
:is-visible="isVisible"
:default-value="date"
type="range"
:start-date="`2019-12-22`"
:end-date="`2021-01-08`"
@close="closeSwitch('isVisible')"
@choose="setChooseValue"
>
</nut-calendar>
```
```javascript
setup() {
const state: TestCalendarState = reactive({
date: ['2019-12-23', '2019-12-26'],
isVisible2: true
});
const openSwitch = param => {
state[`${param}`] = true;
};
const closeSwitch = param => {
state[`${param}`] = false;
};
const setChooseValue= param => {
state.date = [...[param[0][3], param[1][3]]];
};
return {
...toRefs(state),
openSwitch,
closeSwitch,
setChooseValue,
};
}
```
### 自定义日历-自动回填
```html
<nut-cell
:showIcon="true"
title="选择日期"
:desc="date ? date : '请选择'"
@click="openSwitch('isVisible')"
>
</nut-cell>
<nut-calendar
:is-visible="isVisible"
@close="closeSwitch('isVisible')"
@choose="setChooseValue"
:start-date="null"
:end-date="null"
:is-auto-back-fill="true"
>
</nut-calendar>
```
```javascript
setup() {
const state: TestCalendarState = reactive({
date: '',
isVisible: false
});
const openSwitch = param => {
state[`${param}`] = true;
};
const closeSwitch = param => {
state[`${param}`] = false;
};
const setChooseValue = param => {
state.date= param[3];
};
return {
...toRefs(state),
setChooseValue
};
}
```
### 平铺展示
```html
<div class="test-calendar-wrapper">
<nut-calendar
:poppable="false"
:is-auto-back-fill="true"
:default-value="date"
@choose="setChooseValue"
>
</nut-calendar
></div>
```
```javascript
setup() {
const state: TestCalendarState = reactive({
date: '2020-07-08'
});
const setChooseValue = param => {
state.date = param[3];
};
return {
...toRefs(state),
setChooseValue
};
}
```
### 基础用法
```html
```
```javascript
```
## API
### Props
| 字段 | 说明 | 类型 | 默认值 |
| ----------------- | ------------------------------------------------- | -------------- | --------------- |
| type | 类型,日期选择'one',区间选择'range' | String | 'one' |
| is-visible | 是否可见 | Boolean | false |
| poppable | 是否弹窗状态展示 | Boolean | true |
| is-auto-back-fill | 自动回填 | Boolean | false |
| title | 显示标题 | String | ‘日期选择’ |
| default-value | 默认值,日期选择 String 格式,区间选择 Array 格式 | String / Array | null |
| start-date | 开始日期, 如果不限制开始日期传 null | String | 今天 |
| end-date | 结束日期,如果不限制结束日期传 null | String | 距离今天 365 天 |
### Events
| 事件名 | 说明 | 回调参数 |
| choose | 选择之后或是点击确认按钮触发 | 日期数组(包含年月日和星期)
| close | 关闭时触发 | -
.nut-calendar {
position: relative;
display: flex;
flex: 1;
height: 100%;
padding-top: 132px;
padding-bottom: 78px;
color: $calendar-base-color;
font-size: $calendar-base-font;
background-color: $white;
overflow: hidden;
&.nut-calendar-tile {
padding-top: 46px;
padding-bottom: 0;
.nut-calendar-header {
.calendar-title {
font-size: $calendar-base-font;
}
}
}
// 头部导航
.nut-calendar-header {
position: absolute;
top: -1px;
left: 0;
right: 0;
display: flex;
flex-direction: column;
text-align: center;
padding-top: 1px;
background-color: $white;
z-index: 1;
.calendar-title {
padding-top: 22px;
font-size: $calendar-title-font;
line-height: 25px;
border-radius: 12px 12px 0 0;
}
.calendar-curr-month {
padding: 10px 0 7px;
line-height: 22px;
}
.calendar-weeks {
display: flex;
align-items: center;
justify-content: space-around;
height: 46px;
border-radius: 0px 0px 12px 12px;
box-shadow: 0px 4px 10px 0px rgba($color: #000000, $alpha: 0.06);
.calendar-week-item {
&:first-of-type,
&:last-of-type {
color: $calendar-primary-color;
}
}
}
}
// 月份
.nut-calendar-content {
flex: 1;
.calendar-months-panel {
position: relative;
width: 100%;
height: auto;
display: block;
.calendar-month {
display: flex;
flex-direction: column;
text-align: center;
}
div:nth-of-type(2) {
.calendar-month-title {
padding-top: 0;
}
}
.calendar-loading-tip {
height: 50px;
line-height: 50px;
text-align: center;
position: absolute;
top: -50px;
left: 0;
right: 0;
font-size: $calendar-text-font;
color: $text-color;
}
.calendar-month-title {
height: 23px;
line-height: 23px;
margin: 8px 0;
}
.calendar-month-con {
overflow: hidden;
.calendar-month-item {
.calendar-month-day:nth-child(7n + 0),
.calendar-month-day:nth-child(7n + 1) {
color: $calendar-primary-color;
}
}
.calendar-month-day {
float: left;
width: 14.28%;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
.curr-tips,
.calendar-day-tip {
position: absolute;
top: 10px;
width: 100%;
font-size: 11px;
line-height: 12px;
color: $calendar-primary-color;
}
&-active {
background-color: $calendar-primary-color;
color: $white !important;
.curr-tips {
visibility: hidden;
}
.calendar-day-tip {
color: $white;
}
}
&-disabled {
color: $calendar-disable-color !important;
}
&-choose {
background-color: $calendar-choose-color;
}
.calendar-day {
padding: 4px 0;
font-size: $calendar-day-font;
}
}
}
}
}
// 底部导航
.nut-calendar-footer {
position: absolute;
left: 0;
right: 0;
bottom: -1px;
display: flex;
height: 78px;
width: 100%;
background-color: $white;
.calendar-confirm-btn {
height: 44px;
width: 100%;
margin: 14px 18px;
border-radius: 22px;
background: $button-primary-background-color;
color: $white;
text-align: center;
line-height: 44px;
}
}
}
<template>
<nut-popup
v-if="poppable"
v-model:show="state.childIsVisible"
position="bottom"
round
:closeable="true"
@click-overlay="closePopup"
@click-close-icon="closePopup"
>
<nut-calendar-item
props
ref="calendarRef"
:type="type"
:is-auto-back-fill="isAutoBackFill"
:poppable="poppable"
:title="title"
:default-value="defaultValue"
:start-date="startDate"
:end-date="endDate"
@update="update"
@close="close"
@choose="choose"
>
</nut-calendar-item>
</nut-popup>
<nut-calendar-item
v-else
:type="type"
:is-auto-back-fill="isAutoBackFill"
:poppable="poppable"
:title="title"
:default-value="defaultValue"
:start-date="startDate"
:end-date="endDate"
@close="close"
@choose="choose"
>
</nut-calendar-item>
</template>
<script lang="ts">
import { PropType, reactive, ref, watch, toRefs } from 'vue';
import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('calendar');
import Popup from '@/packages/popup/index.vue';
import CalendarItem from '@/packages/calendar-item/index.vue';
import Utils from '@/utils/date';
type InputDate = string | string[];
export default create({
props: {
type: {
type: String,
default: 'one'
},
isAutoBackFill: {
type: Boolean,
default: false
},
poppable: {
type: Boolean,
default: true
},
isVisible: {
type: Boolean
},
title: {
type: String,
default: '日历选择'
},
defaultValue: {
type: String as PropType<InputDate>
},
startDate: {
type: String,
default: Utils.getDay(0)
},
endDate: {
type: String,
default: Utils.getDay(365)
}
},
components: {},
emits: ['choose', 'close'],
setup(props, { emit }) {
// element refs
const calendarRef = ref<null | HTMLElement>(null);
// state
const state = reactive({
childIsVisible: false
});
// methods
const update = () => {
state.childIsVisible = false;
};
const close = () => {
emit('close');
};
const choose = param => {
close();
emit('choose', param);
};
const closePopup = () => {
close();
};
watch(
() => props.isVisible,
val => {
if (val) {
state.childIsVisible = true;
}
}
);
return {
closePopup,
update,
close,
choose,
calendarRef,
state,
...toRefs(props)
};
}
});
</script>
<style lang="scss">
.popup-box {
height: 518px;
}
</style>
.nut-calendar {
position: relative;
display: flex;
flex: 1;
height: 518px;
padding-top: 132px;
padding-bottom: 78px;
color: $calendar-base-color;
font-size: $calendar-base-font;
background-color: $white;
overflow: hidden;
&.nut-calendar-tile {
height: 100%;
padding-top: 46px;
padding-bottom: 0;
.nut-calendar-header {
.calendar-title {
font-size: $calendar-base-font;
}
}
}
&.nut-calendar-nofooter {
padding-bottom: 0;
}
// 头部导航
.nut-calendar-header {
position: absolute;
top: -1px;
left: 0;
right: 0;
display: flex;
flex-direction: column;
text-align: center;
padding-top: 1px;
background-color: $white;
z-index: 1;
.calendar-title {
padding-top: 22px;
font-size: $calendar-title-font;
line-height: 25px;
border-radius: 12px 12px 0 0;
}
.calendar-curr-month {
padding: 10px 0 7px;
line-height: 22px;
}
.calendar-weeks {
display: flex;
align-items: center;
justify-content: space-around;
height: 46px;
box-shadow: 0px 4px 10px 0px rgba($color: #000000, $alpha: 0.06);
.calendar-week-item {
&:first-of-type,
&:last-of-type {
color: $calendar-primary-color;
}
}
}
}
// 月份
.nut-calendar-content {
flex: 1;
.calendar-months-panel {
position: relative;
width: 100%;
height: auto;
display: block;
.calendar-month {
display: flex;
flex-direction: column;
text-align: center;
}
div:nth-of-type(2) {
.calendar-month-title {
padding-top: 0;
}
}
.calendar-loading-tip {
height: 50px;
line-height: 50px;
text-align: center;
position: absolute;
top: -50px;
left: 0;
right: 0;
font-size: $calendar-text-font;
color: $text-color;
}
.calendar-month-title {
height: 23px;
line-height: 23px;
margin: 8px 0;
}
.calendar-month-con {
overflow: hidden;
.calendar-month-item {
.calendar-month-day:nth-child(7n + 0),
.calendar-month-day:nth-child(7n + 1) {
color: $calendar-primary-color;
}
}
.calendar-month-day {
float: left;
width: 14.28%;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
.calendar-curr-tips,
.calendar-day-tip {
position: absolute;
top: 10px;
width: 100%;
font-size: 11px;
line-height: 12px;
color: $calendar-primary-color;
}
&-active {
background-color: $calendar-primary-color;
color: $white !important;
.calendar-curr-tips {
visibility: hidden;
}
.calendar-day-tip {
color: $white;
}
}
&-disabled {
color: $calendar-disable-color !important;
}
&-choose {
background-color: $calendar-choose-color;
}
.calendar-day {
padding: 4px 0;
font-size: $calendar-day-font;
}
}
}
}
}
// 底部导航
.nut-calendar-footer {
position: absolute;
left: 0;
right: 0;
bottom: -1px;
display: flex;
height: 78px;
width: 100%;
background-color: $white;
.calendar-confirm-btn {
height: 44px;
width: 100%;
margin: 14px 18px;
border-radius: 22px;
background: $button-primary-background-color;
color: $white;
text-align: center;
line-height: 44px;
}
}
}
<template>
<view
class="nut-calendar"
:class="{
'nut-calendar-tile': !poppable,
'nut-calendar-nofooter': isAutoBackFill
}"
>
<!-- header -->
<view
class="nut-calendar-header"
:class="{ 'nut-calendar-header-tile': !poppable }"
>
<template v-if="poppable">
<view class="calendar-title">{{ title }}</view>
<view class="calendar-curr-month">{{ yearMonthTitle }}</view>
</template>
<view class="calendar-weeks" ref="weeksPanel">
<view
class="calendar-week-item"
v-for="(item, index) of weeks"
:key="index"
>{{ item }}</view
>
</view>
</view>
<!-- content-->
<view
class="nut-calendar-content"
ref="months"
@touchstart.stop="touchStart"
@touchmove.stop.prevent="touchMove"
@touchend.stop="touchEnd"
>
<view class="calendar-months-panel" ref="monthsPanel">
<view class="calendar-loading-tip">{{
!unLoadPrev ? '加载上一个月' : '没有更早月份'
}}</view>
<view
class="calendar-month"
v-for="(month, index) of monthsData"
:key="index"
>
<view class="calendar-month-title">{{ month.title }}</view>
<view class="calendar-month-con">
<view
class="calendar-month-item"
:class="type === 'range' ? 'month-item-range' : ''"
>
<template v-for="(day, i) of month.monthData" :key="i">
<view
class="calendar-month-day"
:class="getClass(day, month)"
@click="chooseDay(day, month)"
>
<view class="calendar-day">{{
day.type == 'curr' ? day.day : ''
}}</view>
<view
class="calendar-curr-tips"
v-if="isCurrDay(month, day.day)"
>今天</view
>
<view
class="calendar-day-tip"
v-if="isStartTip(day, month)"
>{{ '开始' }}</view
>
<view
class="calendar-day-tip"
v-else-if="isEndTip(day, month)"
>{{ '结束' }}</view
>
</view>
</template>
</view>
</view>
</view>
</view>
</view>
<!-- footer-->
<view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
<view class="calendar-confirm-btn" @click="confirm">确定</view>
</view>
</view>
</template>
<script lang="ts">
import { PropType, reactive, ref, watch, toRefs } from 'vue';
import { createComponent } from '../../utils/create';
const { create } = createComponent('calendar-item');
import Utils from '../../utils/date';
import requestAniFrame from '../../utils/raf';
type InputDate = string | string[];
interface CalendarState {
yearMonthTitle: string;
currDate: InputDate;
unLoadPrev: boolean;
touchParams: any;
transformY: number;
translateY: number;
scrollDistance: number;
defaultData: InputDate;
chooseData: any;
monthsData: any[];
dayPrefix: string;
startData: InputDate;
endData: InputDate;
isRange: boolean;
timer: number;
}
export default create({
props: {
type: {
type: String,
default: 'one'
},
isAutoBackFill: {
type: Boolean,
default: false
},
poppable: {
type: Boolean,
default: true
},
title: {
type: String,
default: '日历选择'
},
defaultValue: {
type: String as PropType<InputDate>,
default: null
},
startDate: {
type: String,
default: Utils.getDay(0)
},
endDate: {
type: String,
default: Utils.getDay(365)
}
},
components: {},
emits: ['choose', 'update', 'close'],
setup(props, { emit }) {
const weeks = ref(['', '', '', '', '', '', '']);
// element refs
const months = ref<null | HTMLElement>(null);
const monthsPanel = ref<null | HTMLElement>(null);
const weeksPanel = ref<null | HTMLElement>(null);
// state
const state: CalendarState = reactive({
yearMonthTitle: '',
currDate: '',
unLoadPrev: false,
touchParams: {
startY: 0,
endY: 0,
startTime: 0,
endTime: 0,
lastY: 0,
lastTime: 0
},
transformY: 0,
translateY: 0,
scrollDistance: 0,
defaultData: [],
chooseData: [],
monthsData: [],
dayPrefix: 'calendar-month-day',
startData: '',
endData: '',
isRange: props.type === 'range',
timer: 0
});
// 日期转化成数组
const splitDate = date => {
return date.split('-');
};
const isStart = currDate => {
return Utils.isEqual(state.currDate[0], currDate);
};
const isEnd = currDate => {
return Utils.isEqual(state.currDate[1], currDate);
};
// 获取当前数据
const getCurrDate = (day, month, isRange?) => {
return isRange
? month.curData[3] +
'-' +
month.curData[4] +
'-' +
Utils.getNumTwoBit(day.day)
: month.curData[0] +
'-' +
month.curData[1] +
'-' +
Utils.getNumTwoBit(day.day);
};
// 获取样式
const getClass = (day, month, isRange?) => {
const currDate = getCurrDate(day, month, isRange);
if (day.type == 'curr') {
if (
(!state.isRange &&
Utils.isEqual(state.currDate as string, currDate)) ||
(state.isRange && (isStart(currDate) || isEnd(currDate)))
) {
return `${state.dayPrefix}-active`;
} else if (
(props.startDate && Utils.compareDate(currDate, props.startDate)) ||
(props.endDate && Utils.compareDate(props.endDate, currDate))
) {
return `${state.dayPrefix}-disabled`;
} else if (
state.isRange &&
Array.isArray(state.currDate) &&
Object.values(state.currDate).length == 2 &&
Utils.compareDate(state.currDate[0], currDate) &&
Utils.compareDate(currDate, state.currDate[1])
) {
return `${state.dayPrefix}-choose`;
} else {
return null;
}
} else {
return `${state.dayPrefix}-disabled`;
}
};
const confirm = () => {
if ((state.isRange && state.chooseData.length == 2) || !state.isRange) {
emit('choose', state.chooseData);
if (props.poppable) {
emit('update');
}
}
};
// 选中数据
const chooseDay = (day, month, isFirst, isRange?) => {
if (getClass(day, month, isRange) != `${state.dayPrefix}-disabled`) {
let days = [...month.curData];
days = isRange ? days.splice(3) : days.splice(0, 3);
days[2] =
typeof day.day == 'number' ? Utils.getNumTwoBit(day.day) : day.day;
days[3] = `${days[0]}-${days[1]}-${days[2]}`;
days[4] = Utils.getWhatDay(days[0], days[1], days[2]);
if (!state.isRange) {
state.currDate = days[3];
state.chooseData = [...days];
} else {
if (Object.values(state.currDate).length == 2) {
state.currDate = [days[3]];
} else {
if (Utils.compareDate(state.currDate[0], days[3])) {
Array.isArray(state.currDate) && state.currDate.push(days[3]);
} else {
Array.isArray(state.currDate) && state.currDate.unshift(days[3]);
}
}
if (state.chooseData.length == 2 || !state.chooseData.length) {
state.chooseData = [...days];
} else {
if (Utils.compareDate(state.chooseData[3], days[3])) {
state.chooseData = [[...state.chooseData], [...days]];
} else {
state.chooseData = [[...days], [...state.chooseData]];
}
}
}
if (props.isAutoBackFill && !isFirst) {
confirm();
}
}
};
// 获取当前月数据
const getCurrData = type => {
const monthData =
type == 'prev'
? state.monthsData[0]
: state.monthsData[state.monthsData.length - 1];
let year = parseInt(monthData.curData[0]);
let month = parseInt(monthData.curData[1].toString().replace(/^0/, ''));
switch (type) {
case 'prev':
month == 1 && (year -= 1);
month = month == 1 ? 12 : --month;
break;
case 'next':
month == 12 && (year += 1);
month = month == 12 ? 1 : ++month;
break;
}
return [year, Utils.getNumTwoBit(month), monthData.curData[2]];
};
// 获取日期状态
const getDaysStatus = (days, type) => {
// 修复:当某个月的1号是周日时,月份下方会空出来一行
if (type == 'prev' && days >= 7) {
days -= 7;
}
return Array.from(Array(days), (v, k) => {
return {
day: k + 1,
type: type
};
});
};
// 获取月数据
const getMonth = (curData, type) => {
const preMonthDays = Utils.getMonthPreDay(curData[0], curData[1]);
const currMonthDays = Utils.getMonthDays(curData[0], curData[1]);
const title = {
year: curData[0],
month: curData[1]
};
const monthInfo = {
curData: curData,
title: `${title.year}${title.month}月`,
monthData: [
...getDaysStatus(preMonthDays, 'prev'),
...getDaysStatus(currMonthDays, 'curr')
]
};
if (type == 'next') {
if (
!state.endData ||
!Utils.compareDate(
`${state.endData[0]}-${state.endData[1]}-${Utils.getMonthDays(
state.endData[0],
state.endData[1]
)}`,
`${curData[0]}-${curData[1]}-${curData[2]}`
)
) {
state.monthsData.push(monthInfo);
}
} else {
if (
!state.startData ||
!Utils.compareDate(
`${curData[0]}-${curData[1]}-${curData[2]}`,
`${state.startData[0]}-${state.startData[1]}-01`
)
) {
state.monthsData.unshift(monthInfo);
} else {
state.unLoadPrev = true;
}
}
};
// 初始化数据
const initData = () => {
// 初始化开始结束数据
state.startData = props.startDate ? splitDate(props.startDate) : null;
state.endData = props.endDate ? splitDate(props.endDate) : null;
// 初始化当前日期
if (!props.defaultValue) {
state.currDate = state.isRange
? [Utils.date2Str(new Date()), Utils.getDay(1)]
: Utils.date2Str(new Date());
} else {
state.currDate = state.isRange
? [...props.defaultValue]
: props.defaultValue;
}
// 日期转化为数组
if (state.isRange && Array.isArray(state.currDate)) {
if (
props.startDate &&
Utils.compareDate(state.currDate[0], props.startDate)
) {
state.currDate.splice(0, 1, props.startDate);
}
if (
props.endDate &&
Utils.compareDate(props.endDate, state.currDate[1])
) {
state.currDate.splice(1, 1, props.endDate);
}
state.defaultData = [
...splitDate(state.currDate[0]),
...splitDate(state.currDate[1])
];
} else {
if (
props.startDate &&
Utils.compareDate(state.currDate as string, props.startDate)
) {
state.currDate = props.startDate;
} else if (
props.endDate &&
!Utils.compareDate(state.currDate as string, props.endDate)
) {
state.currDate = props.endDate;
}
state.defaultData = [...splitDate(state.currDate)];
}
getMonth(state.defaultData, 'next');
state.yearMonthTitle = state.monthsData[0].title;
let i = 1;
do {
getMonth(getCurrData('next'), 'next');
} while (i++ < 4);
if (state.isRange) {
chooseDay(
{ day: state.defaultData[2], type: 'curr' },
state.monthsData[0],
true
);
chooseDay(
{ day: state.defaultData[5], type: 'curr' },
state.monthsData[0],
true,
true
);
} else {
chooseDay(
{ day: state.defaultData[2], type: 'curr' },
state.monthsData[0],
true
);
}
};
// 区间选择&&当前月&&选中态
const isActive = (day, month) => {
return (
state.isRange &&
day.type == 'curr' &&
getClass(day, month) == 'calendar-month-day-active'
);
};
// 是否有开始提示
const isStartTip = (day, month) => {
if (isActive(day, month)) {
return isStart(getCurrDate(day, month));
} else {
return false;
}
};
// 是否有结束提示
const isEndTip = (day, month) => {
return isActive(day, month);
};
// 是否有是当前日期
const isCurrDay = (month, day) => {
const date = `${month.curData[0]}-${month.curData[1]}-${day}`;
return Utils.isEqual(date, Utils.date2Str(new Date()));
};
// 监听月份滚动,改变月份标题
const loadScroll = () => {
if (!props.poppable) {
return false;
}
requestAniFrame(() => {
if (weeksPanel?.value && monthsPanel?.value) {
const top = weeksPanel?.value.getBoundingClientRect().bottom;
const monthsDoms = monthsPanel.value.querySelectorAll(
'.calendar-month'
);
for (let i = 0; i < monthsDoms.length; i++) {
if (
monthsDoms[i].getBoundingClientRect().top <= top &&
monthsDoms[i].getBoundingClientRect().bottom >= top
) {
state.yearMonthTitle = state.monthsData[i].title;
} else if (state.scrollDistance === 0) {
state.yearMonthTitle = state.monthsData[0].title;
}
}
}
});
};
// 设置月份滚动距离和速度
const setTransform = (translateY = 0, type?, time = 1000) => {
if (monthsPanel?.value) {
if (type === 'end') {
monthsPanel.value.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
clearTimeout(state.timer);
state.timer = setTimeout(() => {
loadScroll();
}, time);
} else {
monthsPanel.value.style.webkitTransition = '';
loadScroll();
}
monthsPanel.value.style.webkitTransform = `translateY(${translateY}px)`;
state.scrollDistance = translateY;
}
};
// 计算滚动方向和距离
const setMove = (move, type?, time?) => {
let updateMove = move + state.transformY;
const h = months.value?.offsetHeight || 0;
const offsetHeight = monthsPanel.value?.offsetHeight || 0;
if (type === 'end') {
// 限定滚动距离
if (updateMove > 0) {
updateMove = 0;
}
if (updateMove < 0 && updateMove < -offsetHeight + h) {
updateMove = -offsetHeight + h;
}
if (offsetHeight <= h && state.monthsData.length == 1) {
updateMove = 0;
}
setTransform(updateMove, type, time);
} else {
if (updateMove > 0 && updateMove > 100) {
updateMove = 100;
}
if (
updateMove < -offsetHeight + h - 100 &&
state.monthsData.length > 1
) {
updateMove = -offsetHeight + h - 100;
}
if (
updateMove < 0 &&
updateMove < -100 &&
state.monthsData.length == 1
) {
updateMove = -100;
}
setTransform(updateMove);
}
};
// 监听touch开始
const touchStart = event => {
const changedTouches = event.changedTouches[0];
state.touchParams.startY = changedTouches.pageY;
state.touchParams.startTime = event.timestamp || Date.now();
state.transformY = state.scrollDistance;
};
// 监听touchmove
const touchMove = event => {
//event.preventDefault();
const changedTouches = event.changedTouches[0];
state.touchParams.lastY = changedTouches.pageY;
state.touchParams.lastTime = event.timestamp || Date.now();
const move = state.touchParams.lastY - state.touchParams.startY;
if (Math.abs(move) < 5) {
return false;
}
setMove(move);
};
// 监听touchend
const touchEnd = event => {
const changedTouches = event.changedTouches[0];
state.touchParams.lastY = changedTouches.pageY;
state.touchParams.lastTime = event.timestamp || Date.now();
let move = state.touchParams.lastY - state.touchParams.startY;
if (Math.abs(move) < 5) {
return false;
}
const updateMove = move + state.transformY;
const h = months.value?.offsetHeight || 0;
const offsetHeight = monthsPanel.value?.offsetHeight || 0;
if (updateMove > 0) {
getMonth(getCurrData('prev'), 'prev');
} else if (updateMove < -offsetHeight + h * 2) {
getMonth(getCurrData('next'), 'next');
if (Math.abs(move) >= 300) {
getMonth(getCurrData('next'), 'next');
}
}
let moveTime = state.touchParams.lastTime - state.touchParams.startTime;
if (moveTime <= 300) {
move = move * 2;
moveTime = moveTime + 1000;
setMove(move, 'end', moveTime);
} else {
setMove(move, 'end');
}
};
// 重新渲染
const resetRender = () => {
state.chooseData.splice(0);
state.monthsData.splice(0);
state.scrollDistance = 0;
state.translateY = 0;
setTransform(state.scrollDistance);
initData();
};
// 初始化数据
initData();
//监听 默认值更改
watch(
() => props.defaultValue,
val => {
if (val) {
resetRender();
}
}
);
return {
weeks,
touchStart,
touchMove,
touchEnd,
getClass,
isStartTip,
isEndTip,
chooseDay,
isCurrDay,
confirm,
monthsPanel,
months,
weeksPanel,
...toRefs(state),
...toRefs(props)
};
}
});
</script>
<style lang="scss">
@import 'index.scss';
</style>
<template>
<view :class="classes" @click="handleClick">
<slot>
<view class="nut-cell__title" :class="{ icon: icon }" v-if="title || subTitle || icon">
<view
class="nut-cell__title"
:class="{ icon: icon }"
v-if="title || subTitle || icon"
>
<nut-icon v-if="icon" class="icon" :name="icon"></nut-icon>
<template v-if="subTitle">
<view class="title">{{ title }}</view>
......@@ -11,8 +15,18 @@
{{ title }}
</template>
</view>
<view v-if="desc" class="nut-cell__value" :style="{ 'text-align': descTextAlign }">{{ desc }}</view>
<nut-icon v-if="isLink || to" size="14px" color="#979797" name="right"></nut-icon>
<view
v-if="desc"
class="nut-cell__value"
:style="{ 'text-align': descTextAlign }"
>{{ desc }}</view
>
<nut-icon
v-if="isLink || to"
size="14px"
color="#979797"
name="right"
></nut-icon>
</slot>
</view>
</template>
......@@ -54,7 +68,9 @@ export default create({
if (props.to && router) {
router[props.replace ? 'replace' : 'push'](props.to);
} else if (props.url) {
props.replace ? location.replace(props.url) : (location.href = props.url);
props.replace
? location.replace(props.url)
: (location.href = props.url);
}
};
......
<template>
<div class="demo">
<h2>checkbox</h2>
<nut-cell>
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail" txt="test txt"></nut-temp>
</nut-cell>
<div class="demo-list">
<h4>基础用法</h4>
<div class="show-demo">
<nut-checkbox v-model="checkbox1" @change="changeBox1"
>复选框</nut-checkbox
>
<span>{{ checkbox1 }}</span>
</div>
<p class="p-word"
>组合使用 checkbox 时推荐使用 checkboxgroup 组件,见下方示例</p
>
<h4>CheckboxGroup基本用法</h4>
<div class="show-demo">
<nut-checkboxgroup v-model="checkboxGroup1">
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
<p>选择状态:{{ checkboxGroup1 }}</p>
</div>
<h4>禁用状态</h4>
<div class="show-demo show-demo-block">
<nut-checkbox v-model="checkbox2" disabled>未选时禁用状态</nut-checkbox>
<nut-checkbox v-model="checkbox3" disabled>已选时禁用状态</nut-checkbox>
</div>
<h4>CheckboxGroup整体禁用</h4>
<div class="show-demo">
<nut-checkboxgroup v-model="checkboxGroup2" disabled>
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
<p>选择状态:{{ checkboxGroup2 }}</p>
</div>
<h4>自定义尺寸</h4>
<div class="show-demo show-demo-block">
<nut-checkbox v-model="checkbox4" size="small">小号1</nut-checkbox>
<nut-checkbox v-model="checkbox5" size="base">默认</nut-checkbox>
<nut-checkbox v-model="checkbox6" size="large">大号</nut-checkbox>
<p>size可选值:'small', 'base', 'large'</p>
</div>
<h4>CheckboxGroup整体尺寸</h4>
<div class="show-demo">
<nut-checkboxgroup v-model="checkboxGroup3" size="small">
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
</div>
<h4>禁用动效</h4>
<div class="show-demo">
<nut-checkbox v-model="checkbox7" :animation="false"
>没有动效</nut-checkbox
>
<p>animation属性值为false时,禁用自带动效</p>
</div>
<h4>CheckboxGroup整体禁用动效</h4>
<div class="show-demo">
<nut-checkboxgroup v-model="checkboxGroup4" :animation="false">
<nut-checkbox label="没有动效1"></nut-checkbox>
<nut-checkbox label="没有动效2"></nut-checkbox>
</nut-checkboxgroup>
</div>
<h4>事件</h4>
<div class="show-demo">
<div class="demo-box">
<nut-checkbox
v-model="checkbox9"
:label="'change事件'"
@change="checkboxChange"
>备选项</nut-checkbox
>
<span>{{ result1 }}</span>
</div>
</div>
<h4>CheckboxGroup整体事件</h4>
<div class="show-demo">
<div>
<nut-checkboxgroup v-model="checkboxGroup5" @change="getChange">
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
</div>
<span>{{ result2 }}</span>
</div>
<h4>自定义Class</h4>
<div class="show-demo">
<nut-checkbox class="my-checkbox" v-model="checkbox12"
>自定义Class:"my-checkbox"</nut-checkbox
>
</div>
<h4>全选与反选</h4>
<div class="show-demo">
<div>
<nut-checkboxgroup
ref="checkboxGroupDemo"
v-model="checkboxGroup6"
@change="getChange2"
>
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
<nut-checkbox label="选项三"></nut-checkbox>
</nut-checkboxgroup>
</div>
<div>
<span>{{ result3 }}</span>
</div>
<nut-button size="small" type="primary" @click="chooseAll(true)"
>全选</nut-button
>
<nut-button size="small" type="primary" @click="chooseAll()"
>反选</nut-button
>
<nut-button size="small" type="primary" @click="chooseAll(false)"
>取消</nut-button
>
</div>
<h4>CheckboxGroup排列方向</h4>
<div class="show-demo">
<div>
<nut-checkboxgroup
v-model="checkboxGroup7"
direction="vertical"
@change="getChange3"
>
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
<nut-checkbox label="选项三"></nut-checkbox>
</nut-checkboxgroup>
</div>
<span>{{ result2 }}</span>
</div>
</div>
</template>
<script lang="ts">
import { reactive, ref, toRefs } from 'vue';
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('temp');
const { createDemo } = createComponent('checkbox');
export default createDemo({
props: {},
setup() {
return {};
setup(props, context) {
const data = reactive({
checkbox1: false,
checkbox2: false,
checkbox3: true,
checkbox4: true,
checkbox5: true,
checkbox6: true,
checkbox7: false,
checkbox8: true,
checkbox9: false,
checkbox10: true,
checkbox11: false,
checkbox12: true,
checkbox13: false,
checkboxGroup1: ['选项一'],
checkboxGroup2: ['选项一'],
checkboxGroup3: [],
checkboxGroup4: ['没有动效1'],
checkboxGroup5: ['选项一'],
checkboxGroup6: [],
checkboxGroup7: []
});
const result = reactive({
result1: '',
result2: '',
result3: '',
result4: ''
});
const changeBox1 = (state: boolean) => {
data.checkbox1 = state;
};
const checkboxChange = (state: string, val: string) => {
result.result1 = state;
};
const getChange = (val: string) => {
result.result2 = '选中状态选项:' + val;
};
const getChange2 = (val: string) => {
result.result3 = '选中状态选项:' + val;
};
const getChange3 = (val: string) => {
result.result4 = '选中状态选项:' + val;
};
const checkboxGroupDemo = ref(null);
const chooseAll = (val: boolean | string) => {
(checkboxGroupDemo.value as any).toggleAll(val);
};
return {
changeBox1,
checkboxChange,
checkboxGroupDemo,
getChange,
getChange2,
getChange3,
chooseAll,
...toRefs(data),
...toRefs(result)
};
}
});
</script>
<style lang="scss" scoped></style>
<style>
#app {
background: #f7f8fa;
}
</style>
<style lang="scss" scoped>
.demo-list {
margin: 60px 0;
padding: 17px;
h4 {
margin-top: 10px;
line-height: 20px;
color: #909ca4;
font-size: 14px;
}
.show-demo {
margin-top: 10px;
padding: 15px;
background-color: #ffffff;
border-radius: 7px;
box-shadow: 0px 1px 7px 0px rgba(237, 238, 241, 1);
p,
span {
margin-top: 10px;
font-size: 14px;
color: #636363;
}
span {
font-size: 12px;
}
.nut-button {
margin: 10px 10px 0 0;
}
}
.p-word {
margin: 15px 0;
font-size: 14px;
color: #636363;
padding-left: 5px;
border-left: 8px solid #03a9f4;
}
.show-demo-block {
view {
display: block;
margin-bottom: 10px;
}
}
.my-checkbox::v-deep .nut-checkbox {
input:checked {
background-image: url('https://img13.360buyimg.com/imagetools/jfs/t1/154120/1/10623/372/5fe013e6E4694fbf9/fd38d389b3a3b9c6.png');
background-size: 100%;
border: none;
}
}
}
</style>
# Checkbox 组件
# Checkbox 复选按钮
### 介绍
基于 xxxxxxx
多选按钮用于选择。
### 安装
``` javascript
import { createApp } from 'vue';
import { Temp } from '@nutui/nutui';
import { Checkbox } from '@nutui/nutui';
const app = createApp();
app.use(Temp);
app.use(Checkbox);
```
## 基本用法
```html
<nut-checkbox v-model="checkbox">选项</nut-checkbox>
```
```javascript
setup() {
return {
checkbox: false,
};
}
```
## CheckboxGroup基本用法
```html
<nut-checkboxgroup v-model="checkboxGroup">
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
```
```javascript
setup() {
return {
checkboxGroup: ['选项一'],
};
}
```
## 禁用状态
```html
<nut-checkbox v-model="checkbox1" disabled>未选时禁用状态</nut-checkbox>
<nut-checkbox v-model="checkbox2" disabled>已选时禁用状态</nut-checkbox>
```
```javascript
setup() {
return {
checkbox1: false,
checkbox2: true,
};
}
```
## CheckboxGroup整体禁用
```html
<nut-checkboxgroup v-model="checkboxGroup" disabled>
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
```
```javascript
setup() {
return {
checkboxGroup: ['选项一'],
};
}
```
## 自定义尺寸
内置 **small****base****large** 三种规格供使用。
```html
<nut-checkbox v-model="checkbox1" size="small">小号1</nut-checkbox>
<nut-checkbox v-model="checkbox2" size="base">默认</nut-checkbox>
<nut-checkbox v-model="checkbox3" size="large">大号</nut-checkbox>
```
## 代码演示
```javascript
setup() {
return {
checkbox1: true,
checkbox2: true,
checkbox3: true,
};
}
### 基础用法1
```
`Icon``name` 属性支持传入图标名称或图片链接。
## CheckboxGroup整体尺寸
```html
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail"></nut-temp>
<nut-checkboxgroup v-model="checkboxGroup" size="small">
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
```
### 基础用法2
```javascript
setup() {
return {
checkboxGroup: [],
};
}
```
`Icon``name` 属性支持传入图标名称或图片链接。
## 禁用动效
animation属性值为false时,禁用自带动效
```html
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail"></nut-temp>
<nut-checkbox v-model="checkbox" :animation="false">没有动效</nut-checkbox>
```
### 基础用法3
```javascript
setup() {
return {
checkbox: false,
};
}
```
`Icon``name` 属性支持传入图标名称或图片链接。
## CheckboxGroup整体禁用动效
```html
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail"></nut-temp>
<nut-checkboxgroup v-model="checkboxGroup" :animation="false">
<nut-checkbox label="没有动效1"></nut-checkbox>
<nut-checkbox label="没有动效2"></nut-checkbox>
</nut-checkboxgroup>
```
```javascript
setup() {
return {
checkboxGroup: ['没有动效1'],
};
}
```
## 事件
值发生变化时,将触发change事件
```html
<nut-checkbox v-model="checkbox" @change="checkboxChange">
change事件
</nut-checkbox>
```
```javascript
setup() {
const checkbox = ref(false);
const checkboxChange= (checked) => {
console.log('change事件触发' + checked);
}
return {
checkbox,
checkboxChange
};
}
```
## CheckboxGroup整体事件
```html
<nut-checkboxgroup v-model="checkboxGroup" @change="getChange">
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
</nut-checkboxgroup>
```
```javascript
setup() {
const checkboxGroup = reactive(['选项一']);
const getChange= (val) => {
console.log('选中状态选项:' + val);
}
return {
checkboxGroup,
getChange
};
}
```
## 自定义class
```html
<nut-checkbox class="my-checkbox" v-model="checkbox">自定义Class:"my-checkbox"</nut-checkbox>
```
```javascript
setup() {
const checkbox = ref(false);
return {
checkbox,
};
}
```
## 全选与反选
```html
<nut-checkboxgroup
ref="checkboxGroupDemo"
v-model="checkboxGroup"
@change="getChange"
>
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
<nut-checkbox label="选项三"></nut-checkbox>
</nut-checkboxgroup>
<nut-button size="small" type="primary" @click="chooseAll(true)">全选</nut-button>
<nut-button size="small" type="primary" @click="chooseAll()">反选</nut-button>
<nut-button size="small" type="primary" @click="chooseAll(false)">取消</nut-button>
```
```javascript
setup() {
const checkboxGroup = ref([]);
const getChange = (val: string) => {
console.log('选中状态选项:' + val);
};
const checkboxGroupDemo = ref(null);
const chooseAll = (val: boolean | string) => {
(checkboxGroupDemo.value as any).toggleAll(val);
};
return {
checkboxGroup,
getChange,
chooseAll
};
}
```
## CheckboxGroup排列方向
```html
<nut-checkboxgroup
v-model="checkboxGroup"
direction="vertical"
@change="getChange"
>
<nut-checkbox label="选项一"></nut-checkbox>
<nut-checkbox label="选项二"></nut-checkbox>
<nut-checkbox label="选项三"></nut-checkbox>
</nut-checkboxgroup>
```
```javascript
setup() {
const checkboxGroup = ref([]);
const getChange = (val: string) => {
console.log('选中状态选项:' + val);
};
return {
checkboxGroup,
getChange,
};
}
```
## Prop
| 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | -----
| name | checkbox的name属性 | String | -
| v-model | 必填项,当前选中项的选中状态,同步value | Boolean | false
| label | 当前选中项的label值,(可不设,设置后label有值,替换插值内容) | String | -
| checked | checkbox的checked属性 | Boolean | false
| size | 尺寸,可选值small/base/large | String | base
| disabled | 是否禁用 | Boolean | false
| animation | 是否需要动效 | Boolean | true
## API
### CheckGroup
### Props
| 字段 | 说明 | 类型 | 默认值
|----- | ----- | ----- | -----
| v-model | 必填项,当前选中项的选中状态,同步value | Boolean | false
| size | 尺寸,可选值small/base/large | String | base
| disabled | 是否禁用 | Boolean | false
| animation | 是否需要动效 | Boolean | true
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| name | 图标名称或图片链接 | String | - |
| color | 图标颜色 | String | - |
| size | 图标大小,如 `20px` `2em` `2rem` | String | - |
| class-prefix | 类名前缀,用于使用自定义图标 | String | `nutui-iconfont` |
| tag | HTML 标签 | String | `i` |
### Events
## Event
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| click | 点击图标时触发 | event: Event |
| 字段 | 说明 | 回调参数
|----- | ----- | -----
| change | 值变化时触发 | 当前选中项状态(checked),当前选中项值(label)【设置label后有值、默认为空】,event
.nut-temp {
.nut-checkboxgroup-vertical .nut-checkbox {
display: block;
margin-top: 8px;
}
.nut-checkbox {
margin-right: 10px;
.nut-checkbox-label {
pointer-events: none;
vertical-align: middle;
}
input {
position: relative;
-webkit-appearance: none;
border-radius: 1%;
background-image: url('https://img11.360buyimg.com/imagetools/jfs/t1/153637/38/10229/854/5fd9cfd3E25c62c2a/fed43bc74cd473dd.png');
background-size: cover;
outline: 0;
opacity: 1;
vertical-align: middle;
margin: 0;
&::after {
position: absolute;
left: 50%;
top: 50%;
content: '';
width: 0;
height: 0;
transform: translate(-50%, -50%);
background: $primary-color;
border-radius: 1%;
opacity: 0;
pointer-events: none;
}
&:checked {
background-image: url('https://img13.360buyimg.com/imagetools/jfs/t1/131359/19/19372/2463/5fd3101bEdaf04120/3c8fa97cd4b22c02.png');
background-repeat: no-repeat;
background-position: center;
border-color: $primary-color;
&:not(:disabled).nut-checkbox-ani::after {
animation: nutPulse 0.25s;
}
}
&:disabled {
background-image: url('https://img14.360buyimg.com/imagetools/jfs/t1/155506/4/9365/704/5fd3103cE779ad491/1939699720df7770.png');
// border-color: $disable-color;
&:checked {
background-image: url('https://img12.360buyimg.com/imagetools/jfs/t1/151947/10/10769/2246/5fe04ceeEd31c1a06/2bb48d747c49f9dd.png');
}
}
}
.nut-checkbox-label {
color: #1d1e1e;
}
&.nut-checkbox-size-base {
input {
width: 18px;
height: 18px;
}
.nut-checkbox-label {
font-size: $font-size-2;
margin-left: 10px;
}
}
&.nut-checkbox-size-small {
input {
width: 16px;
height: 16px;
}
.nut-checkbox-label {
font-size: $font-size-1;
margin-left: 6px;
}
}
&.nut-checkbox-size-large {
input {
width: 22px;
height: 22px;
}
.nut-checkbox-label {
font-size: $font-size-3;
margin-left: 10px;
}
}
}
@keyframes nutPulse {
0% {
width: 100%;
height: 100%;
opacity: 0.5;
}
100% {
width: 150%;
height: 150%;
opacity: 0;
}
}
<template>
<view :class="classes" @click="handleClick">
<view>{{ name }}</view>
<view>{{ txt }}</view>
<view>
<label :class="['nut-checkbox', 'nut-checkbox-size-' + currentSize]">
<input
type="checkbox"
:name="name"
:class="{ 'nut-checkbox-ani': isAnimated }"
:disabled="isDisabled"
:checked.prop="isChecked"
:value="submittedValue"
@change="changeEvt"
/>
<span class="nut-checkbox-label" v-if="label">
{{ label }}
</span>
<span class="nut-checkbox-label" v-else>
<slot></slot>
</span>
</label>
</view>
</template>
<script lang="ts">
import { toRefs } from 'vue';
import {
reactive,
ref,
toRefs,
watch,
watchEffect,
computed,
getCurrentInstance,
inject
} from 'vue';
import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('temp');
const { componentName, create } = createComponent('checkbox');
export default create({
props: {
name: {
type: String
},
size: {
type: [String, Number, Boolean],
default: 'base'
},
label: {
type: String,
default: ''
},
txt: {
modelValue: {
required: true
},
trueValue: {
default: true
},
falseValue: {
default: false
},
submittedValue: {
type: String,
default: ''
default: 'on' // HTML default
},
checked: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
animation: {
type: Boolean,
default: true
}
},
components: {},
emits: ['click'],
setup(props, { emit }) {
console.log('componentName', componentName);
const parentGroup = inject('checkboxgroup', {
parentNode: false,
changeVal: val => {
console.log();
}
});
const parentProps = getCurrentInstance()?.parent?.props;
const { name, txt } = toRefs(props);
const isChecked = computed(() => {
if (parentGroup && parentGroup.parentNode) {
const choosedVal = parentProps?.modelValue;
const chooseFlag =
(choosedVal as any).indexOf(props.label) == -1 ? false : true;
return chooseFlag;
} else {
const isCheckedVal =
props.modelValue == props.trueValue || props.checked;
return isCheckedVal;
}
});
// const isCheckedVal = props.modelValue == props.trueValue || props.checked;
// const isChecked = ref(isCheckedVal);
const isObject = obj => {
return obj !== null && typeof obj === 'object';
};
// const looseEqual = (a, b) => {
// return (
// a == b ||
// (isObject(a) && isObject(b)
// ? JSON.stringify(a) === JSON.stringify(b)
// : false)
// );
// };
const isDisabled = computed(() => {
if (parentGroup && parentGroup.parentNode) {
return parentProps?.disabled;
} else {
return props.disabled;
}
});
const currentSize = computed(() => {
if (parentGroup && parentGroup.parentNode) {
return parentProps?.size;
} else {
return props.size;
}
});
const isAnimated = computed(() => {
if (parentGroup && parentGroup.parentNode) {
return parentProps?.animated;
} else {
return props.animation;
}
});
const handleClick = (event: Event) => {
emit('click', event);
const { label, name, submittedValue } = reactive(props);
const setParentValue = checked => {
// const { label } = props;
// const { max, modelValue } = parentProps?.modelValue;
const modelValue = parentProps?.modelValue;
const value = (modelValue as any).slice();
if (checked) {
if (value.indexOf(label) === -1) {
value.push(label);
parentGroup?.changeVal(value);
}
} else {
const index = value.indexOf(label);
if (index !== -1) {
value.splice(index, 1);
parentGroup?.changeVal(value);
}
}
};
const changeEvt = (event: any) => {
event?.stopPropagation();
const isCheck: boolean = event.target.checked;
if (isDisabled.value) {
return false;
}
if (parentGroup.parentNode) {
setParentValue(isCheck);
return false;
}
emit('update:modelValue', isCheck);
emit(
'input',
isCheck ? props.trueValue : props.falseValue,
props.label,
event
);
if (isChecked.value !== isCheck) {
emit(
'change',
isCheck ? props.trueValue : props.falseValue,
props.label,
event
);
}
};
return { name, txt, handleClick };
return {
currentSize,
label,
name,
isDisabled,
submittedValue,
isAnimated,
isChecked,
changeEvt
};
}
});
</script>
......
<template>
<div class="demo">
<h2>基础用法</h2>
<nut-cell>
<nut-temp name="wifi"></nut-temp>
<nut-temp name="mail" txt="test txt"></nut-temp>
</nut-cell>
</div>
</template>
<script lang="ts">
import { createComponent } from '@/utils/create';
const { createDemo } = createComponent('checkboxgroup');
export default createDemo({
props: {},
setup() {
return {};
}
});
</script>
<style lang="scss" scoped>
.nut-temp {
}
</style>
# checkboxgroup组件
### 介绍
基于 xxxxxxx
### 安装
## 代码演示
### 基础用法1
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| name | 图标名称或图片链接 | String | - |
| color | 图标颜色 | String | - |
| size | 图标大小,如 '20px' '2em' '2rem' | String | - |
| class-prefix | 类名前缀,用于使用自定义图标 | String | 'nutui-iconfont' |
| tag | HTML 标签 | String | 'i' |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| click | 点击图标时触发 | event: Event |
\ No newline at end of file
<template>
<view :class="['nut-checkboxgroup', 'nut-checkboxgroup-' + direction]">
<slot></slot>
</view>
</template>
<script lang="ts">
import { toRefs, watch, reactive, provide, getCurrentInstance } from 'vue';
import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('checkboxgroup');
export default create({
props: {
modelValue: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: false
},
size: {
type: String,
default: 'base'
},
animated: {
type: Boolean,
default: true
},
direction: {
type: String,
default: 'horizontal'
}
},
emits: ['change', 'update:modelValue'],
setup(props, { slots, emit }) {
watch(
() => props.modelValue,
value => {
emit('change', value);
}
);
function useExtend(apis: any) {
const instance = getCurrentInstance();
if (instance) {
Object.assign(instance.proxy, apis);
}
}
const toggleAll = checked => {
const children = (slots as any)?.default();
if (checked === false) {
emit('update:modelValue', []);
} else if (checked === true) {
const labels = children.map(item => item.props?.label);
emit('update:modelValue', labels);
} else {
const names = children
.filter(item => {
const label = item.props?.label;
const idx = props.modelValue.indexOf(label);
if (idx == -1) {
return label;
}
})
.map(item => item.props?.label);
emit('update:modelValue', names);
}
};
useExtend({ toggleAll });
provide('checkboxgroup', {
parentNode: true,
changeVal: val => {
if (props.disabled) {
return false;
}
emit('update:modelValue', val);
}
});
}
});
</script>
.nut-col {
float: left;
box-sizing: border-box;
word-break: break-all;
}
@for $i from 1 through 24 {
.nut-col-offset-#{$i} {
margin-left: 100/24 * $i * 1%;
}
.nut-col-#{$i} {
width: 100/24 * $i * 1%;
}
}
<template>
<view class="nut-col" :class="classObject" :style="styleObj">
<slot></slot>
</view>
</template>
<script lang="ts">
import { toRefs, watch, reactive, inject } from 'vue';
import { createComponent } from '@/utils/create';
const { componentName, create } = createComponent('col');
export default create({
props: {
span: {
type: [String, Number],
default: '24'
},
offset: {
type: [String, Number],
default: '0'
}
},
setup(props, { emit, slots }) {
const gutter = inject('gutter');
const classObject = reactive({
['nut-col-' + props.span]: true,
['nut-col-offset-' + props.offset]: true
});
const styleObj = reactive({
'padding-left': gutter + 'px',
'padding-right': gutter + 'px'
});
return {
classObject,
styleObj
};
}
});
</script>
<style lang="scss">
@import 'index.scss';
</style>
......@@ -9,7 +9,19 @@
<nut-collapse-item :title="title2" :name="2">
京东到家:教师节期间 创意花束销量增长53倍
</nut-collapse-item>
<nut-collapse-item :title="title3" :name="3" disabled> </nut-collapse-item>
<nut-collapse-item :title="title3" :name="3" disabled>
</nut-collapse-item>
</nut-collapse>
</div>
<h4>无icon样式</h4>
<div class="show-demo">
<nut-collapse v-model:active="active4" :accordion="true" :icon="icon2">
<nut-collapse-item :title="title1" :name="1">
2020年中国数字游戏市场规模超2786亿元
</nut-collapse-item>
<nut-collapse-item :title="title2" :name="2">
基于区块链技术的取证APP在浙江省杭州市发布
</nut-collapse-item>
</nut-collapse>
</div>
<div class="show-demo">
......@@ -27,7 +39,7 @@
</nut-collapse>
</div>
<div class="show-demo">
<h4>图标展示</h4>
<h4>自定义折叠图标</h4>
<nut-collapse
v-model:active="active3"
:accordion="true"
......@@ -45,6 +57,22 @@
</nut-collapse-item>
</nut-collapse>
</div>
<div class="show-demo">
<h4>自定义标题图标</h4>
<nut-collapse
v-model:active="active5"
:titleIcon="titleIcon"
:accordion="true"
:rotate="rotate"
>
<nut-collapse-item :title="title1" :name="1">
“森亿智能”获4亿元D轮融资
</nut-collapse-item>
<nut-collapse-item :title="title2" :name="2">
快看漫画与全球潮玩集合店X11达成战略合作
</nut-collapse-item>
</nut-collapse>
</div>
</div>
</template>
<script lang="ts">
......@@ -57,12 +85,18 @@ export default createDemo({
active1: [1, '2'],
active2: 1,
active3: 1,
active4: 1,
active5: 1,
expandIconPosition: 'left',
title1: '标题1',
title2: '标题2',
title3: '标题3',
subTitle: '副标题',
icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/132849/8/9709/550/5f5f0d8aE802abee7/68bd02b3a52c3988.png',
icon:
'https://img11.360buyimg.com/imagetools/jfs/t1/132849/8/9709/550/5f5f0d8aE802abee7/68bd02b3a52c3988.png',
icon2: 'none',
titleIcon:
'https://img13.360buyimg.com/imagetools/jfs/t1/144156/13/19748/1977/5fe2f5f8E21020483/a15144ab447bfbf2.png',
iconWidth: '20px',
iconHeight: '20px',
rotate: 90
......@@ -85,9 +119,10 @@ export default createDemo({
</style>
<style lang="scss" scoped>
.demo-list {
padding-top: 0;
margin: 60px 0;
h4 {
margin: 20px 0 10px 25px;
margin: 20px 0 10px 10px;
font-size: 14px;
color: #909ca4;
}
......
......@@ -18,13 +18,12 @@
```
``` javascript
export default {
data() {
return {
activeNames: [1, 2]
};
}
};
setup() {
const activeNames = reactive([1, 2]);
return {
activeNames
};
}
```
......@@ -47,14 +46,13 @@ export default {
```
``` javascript
export default {
data() {
return {
activeName: 1,
subTitle: '副标题'
};
}
};
setup() {
const activeName = ref(1);
return {
activeName,
subTitle: '副标题'
};
}
```
......@@ -75,18 +73,16 @@ export default {
```
``` javascript
export default {
data() {
return {
activeName: 1,
expandIconPosition: 'left',
icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/132849/8/9709/550/5f5f0d8aE802abee7/68bd02b3a52c3988.png'
rotate: 180,
iconWidth: '20px',
iconHeight: '20px',
};
}
};
setup() {
return {
activeName: 1,
expandIconPosition: 'left',
icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/132849/8/9709/550/5f5f0d8aE802abee7/68bd02b3a52c3988.png'
rotate: 180,
iconWidth: '20px',
iconHeight: '20px',
};
}
```
## Collapse Prop
......@@ -109,8 +105,12 @@ export default {
| title | 标题栏左侧内容 | string | - |
| name | 唯一标识符,必填 | string \ number | -1 |
| expand-icon-position | 标题图标的位置 | string | right |
| title-icon | 标题图标链接 | string | ‘’ 为不展示图标,默认 ‘’ |
| title-icon-width | 标题图标宽度 | string | 13px |
| title-icon-height | 标题图标高度 | string | 13px |
| title-icon-position | 标题图标位置 | string | ‘left' 'right' |
| sub-title | 标题栏副标题 | string | - |
| icon | 标题栏自定义图标链接 | string | - |
| icon | 标题栏自定义图标链接 | string | ‘none’ 为不展示图标,默认 ‘’ |
| icon-width | 标题栏自定义图标宽度 | string | 24px |
| icon-height | 标题栏自定义图标高度 | string | 12px |
| rotate | 点击折叠和展开的旋转角度,在自定义图标模式下生效 | string \ number | 180 |
\ No newline at end of file
......@@ -22,28 +22,48 @@ export default create({
type: String,
default: 'right'
},
titleIcon: {
type: String,
default: ''
},
titleIconWidth: {
type: String,
default: '13px'
},
titleIconHeight: {
type: String,
default: '13px'
},
titleIconPosition: {
type: String,
default: 'left'
},
icon: {
type: String,
default: ''
},
iconWidth: {
type: String,
default: ''
default: '24px'
},
iconHeight: {
type: String,
default: ''
default: '12px'
},
rotate: {
type: [String, Number],
default: 180
}
},
emits: ['update:active', 'change'],
setup(props, { emit }) {
const { active } = toRefs(props);
// 多个 item 展开
const changeValAry = (name: any) => {
const activeItem: any = active?.value instanceof Object ? Object.values(active.value) : active?.value;
const activeItem: any =
active?.value instanceof Object
? Object.values(active.value)
: active?.value;
let index = -1;
activeItem.forEach((item: string | number, idx: number) => {
if (String(item) == String(name)) {
......@@ -53,11 +73,16 @@ export default create({
const v = JSON.parse(JSON.stringify(activeItem));
index > -1 ? v.splice(index, 1) : v.push(name);
emit('update:active', v);
emit('change', v);
};
// 更新v-modal的值
const changeVal = (val: string | number | Array<string | number>, expanded: boolean) => {
const changeVal = (
val: string | number | Array<string | number>,
expanded: boolean
) => {
emit('update:active', val);
emit('change', val);
};
const isExpanded = (name: string | number | Array<string | number>) => {
......@@ -76,6 +101,8 @@ export default create({
value: props.active,
accordion: props.accordion,
expandIconPosition: props.expandIconPosition,
titleIcon: props.titleIcon,
titleIconPosition: props.titleIconPosition,
icon: props.icon,
rotate: props.rotate,
changeValAry,
......
此差异已折叠。
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 63 (92445) - https://sketch.com -->
<title>ic_close@1x</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="弹框-带关闭按钮的" transform="translate(-351.000000, -1009.000000)">
<g id="编组-3" transform="translate(87.000000, 278.000000)">
<g id="ic_close" transform="translate(264.000000, 731.000000)">
<g id="编组-15">
<circle id="椭圆形" stroke="#FFFFFF" stroke-width="3" cx="24" cy="24" r="22.5"></circle>
<path d="M29.4364935,16.4381608 C30.0222799,15.8523744 30.9720274,15.8523744 31.5578138,16.4381608 L31.5578138,16.4381608 C32.1476256,17.0279726 32.1476256,17.9777201 31.5618392,18.5635065 L31.5618392,18.5635065 L26.125,24 L31.5618392,29.4364935 C32.1476256,30.0222799 32.1476256,30.9720274 31.5618392,31.5578138 L31.5578138,31.5618392 C30.9720274,32.1476256 30.0222799,32.1476256 29.4364935,31.5618392 L24,26.125 L18.5635065,31.5618392 C18.0143317,32.111014 17.1452693,32.1453374 16.5561086,31.6648094 L16.4421862,31.5618392 C15.8523744,30.9720274 15.8523744,30.0222799 16.4381608,29.4364935 L16.4381608,29.4364935 L21.875,24 L16.4381608,18.5635065 C15.8523744,17.9777201 15.8523744,17.0279726 16.4381608,16.4421862 L16.4421862,16.4381608 C17.0279726,15.8523744 17.9777201,15.8523744 18.5635065,16.4381608 L24,21.875 Z" id="形状结合" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册