init
This commit is contained in:
78
uni_modules/nutui-uni/components/ecard/ecard.ts
Normal file
78
uni_modules/nutui-uni/components/ecard/ecard.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import { UPDATE_MODEL_EVENT } from '../_constants'
|
||||
import {
|
||||
commonProps,
|
||||
isNumber,
|
||||
isString,
|
||||
makeArrayProp,
|
||||
makeNumberProp,
|
||||
makeNumericProp,
|
||||
makeStringProp,
|
||||
truthProp,
|
||||
} from '../_utils'
|
||||
import type { EcardChangeEvent, EcardDataItem, EcardDataValue } from './type'
|
||||
|
||||
export const ecardProps = {
|
||||
...commonProps,
|
||||
/**
|
||||
* @description 购买电子卡所需价钱
|
||||
*/
|
||||
modelValue: makeNumericProp(0),
|
||||
/**
|
||||
* @description 电子卡面值列表
|
||||
*/
|
||||
list: makeArrayProp<EcardDataItem>([]),
|
||||
/**
|
||||
* @description 选择面值文案
|
||||
*/
|
||||
chooseText: makeStringProp(''),
|
||||
/**
|
||||
* @description 是否显示其他面值
|
||||
*/
|
||||
showOther: truthProp,
|
||||
/**
|
||||
* @description 其他面值文案
|
||||
*/
|
||||
otherValueText: makeStringProp(''),
|
||||
/**
|
||||
* @description 其他面值默认提示语
|
||||
*/
|
||||
placeholder: makeStringProp(''),
|
||||
/**
|
||||
* @description 符号标示
|
||||
*/
|
||||
suffix: makeStringProp('¥'),
|
||||
/**
|
||||
* @description 其它面值最小值
|
||||
*/
|
||||
cardAmountMin: makeNumericProp(1),
|
||||
/**
|
||||
* @description 其他面值最大值
|
||||
*/
|
||||
cardAmountMax: makeNumericProp(9999),
|
||||
/**
|
||||
* @description 是否显示步进
|
||||
*/
|
||||
showStep: truthProp,
|
||||
/**
|
||||
* @description 购买数量最小值
|
||||
*/
|
||||
cardBuyMin: makeNumberProp(1),
|
||||
/**
|
||||
* @description 购买数量最大值
|
||||
*/
|
||||
cardBuyMax: makeNumberProp(9999),
|
||||
}
|
||||
|
||||
export type ECardProps = ExtractPropTypes<typeof ecardProps>
|
||||
|
||||
export const ecardEmits = {
|
||||
[UPDATE_MODEL_EVENT]: (val: EcardDataValue) => isNumber(val) || isString(val),
|
||||
update: (val: EcardDataValue) => isNumber(val) || isString(val),
|
||||
change: (evt: EcardChangeEvent) => evt instanceof Object,
|
||||
inputChange: (val: string) => isString(val),
|
||||
changeStep: (val1: number, val2: EcardDataValue) => isNumber(val1) && (isNumber(val2) || isString(val2)),
|
||||
inputClick: () => true,
|
||||
}
|
||||
|
||||
export type ECardEmits = typeof ecardEmits
|
||||
205
uni_modules/nutui-uni/components/ecard/ecard.vue
Normal file
205
uni_modules/nutui-uni/components/ecard/ecard.vue
Normal file
@@ -0,0 +1,205 @@
|
||||
<script setup lang="ts">
|
||||
import type { InputOnInputEvent } from '@uni-helper/uni-app-types'
|
||||
import { computed, defineComponent, nextTick, ref } from 'vue'
|
||||
import { PREFIX, UPDATE_MODEL_EVENT } from '../_constants'
|
||||
import { getMainClass } from '../_utils'
|
||||
import { useTranslate } from '../../locale'
|
||||
import NutInputNumber from '../inputnumber/inputnumber.vue'
|
||||
import { ecardEmits, ecardProps } from './ecard'
|
||||
import type { EcardDataItem, EcardDataValue, EcardUpdateOptions } from './type'
|
||||
|
||||
const props = defineProps(ecardProps)
|
||||
|
||||
const emit = defineEmits(ecardEmits)
|
||||
|
||||
const innerValue = ref<EcardDataValue>(0)
|
||||
|
||||
const currentIndex = ref<number | null>(null)
|
||||
|
||||
const inputValue = ref<string>('')
|
||||
|
||||
const innerCount = ref<number>(props.cardBuyMin)
|
||||
|
||||
const finalValue = computed(() => {
|
||||
return Number(innerValue.value) * innerCount.value
|
||||
})
|
||||
|
||||
const classes = computed(() => {
|
||||
return getMainClass(props, componentName)
|
||||
})
|
||||
|
||||
async function forceUpdateInputValue(value: string) {
|
||||
if (value !== inputValue.value) {
|
||||
inputValue.value = value
|
||||
return
|
||||
}
|
||||
|
||||
inputValue.value = value.slice(0, -1)
|
||||
await nextTick()
|
||||
inputValue.value = value
|
||||
}
|
||||
|
||||
function handleClick(item: EcardDataItem, index: number) {
|
||||
innerValue.value = item.price
|
||||
currentIndex.value = index
|
||||
|
||||
forceUpdateInputValue('')
|
||||
innerCount.value = props.cardBuyMin
|
||||
|
||||
emit('change', item)
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, finalValue.value)
|
||||
emit('update', finalValue.value)
|
||||
}
|
||||
|
||||
function handleInputValue(value: number) {
|
||||
if (value > Number(props.cardAmountMax))
|
||||
return props.cardAmountMax
|
||||
|
||||
if (value < Number(props.cardAmountMin))
|
||||
return props.cardAmountMin
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
function handleInputClick() {
|
||||
emit('inputClick')
|
||||
|
||||
if (currentIndex.value === -1)
|
||||
return
|
||||
|
||||
innerValue.value = 0
|
||||
currentIndex.value = -1
|
||||
|
||||
forceUpdateInputValue('')
|
||||
innerCount.value = props.cardBuyMin
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, finalValue.value)
|
||||
emit('update', finalValue.value)
|
||||
}
|
||||
|
||||
function handleInputChange(event: InputOnInputEvent) {
|
||||
const value = Number(event.detail.value.replace(/\D/g, ''))
|
||||
|
||||
const valued = handleInputValue(value)
|
||||
const stringValued = String(valued)
|
||||
|
||||
forceUpdateInputValue(stringValued)
|
||||
innerValue.value = valued
|
||||
|
||||
emit('inputChange', stringValued)
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, finalValue.value)
|
||||
emit('update', finalValue.value)
|
||||
}
|
||||
|
||||
function handleStepChange(value: number | string) {
|
||||
innerCount.value = Number(value)
|
||||
|
||||
emit('changeStep', innerCount.value, innerValue.value)
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, finalValue.value)
|
||||
emit('update', finalValue.value)
|
||||
}
|
||||
|
||||
function update(options: EcardUpdateOptions) {
|
||||
const { index, input, count } = options
|
||||
|
||||
if (index !== undefined) {
|
||||
if (index != null && (index < -1 || index >= props.list.length))
|
||||
return
|
||||
|
||||
currentIndex.value = index
|
||||
|
||||
if (index == null || index === -1) {
|
||||
if (input != null) {
|
||||
innerValue.value = Number(input)
|
||||
forceUpdateInputValue(input)
|
||||
}
|
||||
}
|
||||
else {
|
||||
innerValue.value = props.list[index].price
|
||||
forceUpdateInputValue('')
|
||||
}
|
||||
}
|
||||
|
||||
if (count != null)
|
||||
innerCount.value = count
|
||||
|
||||
emit(UPDATE_MODEL_EVENT, finalValue.value)
|
||||
emit('update', finalValue.value)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
update,
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const componentName = `${PREFIX}-ecard`
|
||||
const { translate } = useTranslate(componentName)
|
||||
|
||||
export default defineComponent({
|
||||
name: componentName,
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view :class="classes" :style="props.customStyle">
|
||||
<view class="nut-ecard__title">
|
||||
{{ props.chooseText || translate('chooseText') }}
|
||||
</view>
|
||||
|
||||
<view class="nut-ecard__list">
|
||||
<view
|
||||
v-for="(item, index) in props.list"
|
||||
:key="index"
|
||||
class="nut-ecard__list__item"
|
||||
:class="{ active: currentIndex === index }"
|
||||
@click="handleClick(item, index)"
|
||||
>
|
||||
{{ item.price }}
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="props.showOther"
|
||||
class="nut-ecard__list__input"
|
||||
:class="{ active: currentIndex === -1 }"
|
||||
@click="handleInputClick"
|
||||
>
|
||||
<view>{{ props.otherValueText || translate('otherValueText') }}</view>
|
||||
|
||||
<view class="nut-ecard__list__input--con">
|
||||
<input
|
||||
class="nut-ecard-input"
|
||||
:value="inputValue as string"
|
||||
type="text"
|
||||
:placeholder="props.placeholder || translate('placeholder')"
|
||||
@input="handleInputChange"
|
||||
>
|
||||
{{ props.suffix }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="props.showStep" class="nut-ecard__list__step">
|
||||
<view>{{ props.suffix }}{{ props.modelValue }}</view>
|
||||
|
||||
<NutInputNumber
|
||||
:model-value="innerCount"
|
||||
:min="props.cardBuyMin"
|
||||
:max="props.cardBuyMax"
|
||||
@change="handleStepChange"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./index";
|
||||
</style>
|
||||
117
uni_modules/nutui-uni/components/ecard/index.scss
Normal file
117
uni_modules/nutui-uni/components/ecard/index.scss
Normal file
@@ -0,0 +1,117 @@
|
||||
@import "../inputnumber/index";
|
||||
|
||||
.nut-theme-dark {
|
||||
.nut-ecard {
|
||||
color: $dark-color3;
|
||||
|
||||
.nut-ecard__list__item {
|
||||
background: $dark-background5;
|
||||
border-color: $dark-background5;
|
||||
|
||||
&.active {
|
||||
color: $dark-color2;
|
||||
background: $dark-background6;
|
||||
border-color: $dark-color2;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-ecard__list__input {
|
||||
color: $dark-color3;
|
||||
background: $dark-background7;
|
||||
|
||||
&.active {
|
||||
background: $dark-background7;
|
||||
|
||||
.nut-ecard-input {
|
||||
background: $dark-background7;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-ecard__list__input--con > .nut-ecard-input {
|
||||
color: $dark-color3;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-ecard {
|
||||
width: 100%;
|
||||
|
||||
&__title {
|
||||
font-size: 15px;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
margin-top: 15px;
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48%;
|
||||
height: 46px;
|
||||
margin-bottom: 12px;
|
||||
background: $ecard-bg-color;
|
||||
border: 1px solid $ecard-bg-color;
|
||||
border-radius: 4px;
|
||||
|
||||
&.active {
|
||||
background: $white;
|
||||
border-color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
padding: 0 15px 0 20px;
|
||||
font-size: 14px;
|
||||
background: $ecard-bg-color;
|
||||
border: 1px solid $ecard-bg-color;
|
||||
border-radius: 4px;
|
||||
|
||||
&--con {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
|
||||
.nut-ecard-input {
|
||||
margin-right: 10px;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
caret-color: $primary-color;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $white;
|
||||
border-color: $primary-color;
|
||||
|
||||
.nut-ecard-input {
|
||||
background: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__step {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
margin-top: 17px;
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
uni_modules/nutui-uni/components/ecard/index.ts
Normal file
2
uni_modules/nutui-uni/components/ecard/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './ecard'
|
||||
export * from './type'
|
||||
33
uni_modules/nutui-uni/components/ecard/type.ts
Normal file
33
uni_modules/nutui-uni/components/ecard/type.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export type EcardDataValue = number | string
|
||||
|
||||
export interface EcardDataItem {
|
||||
price: EcardDataValue
|
||||
}
|
||||
|
||||
export interface EcardChangeEvent {
|
||||
price: EcardDataValue
|
||||
}
|
||||
|
||||
export interface EcardUpdateOptions {
|
||||
/**
|
||||
* 选中项(从0开始的索引,-1表示选中输入框,null表示不选中)
|
||||
*/
|
||||
index?: number | null
|
||||
/**
|
||||
* 其他面值(当index为-1或null时有效)
|
||||
*/
|
||||
input?: string
|
||||
/**
|
||||
* 数量
|
||||
*/
|
||||
count?: number
|
||||
}
|
||||
|
||||
export interface EcardInst {
|
||||
/**
|
||||
* 更新面值
|
||||
*
|
||||
* @param options 配置项
|
||||
*/
|
||||
update: (options: EcardUpdateOptions) => void
|
||||
}
|
||||
Reference in New Issue
Block a user