fix返回用户管理筛选

This commit is contained in:
2025-12-19 11:28:59 +08:00
parent 210399ca53
commit 8cb98a5184
5 changed files with 734 additions and 7 deletions

View File

@@ -4,7 +4,7 @@
<template #stats v-if="singleUserMode">
<div class="flex items-center gap-2 text-sm text-gray-600">
<User class="w-4 h-4" />
<span>当前用户{{ currentUser?.company_name || currentUser?.phone }}</span>
<span>当前用户{{ currentUser?.enterprise_info?.company_name || currentUser?.phone }}</span>
<span class="text-gray-400">(仅显示当前用户)</span>
</div>
</template>
@@ -14,7 +14,7 @@
<div class="flex items-center gap-3">
<div class="flex items-center gap-2 text-sm text-gray-600">
<User class="w-4 h-4" />
<span>{{ currentUser?.company_name || currentUser?.phone }}</span>
<span>{{ currentUser?.enterprise_info?.company_name || currentUser?.phone }}</span>
</div>
<el-button size="small" @click="exitSingleUserMode">
<Close class="w-4 h-4 mr-1" />
@@ -622,7 +622,22 @@ const exitSingleUserMode = () => {
// 返回用户管理
const goBackToUsers = () => {
router.push({ name: 'AdminUsers' })
const query = { user_id: currentUser.value?.id }
// 如果当前用户有企业名称,添加到查询参数
if (currentUser.value?.enterprise_info?.company_name) {
query.company_name = currentUser.value.enterprise_info.company_name
}
// 如果当前用户有手机号,添加到查询参数
if (currentUser.value?.phone) {
query.phone = currentUser.value.phone
}
console.log('query', query)
router.push({
name: 'AdminUsers',
query
})
}
// 查看详情

View File

@@ -9,7 +9,7 @@
<div class="user-info">
<el-icon class="user-icon"><user /></el-icon>
<div class="user-details">
<div class="company-name">{{ currentUser?.company_name || '未知公司' }}</div>
<div class="company-name">{{ currentUser?.enterprise_info?.company_name || '未知公司' }}</div>
<div class="user-phone">{{ currentUser?.phone || '-' }}</div>
</div>
</div>
@@ -727,7 +727,22 @@ const exitSingleUserMode = () => {
// 返回用户管理
const goBackToUsers = () => {
router.push({ name: 'AdminUsers' })
const query = { user_id: currentUser.value?.id }
// 如果当前用户有手机号,添加到查询参数
if (currentUser.value?.phone) {
query.phone = currentUser.value.phone
}
// 如果当前用户有企业名称,添加到查询参数
if (currentUser.value?.enterprise_info?.company_name) {
query.company_name = currentUser.value.enterprise_info.company_name
}
router.push({
name: 'AdminUsers',
query
})
}
// 监听路由变化

View File

@@ -516,7 +516,23 @@ const exitSingleUserMode = () => {
// 返回用户管理
const goBackToUsers = () => {
router.push({ name: 'AdminUsers' })
const query = { user_id: currentUser.value?.id }
// 如果当前用户有手机号,添加到查询参数
if (currentUser.value?.phone) {
query.phone = currentUser.value.phone
}
// 如果当前用户有企业名称,添加到查询参数
if (currentUser.value?.enterprise_info?.company_name) {
query.company_name = currentUser.value.enterprise_info.company_name
}
router.push({
name: 'AdminUsers',
query
})
}
// 查看详情

View File

@@ -0,0 +1,666 @@
<template>
<div class="ui-components-page">
<el-card class="filter-card">
<el-form :model="filterForm" inline>
<el-form-item label="关键词">
<el-input
v-model="filterForm.keyword"
placeholder="请输入关键词搜索"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="filterForm.is_active" placeholder="请选择状态" clearable>
<el-option label="启用" :value="true" />
<el-option label="禁用" :value="false" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button @click="handleReset">重置</el-button>
<el-button type="success" @click="handleCreate">新增UI组件</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="table-card">
<el-table
v-loading="loading"
:data="componentList"
stripe
border
>
<el-table-column prop="component_code" label="组件编码" width="150" />
<el-table-column prop="component_name" label="组件名称" width="200" />
<el-table-column prop="description" label="描述" show-overflow-tooltip />
<el-table-column prop="version" label="版本" width="100" />
<el-table-column label="文件状态" width="120">
<template #default="{ row }">
<el-tag v-if="row.is_extracted" type="success">已解压</el-tag>
<el-tag v-else-if="row.file_path" type="warning">已上传</el-tag>
<el-tag v-else type="info">未上传</el-tag>
</template>
</el-table-column>
<el-table-column label="文件大小" width="120">
<template #default="{ row }">
<span v-if="row.file_size">{{ formatFileSize(row.file_size) }}</span>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.is_active ? 'success' : 'danger'">
{{ row.is_active ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort_order" label="排序" width="80" />
<el-table-column prop="created_at" label="创建时间" width="180">
<template #default="{ row }">
{{ formatDateTime(row.created_at) }}
</template>
</el-table-column>
<el-table-column label="操作" width="320" fixed="right">
<template #default="{ row }">
<el-button size="small" @click="handleEdit(row)">编辑</el-button>
<el-button
v-if="!row.file_path"
size="small"
type="primary"
@click="handleUpload(row)"
>
上传文件
</el-button>
<el-button
v-if="row.file_path && !row.is_extracted"
size="small"
type="warning"
@click="handleUploadExtract(row)"
>
上传并解压
</el-button>
<el-button
v-if="row.is_extracted"
size="small"
type="success"
@click="handleViewFolder(row)"
>
查看文件夹
</el-button>
<el-button
v-if="row.file_path"
size="small"
type="info"
@click="handleDownload(row)"
>
下载文件
</el-button>
<el-button
v-if="row.is_extracted"
size="small"
type="danger"
@click="handleDeleteFolder(row)"
>
删除文件夹
</el-button>
<el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 创建/编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="600px"
@close="handleDialogClose"
>
<el-form
ref="formRef"
:model="form"
:rules="formRules"
label-width="100px"
>
<el-form-item label="组件编码" prop="component_code">
<el-input
v-model="form.component_code"
:disabled="isEdit"
placeholder="请输入组件编码"
/>
</el-form-item>
<el-form-item label="组件名称" prop="component_name">
<el-input v-model="form.component_name" placeholder="请输入组件名称" />
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.description"
type="textarea"
:rows="3"
placeholder="请输入组件描述"
/>
</el-form-item>
<el-form-item label="版本">
<el-input v-model="form.version" placeholder="请输入版本号" />
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="form.is_active" active-text="启用" inactive-text="禁用" />
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort_order" :min="0" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
确定
</el-button>
</span>
</template>
</el-dialog>
<!-- 文件上传对话框 -->
<el-dialog
v-model="uploadDialogVisible"
:title="uploadDialogTitle"
width="500px"
>
<el-upload
ref="uploadRef"
:auto-upload="false"
:limit="1"
accept=".zip"
:on-change="handleFileChange"
:on-remove="handleFileRemove"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
将文件拖到此处<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
只能上传zip文件且不超过100MB
</div>
</template>
</el-upload>
<template #footer>
<span class="dialog-footer">
<el-button @click="uploadDialogVisible = false">取消</el-button>
<el-button
type="primary"
@click="handleFileSubmit"
:loading="uploading"
:disabled="!selectedFile"
>
上传
</el-button>
</span>
</template>
</el-dialog>
<!-- 文件夹内容预览对话框 -->
<el-dialog
v-model="folderDialogVisible"
:title="folderDialogTitle"
width="800px"
>
<el-tree
:data="folderTree"
:props="defaultProps"
default-expand-all
show-checkbox
node-key="path"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-icon>
<folder v-if="data.type === 'folder'" />
<document v-else />
</el-icon>
<span>{{ data.name }}</span>
<span class="file-size">({{ formatSize(data.size) }})</span>
</span>
</template>
</el-tree>
<template #footer>
<span class="dialog-footer">
<el-button @click="folderDialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { uiComponentApi } from '@/api/ui-component'
import { Document, Folder, UploadFilled } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { computed, onMounted, reactive, ref } from 'vue'
// 响应式数据
const loading = ref(false)
const componentList = ref([])
const dialogVisible = ref(false)
const uploadDialogVisible = ref(false)
const folderDialogVisible = ref(false)
const submitting = ref(false)
const uploading = ref(false)
const isEdit = ref(false)
const currentComponent = ref(null)
const selectedFile = ref(null)
const formRef = ref(null)
const uploadRef = ref(null)
const folderTree = ref([])
// 筛选表单
const filterForm = reactive({
keyword: '',
is_active: null
})
// 分页数据
const pagination = reactive({
page: 1,
pageSize: 10,
total: 0
})
// 表单数据
const form = reactive({
id: '',
component_code: '',
component_name: '',
description: '',
version: '',
is_active: true,
sort_order: 0
})
// 表单验证规则
const formRules = {
component_code: [
{ required: true, message: '请输入组件编码', trigger: 'blur' }
],
component_name: [
{ required: true, message: '请输入组件名称', trigger: 'blur' }
]
}
// 树形组件默认属性
const defaultProps = {
children: 'children',
label: 'name'
}
// 计算属性
const dialogTitle = computed(() => isEdit.value ? '编辑UI组件' : '新增UI组件')
const uploadDialogTitle = computed(() => `上传文件 - ${currentComponent.value?.component_name || ''}`)
const folderDialogTitle = computed(() => `文件夹内容 - ${currentComponent.value?.component_name || ''}`)
// 方法
const fetchComponentList = async () => {
loading.value = true
try {
const params = {
page: pagination.page,
page_size: pagination.pageSize,
keyword: filterForm.keyword,
is_active: filterForm.is_active
}
const response = await uiComponentApi.getUIComponentList(params)
componentList.value = response.data.components
pagination.total = response.data.total
} catch (error) {
ElMessage.error('获取UI组件列表失败')
console.error('获取UI组件列表失败:', error)
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.page = 1
fetchComponentList()
}
const handleReset = () => {
filterForm.keyword = ''
filterForm.is_active = null
pagination.page = 1
fetchComponentList()
}
const handleSizeChange = (val) => {
pagination.pageSize = val
pagination.page = 1
fetchComponentList()
}
const handleCurrentChange = (val) => {
pagination.page = val
fetchComponentList()
}
const handleCreate = () => {
isEdit.value = false
resetForm()
dialogVisible.value = true
}
const handleEdit = (row) => {
isEdit.value = true
currentComponent.value = row
Object.assign(form, {
id: row.id,
component_code: row.component_code,
component_name: row.component_name,
description: row.description,
version: row.version,
is_active: row.is_active,
sort_order: row.sort_order
})
dialogVisible.value = true
}
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除UI组件"${row.component_name}"吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
try {
await uiComponentApi.deleteUIComponent(row.id)
ElMessage.success('删除成功')
fetchComponentList()
} catch (error) {
ElMessage.error('删除失败')
console.error('删除UI组件失败:', error)
}
}).catch(() => {
// 用户取消删除
})
}
const handleSubmit = async () => {
if (!formRef.value) return
try {
await formRef.value.validate()
submitting.value = true
if (isEdit.value) {
await uiComponentApi.updateUIComponent(form.id, form)
ElMessage.success('更新成功')
} else {
await uiComponentApi.createUIComponent(form)
ElMessage.success('创建成功')
}
dialogVisible.value = false
fetchComponentList()
} catch (error) {
if (error !== false) { // 不是表单验证错误
ElMessage.error(isEdit.value ? '更新失败' : '创建失败')
console.error('提交UI组件失败:', error)
}
} finally {
submitting.value = false
}
}
const handleDialogClose = () => {
resetForm()
}
const resetForm = () => {
Object.assign(form, {
id: '',
component_code: '',
component_name: '',
description: '',
version: '',
is_active: true,
sort_order: 0
})
if (formRef.value) {
formRef.value.resetFields()
}
}
const handleUpload = (row) => {
currentComponent.value = row
selectedFile.value = null
if (uploadRef.value) {
uploadRef.value.clearFiles()
}
uploadDialogVisible.value = true
}
const handleUploadExtract = (row) => {
currentComponent.value = row
selectedFile.value = null
if (uploadRef.value) {
uploadRef.value.clearFiles()
}
uploadDialogVisible.value = true
}
const handleFileChange = (file) => {
selectedFile.value = file.raw
}
const handleFileRemove = () => {
selectedFile.value = null
}
const handleFileSubmit = async () => {
if (!selectedFile.value) {
ElMessage.warning('请选择要上传的文件')
return
}
uploading.value = true
try {
const formData = new FormData()
formData.append('file', selectedFile.value)
// 根据当前组件是否已解压决定使用哪个接口
if (currentComponent.value.is_extracted) {
// 已解压,使用普通上传接口
await uiComponentApi.uploadUIComponentFile(currentComponent.value.id, formData)
ElMessage.success('文件上传成功')
} else {
// 未解压,使用上传并解压接口
await uiComponentApi.uploadAndExtractUIComponentFile(currentComponent.value.id, formData)
ElMessage.success('文件上传并解压成功')
}
uploadDialogVisible.value = false
fetchComponentList()
} catch (error) {
ElMessage.error('文件上传失败')
console.error('上传UI组件文件失败:', error)
} finally {
uploading.value = false
}
}
const handleDownload = async (row) => {
try {
const response = await uiComponentApi.downloadUIComponentFile(row.id)
// 创建下载链接
const url = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement('a')
link.href = url
link.setAttribute('download', `${row.component_name}.zip`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
} catch (error) {
ElMessage.error('文件下载失败')
console.error('下载UI组件文件失败:', error)
}
}
const handleViewFolder = async (row) => {
try {
const response = await uiComponentApi.getUIComponentFolderContent(row.id)
folderTree.value = buildTree(response.data)
currentComponent.value = row
folderDialogVisible.value = true
} catch (error) {
ElMessage.error('获取文件夹内容失败')
console.error('获取UI组件文件夹内容失败:', error)
}
}
const handleDeleteFolder = (row) => {
ElMessageBox.confirm(
`确定要删除UI组件"${row.component_name}"的文件夹吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
try {
await uiComponentApi.deleteUIComponentFolder(row.id)
ElMessage.success('文件夹删除成功')
fetchComponentList()
} catch (error) {
ElMessage.error('文件夹删除失败')
console.error('删除UI组件文件夹失败:', error)
}
}).catch(() => {
// 用户取消删除
})
}
// 构建树形结构
const buildTree = (files) => {
const tree = []
const pathMap = {}
// 先创建所有节点
files.forEach(file => {
const parts = file.path.split('/')
let currentPath = ''
parts.forEach((part, index) => {
if (index === 0) {
currentPath = part
} else {
currentPath = currentPath + '/' + part
}
if (!pathMap[currentPath]) {
pathMap[currentPath] = {
name: part,
path: file.path,
type: index === parts.length - 1 ? file.type : 'folder',
size: index === parts.length - 1 ? file.size : 0,
children: []
}
}
})
})
// 构建树形结构
Object.keys(pathMap).forEach(path => {
const node = pathMap[path]
const parentPath = path.substring(0, path.lastIndexOf('/'))
if (parentPath && pathMap[parentPath]) {
pathMap[parentPath].children.push(node)
} else if (!parentPath) {
tree.push(node)
}
})
return tree
}
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
const formatSize = (size) => {
return formatFileSize(size)
}
const formatDateTime = (dateTime) => {
if (!dateTime) return ''
const date = new Date(dateTime)
return date.toLocaleString('zh-CN')
}
// 生命周期
onMounted(() => {
fetchComponentList()
})
</script>
<style scoped>
.ui-components-page {
padding: 20px;
}
.filter-card {
margin-bottom: 20px;
}
.table-card {
margin-bottom: 20px;
}
.pagination-container {
display: flex;
justify-content: flex-end;
margin-top: 20px;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.file-size {
color: #999;
font-size: 12px;
}
</style>

View File

@@ -645,7 +645,7 @@ import ListPageLayout from '@/components/common/ListPageLayout.vue'
import { useMobileTable } from '@/composables/useMobileTable'
import { ArrowDown, Document, Money, Tickets, Wallet, Warning } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
// 获取路由实例
const router = useRouter()
@@ -723,6 +723,21 @@ let searchTimer = null
// 初始化
onMounted(() => {
// 检查是否有从交易页面传来的查询参数
const route = useRoute()
// 如果有企业名称参数,设置为搜索条件
if (route.query.company_name) {
filters.company_name = route.query.company_name
}
// 如果有手机号参数,设置为搜索条件
if (route.query.phone) {
filters.phone = route.query.phone
}
loadUsers()
loadStats()
})