重构整个项目

This commit is contained in:
2026-05-19 16:36:35 +08:00
parent f6555fdd15
commit 93bd773c51
12 changed files with 599 additions and 321 deletions

View File

@@ -57,24 +57,39 @@ export const ticketApi = {
}
export const customerApi = {
getCustomers() {
return adminRequest.get('/customers')
},
// 添加客户,data 结构应为 { company_name: 'xxx' }
addCustomerName(data) {
return adminRequest.post('/addcustomername', data)
addCompany(data) {
return adminRequest.post('/addCompany', data)
},
addEmployee(data) {
return adminRequest.post('/addemployee', data)
addOperator(data) {
return adminRequest.post('/addOperator', data)
},
deleteEmployee(empId) {
return adminRequest.delete(`/deleteemployee/${empId}`)
deleteOperator(operatorId) { // 已修改
return adminRequest.delete(`/deleteoperator/${operatorId}`)
},
deleteCustomer(customerId) {
return adminRequest.delete(`/deletecustomer/${customerId}`)
deleteCompany(CompanyId) { // 已修改
return adminRequest.delete(`/deletecompany/${CompanyId}`)
}
}
export const accountApi = {
getAllUsers() {
return adminRequest.get('/getAllUsers')
},
addAccount(data) {
return adminRequest.post('/addAccount', data)
},
updateAccountStatus(id, status) {
return adminRequest.put(`/account/${id}/status`, { status })
},
deleteAccount(id) {
return adminRequest.delete(`/account/${id}`)
}
}

View File

@@ -1,60 +0,0 @@
<template>
<el-dialog :model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)" title="新增客户员工"
width="450px">
<el-form :model="form" label-width="100px">
<el-form-item label="员工姓名" required>
<el-input v-model="form.employee_name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="所属客户" required>
<el-select v-model="form.customer_id" placeholder="请选择归属单位" style="width: 100%">
<el-option v-for="item in customerList" :key="item.customer_id" :label="item.company_name"
:value="item.customer_id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="$emit('update:modelValue', false)">取消</el-button>
<el-button type="success" @click="handleSubmit">保存员工</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { customerApi } from "@/api/ticket";
const props = defineProps({
modelValue: Boolean,
customerList: Array,
});
const emit = defineEmits(["update:modelValue", "success"]);
const form = ref({ employee_name: "", customer_id: null });
watch(
() => props.modelValue,
(val) => {
if (val) form.value = { employee_name: "", customer_id: null };
}
);
const handleSubmit = async () => {
if (!form.value.employee_name || !form.value.customer_id) {
return ElMessage.error("请填写员工姓名并选择所属单位");
}
try {
const res = await customerApi.addEmployee({
emp_name: form.value.employee_name,
customer_id: form.value.customer_id,
});
if (res.code === 200) {
ElMessage.success("员工保存成功");
emit("success");
emit("update:modelValue", false);
}
} catch (error) {
ElMessage.error("保存失败");
}
};
</script>

View File

@@ -1,52 +0,0 @@
<template>
<el-dialog
:model-value="modelValue"
@update:model-value="$emit('update:modelValue', $event)"
:title="`【${customerName}】员工名单`"
width="500px"
>
<el-table :data="employees" border stripe>
<el-table-column prop="employee_id" label="员工ID" width="100" />
<el-table-column prop="employee_name" label="姓名" />
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button
size="small"
type="danger"
link
@click="handleDelete(scope.row.employee_id)"
>移除</el-button
>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script setup>
import { ElMessage, ElMessageBox } from "element-plus";
import { customerApi } from "@/api/ticket";
const props = defineProps({
modelValue: Boolean,
customerName: String,
employees: Array,
});
const emit = defineEmits(["update:modelValue", "refresh"]);
const handleDelete = (empId) => {
ElMessageBox.confirm("确定要从该单位移除此员工吗?", "提示", { type: "warning" })
.then(async () => {
try {
const res = await customerApi.deleteEmployee(empId);
if (res.code == 200 || res.code == true) {
ElMessage.success("操作成功");
emit("refresh"); // 通知父组件重新获取数据
}
} catch (e) {
ElMessage.error("删除失败");
}
})
.catch(() => {});
};
</script>

View File

@@ -33,16 +33,18 @@ watch(
const handleSubmit = async () => {
if (!form.value.company_name) return ElMessage.error("名称不能为空");
try {
const res = await customerApi.addCustomerName({
const res = await customerApi.addCompany({
company_name: form.value.company_name,
});
if (res.code === 200) {
ElMessage.success("客户单位创建成功");
emit("success");
emit("update:modelValue", false);
} else {
ElMessage.error(res.msg || "创建失败");
}
} catch (error) {
ElMessage.error("创建失败");
ElMessage.error(res.msg || "创建失败");
}
};
</script>

View File

@@ -0,0 +1,63 @@
<template>
<el-dialog :model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)" title="新增运营人员"
width="450px">
<el-form :model="form" label-width="100px">
<el-form-item label="客户单位" required>
<el-select v-model="form.company_id" placeholder="请选择归属客户" style="width: 100%">
<el-option v-for="item in companyList" :key="item.company_id" :label="item.company_name"
:value="item.company_id" />
</el-select>
</el-form-item>
<el-form-item label="运营人员" required>
<el-input v-model="form.operator_name" placeholder="请输入运营人员姓名" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="$emit('update:modelValue', false)">取消</el-button>
<el-button type="success" @click="handleSubmit">保存员工</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from "vue";
import { ElMessage } from "element-plus";
import { customerApi } from "@/api/ticket";
const props = defineProps({
modelValue: Boolean,
companyList: Array,
});
const emit = defineEmits(["update:modelValue", "success"]);
const form = ref({ operator_name: "", company_id: null });
watch(
() => props.modelValue,
(val) => {
if (val) form.value = { operator_name: "", company_id: null };
}
);
const handleSubmit = async () => {
if (!form.value.operator_name || !form.value.company_id) {
return ElMessage.error("请填写运营人员姓名并选择所属单位");
}
try {
const res = await customerApi.addOperator({
operator_name: form.value.operator_name,
company_id: form.value.company_id,
status: 1
});
if (res.code === 200) {
ElMessage.success("运营人员保存成功");
emit("success");
emit("update:modelValue", false);
}else{
ElMessage.error(res.msg || "保存失败");
}
} catch (error) {
ElMessage.error("保存失败");
}
};
</script>

View File

@@ -0,0 +1,55 @@
<template>
<el-dialog :model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)"
:title="`【${companyName}】运营名单`" width="500px">
<el-table :data="localOperators" border stripe>
<!-- <el-table-column prop="id" label="员工ID" width="100" /> -->
<el-table-column prop="operator_name" label="姓名" />
<el-table-column label="操作" width="100" align="center">
<template #default="scope">
<el-button size="small" type="danger" link @click="handleDelete(scope.row.id)">移除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from "element-plus";
import { customerApi } from "@/api/ticket";
// 本地数据副本,用于立即更新UI
const localOperators = ref([]);
const props = defineProps({
modelValue: Boolean,
companyName: String,
operators: Array,
});
const emit = defineEmits(["update:modelValue", "refresh"]);
// 监听 props.operators 的变化,同步到本地
watch(() => props.operators, (newVal) => {
localOperators.value = [...(newVal || [])];
}, { immediate: true, deep: true });
const handleDelete = (operatorId) => {
ElMessageBox.confirm("确定要从该单位移除此员工吗?", "提示", { type: "warning" })
.then(async () => {
try {
const res = await customerApi.deleteOperator(operatorId);
if (res.code == 200 || res.code == true) {
ElMessage.success("操作成功");
localOperators.value = localOperators.value.filter(item => item.id !== operatorId);
emit("refresh"); // 通知父组件重新获取数据
}
} catch (e) {
ElMessage.error("删除失败");
}
})
.catch(() => { });
};
</script>

View File

@@ -4,12 +4,7 @@
<el-aside width="200px" class="siderbar">
<div class="sidebar-header">
<!-- 修复:将 logo 区域包装成一个正确的外部链接 -->
<a
href="https://www.example.com"
target="_blank"
rel="noopener noreferrer"
class="logo-link"
>
<a href="https://www.example.com" target="_blank" rel="noopener noreferrer" class="logo-link">
<div class="logo-container">
<img src="@/assets/logo.jpg" alt="方舟网络" class="logo-image" />
<div class="text-content">
@@ -22,7 +17,9 @@
<el-menu default-active="/" class="sidebar-menu" router>
<el-menu-item index="/">
<el-icon><icon-menu /></el-icon>
<el-icon>
<icon-menu />
</el-icon>
<span>工单管理</span>
</el-menu-item>
@@ -33,12 +30,20 @@
<span>数据统计</span>
</el-menu-item>
<el-menu-item index="/customers">
<el-menu-item index="/companyAoperators">
<el-icon>
<User />
</el-icon>
<span>客户管理</span>
</el-menu-item>
<el-menu-item index="/accountRegistration">
<el-icon>
<Plus />
</el-icon>
<span>账户注册</span>
</el-menu-item>
</el-menu>
</el-aside>
@@ -48,7 +53,7 @@
<div class="user-info">
<el-dropdown trigger="click">
<div class="user-profile">
<el-avatar :size="32" :src="user.avatar" />
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
<span class="username">{{ user.name }}</span>
</div>
<template #dropdown>

View File

@@ -34,10 +34,16 @@ export const dynamicRoutes = [
meta: { title: '数据统计' }
},
{
path: 'customers',
name: 'customers',
component: () => import('@/views/CustomersManagement.vue'),
path: 'companyAoperators',
name: 'company_and_operators',
component: () => import('@/views/CompanyAndOperatorsManagement.vue'),
meta: { title: '客户管理' }
},
{
path: 'accountRegistration',
name: 'account_registration',
component: () => import('@/views/AccountRegistrationView.vue'),
meta: { title: '账户注册' }
}
]
},

View File

@@ -0,0 +1,269 @@
<template>
<div class="account-container">
<h2>账户管理</h2>
<div class="action-bar">
<div class="search-group">
<el-input v-model="searchQuery" placeholder="搜索用户名/真实姓名" style="width: 250px" clearable />
</div>
<el-button type="primary" :icon="Plus" @click="openRegisterDialog">注册用户</el-button>
</div>
<el-table :data="filteredUsers" border style="width: 100%; margin-top: 20px" v-loading="loading">
<template #empty>
<el-empty :image-size="100" description="暂无用户数据" />
</template>
<!-- <el-table-column prop="id" label="ID" width="70" align="center" /> -->
<el-table-column prop="username" label="用户名" min-width="120" />
<el-table-column prop="real_name" label="真实姓名" min-width="120">
<template #default="{ row }">
{{ row.real_name || '-' }}
</template>
</el-table-column>
<el-table-column label="角色" width="120" align="center">
<template #default="{ row }">
<el-tag :type="roleTagType(row.role)" size="small" effect="plain">
{{ roleLabel(row.role) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small" effect="plain">
{{ row.status === 1 ? '正常' : '已禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" width="160" align="center">
<template #default="{ row }">
{{ formatDate(row.create_time) }}
</template>
</el-table-column>
<el-table-column label="操作" width="180" fixed="right" align="center">
<template #default="scope">
<el-button
size="small"
:type="scope.row.status === 1 ? 'warning' : 'success'"
plain
@click="handleToggleStatus(scope.row)"
>
{{ scope.row.status === 1 ? '禁用' : '启用' }}
</el-button>
<el-button size="small" type="danger" plain @click="handleDeleteAccount(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 注册弹窗 -->
<el-dialog
v-model="registerDialogVisible"
title="注册新用户"
width="450px"
destroy-on-close
@close="resetRegisterForm"
>
<el-form ref="registerFormRef" :model="registerForm" :rules="registerRules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input v-model="registerForm.username" placeholder="登录用" clearable />
</el-form-item>
<el-form-item label="真实姓名" prop="real_name">
<el-input v-model="registerForm.real_name" placeholder="选填" clearable />
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="registerForm.password" type="password" placeholder="请输入密码" show-password />
</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="registerForm.role" placeholder="请选择角色" style="width: 100%">
<el-option label="管理员" value="admin" />
<el-option label="员工" value="user" />
<el-option label="客户" value="client" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="registerDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handleRegister">确定注册</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { accountApi } from "@/api/ticket";
import { formatDate } from "@/utils/format";
const loading = ref(false);
const users = ref([]);
const searchQuery = ref("");
const registerDialogVisible = ref(false);
const registerFormRef = ref(null);
const submitting = ref(false);
const registerForm = reactive({
username: "",
real_name: "",
password: "",
role: "user",
});
const registerRules = {
username: [
{ required: true, message: "请输入用户名", trigger: "blur" },
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 6, message: "密码至少6位", trigger: "blur" },
],
role: [
{ required: true, message: "请选择角色", trigger: "change" },
],
};
const roleLabel = (role) => {
const map = { admin: "管理员", user: "员工", client: "客户" };
return map[role] || role;
};
const roleTagType = (role) => {
const map = { admin: "danger", user: "warning", client: "info" };
return map[role] || "info";
};
const rolePriority = { admin: 0, user: 1, client: 2 };
const filteredUsers = computed(() => {
let list = users.value;
if (searchQuery.value) {
const q = searchQuery.value.toLowerCase();
list = list.filter(
(u) =>
u.username.toLowerCase().includes(q) ||
(u.real_name && u.real_name.toLowerCase().includes(q))
);
}
return [...list].sort((a, b) => (rolePriority[a.role] ?? 99) - (rolePriority[b.role] ?? 99));
});
const fetchUsers = async () => {
loading.value = true;
try {
const res = await accountApi.getAllUsers();
if (res.code === 200) {
users.value = res.data || [];
} else {
ElMessage.error(res.msg || "获取用户列表失败");
}
} catch (error) {
ElMessage.error("获取用户数据失败");
} finally {
loading.value = false;
}
};
const openRegisterDialog = () => {
registerDialogVisible.value = true;
};
const resetRegisterForm = () => {
if (registerFormRef.value) registerFormRef.value.resetFields();
registerForm.username = "";
registerForm.real_name = "";
registerForm.password = "";
registerForm.role = "user";
};
const handleRegister = async () => {
if (!registerFormRef.value) return;
try {
await registerFormRef.value.validate();
submitting.value = true;
const res = await accountApi.addAccount({ ...registerForm });
if (res.code === 200) {
ElMessage.success("用户注册成功");
registerDialogVisible.value = false;
fetchUsers();
} else {
ElMessage.error(res.msg || "注册失败");
}
} catch (e) {
if (e?.name !== "ValidationError") {
ElMessage.error("注册请求异常");
}
} finally {
submitting.value = false;
}
};
const handleToggleStatus = (row) => {
const newStatus = row.status === 1 ? 0 : 1;
const action = newStatus === 0 ? "禁用" : "启用";
ElMessageBox.confirm(`确定${action}用户【${row.username}】吗?`, "提示", {
type: "warning",
})
.then(async () => {
const res = await accountApi.updateAccountStatus(row.id, newStatus);
if (res.code === 200) {
row.status = newStatus;
ElMessage.success(`${action}成功`);
} else {
ElMessage.error(res.msg || `${action}失败`);
}
})
.catch(() => {});
};
const handleDeleteAccount = (row) => {
ElMessageBox.confirm(
`确定删除用户【${row.username}】吗?此操作不可逆。`,
"警告",
{ type: "error", confirmButtonClass: "el-button--danger" }
)
.then(async () => {
const res = await accountApi.deleteAccount(row.id);
if (res.code === 200) {
const idx = users.value.findIndex((u) => u.id === row.id);
if (idx > -1) users.value.splice(idx, 1);
ElMessage.success("删除成功");
} else {
ElMessage.error(res.msg || "删除失败");
}
})
.catch(() => {});
};
onMounted(fetchUsers);
</script>
<style scoped>
.account-container {
padding: 30px;
background-color: #f5f7fa;
min-height: 100vh;
}
h2 {
margin-bottom: 25px;
color: #303133;
font-weight: 600;
}
.action-bar {
display: flex;
justify-content: space-between;
background: #fff;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
</style>

View File

@@ -0,0 +1,159 @@
<template>
<div class="customers-container">
<h2>运营人员管理</h2>
<div class="action-bar">
<div class="search-group">
<el-input v-model="searchQuery" placeholder="搜索客户单位" style="width: 250px" clearable />
</div>
<div class="button-group">
<el-button type="primary" :icon="Plus" @click="customerDialogVisible = true">
新增客户单位
</el-button>
<el-button type="success" :icon="User" @click="handleAddOperator">
新增运营人员
</el-button>
</div>
</div>
<el-table :data="companyAndOperators" border style="width: 100%; margin-top: 20px" v-loading="loading">
<template #empty>
<el-empty :image-size="100" description="暂无客户数据" />
</template>
<el-table-column prop="id" label="客户ID" width="100" />
<el-table-column prop="company_name" label="客户单位名称" />
<el-table-column label="员工管理" width="250" align="center">
<template #default="scope">
<span style="margin-right: 15px">
共 <b style="color: #409eff">{{ scope.row.companyoperators.length }}</b> 人
</span>
<el-button size="small" type="primary" link :icon="View" @click="showCompanyAndOperatorsDetails(scope.row)">
查看详情
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button size="small" type="danger" plain @click="handleDeleteCompany(scope.row)">
删除单位
</el-button>
</template>
</el-table-column>
</el-table>
<CompanyEditDialog v-model="customerDialogVisible" @success="fetchCompanyAndOperators" />
<OperatorEditDialog v-model="operatorDialogVisible" :company-list="companyList"
@success="fetchCompanyAndOperators" />
<OperatorListDialog v-model="employeeDetailVisible" :company-name="activeCompanyName"
:operators="currentCompanyOperators" @refresh="fetchCompanyAndOperators" />
</div>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import { Plus, User, View } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { customerApi, ticketApi } from "@/api/ticket";
import CompanyEditDialog from "@/components/operators/CompanyEditDialog.vue";
import OperatorEditDialog from "@/components/operators/OperatorEditDialog.vue";
import OperatorListDialog from "@/components/operators/OperatorListDialog.vue";
const loading = ref(false);
const companyList = ref([]);
const employeeList = ref([]);
const searchQuery = ref("");
const companyAndOperators = ref([]);
const customerDialogVisible = ref(false);
const operatorDialogVisible = ref(false);
const employeeDetailVisible = ref(false);
const activeCompanyName = ref(""); // 当前选中客户单位的名称
const currentCompanyOperators = ref([]); // 当前选中客户单位的员工列表
const fetchCompanyAndOperators = async () => {
loading.value = true;
try {
const response = await ticketApi.getOperatorsNew();
if (response.code === 200) {
companyAndOperators.value = response.data
companyList.value = response.data.map(item => ({ company_id: item.id, company_name: item.company_name }))
}
// console.log('获取数据', companyAndOperators.value);
} catch (error) {
ElMessage.error("获取客户数据失败,请检查网络");
} finally {
loading.value = false;
}
};
const handleAddOperator = () => {
if (companyList.value.length === 0) {
return ElMessage.warning("请先创建客户单位");
}
operatorDialogVisible.value = true;
};
const showCompanyAndOperatorsDetails = (row) => {
activeCompanyName.value = row.company_name;
currentCompanyOperators.value = row.companyoperators || [];
employeeDetailVisible.value = true;
};
// 删除客户单位
const handleDeleteCompany = (row) => {
ElMessageBox.confirm(
`确定删除单位【${row.company_name}】吗?操作不可恢复!`,
"警告",
{ type: "error", confirmButtonClass: "el-button--danger" }
)
.then(async () => {
const res = await customerApi.deleteCompany(row.id);
if (res.code === 200) {
ElMessage.success("清理成功");
fetchCompanyAndOperators();
} else {
ElMessage.error(res.msg || "删除失败");
}
})
.catch(() => { });
};
onMounted(fetchCompanyAndOperators);
</script>
<style scoped>
.customers-container {
padding: 30px;
background-color: #f5f7fa;
min-height: 100vh;
}
h2 {
margin-bottom: 25px;
color: #303133;
font-weight: 600;
}
.action-bar {
display: flex;
justify-content: space-between;
background: #fff;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
.button-group .el-button {
margin-left: 10px;
}
</style>

View File

@@ -1,183 +0,0 @@
<template>
<div class="customers-container">
<h2>客户及员工管理系统</h2>
<div class="action-bar">
<div class="search-group">
<el-input
v-model="searchQuery"
placeholder="搜索客户单位"
style="width: 250px"
clearable
/>
</div>
<div class="button-group">
<el-button type="primary" :icon="Plus" @click="customerDialogVisible = true"
>新增客户单位</el-button
>
<el-button type="success" :icon="User" @click="handleAddEmployee"
>新增员工</el-button
>
</div>
</div>
<el-table
:data="filteredData"
border
style="width: 100%; margin-top: 20px"
v-loading="loading"
>
<el-table-column prop="customer_id" label="客户ID" width="100" />
<el-table-column prop="company_name" label="客户单位名称" />
<el-table-column label="员工管理" width="250" align="center">
<template #default="scope">
<span style="margin-right: 15px">
共
<b style="color: #409eff">{{ getEmployeeCount(scope.row.customer_id) }}</b> 人
</span>
<el-button
size="small"
type="primary"
link
:icon="View"
@click="showEmployeeDetails(scope.row)"
>查看详情</el-button
>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="scope">
<el-button
size="small"
type="danger"
plain
@click="handleDeleteCustomer(scope.row)"
>删除单位</el-button
>
</template>
</el-table-column>
</el-table>
<CustomerEditDialog v-model="customerDialogVisible" @success="fetchCustomers" />
<EmployeeEditDialog
v-model="employeeDialogVisible"
:customer-list="customerList"
@success="fetchCustomers"
/>
<EmployeeListDialog
v-model="employeeDetailVisible"
:customer-name="activeCustomerName"
:employees="currentCustomerEmployees"
@refresh="fetchCustomers"
/>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from "vue";
import { Plus, User, View } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { customerApi } from "@/api/ticket";
// 导入新组件
import CustomerEditDialog from "@/components/customer/CustomerEditDialog.vue";
import EmployeeEditDialog from "@/components/customer/EmployeeEditDialog.vue";
import EmployeeListDialog from "@/components/customer/EmployeeListDialog.vue";
// --- 状态数据 ---
const loading = ref(false);
const customerList = ref([]);
const employeeList = ref([]);
const searchQuery = ref("");
const customerDialogVisible = ref(false);
const employeeDialogVisible = ref(false);
const employeeDetailVisible = ref(false);
const activeCustomerName = ref("");
const activeCustomerId = ref(null);
// --- 核心逻辑 ---
const fetchCustomers = async () => {
loading.value = true;
try {
const res = await customerApi.getCustomers();
if (res.code === 200) {
customerList.value = res.customers || [];
employeeList.value = res.employees || [];
}
} finally {
loading.value = false;
}
};
const getEmployeeCount = (id) =>
employeeList.value.filter((emp) => emp.customer_id === id).length;
const currentCustomerEmployees = computed(() =>
employeeList.value.filter((emp) => emp.customer_id === activeCustomerId.value)
);
const filteredData = computed(() =>
customerList.value.filter((item) =>
item.company_name.toLowerCase().includes(searchQuery.value.toLowerCase())
)
);
const handleAddEmployee = () => {
if (customerList.value.length === 0) return ElMessage.warning("请先创建客户单位");
employeeDialogVisible.value = true;
};
const showEmployeeDetails = (row) => {
activeCustomerId.value = row.customer_id;
activeCustomerName.value = row.company_name;
employeeDetailVisible.value = true;
};
const handleDeleteCustomer = (row) => {
ElMessageBox.confirm(`确定删除单位【${row.company_name}】吗?操作不可恢复!`, "警告", {
type: "error",
confirmButtonClass: "el-button--danger",
})
.then(async () => {
const res = await customerApi.deleteCustomer(row.customer_id);
if (res.code === 200) {
ElMessage.success("清理成功");
fetchCustomers();
}
})
.catch(() => {});
};
onMounted(fetchCustomers);
</script>
<style scoped>
/* 保持原有样式不变 */
.customers-container {
padding: 30px;
background-color: #f5f7fa;
min-height: 100vh;
}
h2 {
margin-bottom: 25px;
color: #303133;
font-weight: 600;
}
.action-bar {
display: flex;
justify-content: space-between;
background: #fff;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
}
.button-group .el-button {
margin-left: 10px;
}
</style>

View File

@@ -109,7 +109,6 @@ const handleLogin = async () => {
} catch (error) {
if (error.name !== "ValidationError") {
ElMessage.error("登录请求异常,请稍后重试");
// console.error("登录错误:", error);
}
} finally {
loading.value = false;