接口调试

This commit is contained in:
BianLzhaoMin 2025-11-15 17:29:06 +08:00
parent b18f424714
commit ae56e3cabc
8 changed files with 1602 additions and 484 deletions

View File

@ -44,3 +44,74 @@ export function uploadFileTagAPI(data = {}) {
data,
})
}
// 删除文档中心
export function deleteDocCenterAPI(data = {}) {
return request({
url: '/screen/document/delete',
method: 'POST',
data,
})
}
// 移动文档/文件
export function moveDocCenterAPI(data = {}) {
return request({
url: '/screen/document/move',
method: 'POST',
data,
})
}
// 复制文档/文件
export function copyDocCenterAPI(data = {}) {
return request({
url: '/screen/document/copy',
method: 'POST',
data,
})
}
// 共享-获取部门树
export function getShareDeptTreeAPI() {
return request({
url: '/system/user/deptTree',
method: 'GET',
})
}
// 共享-获取角色列表
export function getShareRoleListAPI(params = { pageSize: 1000, pageNum: 1 }) {
return request({
url: '/system/role/list',
method: 'GET',
params,
})
}
// 共享-根据部门获取人员列表
export function getShareUserListAPI(params = {}) {
return request({
url: '/system/user/list',
method: 'GET',
params,
})
}
// 共享-根据角色获取人员列表
export function getShareUserListByRoleAPI(params = {}) {
return request({
url: '/system/role/authUser/allocatedList',
method: 'GET',
params,
})
}
// 共享-提交授权
export function saveSharePermissionAPI(data = {}) {
return request({
url: '/screen/document/share',
method: 'POST',
data,
})
}

View File

