feat: convert value select component to composition API

This commit is contained in:
IchliebedichZhu 2024-03-10 10:37:47 +00:00
parent c63ca83894
commit 83f7f5e485
3 changed files with 188 additions and 170 deletions

View File

@ -24,7 +24,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref } from 'vue'
// //
const NAME = 'text-input' // const NAME = 'text-input'
type TProps = { type TProps = {
label?: string label?: string

View File

@ -9,61 +9,65 @@
<div id="text-input-area"> <div id="text-input-area">
<p v-if="label" class="input-label">{{ label }}</p> <p v-if="label" class="input-label">{{ label }}</p>
<div :class="{ 'input-wrap': true, active: inputBorder }"> <div :class="{ 'input-wrap': true, active: inputBorder }">
<textarea :maxlength="max" :class="{ 'real-input': true, disable: !editable }" type="text" rows="3" :value="dealValue" :readonly="editable ? false : 'readonly'" @input="updateValue($event.target.value)" @focus="focusInput" @blur="blurInput" /> <textarea
:maxlength="max" :class="{ 'real-input': true, disable: !editable }"
type="text" rows="3" :value="dealValue"
:readonly="!editable" @input="updateValue(($event.target as HTMLTextAreaElement).value)"
@focus="focusInput" @blur="blurInput"
/>
</div> </div>
</div> </div>
</template> </template>
<script> <script lang="ts" setup>
import { ref, computed } from 'vue'
// //
const NAME = 'text-input-area' // const NAME = 'text-input-area'
export default { type TProps = {
name: NAME, label?: string
props: { modelValue?: string
label: { editable?: boolean
default: '', max?: string
}, }
modelValue: {
default: '', type TEmits = {
}, (event:'update:modelValue', data: string): void
editable: { (event: 'finish', data: string): void
default: true, }
},
max: {}, const props = withDefaults(defineProps<TProps>(), {
}, label: '',
emits: ['update:modelValue', 'finish'], modelValue: '',
data() { editable: true,
return { })
inputBorder: false,
tagText: '', const emit = defineEmits<TEmits>()
} const inputBorder = ref(false)
}, const tagText = ref<string>('')
computed: {
dealValue() { const dealValue = computed(() => {
return this.modelValue return props.modelValue
// return this.modelValue.replace(/<br\/>/g, '\r\n').replace(/&nbsp;/g, ' ') })
},
}, function updateValue(value: string) {
methods: { emit('update:modelValue', getValue(value))
updateValue(value) { }
this.$emit('update:modelValue', this.getValue(value))
}, function focusInput() {
focusInput() { inputBorder.value = true
this.inputBorder = true tagText.value = props.modelValue
this.tagText = this.modelValue }
},
blurInput() { function blurInput() {
this.inputBorder = false inputBorder.value = false
let v = this.getValue(this.modelValue) let v = getValue(props.modelValue)
if (v !== this.tagText) { if (v !== tagText.value) {
this.$emit('finish', v) emit('finish', v)
} }
}, }
getValue(value) { function getValue(value: string) {
return value.replace(/\n|\r\n/g, '<br/>').replace(/ /g, '&nbsp;') return value.replace(/\n|\r\n/g, '<br/>').replace(/ /g, '&nbsp;')
},
},
} }
</script> </script>

View File

@ -13,18 +13,22 @@
<el-popover placement="bottom-end" trigger="click" width="auto"> <el-popover placement="bottom-end" trigger="click" width="auto">
<!-- 单列表 --> <!-- 单列表 -->
<ul v-if="data && Array.isArray(data)" class="list-ul"> <ul v-if="data && Array.isArray(data)" class="list-ul">
<li v-for="listItem in data" :key="typeof listItem === 'object' ? listItem.alias : listItem" :class="{ active: listItem == innerValue }" @click="selectItem(listItem)"> <li
<img v-if="listItem.preview" class="preview" :src="listItem.preview" /> v-for="listItem in data" :key="typeof listItem === 'object' ? listItem.alias : listItem"
:class="{ active: listItem == state.innerValue }"
@click="selectItem(listItem)"
>
<img v-if="listItem.preview" class="preview" :src="listItem.preview" alt="preview" />
<span v-else>{{ (typeof listItem === 'object' ? listItem.alias : listItem) + suffix }}</span> <span v-else>{{ (typeof listItem === 'object' ? listItem.alias : listItem) + suffix }}</span>
</li> </li>
</ul> </ul>
<!-- tab分类列表 --> <!-- tab分类列表 -->
<div v-else class="tabs-wrap"> <div v-else class="tabs-wrap">
<el-tabs v-model="activeTab"> <el-tabs v-model="state.activeTab">
<el-tab-pane v-for="(val, key, i) in data" :key="'tab' + i" :label="key" :name="key"> <el-tab-pane v-for="(val, key, i) in data" :key="'tab' + i" :label="key" :name="key">
<ul class="list-ul"> <ul class="list-ul">
<li v-for="listItem in data[key]" :key="typeof listItem === 'object' ? listItem.alias : listItem" :class="{ active: listItem == innerValue }" @click="selectItem(listItem)"> <li v-for="listItem in data[key]" :key="typeof listItem === 'object' ? listItem.alias : listItem" :class="{ active: listItem == state.innerValue }" @click="selectItem(listItem)">
<img v-if="listItem.preview" class="preview" :src="listItem.preview" /> <img v-if="listItem.preview" class="preview" :src="listItem.preview" alt="preview" />
<span v-else :style="{ fontFamily: `'${listItem.value}'` }">{{ (typeof listItem === 'object' ? listItem.alias : listItem) + suffix }}</span> <span v-else :style="{ fontFamily: `'${listItem.value}'` }">{{ (typeof listItem === 'object' ? listItem.alias : listItem) + suffix }}</span>
</li> </li>
</ul> </ul>
@ -32,9 +36,16 @@
</el-tabs> </el-tabs>
</div> </div>
<template #reference> <template #reference>
<div :class="['input-wrap', { active: inputBorder }]" :style="{ width: inputWidth }"> <div :class="['input-wrap', { active: state.inputBorder }]" :style="{ width: inputWidth }">
<!-- <img v-if="innerPreview" class="preview" :src="innerPreview" /> --> <!-- <img v-if="innerPreview" class="preview" :src="innerPreview" /> -->
<input :style="{ fontFamily: modelValue.value }" :class="['real-input', { disable: !disable }]" :readonly="readonly ? 'readonly' : false" type="text" :value="showValue" @input="inputText" @focus="inputBorder = true" @blur="inputBorder = false" @keydown="(e) => opNumber(e)" /> <input
:style="{ fontFamily: modelValue.value }"
:class="['real-input', { disable: !disable }]"
:readonly="readonly" type="text"
:value="showValue"
@input="inputText" @focus="state.inputBorder = true"
@blur="state.inputBorder = false" @keydown="(e) => opNumber(e)"
/>
<!-- <span class="input-unit">{{ suffix }}</span> --> <!-- <span class="input-unit">{{ suffix }}</span> -->
<div class="op-btn"> <div class="op-btn">
<!-- <div class="down" @click="inputBorder = !inputBorder"></div> --> <!-- <div class="down" @click="inputBorder = !inputBorder"></div> -->
@ -46,124 +57,127 @@
</div> </div>
</template> </template>
<script> <script lang="ts" setup>
// //
const NAME = 'value-input' const NAME = 'value-input'
import { ElTabPane, ElTabs } from 'element-plus' import { ElTabPane, ElTabs } from 'element-plus'
import { computed, onMounted, reactive, ref, watch } from 'vue';
export default { type TProps = {
name: NAME, label?: string
components: { ElTabPane, ElTabs }, modelValue?: Record<string, any>
props: { suffix?: string
label: { data: Record<string, any>
default: '', disable?: boolean
}, inputWidth?: string
modelValue: { readonly?: boolean
default: '', step?: number
}, }
suffix: {
default: '', type TEmits = {
}, (event:'update:modelValue', data: Record<string, any> | string | number): void
data: { (event: 'finish', data: Record<string, any> | string | number): void
required: true, }
},
disable: { type TState = {
default: true, inputBorder: boolean
}, tagText: string
inputWidth: { width: string | number
default: '80px', innerValue: string
}, innerPreview: string
// textAlign: { activeTab: string
// default: 'center', }
// },
readonly: { const props = withDefaults(defineProps<TProps>(), {
default: false, label: '',
}, modelValue: () => ({}),
step: { suffic: '',
default: 1, data: () => ({}),
}, disable: true,
}, inputWidth: '80px',
emits: ['finish', 'update:modelValue'], readonly: false,
data() { step: 1,
return { })
inputBorder: false, const emit = defineEmits<TEmits>()
tagText: '', const state = reactive<TState>({
width: '0', inputBorder: false,
innerValue: '', tagText: '',
innerPreview: '', width: '0',
activeTab: '中文', innerValue: '',
innerPreview: '',
activeTab: '中文',
})
const selectRef = ref<HTMLElement | null>(null)
const showValue = computed(() => {
return state.innerValue
})
watch(
() => props.modelValue,
() => {
state.innerValue = typeof props.modelValue === 'object' ? props.modelValue.alias : props.modelValue
}
)
watch(
() => state.inputBorder,
(value) => {
if (value) {
state.tagText = state.innerValue
} else {
if (state.innerValue !== state.tagText) {
emit('finish', state.innerValue)
}
} }
}, }
computed: { )
showValue() {
// return this.innerValue + this.suffix onMounted(() => {
return this.innerValue state.innerValue = typeof props.modelValue === 'object' ? props.modelValue.alias : props.modelValue
}, if (selectRef.value) {
}, state.width = selectRef.value.offsetWidth
watch: { }
modelValue(value) { })
this.innerValue = typeof this.modelValue === 'object' ? this.modelValue.alias : this.modelValue
}, function selectItem(item: Record<string, any>) {
inputBorder(value) { let value = typeof item === 'object' ? item.alias : item
if (value) { if (state.innerValue !== value) {
this.tagText = this.innerValue state.innerValue = value
} else { state.innerPreview = item.preview
if (this.innerValue !== this.tagText) { emit('finish', item)
this.$emit('finish', this.innerValue) }
} }
}
}, function inputText(e: Event) {
}, // this.innerValue = e.target.value.replace(RegExp(this.suffix), '')
created() { state.innerValue = (e.target as HTMLInputElement).value
this.innerValue = typeof this.modelValue === 'object' ? this.modelValue.alias : this.modelValue setTimeout(() => {
}, emit('finish', state.innerValue)
mounted() { }, 100)
this.width = this.$refs.select.offsetWidth }
// if (Object.prototype.toString.call(this.data) === '[Object Object]') { function opNumber(e: KeyboardEvent) {
// for (const key in this.data) { e.stopPropagation()
// console.log(key) switch (e.keyCode) {
// break case 38:
// } typeof state.innerValue === 'number' && up()
// } return
}, case 40:
methods: { typeof state.innerValue === 'number' && down()
selectItem(item) { return
let value = typeof item === 'object' ? item.alias : item }
if (this.innerValue !== value) { }
this.innerValue = value
this.innerPreview = item.preview function up() {
this.$emit('finish', item) emit('update:modelValue', parseInt(`${props.modelValue}` ?? '0', 10) + props.step)
} }
},
inputText(e) { function down() {
// this.innerValue = e.target.value.replace(RegExp(this.suffix), '') let value = parseInt(`${props.modelValue}` ?? '0', 10) - props.step
this.innerValue = e.target.value if (value < 0) {
setTimeout(() => { value = 0
this.$emit('finish', this.innerValue) }
}, 100) emit('update:modelValue', value)
},
opNumber(e) {
e.stopPropagation()
switch (e.keyCode) {
case 38:
typeof this.innerValue === 'number' && this.up()
return
case 40:
typeof this.innerValue === 'number' && this.down()
return
}
},
up() {
this.$emit('update:modelValue', parseInt(this.modelValue || 0, 10) + this.step)
},
down() {
let value = parseInt(this.modelValue || 0, 10) - this.step
if (value < 0) {
value = 0
}
this.$emit('update:modelValue', value)
},
},
} }
</script> </script>