hesabixCore/webUI/src/components/plugins/warranty/SerialDialog.vue

288 lines
7.7 KiB
Vue
Raw Normal View History

2025-08-06 15:16:18 +03:30
<template>
<v-dialog v-model="dialog" max-width="600px" persistent>
<v-card>
<v-card-title class="d-flex align-center p-3 gap-2">
<v-icon class="mr-3" color="primary">mdi-shield-check</v-icon>
<span>{{ isEdit ? 'ویرایش سریال گارانتی' : 'افزودن سریال گارانتی جدید' }}</span>
</v-card-title>
<v-card-text>
<v-form ref="form" v-model="valid">
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="formData.serialNumber"
label="شماره سریال *"
:rules="[rules.serialNumber]"
required
:disabled="isEdit"
variant="outlined"
density="comfortable"
hide-details="auto"
maxlength="50"
counter
></v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-autocomplete
v-model="formData.commodity_id"
label="محصول *"
:items="commodities"
item-title="name"
item-value="id"
:rules="[rules.commodity]"
required
:filter="customFilter"
clearable
return-object
@update:model-value="handleCommoditySelect"
variant="outlined"
density="comfortable"
hide-details="auto"
></v-autocomplete>
</v-col>
<v-col cols="12" md="6">
<h-date-picker
v-model="formData.warrantyStartDate"
label="تاریخ شروع گارانتی"
:rules="[rules.date]"
/>
</v-col>
<v-col cols="12" md="6">
<h-date-picker
v-model="formData.warrantyEndDate"
label="تاریخ پایان گارانتی"
:rules="[rules.date, (value: any) => rules.endDate(value, formData.warrantyStartDate)]"
/>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="formData.status"
label="وضعیت"
:items="statusOptions"
item-title="title"
item-value="value"
variant="outlined"
density="comfortable"
hide-details="auto"
></v-select>
</v-col>
<v-col cols="12">
<v-textarea
v-model="formData.description"
label="توضیحات"
rows="3"
auto-grow
variant="outlined"
density="comfortable"
hide-details="auto"
></v-textarea>
</v-col>
<!-- <v-col cols="12">
<v-textarea
v-model="formData.notes"
label="یادداشت‌ها"
rows="2"
auto-grow
></v-textarea>
</v-col> -->
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="grey" @click="close">انصراف</v-btn>
<v-btn
color="primary"
@click="save"
:loading="loading"
:disabled="!valid"
>
{{ isEdit ? 'ویرایش' : 'ذخیره' }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
const props = defineProps<{
modelValue: boolean
serial?: any
commodities: any[]
}>()
const emit = defineEmits<{
'update:modelValue': [value: boolean]
save: [data: any]
close: []
}>()
const loading = ref(false)
const valid = ref(false)
const form = ref()
const formData = ref({
serialNumber: '',
commodity_id: '',
description: '',
warrantyStartDate: '',
warrantyEndDate: '',
status: 'active',
notes: ''
})
const rules = {
required: (value: any) => !!value || 'این فیلد الزامی است',
serialNumber: (value: any) => {
if (!value) return 'شماره سریال الزامی است'
if (!/^[A-Za-z0-9]+$/.test(value)) {
return 'شماره سریال فقط می‌تواند شامل حروف انگلیسی و اعداد باشد'
}
if (value.length < 3) {
return 'شماره سریال باید حداقل ۳ کاراکتر باشد'
}
if (value.length > 50) {
return 'شماره سریال نمی‌تواند بیشتر از ۵۰ کاراکتر باشد'
}
return true
},
commodity: (value: any) => {
if (!value) return 'انتخاب محصول الزامی است'
return true
},
date: (value: any) => {
if (!value) return true
const date = new Date(value)
if (isNaN(date.getTime())) {
return 'تاریخ نامعتبر است'
}
return true
},
endDate: (value: any, startDate: any) => {
if (!value || !startDate) return true
const end = new Date(value)
const start = new Date(startDate)
if (end <= start) {
return 'تاریخ پایان باید بعد از تاریخ شروع باشد'
}
return true
}
}
const statusOptions = [
{ title: 'فعال', value: 'active' },
{ title: 'غیرفعال', value: 'inactive' },
{ title: 'منقضی شده', value: 'expired' },
// { title: 'استفاده شده', value: 'used' }
]
const dialog = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
})
const isEdit = computed(() => !!props.serial)
const save = async () => {
const isValid = await form.value.validate()
if (!isValid) {
console.log('فرم دارای خطا است')
return
}
if (!formData.value.serialNumber || !formData.value.commodity_id) {
console.log('فیلدهای الزامی پر نشده‌اند')
return
}
try {
loading.value = true
const data = { ...formData.value }
emit('save', data)
} catch (error) {
console.error(error)
} finally {
loading.value = false
}
}
const close = () => {
clearValidationErrors()
emit('close')
}
const resetForm = () => {
formData.value = {
serialNumber: '',
commodity_id: '',
description: '',
warrantyStartDate: '',
warrantyEndDate: '',
status: 'active',
notes: ''
}
if (form.value) {
form.value.resetValidation()
}
}
const clearValidationErrors = () => {
if (form.value) {
form.value.resetValidation()
}
}
const loadSerialData = () => {
if (props.serial) {
formData.value = {
serialNumber: props.serial.serialNumber || '',
commodity_id: props.serial.commodity?.id || '',
description: props.serial.description || '',
warrantyStartDate: props.serial.warrantyStartDate || '',
warrantyEndDate: props.serial.warrantyEndDate || '',
status: props.serial.status || 'active',
notes: props.serial.notes || ''
}
} else {
resetForm()
}
}
const customFilter = (item: any, queryText: string) => {
const text = item.name.toLowerCase()
const searchText = queryText.toLowerCase()
return text.indexOf(searchText) > -1
}
const handleCommoditySelect = (selectedCommodity: any) => {
if (selectedCommodity && selectedCommodity.id) {
formData.value.commodity_id = selectedCommodity.id
} else {
formData.value.commodity_id = ''
}
}
watch(() => props.serial, () => {
nextTick(() => {
loadSerialData()
})
}, { immediate: true })
watch(() => props.modelValue, (newVal) => {
if (newVal) {
nextTick(() => {
loadSerialData()
})
}
})
</script>
<style scoped>
.v-dialog {
direction: rtl;
}
</style>