no message

This commit is contained in:
yinjun.chen1
2026-08-21 11:19:36 +08:00
parent 3bdfc5ecf0
commit 0f66bd1878
76 changed files with 10176 additions and 0 deletions
@@ -0,0 +1,74 @@
<template>
<div class="page-container">
<el-card v-loading="loading">
<template #header>
<div class="header-row">
<span>车主详情</span>
<div>
<el-button type="warning" @click="handleEdit">编辑</el-button>
<el-button @click="handleBack">返回</el-button>
</div>
</div>
</template>
<el-descriptions :column="2" border v-if="detail">
<el-descriptions-item label="ID">{{ detail.id }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{ detail.name }}</el-descriptions-item>
<el-descriptions-item label="身份证号">{{ detail.id_number }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ detail.phone }}</el-descriptions-item>
<el-descriptions-item label="联系地址" :span="2">{{ detail.address || '-' }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ formatDate(detail.created_at) }}</el-descriptions-item>
<el-descriptions-item label="更新时间">{{ formatDate(detail.updated_at) }}</el-descriptions-item>
</el-descriptions>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { getCustomer } from '../../api/customer'
import type { Customer } from '../../types'
const router = useRouter()
const route = useRoute()
const loading = ref(false)
const detail = ref<Customer | null>(null)
function formatDate(dateStr: string) {
if (!dateStr) return ''
const d = new Date(dateStr)
return d.toLocaleString('zh-CN', { hour12: false })
}
async function loadDetail() {
loading.value = true
try {
const res = await getCustomer(Number(route.params.id))
detail.value = res.data
} catch (e: any) {
ElMessage.error(e.message || '获取车主信息失败')
} finally {
loading.value = false
}
}
function handleEdit() {
router.push(`/customers/${route.params.id}/edit`)
}
function handleBack() {
router.push('/customers')
}
onMounted(loadDetail)
</script>
<style scoped>
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>
@@ -0,0 +1,130 @@
<template>
<div class="page-container">
<el-card>
<template #header>
<span>{{ isEdit ? '编辑车主' : '新增车主' }}</span>
</template>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
style="max-width: 600px"
>
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入车主姓名" />
</el-form-item>
<el-form-item label="身份证号" prop="id_number">
<el-input v-model="form.id_number" placeholder="请输入18位身份证号" />
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入11位手机号" />
</el-form-item>
<el-form-item label="联系地址" prop="address">
<el-input v-model="form.address" type="textarea" :rows="3" placeholder="请输入联系地址" />
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitting" @click="handleSubmit">
{{ isEdit ? '保存修改' : '新增' }}
</el-button>
<el-button @click="handleBack">返回</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { getCustomer, createCustomer, updateCustomer } from '../../api/customer'
const router = useRouter()
const route = useRoute()
const formRef = ref<FormInstance>()
const isEdit = computed(() => !!route.params.id)
const form = reactive({
name: '',
id_number: '',
phone: '',
address: '',
})
const rules: FormRules = {
name: [{ required: true, message: '请输入车主姓名', trigger: 'blur' }],
id_number: [
{ required: true, message: '请输入身份证号', trigger: 'blur' },
{
pattern: /^\d{17}[\dXx]$/,
message: '身份证号格式不正确',
trigger: 'blur',
},
],
phone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
{
pattern: /^1[3-9]\d{9}$/,
message: '手机号格式不正确',
trigger: 'blur',
},
],
}
const submitting = ref(false)
async function loadDetail() {
const id = Number(route.params.id)
try {
const res = await getCustomer(id)
Object.assign(form, {
name: res.data.name,
id_number: res.data.id_number,
phone: res.data.phone,
address: res.data.address || '',
})
} catch (e: any) {
ElMessage.error(e.message || '获取车主信息失败')
}
}
async function handleSubmit() {
if (!formRef.value) return
const valid = await formRef.value.validate().catch(() => false)
if (!valid) return
submitting.value = true
try {
if (isEdit.value) {
await updateCustomer(Number(route.params.id), { ...form })
ElMessage.success('保存成功')
} else {
await createCustomer({ ...form })
ElMessage.success('新增成功')
}
router.push('/customers')
} catch (e: any) {
ElMessage.error(e.message || '操作失败')
} finally {
submitting.value = false
}
}
function handleBack() {
router.push('/customers')
}
onMounted(() => {
if (isEdit.value) {
loadDetail()
}
})
</script>
@@ -0,0 +1,152 @@
<template>
<div class="page-container">
<el-card>
<div class="toolbar">
<el-input
v-model="keyword"
placeholder="搜索姓名/身份证号/手机号"
clearable
style="width: 300px"
@keyup.enter="handleSearch"
@clear="handleSearch"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">
<el-icon><Search /></el-icon>&nbsp;查询
</el-button>
<el-button type="success" @click="handleCreate">
<el-icon><Plus /></el-icon>&nbsp;新增车主
</el-button>
</div>
<el-table :data="tableData" v-loading="loading" stripe border>
<el-table-column prop="id" label="ID" width="70" align="center" />
<el-table-column prop="name" label="姓名" width="120" />
<el-table-column prop="id_number" label="身份证号" width="200" />
<el-table-column prop="phone" label="联系电话" width="140" />
<el-table-column prop="address" label="联系地址" show-overflow-tooltip />
<el-table-column prop="created_at" label="创建时间" width="180">
<template #default="{ row }">
{{ formatDate(row.created_at) }}
</template>
</el-table-column>
<el-table-column label="操作" width="220" align="center">
<template #default="{ row }">
<el-button type="primary" link @click="handleDetail(row)">详情</el-button>
<el-button type="warning" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
class="pagination"
v-model:current-page="page"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="fetchData"
@size-change="handleSizeChange"
/>
</el-card>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getCustomers, deleteCustomer } from '../../api/customer'
import type { Customer } from '../../types'
const router = useRouter()
const loading = ref(false)
const tableData = ref<Customer[]>([])
const keyword = ref('')
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
function formatDate(dateStr: string) {
if (!dateStr) return ''
const d = new Date(dateStr)
return d.toLocaleString('zh-CN', { hour12: false })
}
async function fetchData() {
loading.value = true
try {
const res = await getCustomers({
keyword: keyword.value,
page: page.value,
pageSize: pageSize.value,
})
tableData.value = res.data.list
total.value = res.data.total
} catch (e: any) {
ElMessage.error(e.message || '获取车主列表失败')
} finally {
loading.value = false
}
}
function handleSearch() {
page.value = 1
fetchData()
}
function handleSizeChange() {
page.value = 1
fetchData()
}
function handleCreate() {
router.push('/customers/create')
}
function handleDetail(row: Customer) {
router.push(`/customers/${row.id}`)
}
function handleEdit(row: Customer) {
router.push(`/customers/${row.id}/edit`)
}
async function handleDelete(row: Customer) {
try {
await ElMessageBox.confirm(`确定删除车主"${row.name}"吗?`, '删除确认', {
type: 'warning',
confirmButtonText: '确定',
cancelButtonText: '取消',
})
await deleteCustomer(row.id)
ElMessage.success('删除成功')
fetchData()
} catch (e: any) {
if (e !== 'cancel' && e !== 'close') {
ElMessage.error(e.message || '删除失败')
}
}
}
onMounted(fetchData)
</script>
<style scoped>
.toolbar {
display: flex;
gap: 12px;
margin-bottom: 16px;
align-items: center;
}
.pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
</style>