From fa0c718c8ba41851c4bfa53463d791ccbffea4d1 Mon Sep 17 00:00:00 2001
From: GiveMePast <1248298477@qq.com>
Date: Thu, 14 May 2026 15:41:00 +0800
Subject: [PATCH] =?UTF-8?q?=E5=87=8F=E5=B0=91=E5=86=97=E4=BD=99=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
jsconfig.json | 1 -
src/api/ticket.js | 66 +++---
src/components/ticket/TicketFilter.vue | 72 +++++-
src/components/ticket/TicketForm.vue | 92 ++++----
src/components/ticket/TicketList.vue | 170 ++++++++++-----
src/layouts/MainLayout.vue | 49 ++++-
src/router/index.js | 291 +++++++++++++++++--------
src/utils/format.js | 49 +++++
src/utils/request.js | 92 +++-----
src/views/404View.vue | 37 ++++
src/views/ClientView.vue | 27 +--
src/views/LoginView.vue | 68 ++----
src/views/TicketManagement.vue | 178 +++++++++------
src/views/UserView.vue | 46 ++--
vite.config.js | 3 +-
15 files changed, 802 insertions(+), 439 deletions(-)
create mode 100644 src/utils/format.js
create mode 100644 src/views/404View.vue
diff --git a/jsconfig.json b/jsconfig.json
index 8e8440b..877fd65 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -1,6 +1,5 @@
{
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
diff --git a/src/api/ticket.js b/src/api/ticket.js
index 15cfa56..00538a2 100644
--- a/src/api/ticket.js
+++ b/src/api/ticket.js
@@ -1,78 +1,80 @@
-import request from '@/utils/request'
+import { adminRequest, indexRequest } from '@/utils/request'
+// ==================== 前台接口(/api/index) ====================
export const userApi = {
- // 登录接口
login(username, password) {
- // 这里的 data 会被 axios 放在请求体 (Body) 中发送
- return request.post('/login', {
- username,
- password
- })
+ return indexRequest.post('/login', { username, password })
},
- // 退出登录
logout() {
- return request.post('/logout')
+ return indexRequest.post('/logout')
}
}
+// ==================== 后台接口(/api/admin) ====================
export const ticketApi = {
- //获取工单各个完成状态的统计数量
- getticketsstatuscount(){
- return request.get('/getticketstatuscount')
+ // 获取工单各完成状态的统计数量
+ getticketdashboardstats() {
+ return adminRequest.get('/getticketdashboardstats')
},
// 获取工单列表
getTickets(params) {
- return request.get('/ticketsfind', { params })
+ return adminRequest.get('/ticketsfind', { params })
},
// 新增工单
addTicket(data) {
- return request.post('/addticket', data)
+ return adminRequest.post('/addticket', data)
},
// 更新工单
updateTicket(id, data) {
- return request.put(`/ticket/${id}`, data)
+ return adminRequest.put(`/ticket/${id}`, data)
},
// 删除工单
deleteTicket(id) {
- return request.delete(`/ticket/${id}`)
+ return adminRequest.delete(`/ticket/${id}`)
},
- // 获取运营人员列表
- getOperators() {
- return request.get('/operators')
+ // 获取运营人员列表(按公司分组)
+ getOperatorsNew() {
+ return adminRequest.get('/getOperatorList')
},
+
// 获取员工列表
- getStaffs(params) {
- return request.get('/getStaffList', { params })
- }
+ getUserStaffs() {
+ return adminRequest.get('/getUserStaffList')
+ },
+
+ // 批量更新工单
+ batchUpdateTicket(data) {
+ return adminRequest.put('/tickets/batch-update', data)
+ },
}
export const customerApi = {
getCustomers() {
- return request.get('/customers')
+ return adminRequest.get('/customers')
},
-
// 添加客户,data 结构应为 { company_name: 'xxx' }
addCustomerName(data) {
- return request.post('/addcustomername', data)
+ return adminRequest.post('/addcustomername', data)
},
addEmployee(data) {
- return request.post('/addemployee', data) // 对应后端新增员工
+ return adminRequest.post('/addemployee', data)
},
- deleteEmployee(empId) {
- return request.delete(`/deleteemployee/${empId}`) // 对应后端删除员工
- },
- deleteCustomer(customerId) {
- return request.delete(`/deletecustomer/${customerId}`) // 删除客户单位
- }
+ deleteEmployee(empId) {
+ return adminRequest.delete(`/deleteemployee/${empId}`)
+ },
+
+ deleteCustomer(customerId) {
+ return adminRequest.delete(`/deletecustomer/${customerId}`)
+ }
}
\ No newline at end of file
diff --git a/src/components/ticket/TicketFilter.vue b/src/components/ticket/TicketFilter.vue
index 4db594e..d8eb496 100644
--- a/src/components/ticket/TicketFilter.vue
+++ b/src/components/ticket/TicketFilter.vue
@@ -15,24 +15,26 @@
@@ -29,7 +29,7 @@
ID
-
+
@@ -64,17 +64,17 @@ import { ref, reactive } from "vue";
import { DataBoard } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { userApi } from "@/api/ticket";
-import { useRouter } from "vue-router";
+import { useRoute, useRouter } from "vue-router";
const router = useRouter();
-
+const route = useRoute();
const loginForm = reactive({ username: "", password: "" });
const rememberMe = ref(false);
const loading = ref(false);
const loginFormRef = ref(null);
const loginRules = reactive({
- username: [{ required: true, message: "请输入工员编号", trigger: "blur" }],
+ username: [{ required: true, message: "请输入员工编号", trigger: "blur" }],
password: [{ required: true, message: "请输入密钥", trigger: "blur" }],
});
@@ -85,52 +85,32 @@ const handleLogin = async () => {
loading.value = true;
const res = await userApi.login(loginForm.username, loginForm.password);
- // console.log(res)
- // 注意:这里由于你之前的封装,可能需要判断 res.data.code 还是 res.code
- // 如果用了拦截器,直接判断 res.code
+
if (res.code === 200) {
- const token = res.data.token;
- const userInfo = JSON.stringify(res.data.user_info);
- const role = res.data.user_info.role;
+ // ThinkPHP 会自动通过 Set-Cookie 下发 session,浏览器自动接管,无需手动存 token
+ const userInfo = res.data;
+ const userInfoStr = JSON.stringify(userInfo);
- // console.log(role)
+ // 用户信息存前端,仅用于显示用户名和角色路由判断
+ const storage = rememberMe.value ? localStorage : sessionStorage;
+ storage.setItem("user_info", userInfoStr);
- // --- 核心逻辑开始 ---
- if (rememberMe.value) {
- // 勾选了:存入 localStorage,实现持久化
- localStorage.setItem("token", token);
- localStorage.setItem("user_info", userInfo);
- } else {
- // 没勾选:存入 sessionStorage,关闭标签页即失效
- sessionStorage.setItem("token", token);
- sessionStorage.setItem("user_info", userInfo);
- }
- // --- 核心逻辑结束 ---
+ ElMessage({
+ message: "身份验证通过,方舟系统加载中...",
+ type: "success",
+ duration: 300,
+ });
- ElMessage.success("身份验证通过,方舟系统加载中...");
-
- // 3. 根据角色执行路由跳转
- setTimeout(() => {
- switch (role) {
- case "admin":
- router.push("/"); // 管理员跳转至主页
- break;
- case "user":
- router.push("/user"); // 跳转至 UserView (请确保路由配置中有此路径)
- break;
- case "client":
- router.push("/client"); // 跳转至 ClientView (请确保路由配置中有此路径)
- break;
- default:
- ElMessage.warning("未识别的角色权限,请联系管理员");
- break;
- }
- }, 1000);
+ // 触发路由守卫,自动处理跳转
+ await router.push(route.query.redirect || "/");
} else {
- ElMessage.error(res.message || "登录失败");
+ ElMessage.error(res.message || "登录失败,请检查账号和密码");
}
} catch (error) {
- console.error("Login Error:", error);
+ if (error.name !== "ValidationError") {
+ ElMessage.error("登录请求异常,请稍后重试");
+ // console.error("登录错误:", error);
+ }
} finally {
loading.value = false;
}
diff --git a/src/views/TicketManagement.vue b/src/views/TicketManagement.vue
index e38382c..fc5c21b 100644
--- a/src/views/TicketManagement.vue
+++ b/src/views/TicketManagement.vue
@@ -53,11 +53,18 @@
新增任务
-
-
+
+ 已选 {{ selectedTickets.length }} 个工单
+ 一键已完结
+ 取消选择
+
+
+ selectedTickets = val" />
-
+
@@ -445,6 +483,22 @@ onMounted(() => {
margin-bottom: 20px;
}
+.batch-bar {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 16px;
+ margin-bottom: 12px;
+ background: #fdf6ec;
+ border: 1px solid #faecd8;
+ border-radius: 8px;
+}
+
+.batch-info {
+ font-size: 13px;
+ color: #606266;
+}
+
.pagination-container {
margin-top: 20px;
display: flex;
diff --git a/src/views/UserView.vue b/src/views/UserView.vue
index ca3921f..702c54f 100644
--- a/src/views/UserView.vue
+++ b/src/views/UserView.vue
@@ -85,12 +85,18 @@
-
+
+
+
+ {{ row.website }}
+
+
+
@@ -255,10 +261,11 @@
{{
- (["已完成", "已完结"].includes(currentStatus)
- ? scope.row.lock_time
- : scope.row.create_time
- )?.slice(0, 10) || "-"
+ formatDate(
+ ["已完成", "已完结"].includes(currentStatus)
+ ? scope.row.lock_time
+ : scope.row.create_time
+ )
}}
@@ -319,7 +326,9 @@ import {
Checked,
Loading,
} from "@element-plus/icons-vue";
-import { ticketApi } from "@/api/ticket";
+import { ticketApi, userApi } from "@/api/ticket";
+import { resetRouter } from "@/router";
+import { formatUrl, formatDate } from "@/utils/format";
const router = useRouter();
const loading = ref(false);
@@ -377,7 +386,6 @@ const fetchTickets = async () => {
loading.value = false;
return;
}
- console.log("员工id", realStaffInfo.value.id);
params.staff_id = realStaffInfo.value.id;
}
@@ -511,10 +519,16 @@ const handleCancel = (row) => {
};
const handleLogout = () => {
- ElMessageBox.confirm("确定退出当前账号?", "提示", { type: "warning" }).then(() => {
- localStorage.clear();
- sessionStorage.clear();
- router.push("/login");
+ ElMessageBox.confirm("确定退出当前账号?", "提示", { type: "warning" }).then(async () => {
+ try {
+ await userApi.logout();
+ } catch (e) {}
+
+ localStorage.removeItem("user_info");
+ sessionStorage.removeItem("user_info");
+
+ resetRouter();
+ router.push({ name: "login" });
});
};
diff --git a/vite.config.js b/vite.config.js
index 738c75e..77eecf9 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -19,9 +19,10 @@ export default defineConfig({
server: {
port: 8080, // 前端启动的端口
proxy: {
- '/api': { // 所有以api开头的请求都会被代理
+ '/api': {
target: 'http://localhost',
changeOrigin: true,
+ rewrite: (path) => path.replace(/^\/api/, ''),
}
}
}