员工和用户整合前暂存

This commit is contained in:
ChoChoX
2026-06-24 16:42:13 +08:00
parent f856bcbc38
commit 5cd569797c
15 changed files with 2423 additions and 56 deletions

41
src/api/employee.js Normal file
View File

@@ -0,0 +1,41 @@
import axios from 'axios'
import { mockEmployeeAPI } from './mock/employee'
// 开关true 用 mockfalse 用真实接口
const USE_MOCK = true
// axios 实例
const request = axios.create({
baseURL: '/api',
timeout: 5000
})
// 获取员工列表
export const getEmployeeList = (params) => {
if (USE_MOCK) return mockEmployeeAPI.getList(params)
return request.get('/employees', { params }).then(r => r.data)
}
// 获取员工详情
export const getEmployeeDetail = (id) => {
if (USE_MOCK) return mockEmployeeAPI.getDetail(id)
return request.get(`/employees/${id}`).then(r => r.data)
}
// 新增员工
export const createEmployee = (data) => {
if (USE_MOCK) return mockEmployeeAPI.create(data)
return request.post('/employees', data).then(r => r.data)
}
// 更新员工
export const updateEmployee = (id, data) => {
if (USE_MOCK) return mockEmployeeAPI.update(id, data)
return request.put(`/employees/${id}`, data).then(r => r.data)
}
// 删除员工
export const deleteEmployeeAPI = (id) => {
if (USE_MOCK) return mockEmployeeAPI.delete(id)
return request.delete(`/employees/${id}`).then(r => r.data)
}

View File

@@ -93,6 +93,17 @@ export const mockCustomerAPI = {
result = result.filter(item => item.customer_type === params.customer_type)
}
// 地区筛选(省/市/区)
if (params.province) {
result = result.filter(item => item.province === params.province)
}
if (params.city) {
result = result.filter(item => item.city === params.city)
}
if (params.district) {
result = result.filter(item => item.district === params.district)
}
return { code: 200, data: result, total: result.length }
},

204
src/api/mock/employee.js Normal file
View File

@@ -0,0 +1,204 @@
// 模拟员工数据
const mockEmployees = [
{
id: 1,
name: '张伟',
gender: '男',
age: 28,
education: '本科',
department: '运营部',
entry_date: '2023-03-15',
position: '运营主管',
salary: 8500.00,
phone: '13800138001',
email: 'zhangwei@mxbc.com',
status: 1,
remark: '负责门店日常运营',
created_at: '2023-03-15 09:00:00',
updated_at: '2023-03-15 09:00:00'
},
{
id: 2,
name: '李娜',
gender: '女',
age: 25,
education: '专科',
department: '销售部',
entry_date: '2023-06-01',
position: '销售专员',
salary: 6000.00,
phone: '13800138002',
email: 'lina@mxbc.com',
status: 1,
remark: '',
created_at: '2023-06-01 09:00:00',
updated_at: '2023-06-01 09:00:00'
},
{
id: 3,
name: '王强',
gender: '男',
age: 32,
education: '硕士',
department: '技术部',
entry_date: '2022-11-10',
position: '技术总监',
salary: 15000.00,
phone: '13800138003',
email: 'wangqiang@mxbc.com',
status: 1,
remark: '负责系统架构设计',
created_at: '2022-11-10 09:00:00',
updated_at: '2022-11-10 09:00:00'
},
{
id: 4,
name: '刘洋',
gender: '女',
age: 23,
education: '本科',
department: '市场部',
entry_date: '2024-01-20',
position: '市场专员',
salary: 5500.00,
phone: '13800138004',
email: 'liuyang@mxbc.com',
status: 0,
remark: '已离职',
created_at: '2024-01-20 09:00:00',
updated_at: '2025-03-01 17:00:00'
},
{
id: 5,
name: '赵敏',
gender: '女',
age: 27,
education: '本科',
department: '财务部',
entry_date: '2023-09-05',
position: '财务会计',
salary: 7000.00,
phone: '13800138005',
email: 'zhaomin@mxbc.com',
status: 1,
remark: '',
created_at: '2023-09-05 09:00:00',
updated_at: '2023-09-05 09:00:00'
},
{
id: 6,
name: '陈刚',
gender: '男',
age: 35,
education: '高中',
department: '仓储部',
entry_date: '2022-04-12',
position: '仓库管理员',
salary: 5000.00,
phone: '13800138006',
email: 'chengang@mxbc.com',
status: 1,
remark: '负责原料入库出库',
created_at: '2022-04-12 09:00:00',
updated_at: '2022-04-12 09:00:00'
}
]
// 模拟网络延迟
const delay = (ms = 300) => new Promise(r => setTimeout(r, ms))
// Mock API
export const mockEmployeeAPI = {
// 获取列表
async getList(params = {}) {
await delay()
let result = [...mockEmployees]
// 关键词搜索
if (params.keyword) {
const keyword = params.keyword.toLowerCase()
const searchField = params.searchField || '1'
if (searchField === '1') {
// ID精确匹配
result = result.filter(item => String(item.id) === keyword)
} else if (searchField === '2') {
// 姓名:模糊匹配
result = result.filter(item => item.name.toLowerCase().includes(keyword))
} else if (searchField === '3') {
// 部门:模糊匹配
result = result.filter(item => item.department.toLowerCase().includes(keyword))
} else {
result = result.filter(item =>
String(item.id).includes(keyword) ||
item.name.toLowerCase().includes(keyword) ||
item.department.toLowerCase().includes(keyword)
)
}
}
// 在职状态筛选
if (params.status !== undefined && params.status !== '') {
result = result.filter(item => item.status === Number(params.status))
}
// 部门筛选
if (params.department) {
result = result.filter(item => item.department === params.department)
}
return { code: 200, data: result, total: result.length }
},
// 获取详情
async getDetail(id) {
await delay()
const employee = mockEmployees.find(item => item.id === id)
if (employee) {
return { code: 200, data: employee }
}
return { code: 404, message: '员工不存在' }
},
// 新增
async create(data) {
await delay()
const maxId = mockEmployees.length > 0 ? Math.max(...mockEmployees.map(item => item.id)) : 0
const now = new Date().toISOString().replace('T', ' ').slice(0, 19)
const newEmployee = {
id: maxId + 1,
status: 1,
...data,
created_at: now,
updated_at: now
}
mockEmployees.push(newEmployee)
return { code: 200, data: newEmployee, message: '新增成功' }
},
// 更新
async update(id, data) {
await delay()
const index = mockEmployees.findIndex(item => item.id === id)
if (index !== -1) {
mockEmployees[index] = {
...mockEmployees[index],
...data,
updated_at: new Date().toISOString().replace('T', ' ').slice(0, 19)
}
return { code: 200, data: mockEmployees[index], message: '更新成功' }
}
return { code: 404, message: '员工不存在' }
},
// 删除
async delete(id) {
await delay()
const index = mockEmployees.findIndex(item => item.id === id)
if (index !== -1) {
mockEmployees.splice(index, 1)
return { code: 200, message: '删除成功' }
}
return { code: 404, message: '员工不存在' }
}
}

174
src/api/mock/products.js Normal file
View File

