diff --git a/src/api/ticket.js b/src/api/ticket.js
index 87d4701..fcdc8d5 100644
--- a/src/api/ticket.js
+++ b/src/api/ticket.js
@@ -5,14 +5,14 @@ import request from "@/utils/request.js";
* @returns {Promise}
*/
-export function getTickerList(count, page, status, orderBy, order, userId, keyword) {
+export function getTickerList(count, page, status, orderBy, order, userId, keyword, type) {
const params = { count, page }
if (status !== undefined && status !== '') params.status = status
if (orderBy) params.orderBy = orderBy
if (order) params.order = order
if (userId) params.user_id = userId
if (keyword) params.keyword = keyword
- console.log('工单列表请求参数:', params) // 调试日志
+ if (type) params.type = type
return request.get('/api/v1/admin/work_order/list', params)
}
@@ -43,12 +43,16 @@ export function getTicketDetail(work_id) {
// 回复工单
export function replyTicket(work_id, content, files) {
- return request.post('/api/v1/admin/work_order/reply', { work_id, content, files })
+ return request.post('/api/v1/admin/work_order/reply', { work_id, content, files }, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
}
// 关闭工单
export function closeTicket(work_id) {
- return request.post('/api/v1/admin/work_order/close', { work_id })
+ return request.post('/api/v1/admin/work_order/close', { work_id }, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
}
export function getFile(file_id) {
@@ -106,10 +110,9 @@ export function updateTicketType(data) {
}
/**删除工单类型 */
export function deleteTicketType(data) {
- return request.delete('/api/v1/admin/work_order/delete_type', data,{
- headers:{
- 'Content-Type':'multipart/form-data'
- }
+ return request.delete('/api/v1/admin/work_order/delete_type', {
+ data: data,
+ headers: { 'Content-Type': 'multipart/form-data' }
})
}
/**获取工单类型列表 */
@@ -124,3 +127,30 @@ export function updateTicketReplayInfo(data){
}
})
}
+
+/**获取回复模板列表 */
+export function getReplyTemplateList(params = {}) {
+ return request.get('/api/v1/admin/work_order/reply_template/list', params)
+}
+
+/**创建回复模板 */
+export function createReplyTemplate(data) {
+ return request.post('/api/v1/admin/work_order/reply_template/create', data, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
+}
+
+/**修改回复模板 */
+export function updateReplyTemplate(data) {
+ return request.post('/api/v1/admin/work_order/reply_template/update', data, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
+}
+
+/**删除回复模板 */
+export function deleteReplyTemplate(data) {
+ return request.delete('/api/v1/admin/work_order/reply_template/delete', {
+ data: data,
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
+}
diff --git a/src/components/layout/AdminLayout.vue b/src/components/layout/AdminLayout.vue
index fbf3f94..8efa4fd 100644
--- a/src/components/layout/AdminLayout.vue
+++ b/src/components/layout/AdminLayout.vue
@@ -45,12 +45,8 @@
-
-
-
-
+
+
@@ -103,9 +99,9 @@ import { useRoute, useRouter } from 'vue-router'
import SidebarMenuItem from './SidebarMenuItem.vue'
import Breadcrumb from './Breadcrumb.vue'
import TagsView from './TagsView.vue'
+import GlobalSearch from './GlobalSearch.vue'
import { menus as menuConfig } from '@/config/menus'
import {
- FullScreen,
ArrowDown,
User,
Key,
@@ -165,17 +161,6 @@ const closeMobileMenu = () => {
isMobileMenuOpen.value = false
}
-// 切换全屏
-const toggleFullScreen = () => {
- if (!document.fullscreenElement) {
- document.documentElement.requestFullscreen()
- } else {
- if (document.exitFullscreen) {
- document.exitFullscreen()
- }
- }
-}
-
// 退出登录
const handleLogout = () => {
ElMessageBox.confirm('确定要退出登录吗?', '提示', {
diff --git a/src/components/layout/GlobalSearch.vue b/src/components/layout/GlobalSearch.vue
new file mode 100644
index 0000000..9b5d9d2
--- /dev/null
+++ b/src/components/layout/GlobalSearch.vue
@@ -0,0 +1,581 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 用户 {{ results.user.total }}
+
+
+
未找到相关用户
+
+
+ {{ (item.user_name || '')[0] }}
+
+
+
+ ID: {{ item.user_id }} · {{ item.phone || item.email || '—' }}
+
+
+
+
+
+
+
+
+
+ 订单 {{ results.order.total }}
+
+
+
未找到相关订单
+
+
+
+
+
+
+ 用户ID: {{ item.userId }} · ¥{{ (item.price / 100).toFixed(2) }} · {{ item.type }}
+
+
{{ orderStatusText(item.state) }}
+
+
+
+
+
+
+
+ 工单 {{ results.ticket.total }}
+
+
+
未找到相关工单
+
+
+
+
+
+
+ {{ item.user?.userName || ('用户' + item.user?.userId) }} · {{ formatTime(item.created_at) }}
+
+
{{ ticketStatusText(item.status) }}
+
+
+
+
+
+
+
+ 用户商品 {{ results.goods.total }}
+
+
+
未找到相关用户商品
+
+
+
+
+
+
+ 用户: {{ item.user?.UserName || item.userId }} · 到期: {{ formatTime(item.expireTime) }}
+
+
+
+
+
+
+
+
+
+
+
+
输入关键词后按回车搜索
+
+ 支持搜索:用户名/手机号、订单号、工单标题、商品名称
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/config/menus.js b/src/config/menus.js
index de9e6cd..1c50552 100644
--- a/src/config/menus.js
+++ b/src/config/menus.js
@@ -12,6 +12,14 @@ export const menus = [
{
path: '/ticket/list',
title: '工单列表'
+ },
+ {
+ path: '/ticket/types',
+ title: '工单类型'
+ },
+ {
+ path: '/ticket/templates',
+ title: '回复模板'
}
]
},
diff --git a/src/router/index.js b/src/router/index.js
index 9e36974..7dd6a94 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -58,6 +58,22 @@ const routes = [
hidden: true,
activeMenu: '/ticket/list'
}
+ },
+ {
+ path: 'types',
+ name: 'TicketTypes',
+ component: () => import('../views/ticket/TicketTypes.vue'),
+ meta: {
+ title: '工单类型管理'
+ }
+ },
+ {
+ path: 'templates',
+ name: 'TicketTemplates',
+ component: () => import('../views/ticket/TicketTemplates.vue'),
+ meta: {
+ title: '回复模板管理'
+ }
}
]
},
diff --git a/src/views/system/DomainWhitelist.vue b/src/views/system/DomainWhitelist.vue
index f3390c6..e49d305 100644
--- a/src/views/system/DomainWhitelist.vue
+++ b/src/views/system/DomainWhitelist.vue
@@ -118,12 +118,7 @@ const domainForm = reactive({
// 表单规则
const domainRules = {
domain: [
- { required: true, message: '请输入域名', trigger: 'blur' },
- {
- pattern: /^((?!-)[A-Za-z0-9-]{1,63}(?
+
+
+
+
+
+
查询
@@ -69,6 +75,13 @@
+
+
+
+ {{ row.isAdmin ? '管理员' : '用户' }}
+
+
+
{{ formatDate(row.CreatedAt) }}
@@ -102,65 +115,87 @@
-
-
-
文件预览
-
+
+
+
-
-
{{ fileDetail.type || '未知类型' }}
+
+
+
+
+
{{ fileDetail.realName }}
+
+ {{ fileDetail.type || '未知' }}
+ {{ formatFileSize(fileDetail.size) }}
+ {{ fileDetail.openDow ? '公开' : '私有' }}
+ {{ fileDetail.isAdmin ? '管理员' : '用户' }}
+
+
+ ID: {{ fileDetail.id }}
+ 用户: {{ fileDetail.userId }}
+ {{ formatDate(fileDetail.CreatedAt) }}
+
+
+
+ 查看原文件
+
+
+ 下载
+
+
+ 复制路径
+
-
-
- {{ fileDetail.id }}
- {{ fileDetail.userId }}
- {{ fileDetail.realName }}
- {{ fileDetail.saveName }}
-
- {{ fileDetail.type || '未知' }}
-
- {{ formatFileSize(fileDetail.size) }}
-
-
- {{ fileDetail.openDow ? '公开访问' : '私有' }}
-
-
-
- {{ fileDetail.savePath }}
-
-
-
- 点击查看文件
-
- 无URL
-
- {{ formatDate(fileDetail.CreatedAt) }}
- {{ formatDate(fileDetail.UpdatedAt) }}
- {{ fileDetail.content || '无' }}
-
+
+
+
+ 保存名称
+ {{ fileDetail.saveName }}
+
+
+ 保存路径
+ {{ fileDetail.savePath }}
+
+
+
访问地址
+
+ {{ fileDetail.url }}
+
+
+
+
+ 更新时间
+ {{ formatDate(fileDetail.UpdatedAt) }}
+
+
+ 备注
+ {{ fileDetail.content }}
+
+
@@ -255,13 +290,14 @@
+
+
diff --git a/src/views/ticket/TicketTypes.vue b/src/views/ticket/TicketTypes.vue
new file mode 100644
index 0000000..366e13d
--- /dev/null
+++ b/src/views/ticket/TicketTypes.vue
@@ -0,0 +1,194 @@
+
+
+
+
+
+
+
+
+
+ {{ row.note || '—' }}
+
+
+
+
+
+ —
+
+
+
+
+ {{ formatTime(row.CreatedAt) }}
+
+
+
+
+ 编辑
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确定
+
+
+
+
+
+
+
+
diff --git a/src/views/user-vm/UserVmDetail.vue b/src/views/user-vm/UserVmDetail.vue
index 5450c55..bd50bc2 100644
--- a/src/views/user-vm/UserVmDetail.vue
+++ b/src/views/user-vm/UserVmDetail.vue
@@ -1153,7 +1153,7 @@