- 布局: 面包屑/头像下拉/公告气泡(点击外部关闭)/菜单滚动条/路由切换动效 - 工作台: el-statistic 彩色统计卡/el-skeleton 骨架屏/公告轮播/快捷入口 - 订单: 日期范围筛选/列排序/详情抽屉 el-descriptions+el-tabs+el-timeline 日志 - 样板库: el-tree-select 分类树/el-image 大图预览/el-badge 引用徽标 - 客户表单校验规则/优化下料 dashboard 利用率进度环 - 全接口回归 13/13 通过, SPA 路由验证通过
98 lines
3.8 KiB
Vue
98 lines
3.8 KiB
Vue
<template>
|
|
<el-card>
|
|
<div class="toolbar">
|
|
<el-input v-model="keyword" placeholder="姓名/电话/地址" clearable style="width:240px" @keyup.enter="load" />
|
|
<el-button type="primary" @click="load">查询</el-button>
|
|
<div style="flex:1" />
|
|
<el-button type="primary" plain @click="openEdit()">新增客户</el-button>
|
|
</div>
|
|
<el-table :data="rows" v-loading="loading" stripe>
|
|
<el-table-column prop="id" label="ID" width="70" />
|
|
<el-table-column prop="name" label="姓名" min-width="120" />
|
|
<el-table-column prop="phone" label="电话" width="140" />
|
|
<el-table-column prop="address" label="地址" min-width="180" />
|
|
<el-table-column prop="salesmen" label="业务员" width="100" />
|
|
<el-table-column prop="created_at" label="创建时间" width="170" />
|
|
<el-table-column label="操作" width="140" fixed="right">
|
|
<template #default="{ row }">
|
|
<el-button size="small" @click="openEdit(row)">编辑</el-button>
|
|
<el-button size="small" type="danger" plain @click="remove(row)">删</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<el-pagination style="margin-top:12px; justify-content:flex-end" background layout="total, prev, pager, next"
|
|
:total="total" :page-size="page_size" v-model:current-page="page" @current-change="load" />
|
|
|
|
<el-dialog v-model="editVisible" :title="form.id ? '编辑客户' : '新增客户'" width="480px">
|
|
<el-form :model="form" :rules="rules" ref="formRef" label-width="70px">
|
|
<el-form-item label="姓名" prop="name"><el-input v-model="form.name" /></el-form-item>
|
|
<el-form-item label="电话" prop="phone">
|
|
<el-input v-model="form.phone" maxlength="20" />
|
|
</el-form-item>
|
|
<el-form-item label="地址"><el-input v-model="form.address" /></el-form-item>
|
|
<el-form-item label="备注"><el-input v-model="form.note" type="textarea" /></el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="editVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="save">保存</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</el-card>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, reactive, ref } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { api } from '../api'
|
|
|
|
const rows = ref<any[]>([])
|
|
const total = ref(0)
|
|
const page = ref(1)
|
|
const page_size = 15
|
|
const keyword = ref('')
|
|
const loading = ref(false)
|
|
const editVisible = ref(false)
|
|
const formRef = ref()
|
|
const form = reactive({ id: 0, name: '', phone: '', address: '', note: '' })
|
|
const rules = {
|
|
name: [{ required: true, message: '请输入客户姓名', trigger: 'blur' }],
|
|
phone: [{ pattern: /^[0-9+\-\s]{5,20}$/, message: '电话格式不正确', trigger: 'blur' }],
|
|
}
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
try {
|
|
const d = await api<any>('get', `/api/customers?page=${page.value}&page_size=${page_size}&keyword=${keyword.value}`)
|
|
rows.value = d.data
|
|
total.value = d.total
|
|
} finally { loading.value = false }
|
|
}
|
|
|
|
function openEdit(row?: any) {
|
|
Object.assign(form, { id: row?.id ?? 0, name: row?.name ?? '', phone: row?.phone ?? '', address: row?.address ?? '', note: row?.note ?? '' })
|
|
editVisible.value = true
|
|
}
|
|
|
|
async function save() {
|
|
await formRef.value.validate()
|
|
if (form.id) await api('put', `/api/customers/${form.id}`, form)
|
|
else await api('post', '/api/customers', form)
|
|
ElMessage.success('已保存')
|
|
editVisible.value = false
|
|
load()
|
|
}
|
|
|
|
async function remove(row: any) {
|
|
await ElMessageBox.confirm(`确定删除客户「${row.name}」?`, '提示', { type: 'warning' })
|
|
await api('delete', `/api/customers/${row.id}`)
|
|
ElMessage.success('已删除')
|
|
load()
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.toolbar { display: flex; gap: 10px; margin-bottom: 12px; }
|
|
</style>
|