This commit is contained in:
Viet An
2026-06-04 11:36:43 +07:00
parent 8e2dd06def
commit 2981d9790a
13 changed files with 375 additions and 944 deletions

View File

@@ -1,43 +1,52 @@
<script setup>
const props = defineProps({
record: Object,
attr: String,
disabled: Boolean,
placeholder: {
type: String,
default: "",
},
});
const { $copy, $empty, $errEmail } = useNuxtApp();
const emit = defineEmits(["email"]);
const email = ref(props.record?.[props.attr] ? $copy(props.record[props.attr]) : undefined);
const error = ref(false);
watch(
() => props.record?.[props.attr],
(v) => {
email.value = v ? $copy(v) : undefined;
},
);
function doCheck() {
if ($empty(email.value)) {
email.value = undefined;
error.value = false;
return emit("email", null);
}
error.value = !!$errEmail(email.value);
emit("email", email.value);
}
</script>
<template>
<div class="control has-icons-left">
<input
:class="`input ${error ? 'is-danger' : ''} ${disabled ? 'has-text-black' : ''}`"
:class="['input', error && 'is-danger', disabled && 'has-text-black']"
type="text"
:placeholder="placeholder || ''"
v-model="value"
v-model="email"
@keyup="doCheck"
:disabled="disabled || false"
:placeholder="placeholder"
:disabled="disabled"
/>
<span class="icon is-left">
<SvgIcon v-bind="{ name: 'email.svg', type: 'gray', size: 21 }"></SvgIcon>
<Icon
name="material-symbols:mail-outline-rounded"
:size="20"
/>
</span>
</div>
</template>
<script>
export default {
props: ["record", "attr", "placeholder", "disabled"],
data() {
return {
value: this.record[this.attr] ? this.$copy(this.record[this.attr]) : undefined,
error: undefined,
};
},
watch: {
record: function (newVal) {
this.value = this.record[this.attr] ? this.$copy(this.record[this.attr]) : undefined;
},
},
methods: {
doCheck() {
if (this.$empty(this.value)) {
this.value = undefined;
this.error = false;
return this.$emit("email", null);
}
let check = this.$errEmail(this.value);
this.error = check ? true : false;
this.$emit("email", this.value);
},
},
};
</script>

View File

@@ -1,55 +1,59 @@
<script setup>
const props = defineProps({
record: Object,
attr: String,
disabled: Boolean,
placeholder: {
type: String,
default: "",
},
});
const emit = defineEmits(["phone"]);
const phoneNum = ref("");
const initial = props.record && props.record[props.attr];
phoneNum.value = initial ? String(initial) : "";
watch(phoneNum, (newVal) => {
// giữ lại CHỈ chữ số
const digits = newVal.replaceAll(/\D/g, "");
// sync lại UI nếu user nhập ký tự khác số
if (digits !== newVal) {
phoneNum.value = digits;
return;
}
// emit string số hoặc null
emit("phone", digits.length ? digits : null);
});
watch(
() => props.record,
(newRecord) => {
const v = newRecord && newRecord[props.attr];
phoneNum.value = v ? String(v) : "";
},
);
</script>
<template>
<div class="control has-icons-left">
<input
class="input"
type="text"
:placeholder="placeholder || ''"
v-model="value"
:disabled="disabled || false"
v-model="phoneNum"
:placeholder="placeholder"
:disabled="disabled"
inputmode="numeric"
maxlength="15"
autocomplete="tel"
/>
<span class="icon is-left">
<SvgIcon v-bind="{ name: 'phone.png', type: 'gray', size: 20 }"></SvgIcon>
<Icon
name="material-symbols:call-outline-rounded"
:size="20"
/>
</span>
</div>
</template>
<script>
export default {
props: ["record", "attr", "placeholder", "disabled"],
data() {
return {
value: "",
};
},
created() {
const initial = this.record?.[this.attr];
this.value = initial ? String(initial) : "";
},
watch: {
/** giống InputEmail.vue: watch value → emit ngay */
value(newVal) {
// giữ lại CHỈ chữ số
const digits = String(newVal).replace(/\D/g, "");
// sync lại UI nếu user nhập ký tự khác số
if (digits !== newVal) {
this.value = digits;
return;
}
// emit string số hoặc null
this.$emit("phone", digits.length ? digits : null);
},
record(newVal) {
const v = newVal?.[this.attr];
this.value = v ? String(v) : "";
},
},
};
</script>