@ -84,33 +84,20 @@
>
<template slot-scope="scope">
<el-tag
:type="getTypeTagType(scope.row.type)"
:type="getTypeTagType(scope.row.fileType)"
size="small"
>
{{ scope.row.type }}
{{ scope.row.fileType == 0 ? '文件夹' : '文件' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="tags"
prop="labName"
label="标签"
width="150"
align="center"
>
<template slot-scope="scope">
<div class="tags-cell">
<el-tag
v-for="tag in scope.row.tags"
:key="tag"
size="mini"
type="info"
class="tag-item"
>
{{ tag }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
@ -189,6 +176,7 @@
<component
ref="componentRef"
:selectedNode="selectedNode"
v-bind="dialogConfig.outerComponentProps"
:is="dialogConfig.outerComponent"
/>
@ -230,7 +218,10 @@ import Move from './tableCom/move.vue'
import AddCopy from './tableCom/addCopy.vue'
import TagFilter from './tableCom/tagFilter.vue'
import { getDocCenterRightListAPI } from '@/api/publicService/docCenter'
import {
getDocCenterRightListAPI,
deleteDocCenterAPI,
} from '@/api/publicService/docCenter'
export default {
name: 'RightTable',
props: {
@ -267,6 +258,7 @@ export default {
searchValue: '',
parentId: '',
type: '',
labelIds: '',
},
tableData: [],
@ -327,6 +319,7 @@ export default {
outerVisible: false,
outerWidth: '50%',
outerComponent: null,
outerComponentProps: {},
},
//
selectedFilesForDownload: [],
@ -376,7 +369,7 @@ export default {
//
getTypeTagType(type) {
if (type === '文件夹') {
if (type == 0) {
return 'warning'
} else if (type === 'docx') {
return 'primary'
@ -409,6 +402,7 @@ export default {
this.dialogConfig.outerTitle = '新建文档夹'
this.dialogConfig.outerVisible = true
this.dialogConfig.outerComponent = 'AddWord'
this.dialogConfig.outerComponentProps = {}
},
//
@ -417,6 +411,7 @@ export default {
this.dialogConfig.outerTitle = '上传文件'
this.dialogConfig.outerVisible = true
this.dialogConfig.outerComponent = 'Upload'
this.dialogConfig.outerComponentProps = {}
},
//
@ -425,6 +420,7 @@ export default {
this.dialogConfig.outerTitle = '移动'
this.dialogConfig.outerVisible = true
this.dialogConfig.outerComponent = 'Move'
this.dialogConfig.outerComponentProps = {}
},
//
@ -444,8 +440,20 @@ export default {
type: 'warning',
},
)
.then(() => {
this.$modal.msgSuccess('删除功能待实现')
.then(async () => {
// this.$modal.msgSuccess('')
const res = await deleteDocCenterAPI({
id: this.selectedRows.map((row) => row.id).join(','),
parentId: this.selectedNode?.id,
})
if (res.code === 200) {
this.$modal.msgSuccess('删除成功')
this.getTableList()
} else {
this.$modal.msgError(res.message)
}
})
.catch(() => {
this.$modal.msgInfo('已取消删除')
@ -481,6 +489,10 @@ export default {
}))
this.dialogConfig.outerTitle = '共享'
this.dialogConfig.outerVisible = true
this.dialogConfig.outerComponent = 'SharePermissionForm'
this.dialogConfig.outerComponentProps = {
selectedFiles: this.selectedFilesForShare,
}
},
//
@ -488,6 +500,8 @@ export default {
// this.$emit('add-copy', this.selectedRows)
this.dialogConfig.outerTitle = '添加副本'
this.dialogConfig.outerVisible = true
this.dialogConfig.outerComponent = 'AddCopy'
this.dialogConfig.outerComponentProps = {}
},
//
@ -539,7 +553,7 @@ export default {
const res = await this.$refs.componentRef.submit()
console.log(res, '新增结果')
// this.$modal.msgSuccess('')
// this.dialogConfig.outerVisible = false
this.dialogConfig.outerVisible = false
//
this.getTableList()
@ -577,16 +591,20 @@ export default {
},
//
handleTagQuery(selectedTags) {
console.log('选中的标签:', selectedTags)
//
// this.$modal.msgSuccess('')
handleTagQuery(tagIds) {
this.queryParams.pageNum = 1
this.queryParams.labelIds = Array.isArray(tagIds)
? tagIds.join(',')
: ''
this.getTableList()
this.tagFilterVisible = false
},
//
handleTagReset() {
console.log('重置标签筛选')
this.queryParams.labelIds = ''
this.queryParams.pageNum = 1
this.getTableList()
this.$modal.msgInfo('已重置标签筛选')
},

View File

@ -27,7 +27,7 @@
<div class="tags-container" v-loading="loading">
<div
v-for="category in filteredCategories"
:key="category.typeId"
:key="category.key"
class="category-section"
>
<div class="category-title"># {{ category.typeName }} #</div>
@ -124,13 +124,22 @@ export default {
//
initSelectedTags() {
if (this.selectedTags && this.selectedTags.length > 0) {
this.selectedTagIds = this.selectedTags.map((tag) => {
if (typeof tag === 'object') {
// 使 categoryName_labelName
return tag.id || `${tag.typeName}_${tag.labelName}`
}
return tag
})
this.selectedTagIds = this.selectedTags
.map((tag) => {
if (typeof tag === 'object') {
return (
(tag.id ||
tag.typeId ||
tag.labelId ||
tag.value ||
tag.labelCode) ??
''
)
}
return tag ?? ''
})
.filter((id) => id !== '')
.map((id) => String(id))
} else {
this.selectedTagIds = []
}
@ -150,21 +159,32 @@ export default {
//
this.categories = Object.keys(tagsData)
.map((categoryName) => {
.map((categoryName, index) => {
const tagList = tagsData[categoryName] || []
// typeId typeId
const categoryTypeId =
tagList.length > 0 ? tagList[0].typeId : null
return {
typeId: categoryTypeId,
key: `${categoryName}_${index}`,
typeName: categoryName,
tags: tagList.map((tag) => ({
id: `${categoryName}_${tag.labelName}`,
labelName: tag.labelName,
typeId: tag.typeId,
typeName: categoryName,
})),
tags: tagList
.map((tag) => {
const rawId =
tag.id ??
tag.typeId ??
tag.labelId ??
tag.value ??
tag.labelCode
if (!rawId) {
return null
}
return {
id: String(rawId),
labelName:
tag.labelName ?? tag.name ?? '',
typeId: tag.typeId ?? rawId,
typeName: categoryName,
raw: tag,
}
})
.filter(Boolean),
}
})
.filter((category) => category.tags.length > 0)
@ -182,16 +202,17 @@ export default {
//
isTagSelected(tagId) {
return this.selectedTagIds.includes(tagId)
return this.selectedTagIds.includes(String(tagId))
},
//
toggleTag(tag) {
const index = this.selectedTagIds.indexOf(tag.id)
const tagId = String(tag.id)
const index = this.selectedTagIds.indexOf(tagId)
if (index > -1) {
this.selectedTagIds.splice(index, 1)
} else {
this.selectedTagIds.push(tag.id)
this.selectedTagIds.push(tagId)
}
},
@ -199,15 +220,15 @@ export default {
getSelectedTags() {
const selectedTags = []
const typeIdSet = new Set() // typeId
this.categories.forEach((category) => {
category.tags.forEach((tag) => {
if (this.selectedTagIds.includes(tag.id)) {
if (this.selectedTagIds.includes(String(tag.id))) {
selectedTags.push({
id: tag.id,
id: String(tag.id),
labelName: tag.labelName,
typeId: tag.typeId,
typeName: tag.typeName,
raw: tag.raw,
})
// typeId
if (tag.typeId) {
@ -217,9 +238,14 @@ export default {
})
})
const tagIds = selectedTags.map((tag) => tag.id)
const tagLabels = selectedTags.map((tag) => tag.labelName)
// typeId
return {
tags: selectedTags,
tagIds,
tagLabels,
typeIds: Array.from(typeIdSet),
}
},

View File

@ -1,9 +1,247 @@
<template>
<div> 添加副本 </div>
<!-- 添加副本 -->
<div class="move-container">
<div class="tree-wrapper" v-loading="loading">
<el-tree
v-if="treeData.length"
ref="treeRef"
node-key="id"
class="move-tree"
:data="treeData"
:props="treeProps"
highlight-current
default-expand-all
draggable
:allow-drag="allowDrag"
:allow-drop="allowDrop"
:expand-on-click-node="false"
@node-drop="handleNodeDrop"
>
<span class="tree-node" slot-scope="{ data }">
<i :class="getNodeIcon(data)" class="node-icon"></i>
<span class="node-label" :title="data.name">
{{ data.name }}
</span>
<el-tag size="mini" v-if="data.fileType === 0" type="info">
文件夹
</el-tag>
</span>
</el-tree>
<div v-else class="empty-holder">暂无可移动数据</div>
</div>
<div class="tips">
<p class="tips-title">操作提示</p>
<p>1. 仅文件fileType 0可被拖拽</p>
<p>2. 仅支持拖拽到文件夹节点内</p>
<p>3. 拖拽到目标组后会立即复制副本并同步至后台</p>
</div>
</div>
</template>
<script>
export default {}
import { copyDocCenterAPI } from '@/api/publicService/docCenter'
export default {
name: 'AddCopy',
props: {
selectedNode: {
type: Object,
default: () => ({}),
},
},
data() {
return {
treeData: [],
treeProps: {
children: 'childTree',
label: 'name',
},
loading: false,
copyActions: [],
}
},
watch: {
selectedNode: {
handler(newVal) {
this.initTree(newVal)
},
immediate: true,
deep: true,
},
},
methods: {
initTree(node) {
if (!node || !node.id) {
this.treeData = []
return
}
this.treeData = [this.cloneNode(node)]
this.copyActions = []
},
cloneNode(node) {
const childTree = Array.isArray(node.childTree)
? node.childTree.map((child) => this.cloneNode(child))
: []
return {
...node,
childTree,
}
},
getNodeIcon(data) {
// console.log(data.fileType, 'data.fileType')
return data.fileType == 0 ? 'el-icon-folder' : 'el-icon-document'
},
//
allowDrag(draggingNode) {
const data = draggingNode?.data || {}
return data.fileType == 1
},
//
allowDrop(draggingNode, dropNode, type) {
if (type !== 'inner') {
return false
}
const dropData = dropNode?.data || {}
if (dropData.fileType == 1) {
return false
}
const dragData = draggingNode?.data || {}
if (!dragData.id || dragData.id === dropData.id) {
return false
}
return true
},
async handleNodeDrop(draggingNode, dropNode) {
const source = draggingNode?.data
const target = dropNode?.data
if (!source?.id || !target?.id) {
this.$message.error('无法获取拖拽节点信息')
return
}
await this.copyDocument(
source.id,
target.id,
source.parentId,
target.parentIds,
)
},
async copyDocument(documentId, targetParentId, parentId, parentIds) {
//
const params = {
id: documentId, // id
toId: targetParentId,
newParentId: targetParentId, // i
parentId: parentId, // id
parentIds: parentIds,
}
this.loading = true
try {
const res = await copyDocCenterAPI(params)
if (res.code === 200) {
this.$message.success('复制成功')
this.recordAction(documentId, targetParentId)
} else {
this.$message.error(res.msg || res.message || '复制失败')
}
} catch (error) {
this.$message.error(error.message || '复制失败')
} finally {
this.loading = false
}
},
recordAction(documentId, targetParentId) {
const idx = this.copyActions.findIndex(
(item) => item.documentId === documentId,
)
const payload = { documentId, targetParentId }
if (idx > -1) {
this.$set(this.copyActions, idx, payload)
} else {
this.copyActions.push(payload)
}
},
submit() {
if (this.copyActions.length === 0) {
return Promise.reject(new Error('请先拖拽需要复制的文件'))
}
return Promise.resolve({
copied: this.copyActions,
})
},
},
}
</script>
<style></style>
<style scoped lang="scss">
.move-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.tree-wrapper {
min-height: 320px;
border: 1px solid #f0f0f0;
border-radius: 6px;
padding: 12px;
background: #fafafa;
}
.move-tree {
max-height: 360px;
overflow: auto;
.tree-node {
display: flex;
align-items: center;
gap: 8px;
.node-icon {
font-size: 16px;
color: #409eff;
}
.node-label {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.empty-holder {
height: 180px;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
}
.tips {
font-size: 13px;
color: #666;
line-height: 1.8;
background: #fdf6ec;
border: 1px solid #f5dab1;
padding: 12px;
border-radius: 6px;
.tips-title {
font-weight: 600;
margin-bottom: 4px;
}
}
</style>

View File

@ -1,9 +1,303 @@
<template>
<div> 移动 </div>
<div class="move-container">
<div class="tree-wrapper" v-loading="loading">
<el-tree
v-if="treeData.length"
ref="treeRef"
node-key="id"
class="move-tree"
:data="treeData"
:props="treeProps"
highlight-current
default-expand-all
draggable
:allow-drag="allowDrag"
:allow-drop="allowDrop"
:expand-on-click-node="false"
@node-drop="handleNodeDrop"
>
<span class="tree-node" slot-scope="{ data }">
<i :class="getNodeIcon(data)" class="node-icon"></i>
<span class="node-label" :title="data.name">
{{ data.name }}
</span>
<el-tag size="mini" v-if="data.fileType === 0" type="info">
文件夹
</el-tag>
</span>
</el-tree>
<div v-else class="empty-holder">暂无可移动数据</div>
</div>
<div class="tips">
<p class="tips-title">操作提示</p>
<p>1. 仅文件fileType 0可被拖拽</p>
<p>2. 仅支持拖拽到文件夹节点内</p>
<p>3. 拖拽到目标组后会立即同步至后台</p>
</div>
</div>
</template>
<script>
export default {}
import { moveDocCenterAPI } from '@/api/publicService/docCenter'
export default {
name: 'Move',
props: {
selectedNode: {
type: Object,
default: () => ({}),
},
},
data() {
return {
treeData: [],
treeProps: {
children: 'childTree',
label: 'name',
},
loading: false,
moveActions: [],
}
},
watch: {
selectedNode: {
handler(newVal) {
this.initTree(newVal)
},
immediate: true,
deep: true,
},
},
methods: {
initTree(node) {
if (!node || !node.id) {
this.treeData = []
return
}
this.treeData = [this.cloneNode(node)]
this.moveActions = []
},
cloneNode(node) {
const childTree = Array.isArray(node.childTree)
? node.childTree.map((child) => this.cloneNode(child))
: []
return {
...node,
childTree,
}
},
getNodeIcon(data) {
return data.fileType == 0 ? 'el-icon-folder' : 'el-icon-document'
},
//
allowDrag(draggingNode) {
const data = draggingNode?.data || {}
return data.fileType == 1
},
//
allowDrop(draggingNode, dropNode, type) {
if (type !== 'inner') {
return false
}
const dropData = dropNode?.data || {}
if (dropData.fileType == 1) {
return false
}
const dragData = draggingNode?.data || {}
if (!dragData.id || dragData.id === dropData.id) {
return false
}
return true
},
async handleNodeDrop(draggingNode, dropNode) {
const source = draggingNode?.data
const target = dropNode?.data
if (!source?.id || !target?.id) {
this.$message.error('无法获取拖拽节点信息')
return
}
await this.moveDocument(
source.id,
target.id,
source.parentId,
target.parentIds,
)
},
async moveDocument(documentId, targetParentId, parentId, parentIds) {
//
const params = {
id: documentId, // id
newParentId: targetParentId, // id
parentId: parentId, // id
parentIds: parentIds,
}
this.loading = true
try {
const res = await moveDocCenterAPI(params)
if (res.code === 200) {
this.$message.success('移动成功')
this.recordAction(documentId, targetParentId)
this.updateLocalTree(documentId, targetParentId)
} else {
this.$message.error(res.msg || res.message || '移动失败')
}
} catch (error) {
this.$message.error(error.message || '移动失败')
} finally {
this.loading = false
}
},
recordAction(documentId, targetParentId) {
const idx = this.moveActions.findIndex(
(item) => item.documentId === documentId,
)
const payload = { documentId, targetParentId }
if (idx > -1) {
this.$set(this.moveActions, idx, payload)
} else {
this.moveActions.push(payload)
}
},
updateLocalTree(sourceId, targetId) {
const { node: sourceNode, parent } = this.findNodeAndParent(
this.treeData,
sourceId,
)
if (!sourceNode) {
return
}
if (parent) {
const newChildren = (parent.childTree || []).filter(
(child) => child.id !== sourceId,
)
this.$set(parent, 'childTree', newChildren)
} else {
this.treeData = this.treeData.filter(
(item) => item.id !== sourceId,
)
}
const targetNode = this.findNode(this.treeData, targetId)
if (targetNode) {
if (!Array.isArray(targetNode.childTree)) {
this.$set(targetNode, 'childTree', [])
}
targetNode.childTree.push(sourceNode)
}
},
findNodeAndParent(nodes, id, parent = null) {
for (const node of nodes) {
if (node.id === id) {
return { node, parent }
}
const children = node.childTree || []
if (children.length > 0) {
const result = this.findNodeAndParent(children, id, node)
if (result.node) {
return result
}
}
}
return { node: null, parent: null }
},
findNode(nodes, id) {
for (const node of nodes) {
if (node.id === id) {
return node
}
const childResult = this.findNode(node.childTree || [], id)
if (childResult) {
return childResult
}
}
return null
},
submit() {
if (this.moveActions.length === 0) {
return Promise.reject(new Error('请先拖拽需要移动的文件'))
}
return Promise.resolve({
moved: this.moveActions,
})
},
},
}
</script>
<style></style>
<style scoped lang="scss">
.move-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.tree-wrapper {
min-height: 320px;
border: 1px solid #f0f0f0;
border-radius: 6px;
padding: 12px;
background: #fafafa;
}
.move-tree {
max-height: 360px;
overflow: auto;
.tree-node {
display: flex;
align-items: center;
gap: 8px;
.node-icon {
font-size: 16px;
color: #409eff;
}
.node-label {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.empty-holder {
height: 180px;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
}
.tips {
font-size: 13px;
color: #666;
line-height: 1.8;
background: #fdf6ec;
border: 1px solid #f5dab1;
padding: 12px;
border-radius: 6px;
.tips-title {
font-weight: 600;
margin-bottom: 4px;
}
}
</style>

View File

@ -1,74 +1,220 @@
<template>
<div class="permission-form">
<div class="share-permission-form">
<section class="share-section">
<div class="section-title">共享文件</div>
<div class="file-list" v-if="selectedFiles.length">
<el-tag
size="mini"
effect="plain"
v-for="file in selectedFiles"
:key="file.id"
>
{{ file.name }}
</el-tag>
</div>
<el-empty
v-else
description="尚未选择文件"
:image-size="80"
></el-empty>
</section>
<el-form
ref="permissionForm"
ref="shareFormRef"
:model="form"
:rules="rules"
label-width="100px"
class="permission-form-content"
label-width="90px"
class="share-form"
>
<!-- 授权对象 -->
<el-form-item
label="授权对象"
prop="authorizedObject"
class="form-item"
>
<el-select
v-model="form.authorizedObject"
placeholder="请选择"
clearable
filterable
style="width: 100%"
@change="handleAuthorizedObjectChange"
<el-form-item label="授权对象" prop="shareTargets">
<el-input
readonly
placeholder="请选择授权对象"
:value="shareTargetNames"
@focus="openUserPicker"
>
<el-option
v-for="item in authorizedObjectOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<i
slot="suffix"
class="el-input__icon el-icon-more"
@click.stop="openUserPicker"
></i>
</el-input>
<div class="selected-user-tags" v-if="form.shareTargets.length">
<el-tag
v-for="user in form.shareTargets"
:key="user.id"
closable
@close="removeSelectedUser(user.userId)"
>
{{ user.nickName }}
</el-tag>
</div>
</el-form-item>
<!-- 权限设置 -->
<el-form-item label="权限" prop="permission" class="form-item">
<el-radio-group
v-model="form.permission"
@change="handlePermissionChange"
>
<div class="permission-options">
<el-radio label="view" class="permission-radio">
仅可查看
</el-radio>
<el-radio
label="view_download"
class="permission-radio"
>
可查看/下载
</el-radio>
<el-radio label="edit" class="permission-radio">
可编辑
</el-radio>
<el-radio label="manage" class="permission-radio">
可管理
</el-radio>
</div>
<el-form-item label="权限类型" prop="permission">
<el-radio-group v-model="form.permission" size="small">
<el-radio-button label="1">仅可查看</el-radio-button>
<el-radio-button label="2">可查看/下载</el-radio-button>
<el-radio-button label="3">可编辑</el-radio-button>
<el-radio-button label="4">可管理</el-radio-button>
</el-radio-group>
</el-form-item>
</el-form>
<el-dialog
title="人员选择"
append-to-body
:visible.sync="userPickerVisible"
width="80%"
class="user-picker-dialog"
>
<div class="user-picker">
<div class="left-panel">
<el-tabs
v-model="activeSelectorTab"
class="selector-tabs"
@tab-click="handleTabClick"
>
<el-tab-pane label="部门" name="dept">
<div class="tab-toolbar">
<el-input
v-model="deptKeyword"
size="mini"
placeholder="搜索部门"
clearable
prefix-icon="el-icon-search"
/>
<el-button
type="text"
size="mini"
class="toggle-btn"
@click="toggleDeptExpand"
>
{{ deptTreeExpanded ? '折叠' : '展开' }}
</el-button>
</div>
<el-tree
ref="deptTreeRef"
class="dept-tree"
:data="deptTree"
:props="deptTreeProps"
node-key="id"
:highlight-current="true"
:expand-on-click-node="false"
:default-expand-all="false"
:filter-node-method="filterDeptNode"
:current-node-key="activeDeptId"
v-loading="deptLoading"
@node-click="handleDeptClick"
/>
</el-tab-pane>
<el-tab-pane label="角色" name="role">
<div class="tab-toolbar">
<el-input
v-model="roleKeyword"
size="mini"
placeholder="搜索角色"
clearable
prefix-icon="el-icon-search"
/>
</div>
<el-scrollbar
class="role-list"
v-loading="roleLoading"
>
<div
v-for="role in filteredRoleList"
:key="role.id"
class="role-item"
:class="{
active: activeRoleId === role.id,
}"
@click="handleRoleClick(role)"
>
{{ role.name }}
</div>
<el-empty
v-if="
!filteredRoleList.length && !roleLoading
"
:image-size="60"
description="暂无角色"
/>
</el-scrollbar>
</el-tab-pane>
</el-tabs>
</div>
<div class="right-panel">
<div class="panel-header">
<span>人员列表</span>
<!-- <el-input
size="mini"
placeholder="搜索姓名"
prefix-icon="el-icon-search"
v-model="userKeyword"
@keyup.enter.native="fetchUserList"
clearable
@clear="fetchUserList"
/> -->
</div>
<el-table
ref="userTableRef"
:data="userList"
height="320"
border
v-loading="userTableLoading"
@selection-change="handleUserSelectionChange"
>
<el-table-column type="selection" width="60" />
<el-table-column type="index" label="序号" width="80" />
<el-table-column
prop="nickName"
label="姓名"
min-width="200"
show-overflow-tooltip
/>
</el-table>
</div>
</div>
<div class="panel-title selected-title">
已选人员{{ tempSelectedUsers.length }}
</div>
<div class="selected-user-tags" v-if="tempSelectedUsers.length">
<el-tag
v-for="user in tempSelectedUsers"
:key="user.id"
closable
@close="removeTempSelected(user.userId)"
>
{{ user.nickName }}
</el-tag>
</div>
<el-empty v-else description="暂未选择人员" :image-size="60" />
<span slot="footer" class="dialog-footer">
<el-button @click="userPickerVisible = false">取消</el-button>
<el-button type="primary" @click="confirmUserSelection">
确定
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {
getShareDeptTreeAPI,
getShareRoleListAPI,
getShareUserListAPI,
getShareUserListByRoleAPI,
saveSharePermissionAPI,
} from '@/api/publicService/docCenter'
export default {
name: 'SharePermissionForm',
props: {
//
visible: {
type: Boolean,
default: false,
},
//
selectedFiles: {
type: Array,
default: () => [],
@ -78,13 +224,15 @@ export default {
return {
loading: false,
form: {
authorizedObject: '', //
permission: 'view', //
shareTargets: [],
permission: '3',
},
rules: {
authorizedObject: [
shareTargets: [
{
required: true,
type: 'array',
min: 1,
message: '请选择授权对象',
trigger: 'change',
},
@ -92,136 +240,435 @@ export default {
permission: [
{
required: true,
message: '请选择权限',
message: '请选择权限类型',
trigger: 'change',
},
],
},
//
authorizedObjectOptions: [
{ value: 'user_001', label: '张三' },
{ value: 'user_002', label: '李四' },
{ value: 'user_003', label: '王五' },
{ value: 'dept_001', label: '技术部' },
{ value: 'dept_002', label: '产品部' },
{ value: 'role_001', label: '管理员' },
{ value: 'role_002', label: '编辑者' },
],
userPickerVisible: false,
deptTree: [],
deptTreeProps: {
children: 'children',
label: 'label',
},
deptLoading: false,
roleList: [],
roleLoading: false,
activeDeptId: null,
activeRoleId: null,
userKeyword: '',
deptKeyword: '',
roleKeyword: '',
activeSelectorTab: 'dept',
deptTreeExpanded: true,
userList: [],
userTableLoading: false,
tempSelectedUsers: [],
}
},
computed: {
shareTargetNames() {
if (!this.form.shareTargets.length) return ''
return this.form.shareTargets
.map((item) => item.nickName)
.join('')
},
filteredRoleList() {
if (!this.roleKeyword.trim()) {
return this.roleList
}
const keyword = this.roleKeyword.trim().toLowerCase()
return this.roleList.filter((role) =>
(role.name || '').toLowerCase().includes(keyword),
)
},
},
watch: {
visible(newVal) {
if (newVal) {
this.resetForm()
deptKeyword(val) {
if (this.$refs.deptTreeRef) {
this.$refs.deptTreeRef.filter(val)
}
},
},
created() {
this.initStaticData()
},
methods: {
//
resetForm() {
this.form = {
authorizedObject: '',
permission: 'view',
async initStaticData() {
this.fetchDeptTree()
this.fetchRoleList()
},
openUserPicker() {
this.tempSelectedUsers = [...this.form.shareTargets]
this.userPickerVisible = true
if (!this.deptTree.length) {
this.fetchDeptTree()
}
this.$nextTick(() => {
this.$refs.permissionForm &&
this.$refs.permissionForm.clearValidate()
if (!this.roleList.length) {
this.fetchRoleList()
}
if (this.activeDeptId && this.activeRoleId) {
this.fetchUserList()
}
},
async fetchDeptTree() {
this.deptLoading = true
try {
const res = await getShareDeptTreeAPI()
const treeData = res.data || res.rows || []
this.deptTree = this.normalizeDeptTree(treeData)
if (!this.activeDeptId && this.deptTree.length) {
this.activeDeptId = this.deptTree[0].id
}
this.$nextTick(() => {
if (this.$refs.deptTreeRef && this.deptKeyword) {
this.$refs.deptTreeRef.filter(this.deptKeyword)
}
})
if (this.activeDeptId && this.activeRoleId) {
this.fetchUserList()
}
} finally {
this.deptLoading = false
}
},
async fetchRoleList() {
this.roleLoading = true
try {
const res = await getShareRoleListAPI()
const list = res.rows || res.data || []
this.roleList = list.map((item) => ({
id: item.roleId ?? item.id,
name: item.roleName ?? item.name,
}))
if (!this.activeRoleId && this.roleList.length) {
this.activeRoleId = this.roleList[0].id
}
if (this.activeDeptId && this.activeRoleId) {
this.fetchUserList()
}
} finally {
this.roleLoading = false
}
},
async fetchUserList() {
if (!this.activeDeptId && !this.activeRoleId) return
this.userTableLoading = true
try {
// const res = await getShareUserListAPI({
// deptId: this.activeDeptId,
// roleId: this.activeRoleId,
// keyword: this.userKeyword.trim(),
// })
const API =
this.activeSelectorTab == 'dept'
? getShareUserListAPI
: getShareUserListByRoleAPI
const params =
this.activeSelectorTab == 'dept'
? {
pageSize: 1000,
pageNum: 1,
deptId: this.activeDeptId,
}
: {
pageSize: 1000,
pageNum: 1,
roleId: this.activeRoleId,
}
const res = await API(params)
this.userList = res.rows || []
this.syncTableSelection()
} finally {
this.userTableLoading = false
}
},
handleDeptClick(node) {
this.activeDeptId = node.id
this.activeSelectorTab = 'dept'
this.fetchUserList()
},
handleRoleClick(role) {
this.activeRoleId = role.id
this.activeSelectorTab = 'role'
this.fetchUserList()
},
handleUserSelectionChange(selection) {
const currentIds = this.userList.map((item) => item.userId)
this.tempSelectedUsers = this.tempSelectedUsers.filter((user) => {
if (!currentIds.includes(user.userId)) return true
return selection.some((item) => item.userId === user.userId)
})
selection.forEach((item) => {
if (
!this.tempSelectedUsers.some(
(user) => user.userId === item.userId,
)
) {
this.tempSelectedUsers.push(item)
}
})
},
//
handleAuthorizedObjectChange(value) {
console.log('授权对象变化:', value)
removeTempSelected(userId) {
this.tempSelectedUsers = this.tempSelectedUsers.filter(
(item) => item.userId !== userId,
)
if (this.$refs.userTableRef) {
const target = this.userList.find(
(item) => item.userId === userId,
)
if (target) {
this.$refs.userTableRef.toggleRowSelection(target, false)
}
}
},
confirmUserSelection() {
this.form.shareTargets = [...this.tempSelectedUsers]
this.$refs.shareFormRef.clearValidate('shareTargets')
this.userPickerVisible = false
},
removeSelectedUser(userId) {
this.form.shareTargets = this.form.shareTargets.filter(
(item) => item.userId !== userId,
)
this.tempSelectedUsers = this.tempSelectedUsers.filter(
(item) => item.userId !== userId,
)
},
syncTableSelection() {
this.$nextTick(() => {
if (!this.$refs.userTableRef) return
this.$refs.userTableRef.clearSelection()
this.userList.forEach((user) => {
if (
this.tempSelectedUsers.some(
(item) => item.userId === user.userId,
)
) {
this.$refs.userTableRef.toggleRowSelection(user, true)
}
})
})
},
filterDeptNode(value, data) {
if (!value) return true
return (data.label || '').includes(value)
},
toggleDeptExpand() {
if (!this.$refs.deptTreeRef || !this.$refs.deptTreeRef.store) {
return
}
const rootNodes = this.$refs.deptTreeRef.store.root.childNodes || []
const allExpanded = this.isAllDeptExpanded(rootNodes)
if (allExpanded) {
this.collapseNodes(rootNodes)
this.deptTreeExpanded = false
} else {
this.expandNodes(rootNodes)
this.deptTreeExpanded = true
}
},
expandNodes(nodes) {
nodes.forEach((node) => {
node.expanded = true
if (node.childNodes && node.childNodes.length) {
this.expandNodes(node.childNodes)
}
})
},
collapseNodes(nodes) {
nodes.forEach((node) => {
node.expanded = false
if (node.childNodes && node.childNodes.length) {
this.collapseNodes(node.childNodes)
}
})
},
isAllDeptExpanded(nodes) {
for (const node of nodes) {
if (node.childNodes && node.childNodes.length) {
if (
!node.expanded ||
!this.isAllDeptExpanded(node.childNodes)
) {
return false
}
}
}
return true
},
normalizeDeptTree(list = []) {
return list.map((item) => ({
id: item.id ?? item.deptId,
label: item.label ?? item.name ?? item.deptName,
children: item.children
? this.normalizeDeptTree(item.children)
: [],
}))
},
async submit() {
await new Promise((resolve, reject) => {
this.$refs.shareFormRef.validate((valid) => {
if (valid) {
resolve()
} else {
reject(new Error('表单验证失败'))
}
})
})
if (!this.selectedFiles.length) {
throw new Error('未选择需要共享的文件')
}
const payload = {
id: this.selectedFiles.map((file) => file.id).join(','),
userIds: this.form.shareTargets
.map((item) => item.userId)
.join(','),
auth: this.form.permission,
}
this.loading = true
try {
const res = await saveSharePermissionAPI(payload)
if (res.code !== 200) {
throw new Error(res.msg || '共享失败')
}
this.$message.success('共享成功')
return res
} finally {
this.loading = false
}
},
//
handlePermissionChange(value) {
console.log('权限变化:', value)
handleTabClick(tab) {
// this.activeSelectorTab = tab.name
// this.fetchUserList()
// console.log(tab)
this.activeDeptId = null
this.activeRoleId = null
this.userList = []
this.syncTableSelection()
},
},
}
</script>
<style scoped lang="scss">
.permission-form {
padding: 20px;
background: #fff;
border-radius: 8px;
.permission-form-content {
.form-item {
margin-bottom: 24px;
:deep(.el-form-item__label) {
font-weight: 600;
color: #333;
font-size: 14px;
}
:deep(.el-form-item__content) {
line-height: normal;
}
.share-permission-form {
.share-section {
margin-bottom: 16px;
.section-title {
font-size: 14px;
margin-bottom: 8px;
font-weight: 600;
}
.permission-options {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
width: 100%;
.permission-radio {
margin: 0;
padding: 12px 16px;
border: 1px solid #e0e0e0;
border-radius: 6px;
background: #fafafa;
transition: all 0.3s ease;
&:hover {
border-color: #409eff;
background: #f0f7ff;
}
:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #409eff;
font-weight: 600;
}
:deep(.el-radio__input.is-checked .el-radio__inner) {
background-color: #409eff;
border-color: #409eff;
}
:deep(.el-radio__label) {
font-size: 14px;
color: #333;
padding-left: 8px;
}
}
.file-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
}
.share-form {
background: #f9fbff;
padding: 16px;
border-radius: 8px;
}
}
.form-actions {
.selected-user-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.user-picker {
display: flex;
gap: 16px;
.left-panel {
width: 320px;
border-right: 1px solid #f0f0f0;
padding-right: 16px;
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 24px;
padding-top: 16px;
border-top: 1px solid #e0e0e0;
flex-direction: column;
}
.right-panel {
flex: 1;
display: flex;
flex-direction: column;
}
}
.el-button {
min-width: 80px;
.selector-tabs {
flex: 1;
display: flex;
flex-direction: column;
::v-deep .el-tabs__header {
margin-bottom: 8px;
}
::v-deep .el-tabs__content {
flex: 1;
display: flex;
}
::v-deep .el-tab-pane {
flex: 1;
display: flex;
flex-direction: column;
}
}
.tab-toolbar {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
.toggle-btn {
padding: 0;
min-width: 50px;
}
.dept-tree {
flex: 1;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 8px;
overflow: auto;
}
.role-list {
flex: 1;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 8px;
.role-item {
padding: 6px 10px;
border-radius: 4px;
cursor: pointer;
margin-bottom: 4px;
&.active {
background: #ecf5ff;
color: #409eff;
}
}
}
//
@media (max-width: 768px) {
.permission-form {
.permission-options {
grid-template-columns: 1fr;
gap: 12px;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.selected-title {
margin-top: 8px;
}
.user-picker-dialog {
::v-deep .el-dialog__body {
display: flex;
flex-direction: column;
}
}
</style>

View File

@ -1,186 +1,177 @@
<template>
<div class="tag-filter-container">
<!-- 需求分类 -->
<div class="filter-section">
<div class="section-title">需求</div>
<div class="tag-buttons">
<el-button
v-for="(tag, index) in requirementTags"
:key="index"
:class="['tag-button', { active: tag.selected }]"
@click="toggleTag('requirement', index)"
>
{{ tag.name }}
</el-button>
</div>
</div>
<!-- 工程分类 -->
<div class="filter-section">
<div class="section-title">工程</div>
<div class="tag-buttons">
<el-button
v-for="(tag, index) in engineeringTags"
:key="index"
:class="['tag-button', { active: tag.selected }]"
@click="toggleTag('engineering', index)"
>
{{ tag.name }}
</el-button>
</div>
</div>
<!-- 产品分类 -->
<div class="filter-section">
<div class="section-title">产品</div>
<div class="tag-buttons">
<el-button
v-for="(tag, index) in productTags"
:key="index"
:class="['tag-button', { active: tag.selected }]"
@click="toggleTag('product', index)"
>
{{ tag.name }}
</el-button>
</div>
</div>
<!-- 营销分类 -->
<div class="filter-section">
<div class="section-title">营销</div>
<div class="tag-buttons">
<el-button
v-for="(tag, index) in marketingTags"
:key="index"
:class="['tag-button', { active: tag.selected }]"
@click="toggleTag('marketing', index)"
>
{{ tag.name }}
</el-button>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<el-button type="primary" class="query-button" @click="handleQuery">
<i class="el-icon-search"></i>
查询
<div class="search-container">
<el-input
v-model.trim="searchKeyword"
clearable
placeholder="输入搜索标签名称"
class="search-input"
@keyup.enter.native="handleSearch"
>
<i
slot="prefix"
class="el-input__icon el-icon-search"
@click="handleSearch"
></i>
</el-input>
<el-button type="primary" size="mini" @click="handleSearch">
搜索
</el-button>
</div>
<div class="tags-container" v-loading="loading">
<template v-if="filteredCategories.length">
<div
class="filter-section"
v-for="category in filteredCategories"
:key="category.key"
>
<div class="section-title"># {{ category.typeName }} #</div>
<div class="tag-buttons">
<div
v-for="tag in category.tags"
:key="tag.id"
:class="[
'tag-button',
{ active: isTagSelected(tag.id) },
]"
@click="toggleTag(tag)"
>
{{ tag.labelName }}
</div>
</div>
</div>
<div v-if="allTagsLoaded" class="load-status">全部加载完毕</div>
</template>
<el-empty
v-else-if="!loading"
description="暂无匹配标签"
:image-size="80"
/>
</div>
<div class="action-buttons">
<el-button class="reset-button" @click="handleReset">
<i class="el-icon-refresh"></i>
重置
</el-button>
<el-button type="primary" class="query-button" @click="handleQuery">
<i class="el-icon-search"></i>
查询
</el-button>
</div>
</div>
</template>
<script>
import { getDocsTagsListAPI } from '@/api/publicService/docCenter'
export default {
name: 'TagFilter',
data() {
return {
//
requirementTags: [
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
],
//
engineeringTags: [
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: '线路工程', selected: false },
{ name: '调试工程', selected: false },
{ name: '大修技改', selected: false },
{ name: '其他工程', selected: false },
{ name: '调试工程', selected: false },
{ name: '设计工程', selected: false },
{ name: '运维工程', selected: false },
{ name: '监理工程', selected: false },
],
//
productTags: [
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
],
//
marketingTags: [
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
{ name: 'XXXXXX', selected: false },
],
searchKeyword: '',
loading: false,
allTagsLoaded: false,
categories: [],
selectedTagIds: [],
}
},
computed: {
filteredCategories() {
if (!this.searchKeyword) {
return this.categories
}
const keyword = this.searchKeyword.toLowerCase()
return this.categories
.map((category) => {
const filteredTags = category.tags.filter((tag) =>
(tag.labelName || '').toLowerCase().includes(keyword),
)
return {
...category,
tags: filteredTags,
}
})
.filter((category) => category.tags.length > 0)
},
},
created() {
this.loadTags()
},
methods: {
//
toggleTag(category, index) {
const tagList = this.getTagListByCategory(category)
if (tagList && tagList[index]) {
tagList[index].selected = !tagList[index].selected
async loadTags() {
this.loading = true
this.allTagsLoaded = false
try {
const tagsRes = await getDocsTagsListAPI({
labelName: this.searchKeyword,
})
const tagsData = tagsRes?.data || tagsRes || {}
this.categories = Object.keys(tagsData)
.map((categoryName, index) => {
const tagList = tagsData[categoryName] || []
return {
key: `${categoryName}_${index}`,
typeName: categoryName,
tags: tagList
.map((tag) => {
const rawId =
tag.id ??
tag.typeId ??
tag.labelId ??
tag.value ??
tag.labelCode
if (!rawId) {
return null
}
return {
id: String(rawId),
labelName:
tag.labelName ?? tag.name ?? '',
typeId: tag.typeId ?? rawId,
typeName: categoryName,
raw: tag,
}
})
.filter(Boolean),
}
})
.filter((category) => category.tags.length > 0)
this.allTagsLoaded = true
} catch (error) {
console.error('加载标签失败:', error)
this.$message.error('加载标签失败,请重试')
this.categories = []
this.allTagsLoaded = true
} finally {
this.loading = false
}
},
//
getTagListByCategory(category) {
switch (category) {
case 'requirement':
return this.requirementTags
case 'engineering':
return this.engineeringTags
case 'product':
return this.productTags
case 'marketing':
return this.marketingTags
default:
return null
isTagSelected(tagId) {
return this.selectedTagIds.includes(String(tagId))
},
toggleTag(tag) {
const tagId = String(tag.id)
const index = this.selectedTagIds.indexOf(tagId)
if (index > -1) {
this.selectedTagIds.splice(index, 1)
} else {
this.selectedTagIds.push(tagId)
}
},
//
handleSearch() {
this.loadTags()
},
handleQuery() {
const selectedTags = this.getSelectedTags()
this.$emit('query', selectedTags)
this.$emit('query', [...this.selectedTagIds])
},
//
handleReset() {
this.resetAllTags()
this.searchKeyword = ''
this.selectedTagIds = []
this.loadTags()
this.$emit('reset')
},
//
getSelectedTags() {
const selectedTags = {
requirement: this.requirementTags.filter((tag) => tag.selected),
engineering: this.engineeringTags.filter((tag) => tag.selected),
product: this.productTags.filter((tag) => tag.selected),
marketing: this.marketingTags.filter((tag) => tag.selected),
}
return selectedTags
},
//
resetAllTags() {
this.requirementTags.forEach((tag) => {
tag.selected = false
})
this.engineeringTags.forEach((tag) => {
tag.selected = false
})
this.productTags.forEach((tag) => {
tag.selected = false
})
this.marketingTags.forEach((tag) => {
tag.selected = false
})
},
},
}
</script>
@ -191,14 +182,29 @@ export default {
background: #fff;
min-height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.search-container {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
.search-input {
flex: 1;
}
}
.tags-container {
flex: 1;
overflow-y: auto;
padding-right: 4px;
}
.filter-section {
margin-bottom: 24px;
&:last-of-type {
margin-bottom: 32px;
}
margin-bottom: 20px;
}
.section-title {
@ -213,108 +219,94 @@ export default {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 8px;
}
.tag-button {
height: 32px;
padding: 0 16px;
border-radius: 16px;
border: 1px solid #dcdfe6;
background: #f5f7fa;
color: #606266;
font-size: 14px;
transition: all 0.3s;
.tag-button {
min-width: 80px;
height: 32px;
line-height: 32px;
border-radius: 16px;
border: 1px solid #dcdfe6;
background: #f5f7fa;
color: #606266;
font-size: 14px;
text-align: center;
padding: 0 16px;
cursor: pointer;
transition: all 0.3s;
user-select: none;
&:hover {
border-color: #409eff;
color: #409eff;
}
&.active {
background: #409eff;
border-color: #409eff;
color: #fff;
}
&:hover {
border-color: #409eff;
color: #409eff;
}
&.active {
background: #409eff;
border-color: #409eff;
color: #fff;
}
}
.load-status {
text-align: center;
color: #909399;
font-size: 12px;
padding: 12px 0 4px;
}
.action-buttons {
display: flex;
justify-content: center;
gap: 16px;
margin-top: 24px;
padding-top: 20px;
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #e4e7ed;
.query-button {
.query-button,
.reset-button {
height: 36px;
padding: 0 24px;
border-radius: 18px;
background: #409eff;
border-color: #409eff;
color: #fff;
font-size: 14px;
font-weight: 500;
display: flex;
align-items: center;
gap: 6px;
}
.query-button {
background: #409eff;
border-color: #409eff;
color: #fff;
&:hover {
background: #66b1ff;
border-color: #66b1ff;
}
i {
font-size: 14px;
}
}
.reset-button {
height: 36px;
padding: 0 24px;
border-radius: 18px;
background: #f5f7fa;
border-color: #dcdfe6;
color: #606266;
font-size: 14px;
display: flex;
align-items: center;
gap: 6px;
&:hover {
background: #ecf5ff;
border-color: #c6e2ff;
color: #409eff;
}
i {
font-size: 14px;
}
}
}
//
@media (max-width: 768px) {
.tag-filter-container {
padding: 16px;
}
.tag-buttons {
.tag-button {
height: 28px;
padding: 0 12px;
font-size: 12px;
}
}
.action-buttons {
flex-direction: column;
align-items: center;
.query-button,
.reset-button {
width: 120px;
}
.tag-button {
min-width: 70px;
height: 28px;
line-height: 28px;
font-size: 12px;
}
}
</style>

View File

@ -41,7 +41,6 @@
action="#"
:auto-upload="false"
:file-list="fileList"
:before-upload="handleBeforeUpload"
:on-change="handleFileChange"
:on-remove="handleRemove"
:on-exceed="handleExceed"
@ -87,61 +86,45 @@ export default {
TagSelector,
},
props: {
visible: {
type: Boolean,
default: false,
},
selectedNode: {
type: Object,
default: {},
default: () => ({}),
},
},
data() {
return {
dialogVisible: false,
duplicateHandle: '1', // 1-2-
selectedTag: '', // ID
selectedTags: [], //
selectedTagIds: [], // ID
selectedTagLabels: [], //
selectedTypeIds: [], // typeId
tagSelectorVisible: false, //
fileList: [], //
uploading: false, //
}
},
watch: {
visible: {
handler(val) {
this.dialogVisible = val
},
immediate: true,
},
},
computed: {
//
selectedTagText() {
if (this.selectedTags.length === 0) {
if (this.selectedTagLabels.length === 0) {
return ''
}
return this.selectedTags.map((tag) => tag.labelName).join(',')
return this.selectedTagLabels.join(',')
},
},
methods: {
//
handleClose() {
this.dialogVisible = false
this.$emit('update:visible', false)
this.resetForm()
},
//
resetForm() {
this.duplicateHandle = '1'
this.selectedTag = ''
this.selectedTags = []
this.selectedTagIds = []
this.selectedTagLabels = []
this.selectedTypeIds = []
this.fileList = []
this.uploading = false
this.$refs.upload && this.$refs.upload.clearFiles()
this.$nextTick(() => {
this.$refs.upload && this.$refs.upload.clearFiles()
})
},
//
@ -153,38 +136,56 @@ export default {
handleTagConfirm(result) {
// result : { tags: [], typeIds: [] }
this.selectedTags = result?.tags || []
this.selectedTagIds =
result?.tagIds ||
this.selectedTags.map((tag) => String(tag.id ?? ''))
this.selectedTagLabels =
result?.tagLabels ||
this.selectedTags.map((tag) => tag.labelName || '')
this.selectedTypeIds = result?.typeIds || []
// ID
if (this.selectedTags.length > 0) {
this.selectedTag = this.selectedTags[0].id
} else {
this.selectedTag = ''
}
},
//
handleBeforeUpload(file) {
//
// const allowedTypes = ['rar', 'zip']
// const fileExtension = file.name.split('.').pop().toLowerCase()
//
validateFile(file, options = {}) {
const { showMessage = true } = options
const rawFile = file?.raw || file
if (!rawFile) {
if (showMessage) {
this.$modal.msgError('文件信息异常,请重新选择文件!')
}
return false
}
// if (!allowedTypes.includes(fileExtension)) {
// this.$modal.msgError('rar/zip!')
// return false
// }
const fileName = rawFile.name || file.name || ''
const fileSize = rawFile.size || file.size || 0
//
const allowedTypes = ['rar', 'zip']
const fileExtension = fileName.split('.').pop().toLowerCase()
if (!allowedTypes.includes(fileExtension)) {
if (showMessage) {
this.$modal.msgError('只支持上传rar/zip格式文件!')
}
return false
}
// (500kb = 0.5MB)
const maxSize = 0.5
const isLtMaxSize = file.size / 1024 / 1024 < maxSize
const isLtMaxSize = fileSize / 1024 / 1024 < maxSize
if (!isLtMaxSize) {
this.$modal.msgError(`文件大小不能超过 ${maxSize}MB!`)
if (showMessage) {
this.$modal.msgError(`文件大小不能超过 ${maxSize}MB!`)
}
return false
}
//
if (file.name.includes(',')) {
this.$modal.msgError('文件名不能包含英文逗号!')
if (fileName.includes(',')) {
if (showMessage) {
this.$modal.msgError('文件名不能包含英文逗号!')
}
return false
}
@ -193,6 +194,10 @@ export default {
//
handleFileChange(file, fileList) {
// if (!this.validateFile(file, { showMessage: true })) {
// this.fileList = fileList.filter((item) => item.uid !== file.uid)
// return
// }
this.fileList = fileList
},
@ -215,14 +220,21 @@ export default {
async handleConfirm() {
if (this.fileList.length === 0) {
this.$modal.msgWarning('请选择要上传的文件!')
return
throw new Error('表单验证失败')
}
if (this.selectedTags.length === 0) {
this.$modal.msgWarning('请选择文件标签!')
return
throw new Error('表单验证失败')
}
// const hasInvalidFile = this.fileList.some(
// (item) => !this.validateFile(item, { showMessage: true }),
// )
// if (hasInvalidFile) {
// throw new Error('')
// }
this.uploading = true
try {
@ -233,9 +245,12 @@ export default {
folderId: this.selectedNode?.id,
type: this.selectedNode?.type,
repeatType: this.duplicateHandle,
parentId: this.selectedNode?.parentId,
labels: this.selectedTags.map((tag) => tag.id).join(','),
typeIds: this.selectedTypeIds.join(','), // typeId
parentId: this.selectedNode?.id,
parentIds: this.selectedNode?.parentIds,
labelIds: this.selectedTagIds.join(','), // ID
labels: this.selectedTagLabels.join(','), //
level: this.selectedNode?.level * 1 + 1,
// typeIds: this.selectedTypeIds.join(','), // typeId
}
//
@ -250,7 +265,24 @@ export default {
//
const response = await uploadFileTagAPI(formData)
console.log(response, '上传结果')
this.$modal.msgSuccess('文件上传成功!')
const payload = {
success: true,
response,
tagIds: [...this.selectedTagIds],
tagLabels: [...this.selectedTagLabels],
tags: this.selectedTags.map((tag) => ({
id: String(tag.id ?? ''),
labelName: tag.labelName,
typeId: tag.typeId,
typeName: tag.typeName,
})),
}
this.$emit('confirm', payload)
this.resetForm()
return payload
} catch (error) {
this.$modal.msgError('文件上传失败,请重试!')
throw error