@@ -0,0 +1,174 @@
// 模拟产品数据
const mockProducts = [
{
id: 1,
name: '红茶茶叶',
type: 'raw_material',
quantity: 500,
price: 45.00,
unit: '斤',
specification: '特级红茶 500g/袋',
supplier: '福建茶业有限公司',
remark: '常温保存,防潮',
created_at: '2025-01-10 08:00:00',
updated_at: '2025-01-10 08:00:00'
},
{
id: 2,
name: '奶茶杯',
type: 'packaging',
quantity: 10000,
price: 0.15,
unit: '个',
specification: '500ml PP材质 透明',
supplier: '广州包装制品厂',
remark: '',
created_at: '2025-02-15 10:30:00',
updated_at: '2025-02-15 10:30:00'
},
{
id: 3,
name: '制冰机',
type: 'equipment',
quantity: 5,
price: 3500.00,
unit: '台',
specification: 'SM-50 商用日产50kg',
supplier: '深圳冰雪设备有限公司',
remark: '含一年保修',
created_at: '2025-03-20 14:00:00',
updated_at: '2025-03-20 14:00:00'
},
{
id: 4,
name: '食品级清洁剂',
type: 'cleaning',
quantity: 30,
price: 28.50,
unit: '瓶',
specification: '500ml/瓶',
supplier: '洁康日化有限公司',
remark: '适用于设备表面清洁',
created_at: '2025-04-05 09:15:00',
updated_at: '2025-04-05 09:15:00'
},
{
id: 5,
name: '吸管',
type: 'packaging',
quantity: 20000,
price: 0.03,
unit: '根',
specification: '可降解 250mm',
supplier: '广州包装制品厂',
remark: '环保可降解材质',
created_at: '2025-05-10 16:45:00',
updated_at: '2025-05-10 16:45:00'
},
{
id: 6,
name: '果糖糖浆',
type: 'raw_material',
quantity: 200,
price: 32.00,
unit: '瓶',
specification: '2.5L/瓶',
supplier: '上海糖业贸易有限公司',
remark: '开封后冷藏保存',
created_at: '2025-06-01 11:00:00',
updated_at: '2025-06-01 11:00:00'
}
]
// 模拟网络延迟
const delay = (ms = 300) => new Promise(r => setTimeout(r, ms))
// Mock API
export const mockProductsAPI = {
// 获取列表
async getList(params = {}) {
await delay()
let result = [...mockProducts]
// 关键词搜索
if (params.keyword) {
const keyword = params.keyword.toLowerCase()
const searchField = params.searchField || '2'
if (searchField === '1') {
// ID精确匹配
result = result.filter(item => String(item.id) === keyword)
} else if (searchField === '2') {
// 名称:模糊匹配
result = result.filter(item => item.name.toLowerCase().includes(keyword))
} else if (searchField === '3') {
// 供应商:模糊匹配
result = result.filter(item => item.supplier.toLowerCase().includes(keyword))
} else {
result = result.filter(item =>
String(item.id).includes(keyword) ||
item.name.toLowerCase().includes(keyword) ||
item.supplier.toLowerCase().includes(keyword)
)
}
}
// 产品类型筛选
if (params.type) {
result = result.filter(item => item.type === params.type)
}
return { code: 200, data: result, total: result.length }
},
// 获取详情
async getDetail(id) {
await delay()
const product = mockProducts.find(item => item.id === id)
if (product) {
return { code: 200, data: product }
}
return { code: 404, message: '产品不存在' }
},
// 新增
async create(data) {
await delay()
const maxId = mockProducts.length > 0 ? Math.max(...mockProducts.map(item => item.id)) : 0
const now = new Date().toISOString().replace('T', ' ').slice(0, 19)
const newProduct = {
id: maxId + 1,
...data,
created_at: now,
updated_at: now
}
mockProducts.push(newProduct)
return { code: 200, data: newProduct, message: '新增成功' }
},
// 更新
async update(id, data) {
await delay()
const index = mockProducts.findIndex(item => item.id === id)
if (index !== -1) {
mockProducts[index] = {
...mockProducts[index],
...data,
updated_at: new Date().toISOString().replace('T', ' ').slice(0, 19)
}
return { code: 200, data: mockProducts[index], message: '更新成功' }
}
return { code: 404, message: '产品不存在' }
},
// 删除
async delete(id) {
await delay()
const index = mockProducts.findIndex(item => item.id === id)
if (index !== -1) {
mockProducts.splice(index, 1)
return { code: 200, message: '删除成功' }
}
return { code: 404, message: '产品不存在' }
}
}

145
src/api/mock/service.js Normal file
View File

@@ -0,0 +1,145 @@
// 模拟售后数据
const mockServices = [
{
id: 1,
customer_id: 1,
feedback: '收到的奶茶杯有破损,需要更换',
employee_id: 2,
handle_method: '同意补发,已安排快递寄出新品',
handle_status: '已完成',
service_date: '2025-03-15',
remark: '客户态度良好',
created_at: '2025-03-15 10:30:00',
updated_at: '2025-03-16 14:00:00'
},
{
id: 2,
customer_id: 3,
feedback: '制冰机出现异响,疑似零件松动',
employee_id: null,
handle_method: null,
handle_status: '待处理',
service_date: '2025-05-20',
remark: '紧急,客户反映影响营业',
created_at: '2025-05-20 09:00:00',
updated_at: '2025-05-20 09:00:00'
},
{
id: 3,
customer_id: 2,
feedback: '订购的红茶茶叶与样品不符,颜色偏深',
employee_id: 3,
handle_method: '已联系供应商核实,确认为新批次色差,品质无问题',
handle_status: '处理中',
service_date: '2025-06-01',
remark: '等待供应商书面说明',
created_at: '2025-06-01 14:20:00',
updated_at: '2025-06-02 10:00:00'
},
{
id: 4,
customer_id: 4,
feedback: '果糖糖浆保质期标注模糊,要求退换',
employee_id: 1,
handle_method: '已同意退货,安排换货处理',
handle_status: '已完成',
service_date: '2025-06-10',
remark: '',
created_at: '2025-06-10 11:00:00',
updated_at: '2025-06-12 16:30:00'
}
]
// 模拟网络延迟
const delay = (ms = 300) => new Promise(r => setTimeout(r, ms))
// Mock API
export const mockServiceAPI = {
// 获取列表
async getList(params = {}) {
await delay()
let result = [...mockServices]
// 关键词搜索
if (params.keyword) {
const keyword = params.keyword.toLowerCase()
const searchField = params.searchField || '1'
if (searchField === '1') {
// ID精确匹配
result = result.filter(item => String(item.id) === keyword)
} else if (searchField === '2') {
// 客户ID精确匹配
result = result.filter(item => String(item.customer_id) === keyword)
} else if (searchField === '3') {
// 反馈内容:模糊匹配
result = result.filter(item => item.feedback.toLowerCase().includes(keyword))
} else {
result = result.filter(item =>
String(item.id).includes(keyword) ||
String(item.customer_id).includes(keyword) ||
item.feedback.toLowerCase().includes(keyword)
)
}
}
// 处理状态筛选
if (params.handle_status) {
result = result.filter(item => item.handle_status === params.handle_status)
}
return { code: 200, data: result, total: result.length }
},
// 获取详情
async getDetail(id) {
await delay()
const service = mockServices.find(item => item.id === id)
if (service) {
return { code: 200, data: service }
}
return { code: 404, message: '售后记录不存在' }
},
// 新增
async create(data) {
await delay()
const maxId = mockServices.length > 0 ? Math.max(...mockServices.map(item => item.id)) : 0
const now = new Date().toISOString().replace('T', ' ').slice(0, 19)
const newService = {
id: maxId + 1,
handle_status: '待处理',
...data,
created_at: now,
updated_at: now
}
mockServices.push(newService)
return { code: 200, data: newService, message: '新增成功' }
},
// 更新
async update(id, data) {
await delay()
const index = mockServices.findIndex(item => item.id === id)
if (index !== -1) {
mockServices[index] = {
...mockServices[index],
...data,
updated_at: new Date().toISOString().replace('T', ' ').slice(0, 19)
}
return { code: 200, data: mockServices[index], message: '更新成功' }
}
return { code: 404, message: '售后记录不存在' }
},
// 删除
async delete(id) {
await delay()
const index = mockServices.findIndex(item => item.id === id)
if (index !== -1) {
mockServices.splice(index, 1)
return { code: 200, message: '删除成功' }
}
return { code: 404, message: '售后记录不存在' }
}
}

141
src/api/mock/user.js Normal file
View File

@@ -0,0 +1,141 @@
// 模拟用户数据
const mockUsers = [
{
id: 1,
username: 'admin',
password: '123456',
name: '系统管理员',
user_type: 'admin',
status: '1',
createdate: '2025-01-10 08:00:00'
},
{
id: 2,
username: 'zhangsan',
password: '123456',
name: '张三',
user_type: 'user',
status: '1',
createdate: '2025-02-15 10:30:00'
},
{
id: 3,
username: 'lisi',
password: '123456',
name: '李四',
user_type: 'user',
status: '0',
createdate: '2025-03-20 14:00:00'
},
{
id: 4,
username: 'wangwu',
password: '123456',
name: '王五',
user_type: 'admin',
status: '1',
createdate: '2025-04-05 09:15:00'
},
{
id: 5,
username: 'zhaoliu',
password: '123456',
name: '赵六',
user_type: 'user',
status: '0',
createdate: '2025-05-10 16:45:00'
}
]
// 模拟网络延迟
const delay = (ms = 300) => new Promise(r => setTimeout(r, ms))
// Mock API
export const mockUserAPI = {
// 获取列表
async getList(params = {}) {
await delay()
let result = [...mockUsers]
// 关键词搜索
if (params.keyword) {
const keyword = params.keyword.toLowerCase()
const searchField = params.searchField || '1'
if (searchField === '2') {
// 编号:精确匹配
result = result.filter(item => String(item.id) === keyword)
} else if (searchField === '1') {
// 用户名:模糊匹配
result = result.filter(item => item.username.includes(keyword))
} else if (searchField === '3') {
// 真实姓名:模糊匹配
result = result.filter(item => item.name.includes(keyword))
} else {
// 默认:模糊匹配所有字段
result = result.filter(item =>
String(item.id).includes(keyword) ||
item.username.includes(keyword) ||
item.name.includes(keyword)
)
}
}
// 用户类型筛选
if (params.user_type) {
result = result.filter(item => item.user_type === params.user_type)
}
// 用户状态筛选
if (params.status) {
result = result.filter(item => item.status === params.status)
}
return { code: 200, data: result, total: result.length }
},
// 获取详情
async getDetail(id) {
await delay()
const user = mockUsers.find(item => item.id === id)
if (user) {
return { code: 200, data: user }
}
return { code: 404, message: '用户不存在' }
},
// 新增
async create(data) {
await delay()
const maxId = mockUsers.length > 0 ? Math.max(...mockUsers.map(item => item.id)) : 0
const newUser = {
id: maxId + 1,
...data,
createdate: new Date().toISOString().replace('T', ' ').slice(0, 19)
}
mockUsers.push(newUser)
return { code: 200, data: newUser, message: '新增成功' }
},
// 更新
async update(id, data) {
await delay()
const index = mockUsers.findIndex(item => item.id === id)
if (index !== -1) {
mockUsers[index] = { ...mockUsers[index], ...data }
return { code: 200, data: mockUsers[index], message: '更新成功' }
}
return { code: 404, message: '用户不存在' }
},
// 删除
async delete(id) {
await delay()
const index = mockUsers.findIndex(item => item.id === id)
if (index !== -1) {
mockUsers.splice(index, 1)
return { code: 200, message: '删除成功' }
}
return { code: 404, message: '用户不存在' }
}
}

41
src/api/products.js Normal file
View File

@@ -0,0 +1,41 @@
import axios from 'axios'
import { mockProductsAPI } from './mock/products'
// 开关true 用 mockfalse 用真实接口
const USE_MOCK = true
// axios 实例
const request = axios.create({
baseURL: '/api',
timeout: 5000
})
// 获取产品列表
export const getProductsList = (params) => {
if (USE_MOCK) return mockProductsAPI.getList(params)
return request.get('/products', { params }).then(r => r.data)
}
// 获取产品详情
export const getProductsDetail = (id) => {
if (USE_MOCK) return mockProductsAPI.getDetail(id)
return request.get(`/products/${id}`).then(r => r.data)
}
// 新增产品
export const createProducts = (data) => {
if (USE_MOCK) return mockProductsAPI.create(data)
return request.post('/products', data).then(r => r.data)
}
// 更新产品
export const updateProducts = (id, data) => {
if (USE_MOCK) return mockProductsAPI.update(id, data)
return request.put(`/products/${id}`, data).then(r => r.data)
}
// 删除产品
export const deleteProductsAPI = (id) => {
if (USE_MOCK) return mockProductsAPI.delete(id)
return request.delete(`/products/${id}`).then(r => r.data)
}

41
src/api/service.js Normal file
View File

@@ -0,0 +1,41 @@
import axios from 'axios'
import { mockServiceAPI } from './mock/service'
// 开关true 用 mockfalse 用真实接口
const USE_MOCK = true
// axios 实例
const request = axios.create({
baseURL: '/api',
timeout: 5000
})
// 获取售后列表
export const getServiceList = (params) => {
if (USE_MOCK) return mockServiceAPI.getList(params)
return request.get('/after-sales', { params }).then(r => r.data)
}
// 获取售后详情
export const getServiceDetail = (id) => {
if (USE_MOCK) return mockServiceAPI.getDetail(id)
return request.get(`/after-sales/${id}`).then(r => r.data)
}
// 新增售后
export const createService = (data) => {
if (USE_MOCK) return mockServiceAPI.create(data)
return request.post('/after-sales', data).then(r => r.data)
}
// 更新售后
export const updateService = (id, data) => {
if (USE_MOCK) return mockServiceAPI.update(id, data)
return request.put(`/after-sales/${id}`, data).then(r => r.data)
}
// 删除售后
export const deleteServiceAPI = (id) => {
if (USE_MOCK) return mockServiceAPI.delete(id)
return request.delete(`/after-sales/${id}`).then(r => r.data)
}

41
src/api/user.js Normal file
View File

@@ -0,0 +1,41 @@
import axios from 'axios'
import { mockUserAPI } from './mock/user'
// 开关true 用 mockfalse 用真实接口
const USE_MOCK = true
// axios 实例
const request = axios.create({
baseURL: '/api',
timeout: 5000
})
// 获取用户列表
export const getUserList = (params) => {
if (USE_MOCK) return mockUserAPI.getList(params)
return request.get('/users', { params }).then(r => r.data)
}
// 获取用户详情
export const getUserDetail = (id) => {
if (USE_MOCK) return mockUserAPI.getDetail(id)
return request.get(`/users/${id}`).then(r => r.data)
}
// 新增用户
export const createUser = (data) => {
if (USE_MOCK) return mockUserAPI.create(data)
return request.post('/users', data).then(r => r.data)
}
// 更新用户
export const updateUser = (id, data) => {
if (USE_MOCK) return mockUserAPI.update(id, data)
return request.put(`/users/${id}`, data).then(r => r.data)
}
// 删除用户
export const deleteUserAPI = (id) => {
if (USE_MOCK) return mockUserAPI.delete(id)
return request.delete(`/users/${id}`).then(r => r.data)
}

View File

