Revert "feat: 客户管理新增个人用户类型,双Tab分离管理"

This reverts commit 03198557f7.
This commit is contained in:
2026-07-12 21:45:23 +08:00
parent 03198557f7
commit 835e5d38fd
5 changed files with 8 additions and 39 deletions
+1 -4
View File
@@ -50,7 +50,6 @@ async def list_customers(
industry: Optional[str] = Query(None), industry: Optional[str] = Query(None),
service: Optional[str] = Query(None), service: Optional[str] = Query(None),
manager_id: Optional[str] = Query(None), manager_id: Optional[str] = Query(None),
customer_type: Optional[str] = Query(None),
page: int = Query(1, ge=1), page: int = Query(1, ge=1),
page_size: int = Query(100, ge=1, le=1000), page_size: int = Query(100, ge=1, le=1000),
current_user: dict = Depends(get_current_user), current_user: dict = Depends(get_current_user),
@@ -61,8 +60,6 @@ async def list_customers(
base_query = select(Customer) base_query = select(Customer)
if customer_type:
base_query = base_query.where(Customer.customer_type == customer_type)
if industry: if industry:
base_query = base_query.where(Customer.industry.ilike(f"%{industry}%")) base_query = base_query.where(Customer.industry.ilike(f"%{industry}%"))
if service: if service:
@@ -92,7 +89,7 @@ async def list_customers(
# Paginate # Paginate
offset = (page - 1) * page_size offset = (page - 1) * page_size
query = base_query.order_by(Customer.customer_type, Customer.name).offset(offset).limit(page_size) query = base_query.order_by(Customer.name).offset(offset).limit(page_size)
result = await db.execute(query) result = await db.execute(query)
items = result.scalars().all() items = result.scalars().all()
-4
View File
@@ -42,10 +42,6 @@ async def lifespan(app: FastAPI):
await conn.run_sync(lambda c: c.exec_driver_sql( await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE customers ADD COLUMN IF NOT EXISTS last_visit_manager_id UUID" "ALTER TABLE customers ADD COLUMN IF NOT EXISTS last_visit_manager_id UUID"
)) ))
# v0.9 — customer_type for unit vs individual
await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE customers ADD COLUMN IF NOT EXISTS customer_type VARCHAR(20) DEFAULT 'unit'"
))
# New columns for v0.4 # New columns for v0.4
await conn.run_sync(lambda c: c.exec_driver_sql( await conn.run_sync(lambda c: c.exec_driver_sql(
"ALTER TABLE visits ADD COLUMN IF NOT EXISTS companion_names TEXT[] DEFAULT '{}'" "ALTER TABLE visits ADD COLUMN IF NOT EXISTS companion_names TEXT[] DEFAULT '{}'"
-1
View File
@@ -11,7 +11,6 @@ class Customer(Base):
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name: Mapped[str] = mapped_column(String(200), index=True) name: Mapped[str] = mapped_column(String(200), index=True)
customer_type: Mapped[str] = mapped_column(String(20), default="unit") # unit / individual
industry: Mapped[str] = mapped_column(String(100), default="") industry: Mapped[str] = mapped_column(String(100), default="")
address: Mapped[str] = mapped_column(String(500), default="") address: Mapped[str] = mapped_column(String(500), default="")
in_use_services: Mapped[str] = mapped_column(Text, default="") in_use_services: Mapped[str] = mapped_column(Text, default="")
-4
View File
@@ -45,7 +45,6 @@ class ContactOut(BaseModel):
# ── Customer ── # ── Customer ──
class CustomerCreate(BaseModel): class CustomerCreate(BaseModel):
name: str name: str
customer_type: str = "unit"
industry: str = "" industry: str = ""
address: str = "" address: str = ""
in_use_services: str = "" in_use_services: str = ""
@@ -57,7 +56,6 @@ class CustomerCreate(BaseModel):
class CustomerUpdate(BaseModel): class CustomerUpdate(BaseModel):
name: Optional[str] = None name: Optional[str] = None
customer_type: Optional[str] = None
industry: Optional[str] = None industry: Optional[str] = None
address: Optional[str] = None address: Optional[str] = None
in_use_services: Optional[str] = None in_use_services: Optional[str] = None
@@ -69,7 +67,6 @@ class CustomerUpdate(BaseModel):
class CustomerOut(BaseModel): class CustomerOut(BaseModel):
id: uuid.UUID id: uuid.UUID
name: str name: str
customer_type: str = "unit"
industry: str industry: str
address: str address: str
in_use_services: str in_use_services: str
@@ -87,7 +84,6 @@ class CustomerOut(BaseModel):
class CustomerListOut(BaseModel): class CustomerListOut(BaseModel):
id: uuid.UUID id: uuid.UUID
name: str name: str
customer_type: str = "unit"
industry: str industry: str
in_use_services: str in_use_services: str
primary_manager_name: Optional[str] = None primary_manager_name: Optional[str] = None
+7 -26
View File
@@ -15,7 +15,6 @@ const search = ref('')
const filterIndustry = ref('') const filterIndustry = ref('')
const filterService = ref('') const filterService = ref('')
const filterManagerId = ref('') const filterManagerId = ref('')
const customerType = ref('unit') // 'unit' or 'individual'
const customers = ref<any[]>([]) const customers = ref<any[]>([])
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(25) const pageSize = ref(25)
@@ -24,7 +23,7 @@ const dialogVisible = ref(false)
const dialogTitle = ref('新建客户') const dialogTitle = ref('新建客户')
const editId = ref('') const editId = ref('')
const form = ref({ const form = ref({
name: '', customer_type: 'unit', industry: '', address: '', in_use_services: '', name: '', industry: '', address: '', in_use_services: '',
fee_amount: '', fee_unit: '元/月', fee_amount: '', fee_unit: '元/月',
remarks: '', remarks: '',
contacts: [] as any[], contacts: [] as any[],
@@ -69,7 +68,6 @@ async function loadCustomers() {
if (filterIndustry.value) params.industry = filterIndustry.value if (filterIndustry.value) params.industry = filterIndustry.value
if (filterService.value) params.service = filterService.value if (filterService.value) params.service = filterService.value
if (filterManagerId.value) params.manager_id = filterManagerId.value if (filterManagerId.value) params.manager_id = filterManagerId.value
if (customerType.value) params.customer_type = customerType.value
const res = await customersApi.list(params) const res = await customersApi.list(params)
customers.value = res.data.items customers.value = res.data.items
total.value = res.data.total total.value = res.data.total
@@ -103,7 +101,7 @@ function parseMonthlyFee(fee: string) {
} }
function resetForm() { function resetForm() {
form.value = { name: '', customer_type: customerType.value, industry: '', address: '', in_use_services: '', fee_amount: '', fee_unit: '元/月', remarks: '', contacts: [], assignee_id: '' } form.value = { name: '', industry: '', address: '', in_use_services: '', fee_amount: '', fee_unit: '元/月', remarks: '', contacts: [], assignee_id: '' }
feeCustomUnit.value = '' feeCustomUnit.value = ''
existingContacts.value = [] existingContacts.value = []
} }
@@ -130,7 +128,7 @@ async function openEdit(customer: any) {
const res = await customersApi.get(customer.id) const res = await customersApi.get(customer.id)
const c = res.data const c = res.data
resetForm() resetForm()
form.value.name = c.name; form.value.customer_type = c.customer_type || 'unit'; form.value.industry = c.industry; form.value.address = c.address form.value.name = c.name; form.value.industry = c.industry; form.value.address = c.address
form.value.in_use_services = c.in_use_services; form.value.remarks = c.remarks || '' form.value.in_use_services = c.in_use_services; form.value.remarks = c.remarks || ''
parseMonthlyFee(c.monthly_fee) parseMonthlyFee(c.monthly_fee)
existingContacts.value = c.contacts || [] existingContacts.value = c.contacts || []
@@ -143,7 +141,7 @@ async function handleSubmit() {
const monthly_fee = buildMonthlyFee() const monthly_fee = buildMonthlyFee()
try { try {
if (editId.value) { if (editId.value) {
const body: any = { name: form.value.name, customer_type: form.value.customer_type, industry: form.value.industry, address: form.value.address, in_use_services: form.value.in_use_services, monthly_fee, remarks: form.value.remarks } const body: any = { name: form.value.name, industry: form.value.industry, address: form.value.address, in_use_services: form.value.in_use_services, monthly_fee, remarks: form.value.remarks }
if (form.value.assignee_id) body.assignee_id = form.value.assignee_id if (form.value.assignee_id) body.assignee_id = form.value.assignee_id
await customersApi.update(editId.value, body) await customersApi.update(editId.value, body)
for (const c of form.value.contacts) { for (const c of form.value.contacts) {
@@ -151,7 +149,7 @@ async function handleSubmit() {
} }
ElMessage.success('已更新') ElMessage.success('已更新')
} else { } else {
await customersApi.create({ name: form.value.name, customer_type: form.value.customer_type, industry: form.value.industry, address: form.value.address, in_use_services: form.value.in_use_services, monthly_fee, remarks: form.value.remarks, contacts: form.value.contacts, assignee_id: form.value.assignee_id || undefined }) await customersApi.create({ name: form.value.name, industry: form.value.industry, address: form.value.address, in_use_services: form.value.in_use_services, monthly_fee, remarks: form.value.remarks, contacts: form.value.contacts, assignee_id: form.value.assignee_id || undefined })
ElMessage.success('已创建') ElMessage.success('已创建')
} }
dialogVisible.value = false dialogVisible.value = false
@@ -331,14 +329,6 @@ async function handleImport() {
<div class="page-rule"></div> <div class="page-rule"></div>
</div> </div>
<!-- Customer Type Tabs -->
<div style="margin-bottom:14px">
<el-radio-group v-model="customerType" @change="(v: string) => { customerType = v; onFilterChange() }">
<el-radio-button value="unit">单位客户</el-radio-button>
<el-radio-button value="individual">个人用户</el-radio-button>
</el-radio-group>
</div>
<!-- Search & Filter --> <!-- Search & Filter -->
<el-card style="margin-bottom: 16px;"> <el-card style="margin-bottom: 16px;">
<el-row :gutter="12" align="middle"> <el-row :gutter="12" align="middle">
@@ -373,12 +363,7 @@ async function handleImport() {
<el-table :data="customers" stripe v-column-resize @selection-change="onSelectionChange"> <el-table :data="customers" stripe v-column-resize @selection-change="onSelectionChange">
<el-table-column v-if="auth.isDirector" type="selection" width="50" /> <el-table-column v-if="auth.isDirector" type="selection" width="50" />
<el-table-column type="index" label="序号" width="60" :index="(idx: number) => (currentPage - 1) * pageSize + idx + 1" /> <el-table-column type="index" label="序号" width="60" :index="(idx: number) => (currentPage - 1) * pageSize + idx + 1" />
<el-table-column label="类型" width="70"> <el-table-column prop="name" label="单位名称" min-width="180">
<template #default="{ row }">
<el-tag size="small" :type="row.customer_type === 'individual' ? 'info' : ''" effect="plain">{{ row.customer_type === 'individual' ? '个人' : '单位' }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="name" :label="customerType === 'individual' ? '姓名' : '单位名称'" min-width="180">
<template #default="{ row }"> <template #default="{ row }">
<el-link type="primary" @click="openDetail(row)">{{ row.name }}</el-link> <el-link type="primary" @click="openDetail(row)">{{ row.name }}</el-link>
</template> </template>
@@ -404,8 +389,7 @@ async function handleImport() {
<!-- Create/Edit Dialog --> <!-- Create/Edit Dialog -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="520px"> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="520px">
<el-form label-position="top"> <el-form label-position="top">
<el-form-item :label="form.customer_type === 'individual' ? '姓名' : '单位名称'" required><el-input v-model="form.name" :placeholder="form.customer_type === 'individual' ? '请输入姓名' : '请输入单位名称'" @blur="checkName" /><el-alert v-if="duplicateHint" :title="duplicateHint" type="warning" :closable="false" style="margin-top:6px" /></el-form-item> <el-form-item label="单位名称" required><el-input v-model="form.name" placeholder="请输入单位名称" @blur="checkName" /><el-alert v-if="duplicateHint" :title="duplicateHint" type="warning" :closable="false" style="margin-top:6px" /></el-form-item>
<template v-if="form.customer_type === 'unit'">
<el-form-item label="所属行业"><el-input v-model="form.industry" placeholder="如: 教育、医疗、政府..." /></el-form-item> <el-form-item label="所属行业"><el-input v-model="form.industry" placeholder="如: 教育、医疗、政府..." /></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.address" /></el-form-item>
<el-form-item label="在用业务"><el-input v-model="form.in_use_services" placeholder="如: 云桌面、专线、视频会议" /></el-form-item> <el-form-item label="在用业务"><el-input v-model="form.in_use_services" placeholder="如: 云桌面、专线、视频会议" /></el-form-item>
@@ -420,9 +404,7 @@ async function handleImport() {
<el-col v-if="form.fee_unit === '自定义'" :span="8"><el-input v-model="feeCustomUnit" placeholder="单位" /></el-col> <el-col v-if="form.fee_unit === '自定义'" :span="8"><el-input v-model="feeCustomUnit" placeholder="单位" /></el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
</template>
<el-form-item label="备注"><el-input v-model="form.remarks" type="textarea" :rows="2" placeholder="备注信息..." /></el-form-item> <el-form-item label="备注"><el-input v-model="form.remarks" type="textarea" :rows="2" placeholder="备注信息..." /></el-form-item>
<template v-if="form.customer_type === 'unit'">
<el-form-item label="归属客户经理" v-if="auth.isDirector"> <el-form-item label="归属客户经理" v-if="auth.isDirector">
<el-select v-model="form.assignee_id" :placeholder="editId ? '留空则不修改' : '不选则默认分配给自己'" clearable style="width:100%"> <el-select v-model="form.assignee_id" :placeholder="editId ? '留空则不修改' : '不选则默认分配给自己'" clearable style="width:100%">
<el-option v-for="m in managers" :key="m.id" :label="m.name" :value="m.id" /> <el-option v-for="m in managers" :key="m.id" :label="m.name" :value="m.id" />
@@ -442,7 +424,6 @@ async function handleImport() {
</div> </div>
<el-button size="small" @click="form.contacts.push({name:'',phone:'',role_desc:''})">+ 添加联系人</el-button> <el-button size="small" @click="form.contacts.push({name:'',phone:'',role_desc:''})">+ 添加联系人</el-button>
</el-form-item> </el-form-item>
</template>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="dialogVisible = false">取消</el-button> <el-button @click="dialogVisible = false">取消</el-button>