From 984d5b81189b64751616d9cbbb4324806054e843 Mon Sep 17 00:00:00 2001 From: ChoChoX Date: Thu, 25 Jun 2026 21:49:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=9A=82=E6=9C=AA=E5=8F=91?= =?UTF-8?q?=E7=8E=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/contract.js | 27 +-- src/api/customer.js | 29 +-- src/api/employee.js | 34 +-- src/api/mock/contract.js | 6 +- src/api/mock/customer.js | 9 - src/api/mock/employee.js | 39 ++- src/api/mock/user.js | 149 ++++++------ src/api/products.js | 27 +-- src/api/request.js | 53 ++++ src/api/service.js | 27 +-- src/api/suppliers.js | 26 ++ src/api/user.js | 36 +-- src/components/Login.vue | 11 +- src/components/contract.vue | 348 ++++++++++++++++++++------- src/components/customer.vue | 279 +++++++++++++++------ src/components/employee.vue | 360 ++++++++++++++++++++++++---- src/components/panel.vue | 76 ++++-- src/components/products.vue | 228 +++++++++++++++--- src/components/service.vue | 243 +++++++++++++++---- src/components/supplier.vue | 466 ++++++++++++++++++++++++++++++++++++ src/components/user.vue | 391 ++++++++++++++++++++++-------- src/router.js | 53 +++- src/store/user.js | 72 ++++++ src/utils/date.js | 32 +++ vite.config.js | 9 + yarn.lock | 20 ++ 26 files changed, 2414 insertions(+), 636 deletions(-) create mode 100644 src/api/request.js create mode 100644 src/api/suppliers.js create mode 100644 src/components/supplier.vue create mode 100644 src/store/user.js create mode 100644 src/utils/date.js diff --git a/src/api/contract.js b/src/api/contract.js index 16f7c0f..dd8fdc4 100644 --- a/src/api/contract.js +++ b/src/api/contract.js @@ -1,41 +1,26 @@ -import axios from 'axios' -import { mockContractAPI } from './mock/contract' - -// 开关:true 用 mock,false 用真实接口 -const USE_MOCK = true - -// axios 实例 -const request = axios.create({ - baseURL: '/api', - timeout: 5000 -}) +import request from './request' // 获取合同列表 export const getContractList = (params) => { - if (USE_MOCK) return mockContractAPI.getList(params) - return request.get('/contracts', { params }).then(r => r.data) + return request.get('/contracts', { params }) } // 获取合同详情 export const getContractDetail = (id) => { - if (USE_MOCK) return mockContractAPI.getDetail(id) - return request.get(`/contracts/${id}`).then(r => r.data) + return request.get(`/contracts/${id}`) } // 新增合同 export const createContract = (data) => { - if (USE_MOCK) return mockContractAPI.create(data) - return request.post('/contracts', data).then(r => r.data) + return request.post('/contracts', data) } // 更新合同 export const updateContract = (id, data) => { - if (USE_MOCK) return mockContractAPI.update(id, data) - return request.put(`/contracts/${id}`, data).then(r => r.data) + return request.put(`/contracts/${id}`, data) } // 删除合同 export const deleteContractAPI = (id) => { - if (USE_MOCK) return mockContractAPI.delete(id) - return request.delete(`/contracts/${id}`).then(r => r.data) + return request.delete(`/contracts/${id}`) } diff --git a/src/api/customer.js b/src/api/customer.js index 26747e2..179811f 100644 --- a/src/api/customer.js +++ b/src/api/customer.js @@ -1,41 +1,26 @@ -import axios from 'axios' -import { mockCustomerAPI } from './mock/customer' - -// 开关:true 用 mock,false 用真实接口 -const USE_MOCK = true - -// axios 实例 -const request = axios.create({ - baseURL: '/api', - timeout: 5000 -}) +import request from './request' // 获取客户列表 -export const getCustomerList = (params) => { - if (USE_MOCK) return mockCustomerAPI.getList(params) - return request.get('/customers', { params }).then(r => r.data) +export const getCustomerList = (params, config) => { + return request.get('/customers', { params, ...config }) } // 获取客户详情 export const getCustomerDetail = (id) => { - if (USE_MOCK) return mockCustomerAPI.getDetail(id) - return request.get(`/customers/${id}`).then(r => r.data) + return request.get(`/customers/${id}`) } // 新增客户 export const createCustomer = (data) => { - if (USE_MOCK) return mockCustomerAPI.create(data) - return request.post('/customers', data).then(r => r.data) + return request.post('/customers', data) } // 更新客户 export const updateCustomer = (id, data) => { - if (USE_MOCK) return mockCustomerAPI.update(id, data) - return request.put(`/customers/${id}`, data).then(r => r.data) + return request.put(`/customers/${id}`, data) } // 删除客户 export const deleteCustomerAPI = (id) => { - if (USE_MOCK) return mockCustomerAPI.delete(id) - return request.delete(`/customers/${id}`).then(r => r.data) + return request.delete(`/customers/${id}`) } diff --git a/src/api/employee.js b/src/api/employee.js index 69646b4..4a2d414 100644 --- a/src/api/employee.js +++ b/src/api/employee.js @@ -1,41 +1,31 @@ -import axios from 'axios' -import { mockEmployeeAPI } from './mock/employee' - -// 开关:true 用 mock,false 用真实接口 -const USE_MOCK = true - -// axios 实例 -const request = axios.create({ - baseURL: '/api', - timeout: 5000 -}) +import request from './request' // 获取员工列表 -export const getEmployeeList = (params) => { - if (USE_MOCK) return mockEmployeeAPI.getList(params) - return request.get('/employees', { params }).then(r => r.data) +export const getEmployeeList = (params, config) => { + return request.get('/employees', { params, ...config }) +} + +// 获取简易员工列表(无数据范围限制,支持按部门过滤) +export const getSimpleEmployeeList = (params, config) => { + return request.get('/employees/simple', { params, ...config }) } // 获取员工详情 export const getEmployeeDetail = (id) => { - if (USE_MOCK) return mockEmployeeAPI.getDetail(id) - return request.get(`/employees/${id}`).then(r => r.data) + return request.get(`/employees/${id}`) } // 新增员工 export const createEmployee = (data) => { - if (USE_MOCK) return mockEmployeeAPI.create(data) - return request.post('/employees', data).then(r => r.data) + return request.post('/employees', data) } // 更新员工 export const updateEmployee = (id, data) => { - if (USE_MOCK) return mockEmployeeAPI.update(id, data) - return request.put(`/employees/${id}`, data).then(r => r.data) + return request.put(`/employees/${id}`, data) } // 删除员工 export const deleteEmployeeAPI = (id) => { - if (USE_MOCK) return mockEmployeeAPI.delete(id) - return request.delete(`/employees/${id}`).then(r => r.data) + return request.delete(`/employees/${id}`) } diff --git a/src/api/mock/contract.js b/src/api/mock/contract.js index 0099c2a..3de1c94 100644 --- a/src/api/mock/contract.js +++ b/src/api/mock/contract.js @@ -2,7 +2,6 @@ const mockContracts = [ { id: 1, - contract_no: 'HT-2025-001', contract_name: '蜜雪冰城加盟合同', type: '加盟合同', party_a: '蜜雪冰城股份有限公司', @@ -17,7 +16,6 @@ const mockContracts = [ }, { id: 2, - contract_no: 'HT-2025-002', contract_name: '原材料供货合同', type: '供货合同', party_a: '蜜雪冰城股份有限公司', @@ -32,7 +30,6 @@ const mockContracts = [ }, { id: 3, - contract_no: 'HT-2024-010', contract_name: '门店租赁合同', type: '租赁合同', party_a: '蜜雪冰城股份有限公司', @@ -47,7 +44,6 @@ const mockContracts = [ }, { id: 4, - contract_no: 'HT-2025-003', contract_name: '设备维护服务合同', type: '服务合同', party_a: '蜜雪冰城股份有限公司', @@ -76,7 +72,7 @@ export const mockContractAPI = { if (params.keyword) { const keyword = params.keyword.toLowerCase() result = result.filter(item => - item.contract_no.toLowerCase().includes(keyword) || + String(item.id).includes(keyword) || item.contract_name.toLowerCase().includes(keyword) || item.party_b.toLowerCase().includes(keyword) ) diff --git a/src/api/mock/customer.js b/src/api/mock/customer.js index f6cb27f..ee2b97f 100644 --- a/src/api/mock/customer.js +++ b/src/api/mock/customer.js @@ -9,7 +9,6 @@ const mockCustomers = [ district: '南山区', address: '科技园路1号', email: 'zhangsan@example.com', - customer_type: 'VIP', create_time: '2025-01-15 10:30:00' }, { @@ -21,7 +20,6 @@ const mockCustomers = [ district: '西湖区', address: '文三路100号', email: 'lisi@example.com', - customer_type: 'Normal', create_time: '2025-02-20 14:20:00' }, { @@ -33,7 +31,6 @@ const mockCustomers = [ district: '武侯区', address: '天府大道200号', email: 'wangwu@example.com', - customer_type: 'VIP', create_time: '2025-03-10 09:15:00' }, { @@ -45,7 +42,6 @@ const mockCustomers = [ district: '岳麓区', address: '麓山南路100号', email: 'zhaoliu@example.com', - customer_type: 'Normal', create_time: '2025-04-05 16:45:00' } ] @@ -88,11 +84,6 @@ export const mockCustomerAPI = { } } - // 客户类型筛选 - if (params.customer_type) { - result = result.filter(item => item.customer_type === params.customer_type) - } - // 地区筛选(省/市/区) if (params.province) { result = result.filter(item => item.province === params.province) diff --git a/src/api/mock/employee.js b/src/api/mock/employee.js index c6440c1..fcdc919 100644 --- a/src/api/mock/employee.js +++ b/src/api/mock/employee.js @@ -1,5 +1,5 @@ -// 模拟员工数据 -const mockEmployees = [ +// 模拟员工数据(单一数据源,同时包含用户字段) +export const mockEmployees = [ { id: 1, name: '张伟', @@ -14,6 +14,11 @@ const mockEmployees = [ email: 'zhangwei@mxbc.com', status: 1, remark: '负责门店日常运营', + // 用户字段 + username: 'zhangwei', + password: '123456', + user_type: 'admin', + user_status: '1', created_at: '2023-03-15 09:00:00', updated_at: '2023-03-15 09:00:00' }, @@ -31,6 +36,11 @@ const mockEmployees = [ email: 'lina@mxbc.com', status: 1, remark: '', + // 用户字段 + username: 'lina', + password: '123456', + user_type: 'user', + user_status: '1', created_at: '2023-06-01 09:00:00', updated_at: '2023-06-01 09:00:00' }, @@ -48,6 +58,11 @@ const mockEmployees = [ email: 'wangqiang@mxbc.com', status: 1, remark: '负责系统架构设计', + // 用户字段(无用户账号) + username: null, + password: null, + user_type: null, + user_status: null, created_at: '2022-11-10 09:00:00', updated_at: '2022-11-10 09:00:00' }, @@ -65,6 +80,11 @@ const mockEmployees = [ email: 'liuyang@mxbc.com', status: 0, remark: '已离职', + // 用户字段(无用户账号) + username: null, + password: null, + user_type: null, + user_status: null, created_at: '2024-01-20 09:00:00', updated_at: '2025-03-01 17:00:00' }, @@ -82,6 +102,11 @@ const mockEmployees = [ email: 'zhaomin@mxbc.com', status: 1, remark: '', + // 用户字段 + username: 'zhaomin', + password: '123456', + user_type: 'user', + user_status: '0', created_at: '2023-09-05 09:00:00', updated_at: '2023-09-05 09:00:00' }, @@ -99,6 +124,11 @@ const mockEmployees = [ email: 'chengang@mxbc.com', status: 1, remark: '负责原料入库出库', + // 用户字段(无用户账号) + username: null, + password: null, + user_type: null, + user_status: null, created_at: '2022-04-12 09:00:00', updated_at: '2022-04-12 09:00:00' } @@ -169,6 +199,11 @@ export const mockEmployeeAPI = { id: maxId + 1, status: 1, ...data, + // 用户字段初始为空 + username: null, + password: null, + user_type: null, + user_status: null, created_at: now, updated_at: now } diff --git a/src/api/mock/user.js b/src/api/mock/user.js index 21dfe29..46c401e 100644 --- a/src/api/mock/user.js +++ b/src/api/mock/user.js @@ -1,61 +1,26 @@ -// 模拟用户数据 -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' - } -] +import { mockEmployees } from './employee' // 模拟网络延迟 const delay = (ms = 300) => new Promise(r => setTimeout(r, ms)) -// Mock API +// 将员工记录转换为用户格式(字段名映射) +const toUserFormat = (employee) => ({ + id: employee.id, + username: employee.username, + name: employee.name, + user_type: employee.user_type, + status: employee.user_status, + employee_id: employee.id, + createdate: employee.created_at +}) + +// Mock API(操作同一数据源 mockEmployees) export const mockUserAPI = { - // 获取列表 + // 获取用户列表(返回有用户账号的员工记录) async getList(params = {}) { await delay() - let result = [...mockUsers] + // 筛选有用户名的记录(即已创建用户的员工) + let result = mockEmployees.filter(item => item.username).map(toUserFormat) // 关键词搜索 if (params.keyword) { @@ -67,16 +32,15 @@ export const mockUserAPI = { result = result.filter(item => String(item.id) === keyword) } else if (searchField === '1') { // 用户名:模糊匹配 - result = result.filter(item => item.username.includes(keyword)) + result = result.filter(item => item.username && item.username.includes(keyword)) } else if (searchField === '3') { // 真实姓名:模糊匹配 - result = result.filter(item => item.name.includes(keyword)) + result = result.filter(item => item.name.toLowerCase().includes(keyword)) } else { - // 默认:模糊匹配所有字段 result = result.filter(item => String(item.id).includes(keyword) || - item.username.includes(keyword) || - item.name.includes(keyword) + (item.username && item.username.includes(keyword)) || + item.name.toLowerCase().includes(keyword) ) } } @@ -94,47 +58,80 @@ export const mockUserAPI = { 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 } + const employee = mockEmployees.find(item => item.id === id && item.username) + if (employee) { + return { code: 200, data: toUserFormat(employee) } } 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) + // 查找对应的员工记录 + const employeeIndex = mockEmployees.findIndex(item => item.id === data.id || item.id === data.employee_id) + if (employeeIndex === -1) { + return { code: 404, message: '关联员工不存在' } } - mockUsers.push(newUser) - return { code: 200, data: newUser, message: '新增成功' } + + // 检查是否已有用户账号 + if (mockEmployees[employeeIndex].username) { + return { code: 400, message: '该员工已有用户账号' } + } + + // 更新员工记录,添加用户字段 + mockEmployees[employeeIndex] = { + ...mockEmployees[employeeIndex], + username: data.username, + password: data.password, + user_type: data.user_type || 'user', + user_status: data.status || '1', + updated_at: new Date().toISOString().replace('T', ' ').slice(0, 19) + } + + return { code: 200, data: toUserFormat(mockEmployees[employeeIndex]), message: '用户创建成功' } }, - // 更新 + // 更新用户(更新用户字段) async update(id, data) { await delay() - const index = mockUsers.findIndex(item => item.id === id) + const index = mockEmployees.findIndex(item => item.id === id && item.username) if (index !== -1) { - mockUsers[index] = { ...mockUsers[index], ...data } - return { code: 200, data: mockUsers[index], message: '更新成功' } + // 只更新用户相关字段 + const updateFields = {} + if (data.username !== undefined) updateFields.username = data.username + if (data.password !== undefined) updateFields.password = data.password + if (data.user_type !== undefined) updateFields.user_type = data.user_type + if (data.status !== undefined) updateFields.user_status = data.status + + mockEmployees[index] = { + ...mockEmployees[index], + ...updateFields, + updated_at: new Date().toISOString().replace('T', ' ').slice(0, 19) + } + return { code: 200, data: toUserFormat(mockEmployees[index]), message: '更新成功' } } return { code: 404, message: '用户不存在' } }, - // 删除 + // 删除用户(清除用户字段,保留员工记录) async delete(id) { await delay() - const index = mockUsers.findIndex(item => item.id === id) + const index = mockEmployees.findIndex(item => item.id === id && item.username) if (index !== -1) { - mockUsers.splice(index, 1) - return { code: 200, message: '删除成功' } + // 清除用户字段,而不是删除记录 + mockEmployees[index] = { + ...mockEmployees[index], + username: null, + password: null, + user_type: null, + user_status: null, + updated_at: new Date().toISOString().replace('T', ' ').slice(0, 19) + } + return { code: 200, message: '用户已删除' } } return { code: 404, message: '用户不存在' } } diff --git a/src/api/products.js b/src/api/products.js index 2f94dba..c5764ec 100644 --- a/src/api/products.js +++ b/src/api/products.js @@ -1,41 +1,26 @@ -import axios from 'axios' -import { mockProductsAPI } from './mock/products' - -// 开关:true 用 mock,false 用真实接口 -const USE_MOCK = true - -// axios 实例 -const request = axios.create({ - baseURL: '/api', - timeout: 5000 -}) +import request from './request' // 获取产品列表 export const getProductsList = (params) => { - if (USE_MOCK) return mockProductsAPI.getList(params) - return request.get('/products', { params }).then(r => r.data) + return request.get('/products', { params }) } // 获取产品详情 export const getProductsDetail = (id) => { - if (USE_MOCK) return mockProductsAPI.getDetail(id) - return request.get(`/products/${id}`).then(r => r.data) + return request.get(`/products/${id}`) } // 新增产品 export const createProducts = (data) => { - if (USE_MOCK) return mockProductsAPI.create(data) - return request.post('/products', data).then(r => r.data) + return request.post('/products', data) } // 更新产品 export const updateProducts = (id, data) => { - if (USE_MOCK) return mockProductsAPI.update(id, data) - return request.put(`/products/${id}`, data).then(r => r.data) + return request.put(`/products/${id}`, data) } // 删除产品 export const deleteProductsAPI = (id) => { - if (USE_MOCK) return mockProductsAPI.delete(id) - return request.delete(`/products/${id}`).then(r => r.data) + return request.delete(`/products/${id}`) } diff --git a/src/api/request.js b/src/api/request.js new file mode 100644 index 0000000..f4791c4 --- /dev/null +++ b/src/api/request.js @@ -0,0 +1,53 @@ +import axios from 'axios' +import { ElMessage } from 'element-plus' +import router from '../router' + +// 创建 axios 实例 +const request = axios.create({ + baseURL: '/api', + timeout: 10000 +}) + +// 请求拦截器:自动添加 token +request.interceptors.request.use( + (config) => { + const token = localStorage.getItem('token') + if (token) { + config.headers.Authorization = `Bearer ${token}` + } + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// 响应拦截器:统一处理错误(config.quiet = true 时不弹窗) +request.interceptors.response.use( + (response) => { + return response.data + }, + (error) => { + const status = error.response?.status + const message = error.response?.data?.message || '请求失败' + const quiet = error.config?.quiet + const isLogin = error.config?.url?.includes('/user/login') + + if (!quiet) { + if (status === 401) { + localStorage.removeItem('token') + localStorage.removeItem('userInfo') + router.push('/login') + ElMessage.error('登录已过期,请重新登录') + } else if (status === 403 && !isLogin) { + ElMessage.error('权限不足') + } else { + ElMessage.error(message) + } + } + + return Promise.reject(error) + } +) + +export default request diff --git a/src/api/service.js b/src/api/service.js index 5ef0292..c13b0d0 100644 --- a/src/api/service.js +++ b/src/api/service.js @@ -1,41 +1,26 @@ -import axios from 'axios' -import { mockServiceAPI } from './mock/service' - -// 开关:true 用 mock,false 用真实接口 -const USE_MOCK = true - -// axios 实例 -const request = axios.create({ - baseURL: '/api', - timeout: 5000 -}) +import request from './request' // 获取售后列表 export const getServiceList = (params) => { - if (USE_MOCK) return mockServiceAPI.getList(params) - return request.get('/after-sales', { params }).then(r => r.data) + return request.get('/after-sales', { params }) } // 获取售后详情 export const getServiceDetail = (id) => { - if (USE_MOCK) return mockServiceAPI.getDetail(id) - return request.get(`/after-sales/${id}`).then(r => r.data) + return request.get(`/after-sales/${id}`) } // 新增售后 export const createService = (data) => { - if (USE_MOCK) return mockServiceAPI.create(data) - return request.post('/after-sales', data).then(r => r.data) + return request.post('/after-sales', 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) + return request.put(`/after-sales/${id}`, data) } // 删除售后 export const deleteServiceAPI = (id) => { - if (USE_MOCK) return mockServiceAPI.delete(id) - return request.delete(`/after-sales/${id}`).then(r => r.data) + return request.delete(`/after-sales/${id}`) } diff --git a/src/api/suppliers.js b/src/api/suppliers.js new file mode 100644 index 0000000..86c92fc --- /dev/null +++ b/src/api/suppliers.js @@ -0,0 +1,26 @@ +import request from './request' + +// 获取供应商列表 +export const getSupplierList = (params, config) => { + return request.get('/suppliers', { params, ...config }) +} + +// 获取供应商详情 +export const getSupplierDetail = (id) => { + return request.get(`/suppliers/${id}`) +} + +// 新增供应商 +export const createSupplier = (data) => { + return request.post('/suppliers', data) +} + +// 更新供应商 +export const updateSupplier = (id, data) => { + return request.put(`/suppliers/${id}`, data) +} + +// 删除供应商 +export const deleteSupplierAPI = (id) => { + return request.delete(`/suppliers/${id}`) +} diff --git a/src/api/user.js b/src/api/user.js index 76eecfa..975cb75 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -1,41 +1,31 @@ -import axios from 'axios' -import { mockUserAPI } from './mock/user' +import request from './request' -// 开关:true 用 mock,false 用真实接口 -const USE_MOCK = true +// 获取用户列表(管理员) +export const getUserList = (params, config) => { + return request.get('/users', { params, ...config }) +} -// 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 getSimpleUserList = (config) => { + return request.get('/user/list', config) } // 获取用户详情 export const getUserDetail = (id) => { - if (USE_MOCK) return mockUserAPI.getDetail(id) - return request.get(`/users/${id}`).then(r => r.data) + return request.get(`/users/${id}`) } -// 新增用户 +// 创建用户 export const createUser = (data) => { - if (USE_MOCK) return mockUserAPI.create(data) - return request.post('/users', data).then(r => r.data) + return request.post('/users', data) } // 更新用户 export const updateUser = (id, data) => { - if (USE_MOCK) return mockUserAPI.update(id, data) - return request.put(`/users/${id}`, data).then(r => r.data) + return request.put(`/users/${id}`, data) } // 删除用户 export const deleteUserAPI = (id) => { - if (USE_MOCK) return mockUserAPI.delete(id) - return request.delete(`/users/${id}`).then(r => r.data) + return request.delete(`/users/${id}`) } diff --git a/src/components/Login.vue b/src/components/Login.vue index 782cecb..4584503 100644 --- a/src/components/Login.vue +++ b/src/components/Login.vue @@ -67,6 +67,7 @@ import {reactive, ref} from 'vue' import {ElMessage} from 'element-plus' import {Lock, User} from '@element-plus/icons-vue' import {useRouter} from 'vue-router' +import { login } from '../store/user' const router = useRouter() @@ -93,9 +94,13 @@ const handleLogin = async () => { try { await loginFormRef.value.validate() loading.value = true - await new Promise((r) => setTimeout(r, 800)) - ElMessage.success('登录成功(模拟)') - router.push('/panel') + const res = await login(loginForm.username, loginForm.password) + if (res.code === 0 || res.code === 200) { + ElMessage.success('登录成功') + router.push('/panel/home') + } else { + ElMessage.error(res.message || '登录失败') + } } catch { // 校验失败,element-plus 会自动显示红色提示 } finally { diff --git a/src/components/contract.vue b/src/components/contract.vue index 1a986c1..3745011 100644 --- a/src/components/contract.vue +++ b/src/components/contract.vue @@ -1,5 +1,5 @@ @@ -242,21 +349,21 @@ const formatDate = (date) => { @@ -292,12 +392,30 @@ const formatDate = (date) => { 共找到 {{ searchResults.length }} 条合同 - - - + + - - + + + + + + + + + + + + - - + + + + + + + + +
+ +
@@ -326,24 +465,34 @@ const formatDate = (date) => { - - + + - - + + + + + - + + + + + + + + @@ -357,22 +506,35 @@ const formatDate = (date) => { - - - - - - + + + + + + + + + + + + + + + + + + + @@ -404,29 +566,34 @@ const formatDate = (date) => {
- {{ currentDetail.contract_no }} - {{ currentDetail.contract_name }} - {{ currentDetail.customer_id }} - {{ currentDetail.employee_id }} + {{ currentDetail.id }} + {{ currentDetail.contract_name }} + {{ currentDetail.type === 'franchise' ? '加盟合同' : '采购合同' }} + {{ currentDetail.customer_name || currentDetail.customer_id }} + {{ currentDetail.supplier_name || currentDetail.supplier_id }} + {{ currentDetail.employee_name || currentDetail.employee_id }} ¥{{ currentDetail.amount?.toLocaleString() }} {{ currentDetail.status }} - {{ currentDetail.effective_date }} - {{ currentDetail.expiry_date }} + {{ formatDate(currentDetail.effective_date) }} + {{ formatDate(currentDetail.expiry_date) }}
{{ currentDetail.contract_content || '无' }}
{{ currentDetail.remark || '无' }} + + {{ currentDetail.responsible_user_name || currentDetail.responsible_user_id || '无' }} +
@@ -481,7 +648,8 @@ const formatDate = (date) => { font-size: 14px; } -.loading-container, .empty-container { +.loading-container, +.empty-container { padding: 40px 0; } @@ -505,4 +673,12 @@ const formatDate = (date) => { max-height: 120px; overflow-y: auto; } + +.pagination-container { + display: flex; + justify-content: center; + margin-top: 15px; + padding-top: 15px; + border-top: 1px solid #eee; +} \ No newline at end of file diff --git a/src/components/customer.vue b/src/components/customer.vue index 78841f1..e8cca28 100644 --- a/src/components/customer.vue +++ b/src/components/customer.vue @@ -10,6 +10,10 @@ import { updateCustomer, deleteCustomerAPI } from '../api/customer' +import { getSimpleUserList } from '../api/user' +import { hasPermission, getUserInfo } from '../store/user' + +const isAdmin = computed(() => getUserInfo()?.roleName === 'admin') const form = reactive({ id: '', @@ -18,8 +22,8 @@ const form = reactive({ region: [], address: '', email: '', - customer_type: 'Normal', - remark: '' + remark: '', + responsible_user_id: '' }) const search = ref('') @@ -34,6 +38,11 @@ let searchSeq = 0 const showDetailDialog = ref(false) const currentDetail = ref(null) const filterRegion = ref([]) +const userList = ref([]) + +// 分页相关 +const currentPage = ref(1) +const pageSize = ref(10) // 只保留省市两级的地区数据 const regionDataTwoLevel = computed(() => { @@ -46,38 +55,60 @@ const regionDataTwoLevel = computed(() => { })) }) -onMounted(() => { - fetchData() +// 分页后的数据 +const paginatedData = computed(() => { + const start = (currentPage.value - 1) * pageSize.value + const end = start + pageSize.value + return searchResults.value.slice(start, end) }) -// 切换代理商类型时清空已选地区 -watch(() => form.customer_type, () => { - form.region = [] +onMounted(() => { + fetchData() + // 仅管理员可加载用户列表用于选择负责人 + if (isAdmin.value) { + getSimpleUserList({ quiet: true }).then(res => { + userList.value = res.data?.list || res.data || [] + }).catch(() => {}) + } +}) + +// 监听搜索结果变化,重置页码 +watch(searchResults, () => { + currentPage.value = 1 }) // 获取客户列表 const fetchData = async () => { loading.value = true const res = await getCustomerList() - searchResults.value = res.data + if (res.code === 0 || res.code === 200) { + searchResults.value = res.data.list || res.data + } showSearchResults.value = true loading.value = false } // 搜索 const handleSearch = async () => { + // 如果在表单页面,先关闭表单 + if (showAddForm.value) { + cancelAdd() + } + const seq = ++searchSeq loading.value = true 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]] : '' + name: select.value === '1' ? search.value : undefined, + id: select.value === '2' ? Number(search.value) : undefined, + province: filterRegion.value?.[0] ? CodeToText[filterRegion.value[0]] : undefined, + city: filterRegion.value?.[1] ? CodeToText[filterRegion.value[1]] : undefined } const res = await getCustomerList(params) if (seq !== searchSeq) return - searchResults.value = res.data + if (res.code === 0 || res.code === 200) { + searchResults.value = res.data.list || res.data + } showSearchResults.value = true } finally { if (seq === searchSeq) loading.value = false @@ -92,8 +123,8 @@ const resetForm = () => { form.region = [] form.address = '' form.email = '' - form.customer_type = 'Normal' form.remark = '' + form.responsible_user_id = '' isEditMode.value = false currentCustomerId.value = null } @@ -118,8 +149,8 @@ const editCustomer = (row) => { ] form.address = row.address form.email = row.email - form.customer_type = row.customer_type form.remark = row.remark + form.responsible_user_id = row.responsible_user_id || '' showAddForm.value = true showSearchResults.value = false } @@ -133,29 +164,39 @@ const saveCustomer = async () => { district: CodeToText[form.region[2]] || '', address: form.address, email: form.email, - customer_type: form.customer_type, - remark: form.remark - } - if (isEditMode.value) { - await updateCustomer(currentCustomerId.value, formData) - ElMessage.success('更新成功') - } else { - await createCustomer(formData) - ElMessage.success('新增成功') + remark: form.remark, + responsible_user_id: form.responsible_user_id !== '' ? form.responsible_user_id : null } + try { + if (isEditMode.value) { + await updateCustomer(currentCustomerId.value, formData) + ElMessage.success('更新成功') + } else { + await createCustomer(formData) + ElMessage.success('新增成功') + } - cancelAdd() - fetchData() + cancelAdd() + fetchData() + } catch (error) { + ElMessage.error(error.response?.data?.message || error.message || '操作失败') + } } // 删除 const deleteCustomer = async (id) => { ElMessageBox.confirm('确定删除该客户吗?', '提示', { + confirmButtonText: '确认', + cancelButtonText: '取消', type: 'warning' }).then(async () => { - await deleteCustomerAPI(id) - ElMessage.success('删除成功') - fetchData() + try { + await deleteCustomerAPI(id) + ElMessage.success('删除成功') + fetchData() + } catch (error) { + ElMessage.error(error.response?.data?.message || error.message || '删除失败') + } }) } @@ -172,6 +213,7 @@ const clearSearch = () => { filterRegion.value = [] showSearchResults.value = false showAddForm.value = false + currentPage.value = 1 fetchData() } @@ -187,33 +229,41 @@ const closeDetail = () => { currentDetail.value = null } +// 页码变化 +const handleCurrentChange = (val) => { + currentPage.value = val +} + +// 检查权限 +const canCreate = computed(() => hasPermission('customer', 'create')) +const canUpdate = computed(() => hasPermission('customer', 'update')) +const canDelete = computed(() => hasPermission('customer', 'delete')) + - + diff --git a/src/components/employee.vue b/src/components/employee.vue index 8d40991..f222547 100644 --- a/src/components/employee.vue +++ b/src/components/employee.vue @@ -1,7 +1,7 @@
- + + + diff --git a/src/components/panel.vue b/src/components/panel.vue index 5400ef5..784125b 100644 --- a/src/components/panel.vue +++ b/src/components/panel.vue @@ -1,13 +1,36 @@ diff --git a/src/components/supplier.vue b/src/components/supplier.vue new file mode 100644 index 0000000..2068907 --- /dev/null +++ b/src/components/supplier.vue @@ -0,0 +1,466 @@ + + + + + diff --git a/src/components/user.vue b/src/components/user.vue index edf400c..2d713f5 100644 --- a/src/components/user.vue +++ b/src/components/user.vue @@ -1,7 +1,7 @@