@@ -8,6 +8,8 @@ import {
updateContract,
deleteContractAPI
} from '../api/contract'
import { getCustomerList } from '../api/customer'
import { getEmployeeList } from '../api/employee'
const search = ref('')
const select = ref('1')
@@ -20,6 +22,8 @@ const isEditMode = ref(false)
const currentContractId = ref(null)
const showDetailDialog = ref(false)
const currentDetail = ref(null)
const customerList = ref([])
const employeeList = ref([])
const form = reactive({
contract_no: '',
@@ -36,8 +40,16 @@ const form = reactive({
onMounted(() => {
fetchData()
fetchOptions()
})
// 获取客户和员工列表供下拉选择
const fetchOptions = async () => {
const [cRes, eRes] = await Promise.all([getCustomerList(), getEmployeeList()])
customerList.value = cRes.data
employeeList.value = eRes.data
}
// 获取合同列表
const fetchData = async () => {
loading.value = true
@@ -314,12 +326,12 @@ const formatDate = (date) => {
<el-form :model="form" label-width="auto" style="max-width: 700px;" label-position="top">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="合同编号">
<el-form-item label="合同编号" required>
<el-input v-model="form.contract_no" placeholder="如: HT-001" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同名称">
<el-form-item label="合同名称" required>
<el-input v-model="form.contract_name" placeholder="请输入合同名称" />
</el-form-item>
</el-col>
@@ -327,25 +339,29 @@ const formatDate = (date) => {
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="客户ID">
<el-input v-model="form.customer_id" style="width: 100%;" />
<el-form-item label="客户" required>
<el-select v-model="form.customer_id" placeholder="请选择客户" style="width: 100%;" filterable>
<el-option v-for="c in customerList" :key="c.id" :label="`${c.id} - ${c.name}`" :value="c.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务员ID">
<el-input v-model="form.employee_id" style="width: 100%;" />
<el-form-item label="业务员">
<el-select v-model="form.employee_id" placeholder="请选择业务员" style="width: 100%;" filterable>
<el-option v-for="e in employeeList" :key="e.id" :label="`${e.id} - ${e.name}`" :value="e.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="生效日期">
<el-form-item label="生效日期" required>
<el-date-picker v-model="form.effective_date" type="date" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="到期日期">
<el-form-item label="到期日期" required>
<el-date-picker v-model="form.expiry_date" type="date" style="width: 100%;" />
</el-form-item>
</el-col>
@@ -353,12 +369,12 @@ const formatDate = (date) => {
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="合同金额">
<el-form-item label="合同金额" required>
<el-input-number v-model="form.amount" :min="0" :step="1000" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-form-item label="状态" required>
<el-select v-model="form.status" style="width: 100%;">
<el-option label="待审批" value="待审批" />
<el-option label="生效中" value="生效中" />

View File

@@ -1,7 +1,7 @@
<script setup>
import { Search } from '@element-plus/icons-vue'
import { ref, onMounted, reactive } from 'vue'
import { ref, computed, onMounted, reactive, watch } from 'vue'
import { regionData, CodeToText, TextToCode } from 'element-china-area-data'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
@@ -13,31 +13,51 @@ import {
const form = reactive({
id: '',
name: '', // 姓名,默认为空字符串
phone: '', // 电话,默认为空字符串
region: [], // 地区,默认为空数组(省市区三级代码)
address: '', // 详细地址,默认为空字符串
email: '', // 电子邮箱,默认为空字符串
customer_type: 'Normal', // 客户类型,默认选中"普通客户"
remark: '' // 备注
name: '',
phone: '',
region: [],
address: '',
email: '',
customer_type: 'Normal',
remark: ''
})
const search = ref('')
const select = ref('1')
const searchResults = ref([])
const showSearchResults = ref(false) //是否显示搜索结果
const showAddForm = ref(false) //是否显示新增表单
const isEditMode = ref(false) //是否为编辑模式
const currentCustomerId = ref(null) //当前编辑的客户的ID
const showSearchResults = ref(false)
const showAddForm = ref(false)
const isEditMode = ref(false)
const currentCustomerId = ref(null)
const loading = ref(false)
let searchSeq = 0
const showDetailDialog = ref(false)
const currentDetail = ref(null)
const filterRegion = ref([])
// 只保留省市两级的地区数据
const regionDataTwoLevel = computed(() => {
return regionData.map(province => ({
...province,
children: province.children?.map(city => ({
value: city.value,
label: city.label
}))
}))
})
onMounted(() => {
fetchData()
})
//获取用户列表
// 切换代理商类型时清空已选地区
watch(() => form.customer_type, () => {
form.region = []
})
// 获取客户列表
const fetchData = async () => {
loading.value = true;
loading.value = true
const res = await getCustomerList()
searchResults.value = res.data
showSearchResults.value = true
@@ -46,11 +66,22 @@ const fetchData = async () => {
// 搜索
const handleSearch = async () => {
const seq = ++searchSeq
loading.value = true
const res = await getCustomerList({ keyword: search.value, searchField: select.value })
try {
const params = {
keyword: search.value,
searchField: select.value,
province: filterRegion.value?.[0] ? CodeToText[filterRegion.value[0]] : '',
city: filterRegion.value?.[1] ? CodeToText[filterRegion.value[1]] : ''
}
const res = await getCustomerList(params)
if (seq !== searchSeq) return
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
} finally {
if (seq === searchSeq) loading.value = false
}
}
// 清空表单
@@ -80,7 +111,6 @@ const editCustomer = (row) => {
currentCustomerId.value = row.id
form.name = row.name
form.phone = row.phone
// 名称转回代码,让级联选择器显示
form.region = [
TextToCode[row.province]?.code || '',
TextToCode[row.province]?.[row.city]?.code || '',
@@ -139,11 +169,24 @@ const cancelAdd = () => {
// 清空搜索
const clearSearch = () => {
search.value = ''
filterRegion.value = []
showSearchResults.value = false
showAddForm.value = false
fetchData()
}
// 双击行查看详情
const handleRowDblClick = (row) => {
currentDetail.value = { ...row }
showDetailDialog.value = true
}
// 关闭详情弹窗
const closeDetail = () => {
showDetailDialog.value = false
currentDetail.value = null
}
</script>
<template>
@@ -172,6 +215,19 @@ const clearSearch = () => {
新增客户
</el-button>
<div class="filter-row">
<span class="filter-label">筛选条件</span>
<el-cascader
v-model="filterRegion"
:options="regionDataTwoLevel"
:props="{ expandTrigger: 'hover' }"
placeholder="请选择省/市"
clearable
style="width: 280px;"
@change="handleSearch"
/>
</div>
</div>
<div v-if="showSearchResults" class="customer-list">
@@ -185,18 +241,11 @@ const clearSearch = () => {
<div v-else class="customer-table">
<el-table :data="searchResults" v-loading="loading" border style="width: 100%">
<el-table :data="searchResults" v-loading="loading" border style="width: 100%" @row-dblclick="handleRowDblClick">
<el-table-column prop="id" label="编号" width="80" />
<el-table-column prop="name" label="姓名" width="120" />
<el-table-column prop="phone" label="电话" width="150" />
<el-table-column label="地区" width="200">
<template #default="{ row }">
{{ [row.province, row.city, row.district].filter(Boolean).join(' ') }}
</template>
</el-table-column>
<el-table-column prop="address" label="详细地址" width="200" />
<el-table-column prop="email" label="电子邮箱" width="200" />
<el-table-column prop="remark" label="备注" width="200" show-overflow-tooltip />
<el-table-column prop="email" label="邮箱" min-width="180" show-overflow-tooltip />
<el-table-column prop="customer_type" label="代理商类型" width="120">
<template #default="{ row }">
<el-tag :type="row.customer_type === 'VIP' ? 'danger' : 'info'">
@@ -224,21 +273,24 @@ const clearSearch = () => {
<div v-if="showAddForm" class="customer-info-label">
<el-divider content-position="left">{{ isEditMode ? '编辑客户信息' : '新增客户' }}</el-divider>
<el-form :model="form" label-width="auto" style="max-width: 600px" label-position="top">
<el-form-item label="姓名">
<el-form-item label="姓名" required>
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
<el-form-item label="电话">
<el-form-item label="电话" required>
<el-input v-model="form.phone" :controls="false" :min="0" :max="99999999999" :precision="0"
placeholder="请输入11位手机号" style="width: 100%" />
</el-form-item>
<el-form-item label="地区">
<el-cascader v-model="form.region" :options="regionData" :props="{ expandTrigger: 'hover' }"
placeholder="请选择省/市/区" clearable style="width: 100%" />
<el-cascader v-model="form.region"
:options="form.customer_type === 'VIP' ? regionDataTwoLevel : regionData"
:props="{ expandTrigger: 'hover' }"
:placeholder="form.customer_type === 'VIP' ? '请选择省/市(地区总代理)' : '请选择省/市/区'"
clearable style="width: 100%" />
</el-form-item>
<el-form-item label="详细地址">
<el-form-item label="详细地址" v-if="form.customer_type !== 'VIP'">
<el-input v-model="form.address" placeholder=" 请输入详细地址" />
</el-form-item>
@@ -246,7 +298,7 @@ const clearSearch = () => {
<el-input v-model="form.email" placeholder=" 请输入邮箱地址" />
</el-form-item>
<el-form-item label="代理商类型">
<el-form-item label="代理商类型" required>
<el-radio-group v-model="form.customer_type">
<el-radio value="VIP">地区总代理</el-radio>
<el-radio value="Normal">加盟商</el-radio>
@@ -263,6 +315,34 @@ const clearSearch = () => {
</el-form>
</div>
<!-- 客户详情弹窗 -->
<el-dialog v-model="showDetailDialog" title="客户详情" width="650px" @close="closeDetail">
<div v-if="currentDetail" class="detail-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="客户编号">{{ currentDetail.id }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{ currentDetail.name }}</el-descriptions-item>
<el-descriptions-item label="电话">{{ currentDetail.phone }}</el-descriptions-item>
<el-descriptions-item label="电子邮箱">{{ currentDetail.email }}</el-descriptions-item>
<el-descriptions-item label="代理商类型">
<el-tag :type="currentDetail.customer_type === 'VIP' ? 'danger' : 'info'">
{{ currentDetail.customer_type === 'VIP' ? '地区总代理' : '加盟商' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="地区">
{{ [currentDetail.province, currentDetail.city, currentDetail.district].filter(Boolean).join(' ') }}
</el-descriptions-item>
<el-descriptions-item label="详细地址" :span="2">{{ currentDetail.address || '无' }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ currentDetail.remark || '无' }}
</el-descriptions-item>
</el-descriptions>
</div>
<template #footer>
<el-button @click="closeDetail">关闭</el-button>
<el-button type="primary" @click="closeDetail(); editCustomer(currentDetail)">编辑</el-button>
</template>
</el-dialog>
</div>
</template>

View File

@@ -1,9 +1,425 @@
<script setup>
import { Search } from '@element-plus/icons-vue'
import { ref, onMounted, reactive, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getEmployeeList,
createEmployee,
updateEmployee,
deleteEmployeeAPI
} from '../api/employee'
const form = reactive({
name: '',
gender: '男',
age: null,
education: '',
department: '',
entry_date: '',
position: '',
salary: null,
phone: '',
email: '',
status: 1,
remark: ''
})
const search = ref('')
const select = ref('1')
const filterStatus = ref('')
const filterDepartment = ref('')
const searchResults = ref([])
const showSearchResults = ref(false)
const showAddForm = ref(false)
const isEditMode = ref(false)
const currentEmployeeId = ref(null)
const loading = ref(false)
const showDetailDialog = ref(false)
const currentDetail = ref(null)
// 部门列表
const departmentOptions = [
'运营部', '销售部', '技术部', '市场部', '财务部', '仓储部', '人事部', '行政部'
]
// 学历列表
const educationOptions = ['高中', '专科', '本科', '硕士', '博士']
onMounted(() => {
fetchData()
})
// 监听筛选变化
watch([filterStatus, filterDepartment], () => {
handleSearch()
})
// 获取员工列表
const fetchData = async () => {
loading.value = true
const res = await getEmployeeList()
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 搜索
const handleSearch = async () => {
loading.value = true
const res = await getEmployeeList({
keyword: search.value,
searchField: select.value,
status: filterStatus.value,
department: filterDepartment.value
})
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 重置搜索
const resetSearch = () => {
search.value = ''
filterStatus.value = ''
filterDepartment.value = ''
showSearchResults.value = false
showAddForm.value = false
fetchData()
}
// 清空搜索
const clearSearch = () => {
resetSearch()
}
// 清空表单
const resetForm = () => {
form.name = ''
form.gender = '男'
form.age = null
form.education = ''
form.department = ''
form.entry_date = ''
form.position = ''
form.salary = null
form.phone = ''
form.email = ''
form.status = 1
form.remark = ''
isEditMode.value = false
currentEmployeeId.value = null
}
// 显示新增表单
const addEmployee = () => {
resetForm()
showAddForm.value = true
showSearchResults.value = false
}
// 编辑
const editEmployee = (row) => {
isEditMode.value = true
currentEmployeeId.value = row.id
form.name = row.name
form.gender = row.gender
form.age = row.age
form.education = row.education
form.department = row.department
form.entry_date = row.entry_date
form.position = row.position
form.salary = row.salary
form.phone = row.phone
form.email = row.email
form.status = row.status
form.remark = row.remark
showAddForm.value = true
showSearchResults.value = false
}
// 保存员工
const saveEmployee = async () => {
const formData = {
name: form.name,
gender: form.gender,
age: form.age,
education: form.education,
department: form.department,
entry_date: form.entry_date,
position: form.position,
salary: form.salary,
phone: form.phone,
email: form.email,
status: form.status,
remark: form.remark
}
if (isEditMode.value) {
await updateEmployee(currentEmployeeId.value, formData)
ElMessage.success('更新成功')
} else {
await createEmployee(formData)
ElMessage.success('新增成功')
}
cancelAdd()
fetchData()
}
// 删除员工
const deleteEmployee = async (id) => {
ElMessageBox.confirm('确定删除该员工吗?', '提示', {
type: 'warning'
}).then(async () => {
await deleteEmployeeAPI(id)
ElMessage.success('删除成功')
fetchData()
})
}
// 取消
const cancelAdd = () => {
resetForm()
showAddForm.value = false
showSearchResults.value = true
}
// 双击行查看详情
const handleRowDblClick = (row) => {
currentDetail.value = { ...row }
showDetailDialog.value = true
}
// 关闭详情弹窗
const closeDetail = () => {
showDetailDialog.value = false
currentDetail.value = null
}
</script>
<template>
<h1>hello ,im 3</h1>
<div class="employee-container">
<!-- 搜索栏区域 -->
<div class="employee-search">
<!-- 第一行搜索框 + 按钮 -->
<div class="search-row">
<el-input v-model="search" style="max-width: 500px" placeholder="请输入搜索关键词"
class="employee-search-with-select" @keyup.enter="handleSearch">
<template #prepend>
<el-select v-model="select" placeholder="请选择" style="width: 115px">
<el-option label="ID" value="1" />
<el-option label="姓名" value="2" />
<el-option label="部门" value="3" />
</el-select>
</template>
<template #append>
<el-button :icon="Search" @click="handleSearch" />
</template>
</el-input>
<div class="search-buttons">
<el-button type="success" @click="addEmployee">新增员工</el-button>
<el-button type="default" @click="resetSearch">重置</el-button>
</div>
</div>
<!-- 第二行筛选条件 -->
<div class="filter-row">
<span class="filter-label">筛选条件</span>
<el-select v-model="filterDepartment" placeholder="所属部门" clearable style="width: 140px; margin-right: 12px;">
<el-option v-for="dept in departmentOptions" :key="dept" :label="dept" :value="dept" />
</el-select>
<el-select v-model="filterStatus" placeholder="在职状态" clearable style="width: 140px;">
<el-option label="在职" :value="1" />
<el-option label="离职" :value="0" />
</el-select>
</div>
</div>
<div v-if="showSearchResults" class="employee-list">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<div v-else-if="searchResults.length === 0" class="empty-container">
<el-empty description="暂无数据" />
</div>
<div v-else class="employee-table">
<el-table :data="searchResults" v-loading="loading" border style="width: 100%" @row-dblclick="handleRowDblClick">
<el-table-column prop="id" label="ID" width="60" />
<el-table-column prop="name" label="姓名" width="80" />
<el-table-column prop="gender" label="性别" width="60">
<template #default="{ row }">
<span :style="{ color: row.gender === '女' ? '#e6a23c' : '#409eff' }">{{ row.gender }}</span>
</template>
</el-table-column>
<el-table-column prop="age" label="年龄" width="60" />
<el-table-column prop="education" label="学历" width="70" />
<el-table-column prop="department" label="部门" width="90" />
<el-table-column prop="position" label="职务" width="110" />
<el-table-column prop="salary" label="工资" width="100">
<template #default="{ row }">
¥{{ row.salary?.toLocaleString() }}
</template>
</el-table-column>
<el-table-column prop="phone" label="联系电话" width="130" />
<el-table-column prop="entry_date" label="入职时间" width="110" />
<el-table-column prop="status" label="状态" width="70">
<template #default="{ row }">
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
{{ row.status === 1 ? '在职' : '离职' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="editEmployee(row)">编辑</el-button>
<el-button link type="danger" @click="deleteEmployee(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; color: #909399; font-size: 14px;">
找到 {{ searchResults.length }} 条结果
<el-button link type="primary" @click="clearSearch">清空搜索</el-button>
</div>
</div>
</div>
<!-- 新增/编辑员工表单 -->
<div v-if="showAddForm" class="employee-info-label">
<el-divider content-position="left">{{ isEditMode ? '编辑员工信息' : '新增员工' }}</el-divider>
<el-form :model="form" label-width="auto" style="max-width: 700px" label-position="top">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="员工姓名" required>
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" required>
<el-radio-group v-model="form.gender">
<el-radio value="男"></el-radio>
<el-radio value="女"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="年龄">
<el-input-number v-model="form.age" :min="18" :max="65" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学历">
<el-select v-model="form.education" placeholder="请选择学历" style="width: 100%;">
<el-option v-for="edu in educationOptions" :key="edu" :label="edu" :value="edu" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属部门" required>
<el-select v-model="form.department" placeholder="请选择部门" style="width: 100%;">
<el-option v-for="dept in departmentOptions" :key="dept" :label="dept" :value="dept" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="职务/岗位" required>
<el-input v-model="form.position" placeholder="请输入职务" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="入职时间" required>
<el-date-picker v-model="form.entry_date" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工资">
<el-input-number v-model="form.salary" :min="0" :step="500" :precision="2" style="width: 100%;" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="联系电话">
<el-input v-model="form.phone" placeholder="请输入手机号" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="电子邮箱">
<el-input v-model="form.email" placeholder="请输入邮箱" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="在职状态" required>
<el-radio-group v-model="form.status">
<el-radio :value="1">在职</el-radio>
<el-radio :value="0">离职</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注信息" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveEmployee">保存</el-button>
<el-button type="danger" @click="cancelAdd">取消</el-button>
</el-form-item>
</el-form>
</div>
<!-- 员工详情弹窗 -->
<el-dialog v-model="showDetailDialog" title="员工详情" width="650px" @close="closeDetail">
<div v-if="currentDetail" class="detail-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="员工ID">{{ currentDetail.id }}</el-descriptions-item>
<el-descriptions-item label="姓名">{{ currentDetail.name }}</el-descriptions-item>
<el-descriptions-item label="性别">
<span :style="{ color: currentDetail.gender === '女' ? '#e6a23c' : '#409eff' }">{{ currentDetail.gender }}</span>
</el-descriptions-item>
<el-descriptions-item label="年龄">{{ currentDetail.age }}</el-descriptions-item>
<el-descriptions-item label="学历">{{ currentDetail.education }}</el-descriptions-item>
<el-descriptions-item label="部门">{{ currentDetail.department }}</el-descriptions-item>
<el-descriptions-item label="职务">{{ currentDetail.position }}</el-descriptions-item>
<el-descriptions-item label="工资">
<span class="amount-text">¥{{ currentDetail.salary?.toLocaleString() }}</span>
</el-descriptions-item>
<el-descriptions-item label="入职时间">{{ currentDetail.entry_date }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ currentDetail.phone }}</el-descriptions-item>
<el-descriptions-item label="电子邮箱" :span="2">{{ currentDetail.email }}</el-descriptions-item>
<el-descriptions-item label="在职状态">
<el-tag :type="currentDetail.status === 1 ? 'success' : 'danger'">
{{ currentDetail.status === 1 ? '在职' : '离职' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ currentDetail.created_at }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ currentDetail.remark || '无' }}
</el-descriptions-item>
</el-descriptions>
</div>
<template #footer>
<el-button @click="closeDetail">关闭</el-button>
<el-button type="primary" @click="closeDetail(); editEmployee(currentDetail)">编辑</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>

View File

@@ -1,9 +1,360 @@
<script setup>
import { Search } from '@element-plus/icons-vue'
import { ref, onMounted, reactive, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getProductsList,
createProducts,
updateProducts,
deleteProductsAPI
} from '../api/products'
const form = reactive({
name: '',
type: '',
quantity: 0,
price: 0,
unit: '件',
specification: '',
supplier: '',
remark: ''
})
const search = ref('')
const select = ref('2')
const filterType = ref('')
const searchResults = ref([])
const showSearchResults = ref(false)
const showAddForm = ref(false)
const isEditMode = ref(false)
const currentProductId = ref(null)
const loading = ref(false)
const showDetailDialog = ref(false)
const currentDetail = ref(null)
// 产品类型映射
const productTypeMap = {
raw_material: '原材料',
packaging: '包装材料',
equipment: '设备器具',
cleaning: '清洁用品',
promote: '宣传物料'
}
onMounted(() => {
fetchData()
})
// 监听筛选变化
watch(filterType, () => {
handleSearch()
})
// 获取产品列表
const fetchData = async () => {
loading.value = true
const res = await getProductsList()
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 搜索
const handleSearch = async () => {
loading.value = true
const res = await getProductsList({
keyword: search.value,
searchField: select.value,
type: filterType.value
})
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 重置搜索
const resetSearch = () => {
search.value = ''
filterType.value = ''
showSearchResults.value = false
showAddForm.value = false
fetchData()
}
// 清空搜索
const clearSearch = () => {
resetSearch()
}
// 清空表单
const resetForm = () => {
form.name = ''
form.type = ''
form.quantity = 0
form.price = 0
form.unit = '件'
form.specification = ''
form.supplier = ''
form.remark = ''
isEditMode.value = false
currentProductId.value = null
}
// 显示新增表单
const addProduct = () => {
resetForm()
showAddForm.value = true
showSearchResults.value = false
}
// 编辑
const editProduct = (row) => {
isEditMode.value = true
currentProductId.value = row.id
form.name = row.name
form.type = row.type
form.quantity = row.quantity
form.price = row.price
form.unit = row.unit
form.specification = row.specification
form.supplier = row.supplier
form.remark = row.remark
showAddForm.value = true
showSearchResults.value = false
}
// 保存产品
const saveProduct = async () => {
const formData = {
name: form.name,
type: form.type,
quantity: form.quantity,
price: form.price,
unit: form.unit,
specification: form.specification,
supplier: form.supplier,
remark: form.remark
}
if (isEditMode.value) {
await updateProducts(currentProductId.value, formData)
ElMessage.success('更新成功')
} else {
await createProducts(formData)
ElMessage.success('新增成功')
}
cancelAdd()
fetchData()
}
// 删除产品
const deleteProduct = async (id) => {
ElMessageBox.confirm('确定删除该产品吗?', '提示', {
type: 'warning'
}).then(async () => {
await deleteProductsAPI(id)
ElMessage.success('删除成功')
fetchData()
})
}
// 取消
const cancelAdd = () => {
resetForm()
showAddForm.value = false
showSearchResults.value = true
}
// 双击行查看详情
const handleRowDblClick = (row) => {
currentDetail.value = { ...row }
showDetailDialog.value = true
}
// 关闭详情弹窗
const closeDetail = () => {
showDetailDialog.value = false
currentDetail.value = null
}
// 产品类型标签样式
const getTypeTag = (type) => {
const map = {
raw_material: '',
packaging: 'success',
equipment: 'warning',
cleaning: 'info',
promote: 'danger'
}
return map[type] || 'info'
}
</script>
<template>
<h1>hello ,im 3</h1>
<div class="products-container">
<!-- 搜索栏区域 -->
<div class="products-search">
<!-- 第一行搜索框 + 按钮 -->
<div class="search-row">
<el-input v-model="search" style="max-width: 500px" placeholder="请输入搜索关键词"
class="products-search-with-select" @keyup.enter="handleSearch">
<template #prepend>
<el-select v-model="select" placeholder="请选择" style="width: 115px">
<el-option label="ID" value="1" />
<el-option label="名称" value="2" />
<el-option label="供应商" value="3" />
</el-select>
</template>
<template #append>
<el-button :icon="Search" @click="handleSearch" />
</template>
</el-input>
<div class="search-buttons">
<el-button type="success" @click="addProduct">新增产品</el-button>
<el-button type="default" @click="resetSearch">重置</el-button>
</div>
</div>
<!-- 第二行筛选条件 -->
<div class="filter-row">
<span class="filter-label">筛选条件</span>
<el-select v-model="filterType" placeholder="产品类型" clearable style="width: 140px; margin-right: 12px;">
<el-option label="原材料" value="raw_material" />
<el-option label="包装材料" value="packaging" />
<el-option label="设备器具" value="equipment" />
<el-option label="清洁用品" value="cleaning" />
<el-option label="宣传物料" value="promote" />
</el-select>
</div>
</div>
<div v-if="showSearchResults" class="products-list">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<div v-else-if="searchResults.length === 0" class="empty-container">
<el-empty description="暂无数据" />
</div>
<div v-else class="products-table">
<el-table :data="searchResults" v-loading="loading" border style="width: 100%" @row-dblclick="handleRowDblClick">
<el-table-column prop="id" label="ID" width="70" />
<el-table-column prop="name" label="产品名称" min-width="120" />
<el-table-column prop="type" label="产品类型" width="110">
<template #default="{ row }">
<el-tag :type="getTypeTag(row.type)">
{{ productTypeMap[row.type] || row.type }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="quantity" label="库存" width="80" />
<el-table-column prop="price" label="单价" width="100">
<template #default="{ row }">
¥{{ row.price?.toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="unit" label="单位" width="70" />
<el-table-column prop="specification" label="规格" min-width="140" show-overflow-tooltip />
<el-table-column prop="supplier" label="供应商" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="editProduct(row)">编辑</el-button>
<el-button link type="danger" @click="deleteProduct(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; color: #909399; font-size: 14px;">
找到 {{ searchResults.length }} 条结果
<el-button link type="primary" @click="clearSearch">清空搜索</el-button>
</div>
</div>
</div>
<!-- 新增/编辑产品表单 -->
<div v-if="showAddForm" class="products-info-label">
<el-divider content-position="left">{{ isEditMode ? '编辑产品信息' : '新增产品' }}</el-divider>
<el-form :model="form" label-width="auto" style="max-width: 600px" label-position="top">
<el-form-item label="产品名称" required>
<el-input v-model="form.name" placeholder="请输入产品名称" />
</el-form-item>
<el-form-item label="产品类型" required>
<el-select v-model="form.type" placeholder="请选择产品类型" style="width: 100%;">
<el-option label="原材料" value="raw_material" />
<el-option label="包装材料" value="packaging" />
<el-option label="设备器具" value="equipment" />
<el-option label="清洁用品" value="cleaning" />
<el-option label="宣传物料" value="promote" />
</el-select>
</el-form-item>
<el-form-item label="产品库存" required>
<el-input-number v-model="form.quantity" :min="0" :step="10" style="width: 100%;" />
</el-form-item>
<el-form-item label="产品单价" required>
<el-input-number v-model="form.price" :min="0" :step="0.01" :precision="2" style="width: 100%;" />
</el-form-item>
<el-form-item label="计量单位">
<el-input v-model="form.unit" placeholder="请输入计量单位" />
</el-form-item>
<el-form-item label="产品规格">
<el-input v-model="form.specification" placeholder="请输入产品规格/型号" />
</el-form-item>
<el-form-item label="供应商">
<el-input v-model="form.supplier" placeholder="请输入供应商名称" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注信息" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveProduct">保存</el-button>
<el-button type="danger" @click="cancelAdd">取消</el-button>
</el-form-item>
</el-form>
</div>
<!-- 产品详情弹窗 -->
<el-dialog v-model="showDetailDialog" title="产品详情" width="650px" @close="closeDetail">
<div v-if="currentDetail" class="detail-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="产品编号">{{ currentDetail.id }}</el-descriptions-item>
<el-descriptions-item label="产品名称">{{ currentDetail.name }}</el-descriptions-item>
<el-descriptions-item label="产品类型">
<el-tag :type="getTypeTag(currentDetail.type)">
{{ productTypeMap[currentDetail.type] || currentDetail.type }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="库存数量">{{ currentDetail.quantity }}</el-descriptions-item>
<el-descriptions-item label="产品单价">
<span class="amount-text">¥{{ currentDetail.price?.toFixed(2) }}</span>
</el-descriptions-item>
<el-descriptions-item label="计量单位">{{ currentDetail.unit }}</el-descriptions-item>
<el-descriptions-item label="产品规格" :span="2">{{ currentDetail.specification || '无' }}</el-descriptions-item>
<el-descriptions-item label="供应商" :span="2">{{ currentDetail.supplier || '无' }}</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ currentDetail.remark || '无' }}
</el-descriptions-item>
</el-descriptions>
</div>
<template #footer>
<el-button @click="closeDetail">关闭</el-button>
<el-button type="primary" @click="closeDetail(); editProduct(currentDetail)">编辑</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>

View File

@@ -1,9 +1,365 @@
<script setup>
import { Search } from '@element-plus/icons-vue'
import { ref, onMounted, reactive, watch, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getServiceList,
createService,
updateService,
deleteServiceAPI
} from '../api/service'
import { getCustomerList } from '../api/customer'
import { getEmployeeList } from '../api/employee'
const form = reactive({
customer_id: '',
feedback: '',
employee_id: '',
handle_method: '',
handle_status: '待处理',
service_date: '',
remark: ''
})
const search = ref('')
const select = ref('1')
const filterStatus = ref('')
const searchResults = ref([])
const showSearchResults = ref(false)
const showAddForm = ref(false)
const isEditMode = ref(false)
const currentServiceId = ref(null)
const loading = ref(false)
const showDetailDialog = ref(false)
const currentDetail = ref(null)
const customerList = ref([])
const employeeList = ref([])
// 状态映射
const statusMap = {
'待处理': 'warning',
'处理中': '',
'已完成': 'success'
}
onMounted(() => {
fetchData()
fetchOptions()
})
// 获取客户和员工列表供下拉选择
const fetchOptions = async () => {
const [cRes, eRes] = await Promise.all([getCustomerList(), getEmployeeList()])
customerList.value = cRes.data
employeeList.value = eRes.data
}
// 监听状态变化:待处理时清空业务员
watch(() => form.handle_status, (val) => {
if (val === '待处理') {
form.employee_id = ''
}
})
// 监听筛选变化
watch(filterStatus, () => {
handleSearch()
})
// 获取售后列表
const fetchData = async () => {
loading.value = true
const res = await getServiceList()
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 搜索
const handleSearch = async () => {
loading.value = true
const res = await getServiceList({
keyword: search.value,
searchField: select.value,
handle_status: filterStatus.value
})
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 重置搜索
const resetSearch = () => {
search.value = ''
filterStatus.value = ''
showSearchResults.value = false
showAddForm.value = false
fetchData()
}
// 清空搜索
const clearSearch = () => {
resetSearch()
}
// 清空表单
const resetForm = () => {
form.customer_id = ''
form.feedback = ''
form.employee_id = ''
form.handle_method = ''
form.handle_status = '待处理'
form.service_date = ''
form.remark = ''
isEditMode.value = false
currentServiceId.value = null
}
// 显示新增表单
const addService = () => {
resetForm()
showAddForm.value = true
showSearchResults.value = false
}
// 编辑
const editService = (row) => {
isEditMode.value = true
currentServiceId.value = row.id
form.customer_id = row.customer_id
form.feedback = row.feedback
form.employee_id = row.employee_id
form.handle_method = row.handle_method
form.handle_status = row.handle_status
form.service_date = row.service_date
form.remark = row.remark
showAddForm.value = true
showSearchResults.value = false
}
// 保存售后
const saveService = async () => {
// 逻辑校验:处理中/已完成必须有业务员
if (form.handle_status !== '待处理' && !form.employee_id) {
ElMessage.warning('处理中和已完成的售后必须指定业务员')
return
}
const formData = {
customer_id: form.customer_id,
feedback: form.feedback,
employee_id: form.handle_status === '待处理' ? null : form.employee_id,
handle_method: form.handle_method,
handle_status: form.handle_status,
service_date: form.service_date,
remark: form.remark
}
if (isEditMode.value) {
await updateService(currentServiceId.value, formData)
ElMessage.success('更新成功')
} else {
await createService(formData)
ElMessage.success('新增成功')
}
cancelAdd()
fetchData()
}
// 删除售后
const deleteService = async (id) => {
ElMessageBox.confirm('确定删除该售后记录吗?', '提示', {
type: 'warning'
}).then(async () => {
await deleteServiceAPI(id)
ElMessage.success('删除成功')
fetchData()
})
}
// 取消
const cancelAdd = () => {
resetForm()
showAddForm.value = false
showSearchResults.value = true
}
// 双击行查看详情
const handleRowDblClick = (row) => {
currentDetail.value = { ...row }
showDetailDialog.value = true
}
// 关闭详情弹窗
const closeDetail = () => {
showDetailDialog.value = false
currentDetail.value = null
}
</script>
<template>
<h1>hello ,im 3</h1>
<div class="service-container">
<!-- 搜索栏区域 -->
<div class="service-search">
<!-- 第一行搜索框 + 按钮 -->
<div class="search-row">
<el-input v-model="search" style="max-width: 500px" placeholder="请输入搜索关键词"
class="service-search-with-select" @keyup.enter="handleSearch">
<template #prepend>
<el-select v-model="select" placeholder="请选择" style="width: 115px">
<el-option label="ID" value="1" />
<el-option label="客户ID" value="2" />
<el-option label="反馈内容" value="3" />
</el-select>
</template>
<template #append>
<el-button :icon="Search" @click="handleSearch" />
</template>
</el-input>
<div class="search-buttons">
<el-button type="success" @click="addService">新增售后</el-button>
<el-button type="default" @click="resetSearch">重置</el-button>
</div>
</div>
<!-- 第二行筛选条件 -->
<div class="filter-row">
<span class="filter-label">筛选条件</span>
<el-select v-model="filterStatus" placeholder="处理状态" clearable style="width: 140px;">
<el-option label="待处理" value="待处理" />
<el-option label="处理中" value="处理中" />
<el-option label="已完成" value="已完成" />
</el-select>
</div>
</div>
<div v-if="showSearchResults" class="service-list">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<div v-else-if="searchResults.length === 0" class="empty-container">
<el-empty description="暂无数据" />
</div>
<div v-else class="service-table">
<el-table :data="searchResults" v-loading="loading" border style="width: 100%" @row-dblclick="handleRowDblClick">
<el-table-column prop="id" label="ID" width="70" />
<el-table-column prop="customer_id" label="客户ID" width="90" />
<el-table-column prop="feedback" label="反馈内容" min-width="200" show-overflow-tooltip />
<el-table-column prop="employee_id" label="业务员ID" width="100">
<template #default="{ row }">
{{ row.employee_id || '—' }}
</template>
</el-table-column>
<el-table-column prop="handle_status" label="处理状态" width="100">
<template #default="{ row }">
<el-tag :type="statusMap[row.handle_status]">
{{ row.handle_status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="service_date" label="售后日期" width="120" />
<el-table-column prop="handle_method" label="处理方式" min-width="180" show-overflow-tooltip />
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="editService(row)">编辑</el-button>
<el-button link type="danger" @click="deleteService(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; color: #909399; font-size: 14px;">
找到 {{ searchResults.length }} 条结果
<el-button link type="primary" @click="clearSearch">清空搜索</el-button>
</div>
</div>
</div>
<!-- 新增/编辑售后表单 -->
<div v-if="showAddForm" class="service-info-label">
<el-divider content-position="left">{{ isEditMode ? '编辑售后记录' : '新增售后' }}</el-divider>
<el-form :model="form" label-width="auto" style="max-width: 600px" label-position="top">
<el-form-item label="客户" required>
<el-select v-model="form.customer_id" placeholder="请选择客户" style="width: 100%;" filterable>
<el-option v-for="c in customerList" :key="c.id" :label="`${c.id} - ${c.name}`" :value="c.id" />
</el-select>
</el-form-item>
<el-form-item label="反馈内容" required>
<el-input v-model="form.feedback" type="textarea" :rows="3" placeholder="请输入客户反馈意见/售后内容" />
</el-form-item>
<el-form-item label="处理状态" required>
<el-select v-model="form.handle_status" style="width: 100%;">
<el-option label="待处理" value="待处理" />
<el-option label="处理中" value="处理中" />
<el-option label="已完成" value="已完成" />
</el-select>
</el-form-item>
<el-form-item label="售后日期" required>
<el-date-picker v-model="form.service_date" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" style="width: 100%;" />
</el-form-item>
<el-form-item label="处理业务员">
<el-select v-model="form.employee_id" placeholder="请选择业务员" style="width: 100%;" filterable clearable
:disabled="form.handle_status === '待处理'">
<el-option v-for="e in employeeList" :key="e.id" :label="`${e.id} - ${e.name}`" :value="e.id" />
</el-select>
</el-form-item>
<el-form-item label="处理方式">
<el-input v-model="form.handle_method" type="textarea" :rows="3" placeholder="请输入处理方式/解决方案" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注信息" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveService">保存</el-button>
<el-button type="danger" @click="cancelAdd">取消</el-button>
</el-form-item>
</el-form>
</div>
<!-- 售后详情弹窗 -->
<el-dialog v-model="showDetailDialog" title="售后详情" width="650px" @close="closeDetail">
<div v-if="currentDetail" class="detail-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="售后编号">{{ currentDetail.id }}</el-descriptions-item>
<el-descriptions-item label="售后日期">{{ currentDetail.service_date }}</el-descriptions-item>
<el-descriptions-item label="客户ID">{{ currentDetail.customer_id }}</el-descriptions-item>
<el-descriptions-item label="业务员ID">{{ currentDetail.employee_id || '未分配' }}</el-descriptions-item>
<el-descriptions-item label="处理状态">
<el-tag :type="statusMap[currentDetail.handle_status]">
{{ currentDetail.handle_status }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ currentDetail.created_at }}</el-descriptions-item>
<el-descriptions-item label="反馈内容" :span="2">
<div class="content-text">{{ currentDetail.feedback }}</div>
</el-descriptions-item>
<el-descriptions-item label="处理方式" :span="2">
<div class="content-text">{{ currentDetail.handle_method || '暂无' }}</div>
</el-descriptions-item>
<el-descriptions-item label="备注" :span="2">
{{ currentDetail.remark || '无' }}
</el-descriptions-item>
</el-descriptions>
</div>
<template #footer>
<el-button @click="closeDetail">关闭</el-button>
<el-button type="primary" @click="closeDetail(); editService(currentDetail)">编辑</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>

View File

@@ -1,9 +1,318 @@
<script setup>
import { Search, Lock } from '@element-plus/icons-vue'
import { ref, onMounted, reactive, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
getUserList,
createUser,
updateUser,
deleteUserAPI
} from '../api/user'
const form = reactive({
username: '',
password: '',
name: '',
user_type: 'user',
status: '1'
})
const search = ref('')
const select = ref('1')
const filterType = ref('')
const filterStatus = ref('')
const searchResults = ref([])
const showSearchResults = ref(false)
const showAddForm = ref(false)
const isEditMode = ref(false)
const currentUserId = ref(null)
const loading = ref(false)
const showDetailDialog = ref(false)
const currentDetail = ref(null)
onMounted(() => {
fetchData()
})
// 监听筛选变化,自动触发查询
watch([filterType, filterStatus], () => {
handleSearch()
})
// 获取用户列表
const fetchData = async () => {
loading.value = true
const res = await getUserList()
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 搜索
const handleSearch = async () => {
loading.value = true
const res = await getUserList({
keyword: search.value,
searchField: select.value,
user_type: filterType.value,
status: filterStatus.value
})
searchResults.value = res.data
showSearchResults.value = true
loading.value = false
}
// 清空表单
const resetForm = () => {
form.username = ''
form.password = ''
form.name = ''
form.user_type = 'user'
form.status = '1'
isEditMode.value = false
currentUserId.value = null
}
// 显示新增表单
const addUser = () => {
resetForm()
showAddForm.value = true
showSearchResults.value = false
}
// 编辑
const editUser = (row) => {
isEditMode.value = true
currentUserId.value = row.id
form.username = row.username
form.password = ''
form.name = row.name
form.user_type = row.user_type
form.status = row.status
showAddForm.value = true
showSearchResults.value = false
}
// 保存用户
const saveUser = async () => {
const formData = {
username: form.username,
name: form.name,
user_type: form.user_type,
status: form.status
}
if (!isEditMode.value) {
formData.password = form.password
} else if (form.password) {
formData.password = form.password
}
if (isEditMode.value) {
await updateUser(currentUserId.value, formData)
ElMessage.success('更新成功')
} else {
await createUser(formData)
ElMessage.success('新增成功')
}
cancelAdd()
fetchData()
}
// 删除用户
const deleteUser = async (id) => {
ElMessageBox.confirm('确定删除该用户吗?', '提示', {
type: 'warning'
}).then(async () => {
await deleteUserAPI(id)
ElMessage.success('删除成功')
fetchData()
})
}
// 取消
const cancelAdd = () => {
resetForm()
showAddForm.value = false
showSearchResults.value = true
}
// 清空搜索
const clearSearch = () => {
search.value = ''
filterType.value = ''
filterStatus.value = ''
showSearchResults.value = false
showAddForm.value = false
fetchData()
}
// 双击行查看详情
const handleRowDblClick = (row) => {
currentDetail.value = { ...row }
showDetailDialog.value = true
}
// 关闭详情弹窗
const closeDetail = () => {
showDetailDialog.value = false
currentDetail.value = null
}
</script>
<template>
<h1>hello ,im 3</h1>
<div class="user-container">
<!-- 搜索栏区域 -->
<div class="user-search">
<el-input v-model="search" style="max-width: 600px" placeholder="Please input"
class="user-search-with-select" @keyup.enter="handleSearch">
<template #prepend>
<el-select v-model="select" placeholder="Select" style="width: 115px">
<el-option label="用户名" value="1" />
<el-option label="编号" value="2" />
<el-option label="真实姓名" value="3" />
</el-select>
</template>
<template #append>
<el-button :icon="Search" @click="handleSearch" />
</template>
</el-input>
<el-button type="default" @click="clearSearch" style="margin-left:20px">
重置
</el-button>
<el-button color="#E60012" @click="addUser" style="margin-left: 20px">
新增用户
</el-button>
<div class="filter-row">
<span class="filter-label">筛选条件</span>
<el-select v-model="filterType" placeholder="用户类型" clearable style="width: 140px; margin-right: 12px;">
<el-option label="管理员" value="admin" />
<el-option label="普通用户" value="user" />
</el-select>
<el-select v-model="filterStatus" placeholder="用户状态" clearable style="width: 140px;">
<el-option label="启用" value="1" />
<el-option label="封禁中" value="0" />
</el-select>
</div>
</div>
<div v-if="showSearchResults" class="user-list">
<div v-if="loading" class="loading-container">
<el-skeleton :rows="5" animated />
</div>
<div v-else-if="searchResults.length === 0" class="empty-container">
<el-empty description="暂无数据" />
</div>
<div v-else class="user-table">
<el-table :data="searchResults" v-loading="loading" border style="width: 100%" @row-dblclick="handleRowDblClick">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="username" label="用户名" width="120" />
<el-table-column prop="name" label="真实姓名" width="120" />
<el-table-column prop="createdate" label="创建时间" width="200" />
<el-table-column prop="user_type" label="用户类型" width="120">
<template #default="{ row }">
<el-tag :type="row.user_type === 'admin' ? 'danger' : 'info'">
{{ row.user_type === 'admin' ? '管理员' : '普通用户' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="用户状态" width="120">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'danger' : 'info'">
{{ row.status === '0' ? '封禁中' : '启用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<template #default="{ row }">
<el-button link type="primary" @click="editUser(row)">编辑</el-button>
<el-button link type="danger" @click="deleteUser(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; color: #909399; font-size: 14px;">
找到 {{ searchResults.length }} 条结果
<el-button link type="primary" @click="clearSearch">清空搜索</el-button>
</div>
</div>
</div>
<!-- 新增用户表单 -->
<div v-if="showAddForm" class="user-info-label">
<el-divider content-position="left">{{ isEditMode ? '编辑用户信息' : '新增用户' }}</el-divider>
<el-form :model="form" label-width="auto" style="max-width: 600px" label-position="top">
<el-form-item label="用户名" required>
<el-input v-model="form.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="密码" :required="!isEditMode">
<el-input v-model="form.password" :prefix-icon="Lock" clearable placeholder="请输入密码"
show-password type="password" />
</el-form-item>
<el-form-item label="真实姓名" required>
<el-input v-model="form.name" placeholder=" 请输入用户真实姓名" />
</el-form-item>
<el-form-item label="用户类型" required>
<el-radio-group v-model="form.user_type">
<el-radio value="admin">管理员</el-radio>
<el-radio value="user">普通用户</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户状态" required>
<el-radio-group v-model="form.status">
<el-radio value="0">禁用</el-radio>
<el-radio value="1">启用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveUser">保存</el-button>
<el-button type="danger" @click="cancelAdd">取消</el-button>
</el-form-item>
</el-form>
</div>
<!-- 用户详情弹窗 -->
<el-dialog v-model="showDetailDialog" title="用户详情" width="550px" @close="closeDetail">
<div v-if="currentDetail" class="detail-content">
<el-descriptions :column="2" border>
<el-descriptions-item label="用户ID">{{ currentDetail.id }}</el-descriptions-item>
<el-descriptions-item label="用户名">{{ currentDetail.username }}</el-descriptions-item>
<el-descriptions-item label="真实姓名">{{ currentDetail.name }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ currentDetail.createdate }}</el-descriptions-item>
<el-descriptions-item label="用户类型">
<el-tag :type="currentDetail.user_type === 'admin' ? 'danger' : 'info'">
{{ currentDetail.user_type === 'admin' ? '管理员' : '普通用户' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="用户状态">
<el-tag :type="currentDetail.status === '0' ? 'danger' : 'info'">
{{ currentDetail.status === '0' ? '封禁中' : '启用' }}
</el-tag>
</el-descriptions-item>
</el-descriptions>
</div>
<template #footer>
<el-button @click="closeDetail">关闭</el-button>
<el-button type="primary" @click="closeDetail(); editUser(currentDetail)">编辑</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>