Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Sign in / Register
Toggle navigation
Z
zxn-adputin
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lijin
zxn-adputin
Commits
011cb0f5
Commit
011cb0f5
authored
Feb 24, 2025
by
lijin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加素材标签
parent
e6633eb4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1288 additions
and
1 deletion
+1288
-1
index.vue
src/components/FileUpload/index.vue
+440
-0
main.js
src/main.js
+1
-1
index.js
src/router/index.js
+14
-0
MaterialUpload.vue
src/views/createMaterial/MaterialUpload.vue
+158
-0
CascaderSelect.vue
src/views/materialTag/components/CascaderSelect.vue
+346
-0
index.vue
src/views/materialTag/index.vue
+329
-0
No files found.
src/components/FileUpload/index.vue
0 → 100644
View file @
011cb0f5
This diff is collapsed.
Click to expand it.
src/main.js
View file @
011cb0f5
...
...
@@ -47,7 +47,7 @@ Vue.filter("toFixed", function (price, limit) {
});
router
.
beforeEach
((
to
,
from
,
next
)
=>
{
if
(
to
.
path
==
"/login"
)
{
if
(
to
.
path
==
"/login"
||
to
.
path
==
"/materialUpload"
||
to
.
path
==
"/materialTag"
)
{
next
();
}
else
{
gatewayUserRouters
().
then
(
res
=>
{
...
...
src/router/index.js
View file @
011cb0f5
...
...
@@ -132,6 +132,20 @@ export const constantRouterMap = [
path
:
''
,
component
:
()
=>
import
(
'@/views/layout/components/Sidebar/redirect'
)
}]
},
{
path
:
"/materialUpload"
,
// 资产上传
name
:
"materialUpload"
,
component
:
()
=>
import
(
'@/views/createMaterial/MaterialUpload'
),
hidden
:
true
},
{
path
:
"/materialTag"
,
// 素材标签管理
name
:
"materialTag"
,
component
:
()
=>
import
(
'@/views/materialTag'
),
hidden
:
true
}
];
...
...
src/views/createMaterial/MaterialUpload.vue
0 → 100644
View file @
011cb0f5
<
template
>
<div
class=
"upload-page"
>
<h2>
文件上传
</h2>
<!-- 使用上传组件 -->
<file-upload
:upload-url=
"uploadUrl"
:extra-data=
"extraData"
:headers=
"headers"
@
update:files=
"handleFilesUpdate"
@
upload-success=
"handleUploadSuccess"
@
upload-error=
"handleUploadError"
@
upload-complete=
"handleUploadComplete"
ref=
"uploadComponent"
>
<!-- 自定义上传按钮 -->
<template
#
trigger
>
<el-button
type=
"primary"
icon=
"el-icon-upload"
>
选择图片或视频
</el-button>
</
template
>
</file-upload>
<!-- 显示上传成功的文件列表 -->
<!-- <div class="success-files" v-if="successFiles.length > 0">-->
<!-- <h3>已上传文件列表:</h3>-->
<!-- <el-table :data="successFiles" style="width: 100%">-->
<!-- <el-table-column prop="name" label="文件名"></el-table-column>-->
<!-- <el-table-column prop="type" label="类型"></el-table-column>-->
<!-- <el-table-column prop="size" label="大小">-->
<!-- <template slot-scope="scope">-->
<!-- {{ formatFileSize(scope.row.size) }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column label="预览">-->
<!-- <template slot-scope="scope">-->
<!-- <el-button type="text" @click="previewFile(scope.row)">-->
<!-- 预览-->
<!-- </el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </el-table>-->
<!-- <!– 清空按钮 –>-->
<!-- <el-button type="danger" @click="clearFiles" class="clear-btn">-->
<!-- 清空文件-->
<!-- </el-button>-->
<!-- </div>-->
</div>
</template>
<
script
>
import
FileUpload
from
'../../components/FileUpload/index.vue'
export
default
{
name
:
'UploadPage'
,
components
:
{
FileUpload
},
data
()
{
return
{
uploadUrl
:
'http://localhost:8567/material/business/youtube/uploadVideo'
,
successFiles
:
[],
extraData
:
{
tags
:
"abc, def"
,
director
:
'1'
,
resType
:
'1'
,
directoryId
:
'1'
},
headers
:
{
'Authorization'
:
'Bearer '
+
localStorage
.
getItem
(
'token'
)
}
}
},
methods
:
{
// 处理文件列表更新
handleFilesUpdate
(
files
)
{
this
.
successFiles
=
files
console
.
log
(
"parent receive upload success"
,
files
)
},
// 处理单个文件上传成功
handleUploadSuccess
(
file
)
{
// this.$message.success(`文件 ${file.name} 上传成功`)
},
// 处理上传错误
handleUploadError
({
file
,
error
})
{
// this.$message.error(`文件 ${file.name} 上传失败:${error.message}`)
},
// 处理所有文件上传完成
handleUploadComplete
(
files
)
{
// this.$message.success(`所有文件上传完成,共 ${files.length} 个文件`)
},
// 格式化文件大小
formatFileSize
(
bytes
)
{
if
(
bytes
===
0
)
return
'0 B'
const
k
=
1024
const
sizes
=
[
'B'
,
'KB'
,
'MB'
,
'GB'
,
'TB'
]
const
i
=
Math
.
floor
(
Math
.
log
(
bytes
)
/
Math
.
log
(
k
))
return
parseFloat
((
bytes
/
Math
.
pow
(
k
,
i
)).
toFixed
(
2
))
+
' '
+
sizes
[
i
]
},
// 预览文件
previewFile
(
file
)
{
if
(
file
.
type
.
includes
(
'image'
))
{
// 使用 Element UI 的 Image 预览
const
h
=
this
.
$createElement
this
.
$msgbox
({
title
:
file
.
name
,
message
:
h
(
'img'
,
{
attrs
:
{
src
:
file
.
url
,
style
:
'max-width: 100%'
}
}),
showCancelButton
:
false
,
confirmButtonText
:
'关闭'
})
}
else
if
(
file
.
type
.
includes
(
'video'
))
{
// 使用自定义对话框预览视频
this
.
$msgbox
({
title
:
file
.
name
,
message
:
h
(
'video'
,
{
attrs
:
{
src
:
file
.
url
,
controls
:
true
,
style
:
'max-width: 100%'
}
}),
showCancelButton
:
false
,
confirmButtonText
:
'关闭'
})
}
},
}
}
</
script
>
<
style
scoped
>
.upload-page
{
padding
:
20px
;
}
.success-files
{
margin-top
:
30px
;
}
.clear-btn
{
margin-top
:
20px
;
}
</
style
>
src/views/materialTag/components/CascaderSelect.vue
0 → 100644
View file @
011cb0f5
<
template
>
<div
class=
"cascader-select"
>
<!-- 触发器按钮 -->
<el-popover
v-model=
"visible"
placement=
"bottom-start"
trigger=
"click"
:width=
"300"
>
<el-input
v-model=
"searchQuery"
size=
"small"
placeholder=
"搜索标签..."
prefix-icon=
"el-icon-search"
clearable
@
clear=
"handleSearchClear"
class=
"mb-2"
/>
<!-- 面包屑导航 -->
<div
v-if=
"currentPath.length"
class=
"mb-2"
>
<el-breadcrumb
separator=
"/"
>
<el-breadcrumb-item
v-for=
"(item, index) in currentPath"
:key=
"item.id"
:class=
"
{ 'is-link': index !== currentPath.length - 1 }"
@click.native="handlePathClick(index)"
>
{{
item
.
name
}}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 树形选择器 -->
<el-tree
ref=
"tree"
:data=
"currentLevelData"
:props=
"defaultProps"
:filter-node-method=
"filterNode"
node-key=
"id"
:expand-on-click-node=
"false"
@
node-click=
"handleNodeClick"
>
<span
slot-scope=
"
{ node, data }" class="custom-tree-node">
<span>
{{
node
.
label
}}
</span>
<span
v-if=
"isLeaf(data)"
>
<el-checkbox
v-model=
"checkedMap[data.id]"
@
change=
"(val) => handleCheck(data, val)"
@
click
.
native
.
stop
/>
</span>
<i
v-else
class=
"el-icon-arrow-right"
/>
</span>
</el-tree>
<el-button
slot=
"reference"
:class=
"
{ 'is-selected': selectedNodes.length }"
>
<span
v-if=
"selectedNodes.length"
>
已选择
{{
selectedNodes
.
length
}}
项
</span>
<span
v-else
>
{{
placeholder
}}
</span>
<i
class=
"el-icon-arrow-down el-icon--right"
/>
</el-button>
</el-popover>
<!-- 已选择标签展示区域 -->
<div
class=
"selected-tags"
>
<el-tag
v-for=
"node in selectedNodes"
:key=
"node.id"
closable
size=
"small"
class=
"tag-item"
@
close=
"handleRemoveTag(node)"
>
{{
getNodePath
(
node
)
}}
</el-tag>
</div>
</div>
</
template
>
<
script
>
export
default
{
name
:
'CascaderSelect'
,
props
:
{
data
:
{
type
:
Array
,
required
:
true
,
default
:
()
=>
[]
},
value
:
{
type
:
Array
,
default
:
()
=>
[]
},
placeholder
:
{
type
:
String
,
default
:
'请选择标签...'
}
},
data
()
{
return
{
visible
:
false
,
searchQuery
:
''
,
currentPath
:
[],
checkedMap
:
{},
defaultProps
:
{
children
:
'children'
,
label
:
'name'
}
}
},
computed
:
{
// 构建树形数据
treeData
()
{
return
this
.
buildTree
(
this
.
data
)
},
// 当前层级数据
currentLevelData
()
{
if
(
this
.
searchQuery
)
{
return
this
.
filterTreeData
(
this
.
treeData
,
this
.
searchQuery
)
}
if
(
this
.
currentPath
.
length
===
0
)
{
return
this
.
treeData
}
const
current
=
this
.
currentPath
[
this
.
currentPath
.
length
-
1
]
return
current
.
children
||
[]
},
// 选中的节点
selectedNodes
()
{
return
this
.
value
.
map
(
id
=>
this
.
findNodeById
(
this
.
treeData
,
id
)).
filter
(
Boolean
)
}
},
watch
:
{
value
:
{
immediate
:
true
,
handler
(
val
)
{
// 更新选中状态
const
checkedMap
=
{}
val
.
forEach
(
id
=>
{
checkedMap
[
id
]
=
true
})
this
.
checkedMap
=
checkedMap
}
},
searchQuery
(
val
)
{
this
.
$refs
.
tree
&&
this
.
$refs
.
tree
.
filter
(
val
)
}
},
methods
:
{
// 构建树形数据
buildTree
(
items
)
{
const
itemMap
=
new
Map
()
const
result
=
[]
// 创建所有节点
items
.
forEach
(
item
=>
{
itemMap
.
set
(
item
.
id
,
{
...
item
,
children
:
[]
})
})
// 建立父子关系
items
.
forEach
(
item
=>
{
const
node
=
itemMap
.
get
(
item
.
id
)
if
(
item
.
parentId
===
null
)
{
result
.
push
(
node
)
}
else
{
const
parent
=
itemMap
.
get
(
item
.
parentId
)
if
(
parent
)
{
parent
.
children
.
push
(
node
)
}
}
})
return
result
},
// 判断是否为叶子节点
isLeaf
(
node
)
{
return
!
node
.
children
||
node
.
children
.
length
===
0
},
// 处理节点点击
handleNodeClick
(
data
)
{
if
(
!
this
.
isLeaf
(
data
))
{
this
.
currentPath
.
push
(
data
)
}
},
// 处理路径点击
handlePathClick
(
index
)
{
this
.
currentPath
=
this
.
currentPath
.
slice
(
0
,
index
+
1
)
},
// 处理复选框变化
handleCheck
(
node
,
checked
)
{
const
newValue
=
[...
this
.
value
]
if
(
checked
)
{
if
(
!
newValue
.
includes
(
node
.
id
))
{
newValue
.
push
(
node
.
id
)
}
}
else
{
const
index
=
newValue
.
indexOf
(
node
.
id
)
if
(
index
>
-
1
)
{
newValue
.
splice
(
index
,
1
)
}
}
this
.
$emit
(
'input'
,
newValue
)
this
.
$emit
(
'change'
,
newValue
)
},
// 处理标签移除
handleRemoveTag
(
node
)
{
const
newValue
=
this
.
value
.
filter
(
id
=>
id
!==
node
.
id
)
this
.
$emit
(
'input'
,
newValue
)
this
.
$emit
(
'change'
,
newValue
)
},
// 获取节点完整路径
getNodePath
(
node
)
{
const
path
=
this
.
findNodePath
(
this
.
treeData
,
node
.
id
)
return
path
?
path
.
map
(
n
=>
n
.
name
).
join
(
' / '
)
:
''
},
// 查找节点路径
findNodePath
(
nodes
,
targetId
,
path
=
[])
{
for
(
const
node
of
nodes
)
{
const
newPath
=
[...
path
,
node
]
if
(
node
.
id
===
targetId
)
{
return
newPath
}
if
(
node
.
children
)
{
const
found
=
this
.
findNodePath
(
node
.
children
,
targetId
,
newPath
)
if
(
found
)
{
return
found
}
}
}
return
null
},
// 根据ID查找节点
findNodeById
(
nodes
,
targetId
)
{
for
(
const
node
of
nodes
)
{
if
(
node
.
id
===
targetId
)
{
return
node
}
if
(
node
.
children
)
{
const
found
=
this
.
findNodeById
(
node
.
children
,
targetId
)
if
(
found
)
{
return
found
}
}
}
return
null
},
// 搜索过滤
filterNode
(
value
,
data
)
{
if
(
!
value
)
return
true
return
data
.
name
.
indexOf
(
value
)
!==
-
1
},
// 过滤树数据
filterTreeData
(
nodes
,
query
)
{
return
nodes
.
filter
(
node
=>
{
if
(
node
.
name
.
indexOf
(
query
)
!==
-
1
)
{
return
true
}
if
(
node
.
children
)
{
const
filteredChildren
=
this
.
filterTreeData
(
node
.
children
,
query
)
if
(
filteredChildren
.
length
)
{
node
.
children
=
filteredChildren
return
true
}
}
return
false
})
},
// 处理搜索清除
handleSearchClear
()
{
this
.
searchQuery
=
''
this
.
currentPath
=
[]
}
}
}
</
script
>
<
style
scoped
>
.cascader-select
{
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
}
.custom-tree-node
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
width
:
100%
;
}
.selected-tags
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
8px
;
}
.tag-item
{
max-width
:
100%
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.mb-2
{
margin-bottom
:
8px
;
}
.el-button.is-selected
{
color
:
#409EFF
;
border-color
:
#409EFF
;
}
.el-breadcrumb__item.is-link
{
cursor
:
pointer
;
color
:
#409EFF
;
}
.el-tree
{
max-height
:
300px
;
overflow-y
:
auto
;
}
</
style
>
\ No newline at end of file
src/views/materialTag/index.vue
0 → 100644
View file @
011cb0f5
<
template
>
<div
class=
"tag-management"
>
<el-row
:gutter=
"20"
>
<!-- 左侧树形结构 -->
<el-col
:span=
"6"
>
<el-card
class=
"tree-card"
>
<div
slot=
"header"
class=
"card-header"
>
<span>
标签树形结构
</span>
<el-button
type=
"text"
@
click=
"handleAddRoot"
>
添加根标签
</el-button>
</div>
<el-tree
:data=
"treeData"
:props=
"defaultProps"
@
node-click=
"handleNodeClick"
default-expand-all
></el-tree>
</el-card>
</el-col>
<!-- 右侧表单和列表 -->
<el-col
:span=
"18"
>
<el-card>
<div
slot=
"header"
class=
"card-header"
>
<span>
标签列表
</span>
<el-button
type=
"primary"
size=
"small"
@
click=
"handleAdd"
>
新增标签
</el-button>
</div>
<!-- 搜索表单 -->
<el-form
:inline=
"true"
:model=
"searchForm"
class=
"search-form"
>
<el-form-item
label=
"标签名称"
>
<el-input
v-model=
"searchForm.name"
placeholder=
"请输入标签名称"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type=
"primary"
@
click=
"handleSearch"
>
查询
</el-button>
<el-button
@
click=
"resetSearch"
>
重置
</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table
:data=
"tableData"
border
style=
"width: 100%"
>
<el-table-column
prop=
"id"
label=
"ID"
width=
"80"
></el-table-column>
<el-table-column
prop=
"name"
label=
"标签名称"
></el-table-column>
<el-table-column
prop=
"remark"
label=
"标签备注1"
></el-table-column>
<el-table-column
prop=
"remark2"
label=
"标签备注2"
></el-table-column>
<el-table-column
prop=
"created_at"
label=
"创建时间"
width=
"180"
></el-table-column>
<el-table-column
label=
"操作"
width=
"200"
>
<template
slot-scope=
"scope"
>
<el-button
type=
"text"
@
click=
"handleEdit(scope.row)"
>
编辑
</el-button>
<el-button
type=
"text"
@
click=
"handleAddChild(scope.row)"
>
添加子标签
</el-button>
<el-button
type=
"text"
class=
"delete-btn"
@
click=
"handleDelete(scope.row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
<!-- 分页 -->
<div
class=
"pagination-container"
>
<el-pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
:current-page=
"pagination.currentPage"
:page-sizes=
"[10, 20, 50, 100]"
:page-size=
"pagination.pageSize"
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"pagination.total"
>
</el-pagination>
</div>
</el-card>
</el-col>
</el-row>
<!-- 新增/编辑对话框 -->
<el-dialog
:title=
"dialogTitle"
:visible
.
sync=
"dialogVisible"
width=
"500px"
>
<el-form
:model=
"form"
:rules=
"rules"
ref=
"form"
label-width=
"100px"
>
<el-form-item
label=
"标签名称"
prop=
"name"
>
<el-input
v-model=
"form.name"
></el-input>
</el-form-item>
<el-form-item
label=
"标签备注1"
prop=
"remark"
>
<el-input
v-model=
"form.remark"
></el-input>
</el-form-item>
<el-form-item
label=
"标签备注2"
prop=
"remark2"
>
<el-input
v-model=
"form.remark2"
></el-input>
</el-form-item>
</el-form>
<div
slot=
"footer"
class=
"dialog-footer"
>
<el-button
@
click=
"dialogVisible = false"
>
取 消
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm"
>
确 定
</el-button>
</div>
</el-dialog>
</div>
</template>
<
script
>
export
default
{
name
:
'TagManagement'
,
data
()
{
return
{
// 树形数据
treeData
:
[],
defaultProps
:
{
children
:
'children'
,
label
:
'name'
},
// 搜索表单
searchForm
:
{
name
:
''
},
// 表格数据
tableData
:
[],
// 分页
pagination
:
{
currentPage
:
1
,
pageSize
:
10
,
total
:
0
},
// 对话框
dialogVisible
:
false
,
dialogTitle
:
''
,
form
:
{
id
:
null
,
name
:
''
,
remark
:
''
,
remark2
:
''
,
parent_id
:
null
},
// 表单验证规则
rules
:
{
name
:
[
{
required
:
true
,
message
:
'请输入标签名称'
,
trigger
:
'blur'
},
{
min
:
1
,
max
:
50
,
message
:
'长度在 1 到 50 个字符'
,
trigger
:
'blur'
}
]
},
// 当前选中的节点
currentNode
:
null
}
},
created
()
{
this
.
fetchData
()
this
.
fetchTreeData
()
},
methods
:
{
// 获取表格数据
async
fetchData
()
{
try
{
// 这里替换为实际的API调用
const
response
=
await
this
.
axios
.
get
(
'/api/material-tags'
,
{
params
:
{
page
:
this
.
pagination
.
currentPage
,
pageSize
:
this
.
pagination
.
pageSize
,
name
:
this
.
searchForm
.
name
}
})
this
.
tableData
=
response
.
data
.
list
this
.
pagination
.
total
=
response
.
data
.
total
}
catch
(
error
)
{
this
.
$message
.
error
(
'获取数据失败'
)
}
},
// 获取树形数据
async
fetchTreeData
()
{
try
{
// 这里替换为实际的API调用
const
response
=
await
this
.
axios
.
get
(
'/api/material-tags/tree'
)
this
.
treeData
=
response
.
data
}
catch
(
error
)
{
this
.
$message
.
error
(
'获取树形数据失败'
)
}
},
// 处理搜索
handleSearch
()
{
this
.
pagination
.
currentPage
=
1
this
.
fetchData
()
},
// 重置搜索
resetSearch
()
{
this
.
searchForm
=
{
name
:
''
}
this
.
handleSearch
()
},
// 处理分页大小变化
handleSizeChange
(
val
)
{
this
.
pagination
.
pageSize
=
val
this
.
fetchData
()
},
// 处理页码变化
handleCurrentChange
(
val
)
{
this
.
pagination
.
currentPage
=
val
this
.
fetchData
()
},
// 处理节点点击
handleNodeClick
(
data
)
{
this
.
currentNode
=
data
this
.
searchForm
.
name
=
''
this
.
fetchData
()
},
// 新增标签
handleAdd
()
{
this
.
dialogTitle
=
'新增标签'
this
.
form
=
{
id
:
null
,
name
:
''
,
remark
:
''
,
remark2
:
''
,
parent_id
:
this
.
currentNode
?
this
.
currentNode
.
id
:
null
}
this
.
dialogVisible
=
true
},
// 添加根标签
handleAddRoot
()
{
this
.
dialogTitle
=
'添加根标签'
this
.
form
=
{
id
:
null
,
name
:
''
,
remark
:
''
,
remark2
:
''
,
parent_id
:
null
}
this
.
dialogVisible
=
true
},
// 添加子标签
handleAddChild
(
row
)
{
this
.
dialogTitle
=
'添加子标签'
this
.
form
=
{
id
:
null
,
name
:
''
,
remark
:
''
,
remark2
:
''
,
parent_id
:
row
.
id
}
this
.
dialogVisible
=
true
},
// 编辑标签
handleEdit
(
row
)
{
this
.
dialogTitle
=
'编辑标签'
this
.
form
=
{
...
row
}
this
.
dialogVisible
=
true
},
// 删除标签
handleDelete
(
row
)
{
this
.
$confirm
(
'确认删除该标签吗?'
,
'提示'
,
{
type
:
'warning'
}).
then
(
async
()
=>
{
try
{
// 这里替换为实际的API调用
await
this
.
axios
.
delete
(
`/api/material-tags/
${
row
.
id
}
`
)
this
.
$message
.
success
(
'删除成功'
)
this
.
fetchData
()
this
.
fetchTreeData
()
}
catch
(
error
)
{
this
.
$message
.
error
(
'删除失败'
)
}
}).
catch
(()
=>
{})
},
// 提交表单
submitForm
()
{
this
.
$refs
.
form
.
validate
(
async
(
valid
)
=>
{
if
(
valid
)
{
try
{
if
(
this
.
form
.
id
)
{
// 编辑
await
this
.
axios
.
put
(
`/api/material-tags/
${
this
.
form
.
id
}
`
,
this
.
form
)
}
else
{
// 新增
await
this
.
axios
.
post
(
'/api/material-tags'
,
this
.
form
)
}
this
.
$message
.
success
(
'保存成功'
)
this
.
dialogVisible
=
false
this
.
fetchData
()
this
.
fetchTreeData
()
}
catch
(
error
)
{
this
.
$message
.
error
(
'保存失败'
)
}
}
})
}
}
}
</
script
>
<
style
scoped
>
.tag-management
{
padding
:
20px
;
}
.tree-card
{
min-height
:
400px
;
}
.card-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.search-form
{
margin-bottom
:
20px
;
}
.pagination-container
{
margin-top
:
20px
;
text-align
:
right
;
}
.delete-btn
{
color
:
#F56C6C
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment