This commit is contained in:
2026-01-05 12:47:14 +08:00
commit 1fc846fae3
1614 changed files with 162035 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
@import "../popup/index";
.nut-theme-dark {
.nut-short-password {
&-title {
color: $dark-color;
}
&__list {
background: $dark-background3;
border: none;
}
&__item {
position: relative;
&::after {
position: absolute;
top: -50%;
right: -50%;
bottom: -50%;
left: -50%;
box-sizing: border-box;
pointer-events: none;
content: "";
outline: 1px solid #3a3a3c;
transform: scale(0.5);
}
}
&__item-icon {
background: $dark-color;
}
}
}
.nut-short-password {
$block: &;
&-title {
font-size: $font-size-3;
line-height: 1;
color: $title-color;
}
&-subtitle {
display: block;
margin-top: 12px;
font-size: $font-size-1;
line-height: 1;
color: $text-color;
}
&-wrapper {
position: relative;
padding: 12px 0 10px;
text-align: center;
}
&__list {
z-index: 10;
display: flex;
width: 100%;
height: 41px;
margin: 0 auto;
background: $shortpassword-background-color;
border: 1px solid $shortpassword-border-color;
border-radius: 4px;
}
&__item {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
}
&__item-icon {
display: inline-block;
width: 6px;
height: 6px;
background: #000;
border-radius: 50%;
}
&__message {
display: flex;
justify-content: space-between;
width: 247px;
margin-top: 9px;
#{$block}--error {
font-size: $font-size-0;
line-height: 1;
color: $shortpassword-error;
}
#{$block}--forget {
display: flex;
align-items: center;
font-size: $font-size-1;
line-height: 1;
color: $shortpassword-forget;
}
}
}

View File

@@ -0,0 +1 @@
export * from './shortpassword'

View File

@@ -0,0 +1,52 @@
import type { ExtractPropTypes } from 'vue'
import { CLOSE_EVENT, FOCUS_EVENT, UPDATE_MODEL_EVENT, UPDATE_VISIBLE_EVENT } from '../_constants'
import { commonProps, isBoolean, isNumber, isString, makeNumericProp, truthProp } from '../_utils'
export const shortpasswordProps = {
...commonProps,
/**
* @description 标题
*/
title: String,
/**
* @description 密码框描述
*/
desc: String,
/**
* @description 提示语
*/
tips: String,
/**
* @description 是否展示短密码框
*/
visible: Boolean,
/**
* @description 密码初始值
*/
modelValue: String,
/**
* @description 错误信息提示
*/
errorMsg: String,
/**
* @description 是否点击遮罩关闭
*/
closeOnClickOverlay: truthProp,
/**
* @description 密码长度取值为4~6
*/
length: makeNumericProp(6),
}
export type ShortPasswordProps = ExtractPropTypes<typeof shortpasswordProps>
export const shortpasswordEmits = {
[UPDATE_MODEL_EVENT]: (val: string | number, event: Event) => (isString(val) || isNumber(val)) && event instanceof Event,
[UPDATE_VISIBLE_EVENT]: (val: boolean) => isBoolean(val),
complete: (val: string) => isString(val),
tips: () => true,
[CLOSE_EVENT]: () => true,
[FOCUS_EVENT]: () => true,
}
export type ShortPasswordEmits = typeof shortpasswordEmits

View File

@@ -0,0 +1,123 @@
<script lang="ts" setup>
import type { CSSProperties } from 'vue'
import { computed, defineComponent, ref, watch } from 'vue'
import { CLOSE_EVENT, FOCUS_EVENT, PREFIX, UPDATE_VISIBLE_EVENT } from '../_constants'
import { getMainClass } from '../_utils'
import { useTranslate } from '../../locale'
import NutIcon from '../icon/icon.vue'
import NutPopup from '../popup/popup.vue'
import { shortpasswordEmits, shortpasswordProps } from './shortpassword'
const props = defineProps(shortpasswordProps)
const emit = defineEmits(shortpasswordEmits)
const innerVisible = ref(props.visible)
const inputValue = ref(props.modelValue)
const innerValue = computed(() => String(inputValue.value))
const finalLength = computed(() => {
const numberLength = Number(props.length)
return Math.min(Math.max(4, numberLength), 6)
})
const classes = computed(() => {
return getMainClass(props, componentName)
})
const popupStyles: CSSProperties = {
top: '45%',
padding: '30px 24px 20px 24px',
textAlign: 'center',
borderRadius: '12px',
}
watch(() => props.visible, (value) => {
innerVisible.value = value
})
watch(() => props.modelValue, (value) => {
inputValue.value = value
if (String(value).length >= finalLength.value)
emit('complete', value!)
})
function focus() {
emit(FOCUS_EVENT)
}
function close() {
emit(UPDATE_VISIBLE_EVENT, false)
emit(CLOSE_EVENT)
}
function onTipsClick() {
emit('tips')
}
</script>
<script lang="ts">
const componentName = `${PREFIX}-short-password`
const { translate } = useTranslate(componentName)
export default defineComponent({
name: componentName,
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
})
</script>
<template>
<view :class="classes" :style="props.customStyle">
<NutPopup
v-model:visible="innerVisible"
:custom-style="popupStyles"
:closeable="true"
:close-on-click-overlay="props.closeOnClickOverlay"
@close="close"
>
<view class="nut-short-password-title">
{{ props.title || translate('title') }}
</view>
<view class="nut-short-password-subtitle">
{{ props.desc || translate('desc') }}
</view>
<view class="nut-short-password-wrapper">
<view class="nut-short-password__list" @tap="focus">
<view v-for="it in finalLength" :key="it" class="nut-short-password__item">
<view v-if="it <= innerValue.length" class="nut-short-password__item-icon" />
</view>
</view>
</view>
<view class="nut-short-password__message">
<view class="nut-short-password--error">
{{ props.errorMsg }}
</view>
<view
v-if="props.tips || translate('tips')"
class="nut-short-password--forget"
@click="onTipsClick"
>
<NutIcon custom-class="icon" name="tips" size="11px" />
<view>{{ props.tips || translate('tips') }}</view>
</view>
</view>
</NutPopup>
</view>
</template>
<style lang="scss">
@import "./index";
</style>