feat(ui): 为工单对话页新增用户与订单跳转入口
在工单工作台对话页为当前工单用户增加“查看用户” 和“用户订单”入口,支持直接跳转到用户管理与订单管理 用户管理页新增 `user_id/user_email` 路由作用域, 进入后按用户 ID 精准筛选,并支持在重置筛选时清除 该作用域 同步更新 admin-frontend 模块文档、变更归档与测试环境 compose 配置
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ChatLineRound, DataAnalysis, Picture, Search } from '@element-plus/icons-vue'
|
||||
import { ChatLineRound, DataAnalysis, Picture, Search, Tickets, User } from '@element-plus/icons-vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { closeTicket, fetchTickets, getTicketById, replyTicket } from '@/api/admin'
|
||||
import type { AdminTicketDetail, AdminTicketListItem } from '@/types/api'
|
||||
import { formatDateTime } from '@/utils/dashboard'
|
||||
@@ -25,6 +26,7 @@ const emit = defineEmits<{
|
||||
updated: []
|
||||
}>()
|
||||
|
||||
const router = useRouter()
|
||||
const loadingSidebar = ref(false)
|
||||
const loadingDetail = ref(false)
|
||||
const replying = ref(false)
|
||||
@@ -138,6 +140,34 @@ async function handleCloseTicket() {
|
||||
}
|
||||
}
|
||||
|
||||
async function openTicketUser() {
|
||||
if (!detail.value?.user?.id) {
|
||||
return
|
||||
}
|
||||
|
||||
await router.push({
|
||||
name: 'Users',
|
||||
query: {
|
||||
user_id: String(detail.value.user.id),
|
||||
user_email: detail.value.user.email,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function openTicketUserOrders() {
|
||||
if (!detail.value?.user?.id) {
|
||||
return
|
||||
}
|
||||
|
||||
await router.push({
|
||||
name: 'SubscriptionOrders',
|
||||
query: {
|
||||
user_id: String(detail.value.user.id),
|
||||
user_email: detail.value.user.email,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
resetReplyDragState()
|
||||
emit('update:visible', false)
|
||||
@@ -189,6 +219,24 @@ watch(
|
||||
</div>
|
||||
|
||||
<div class="workspace-header__actions">
|
||||
<ElButton
|
||||
v-if="detail?.user?.id"
|
||||
text
|
||||
class="ghost-action"
|
||||
@click="openTicketUser"
|
||||
>
|
||||
<ElIcon><User /></ElIcon>
|
||||
查看用户
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-if="detail?.user?.id"
|
||||
text
|
||||
class="ghost-action"
|
||||
@click="openTicketUserOrders"
|
||||
>
|
||||
<ElIcon><Tickets /></ElIcon>
|
||||
用户订单
|
||||
</ElButton>
|
||||
<ElButton
|
||||
v-if="detail?.user?.id"
|
||||
text
|
||||
|
||||
@@ -15,6 +15,19 @@ export function useUserScopedActions() {
|
||||
const trafficLogUserEmail = ref('')
|
||||
const resettingTrafficId = ref<number | null>(null)
|
||||
|
||||
const scopedUserId = computed(() => {
|
||||
const raw = route.query.user_id
|
||||
const value = Array.isArray(raw) ? raw[0] : raw
|
||||
const numeric = Number(value)
|
||||
return Number.isFinite(numeric) && numeric > 0 ? numeric : null
|
||||
})
|
||||
|
||||
const scopedUserEmail = computed(() => {
|
||||
const raw = route.query.user_email
|
||||
const value = Array.isArray(raw) ? raw[0] : raw
|
||||
return typeof value === 'string' ? value : ''
|
||||
})
|
||||
|
||||
const scopedInviteUserId = computed(() => {
|
||||
const raw = route.query.invite_user_id
|
||||
const value = Array.isArray(raw) ? raw[0] : raw
|
||||
@@ -34,6 +47,21 @@ export function useUserScopedActions() {
|
||||
: []
|
||||
))
|
||||
|
||||
const scopedUserFilters = computed<AdminUserFilter[]>(() => (
|
||||
scopedUserId.value
|
||||
? [{ id: 'id', value: `eq:${scopedUserId.value}` }]
|
||||
: []
|
||||
))
|
||||
|
||||
const scopedUserSummaries = computed(() => {
|
||||
if (!scopedUserId.value) {
|
||||
return []
|
||||
}
|
||||
|
||||
const label = scopedUserEmail.value || `用户 #${scopedUserId.value}`
|
||||
return [`用户:${label}`]
|
||||
})
|
||||
|
||||
const scopedInviteSummaries = computed(() => {
|
||||
if (!scopedInviteUserId.value) {
|
||||
return []
|
||||
@@ -43,6 +71,17 @@ export function useUserScopedActions() {
|
||||
return [`邀请人:${label}`]
|
||||
})
|
||||
|
||||
function clearScopedUserQuery() {
|
||||
if (!scopedUserId.value && !scopedUserEmail.value) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
const nextQuery = { ...route.query }
|
||||
delete nextQuery.user_id
|
||||
delete nextQuery.user_email
|
||||
return router.replace({ name: 'Users', query: nextQuery })
|
||||
}
|
||||
|
||||
function clearScopedInviteQuery() {
|
||||
if (!scopedInviteUserId.value && !scopedInviteUserEmail.value) {
|
||||
return Promise.resolve()
|
||||
@@ -116,9 +155,13 @@ export function useUserScopedActions() {
|
||||
trafficLogUserId,
|
||||
trafficLogUserEmail,
|
||||
resettingTrafficId,
|
||||
scopedUserId,
|
||||
scopedUserFilters,
|
||||
scopedUserSummaries,
|
||||
scopedInviteUserId,
|
||||
scopedInviteFilters,
|
||||
scopedInviteSummaries,
|
||||
clearScopedUserQuery,
|
||||
clearScopedInviteQuery,
|
||||
openAssignOrder,
|
||||
handleAssignOrderSuccess,
|
||||
|
||||
@@ -66,6 +66,7 @@ export function useUsersManagement() {
|
||||
planFilter.value,
|
||||
advancedFilters.value,
|
||||
),
|
||||
...scopedActions.scopedUserFilters.value,
|
||||
...scopedActions.scopedInviteFilters.value,
|
||||
])
|
||||
|
||||
@@ -75,7 +76,10 @@ export function useUsersManagement() {
|
||||
planFilter.value,
|
||||
advancedFilters.value,
|
||||
plans.value,
|
||||
).concat(scopedActions.scopedInviteSummaries.value))
|
||||
).concat(
|
||||
scopedActions.scopedUserSummaries.value,
|
||||
scopedActions.scopedInviteSummaries.value,
|
||||
))
|
||||
|
||||
const batchActions = useUsersBatchActions({
|
||||
loading,
|
||||
@@ -148,9 +152,11 @@ export function useUsersManagement() {
|
||||
statusFilter.value = 'all'
|
||||
planFilter.value = 'all'
|
||||
advancedFilters.value = []
|
||||
void scopedActions.clearScopedInviteQuery().finally(() => {
|
||||
refreshUsers(true)
|
||||
})
|
||||
void scopedActions.clearScopedUserQuery()
|
||||
.then(() => scopedActions.clearScopedInviteQuery())
|
||||
.finally(() => {
|
||||
refreshUsers(true)
|
||||
})
|
||||
}
|
||||
|
||||
function clearAdvancedFilters() {
|
||||
@@ -280,6 +286,8 @@ export function useUsersManagement() {
|
||||
|
||||
watch(
|
||||
() => [
|
||||
scopedActions.route.query.user_id,
|
||||
scopedActions.route.query.user_email,
|
||||
scopedActions.route.query.invite_user_id,
|
||||
scopedActions.route.query.invite_user_email,
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user