重构整个项目
This commit is contained in:
@@ -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}`)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
63
src/components/operators/OperatorEditDialog.vue
Normal file
63
src/components/operators/OperatorEditDialog.vue
Normal 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>
|
||||
55
src/components/operators/OperatorListDialog.vue
Normal file
55
src/components/operators/OperatorListDialog.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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: '账户注册' }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
269
src/views/AccountRegistrationView.vue
Normal file
269
src/views/AccountRegistrationView.vue
Normal 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>
|
||||
159
src/views/CompanyAndOperatorsManagement.vue
Normal file
159
src/views/CompanyAndOperatorsManagement.vue
Normal 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>
|
||||
@@ -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>
|
||||
@@ -109,7 +109,6 @@ const handleLogin = async () => {
|
||||
} catch (error) {
|
||||
if (error.name !== "ValidationError") {
|
||||
ElMessage.error("登录请求异常,请稍后重试");
|
||||
// console.error("登录错误:", error);
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
|
||||
Reference in New Issue
Block a user