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
e3c503d5
Commit
e3c503d5
authored
Dec 22, 2024
by
lijin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修复图片上传
parent
ef53e082
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1006 additions
and
20 deletions
+1006
-20
index.js
src/router/index.js
+1
-1
ImageUploader.vue
src/views/createDelivery/childComponents/ImageUploader.vue
+226
-0
index.vue
src/views/createDelivery/index.vue
+8
-15
AdMaterialManager.vue
src/views/createMaterial/AdMaterialManager.vue
+551
-0
index.vue
src/views/createMaterial/index.vue
+0
-4
mockService.js
src/views/createMaterial/mockService.js
+220
-0
No files found.
src/router/index.js
View file @
e3c503d5
...
...
@@ -107,7 +107,7 @@ export const constantRouterMap = [
{
path
:
"/tools/Youtube"
,
name
:
"tools.Youtube"
,
component
:
()
=>
import
(
"@/views/
tools/YouTubeAuth
"
),
component
:
()
=>
import
(
"@/views/
createMaterial/AdMaterialManager
"
),
meta
:
{
title
:
"Youtube管理"
,
icon
:
"chart"
}
}
]
...
...
src/views/createDelivery/childComponents/ImageUploader.vue
0 → 100644
View file @
e3c503d5
<
template
>
<div
class=
"el-image-uploader"
>
图片上传组件
<el-upload
ref=
"upload"
action
:headers=
"headers"
list-type=
"picture-card"
:multiple=
"true"
:on-preview=
"handlePreview"
:on-remove=
"handleRemove"
:on-error=
"handleError"
:before-upload=
"beforeUpload"
:http-request=
"uploadMulFile"
:file-list=
"fileList"
:data=
"uploadData"
>
<i
class=
"el-icon-plus"
></i>
<div
slot=
"tip"
class=
"el-upload__tip"
>
只能上传jpg/png文件,且不超过5MB
</div>
</el-upload>
<!-- 图片预览对话框 -->
<el-dialog
:visible
.
sync=
"dialogVisible"
append-to-body
>
<img
width=
"100%"
:src=
"dialogImageUrl"
alt=
"Preview"
>
</el-dialog>
</div>
</
template
>
<
script
>
import
ossClient
from
"@/utils/ossClient"
;
import
md5
from
"md5"
;
export
default
{
name
:
'ImageUploader'
,
props
:
{
// 上传时附带的额外参数
uploadData
:
{
type
:
Object
,
default
:
()
=>
({})
},
// 上传的请求头
headers
:
{
type
:
Object
,
default
:
()
=>
({})
},
// 最大文件大小(MB)
maxSize
:
{
type
:
Number
,
default
:
5
}
},
data
()
{
return
{
fileList
:
[],
// 文件列表
dialogImageUrl
:
''
,
// 预览图片URL
dialogVisible
:
false
,
// 预览对话框显示状态
uploadConf
:
{
region
:
null
,
accessKeyId
:
null
,
accessKeySecret
:
null
,
bucket
:
null
,
// stsToken: null,
},
}
},
created
()
{
this
.
initOssInfo
();
},
methods
:
{
// 处理图片预览
handlePreview
(
file
)
{
this
.
dialogImageUrl
=
file
.
url
this
.
dialogVisible
=
true
},
//初始化从后端获取到 TST账户对OSS进行操作 在created初始化
initOssInfo
()
{
//获取阿里云token 这里是后台返回来的数据 暂时使用死数据
const
aliyun
=
{
// "你的Region 注意 这个只要 空间名 不要 http:// 和 .aliyunoss.com !!",
Region
:
"oss-cn-beijing"
,
AccessKeyId
:
"LTAI5tJzo2DiQxQqh9EipDNh"
,
AccessKeySecret
:
"JnduhWY5Tr5VfuFHgDOv9XjqYx1mDg"
,
Bucket
:
"zx-material-new"
,
//"你的SecurityToken"
// SecurityToken: null,
};
const
{
Region
,
AccessKeyId
,
AccessKeySecret
,
Bucket
,
// SecurityToken,
}
=
aliyun
;
//初始化连接oss参数
this
.
uploadConf
.
region
=
Region
;
this
.
uploadConf
.
accessKeyId
=
AccessKeyId
;
this
.
uploadConf
.
accessKeySecret
=
AccessKeySecret
;
this
.
uploadConf
.
bucket
=
Bucket
;
// this.uploadConf.stsToken = SecurityToken;
},
// 处理图片删除
handleRemove
(
file
,
fileList
)
{
this
.
fileList
=
fileList
this
.
$emit
(
'change'
,
this
.
getFiles
())
},
// 处理上传失败
handleError
(
err
,
file
)
{
this
.
$message
.
error
(
'上传失败:'
+
(
err
.
message
||
'未知错误'
))
},
// 上传前的验证
beforeUpload
(
file
)
{
// 验证文件类型
const
isImage
=
file
.
type
.
startsWith
(
'image/'
)
if
(
!
isImage
)
{
this
.
$message
.
error
(
'只能上传图片文件!'
)
return
false
}
// 验证文件大小
const
isLtMaxSize
=
file
.
size
/
1024
/
1024
<
this
.
maxSize
if
(
!
isLtMaxSize
)
{
this
.
$message
.
error
(
`图片大小不能超过
${
this
.
maxSize
}
MB!`
)
return
false
}
return
true
},
// 获取文件列表
getFiles
()
{
return
this
.
fileList
.
map
(
file
=>
({
name
:
file
.
name
,
url
:
file
.
url
,
size
:
file
.
size
,
raw
:
file
.
raw
}))
},
// 清空上传列表
clearFiles
()
{
this
.
$refs
.
upload
.
clearFiles
()
this
.
fileList
=
[]
this
.
$emit
(
'change'
,
[])
},
// 手动上传
submit
()
{
this
.
$refs
.
upload
.
submit
()
},
//上传到阿里云OSS
uploadMulFile
(
uploader
)
{
console
.
log
(
"文件信息"
,
uploader
);
//获取到文件的信息内容
let
file
=
uploader
.
file
;
//获取文件类型 和 上传文件的日期
let
fileType
=
file
.
name
.
split
(
"."
).
pop
();
if
(
file
.
name
.
split
(
"."
).
length
===
1
)
{
fileType
=
"jpg"
;
}
let
curTime
=
new
Date
();
//定义上传到云端的路径和文件名字
let
fileName
=
md5
(
file
.
name
+
Date
.
now
())
+
"."
+
fileType
;
let
upFilePath
=
"ad_putin_materials"
+
"/"
+
curTime
.
getFullYear
()
+
"/"
+
(
curTime
.
getMonth
()
+
1
)
+
"/"
+
curTime
.
getDate
()
+
"/"
+
fileName
//分片上传配置,回显进度以及上传总进度重置,分片大小,超时设置
let
optionsFile
=
{
progress
:
function
(
p
)
{
uploader
.
onProgress
({
percent
:
Math
.
floor
(
p
*
10000
)
/
100
});
},
partSize
:
1000
*
1024
,
//设置分片大小 小于1m 1024*1024
timeout
:
36000000
,
//设置超时时间 - 10小时
};
//实例化上传对象
let
client
=
ossClient
(
this
.
uploadConf
);
/* 分片上传 显示上传进度 */
const
that
=
this
;
//测试地址替换为 "test/" +upFilePath
client
.
multipartUpload
(
upFilePath
,
file
,
optionsFile
)
.
then
((
res
)
=>
{
//解析url并替换前缀可访问
let
strUrl
=
res
.
res
.
requestUrls
[
0
].
split
(
"?"
)[
0
];
strUrl
=
strUrl
.
replace
(
"http://zx-material-new.oss-cn-beijing.aliyuncs.com"
,
"https://cdn.zhangxingames.com"
);
that
.
fileList
=
[...
that
.
fileList
,
{
uid
:
file
.
uid
,
url
:
strUrl
,
name
:
fileName
}];
uploader
.
onSuccess
();
//上传成功(打钩的小图标)
client
=
null
;
this
.
$emit
(
'change'
,
this
.
getFiles
())
})
.
catch
((
err
)
=>
{
that
.
$message
.
warning
(
`上传终止,提示:
${
err
}
`
);
console
.
log
(
err
);
if
(
client
.
isCancel
())
{
uploader
.
onError
();
}
});
},
}
}
</
script
>
<
style
>
.el-image-uploader
{
padding
:
20px
;
}
.el-upload__tip
{
margin-top
:
10px
;
color
:
#909399
;
}
</
style
>
src/views/createDelivery/index.vue
View file @
e3c503d5
...
...
@@ -615,21 +615,8 @@
<div
class=
"drawer-item-title"
>
制作创意
</div>
<div
class=
"drawer-item-con"
>
<el-form
ref=
"form"
>
<el-form-item
label=
"选择素材组"
>
<el-select
v-model=
"makeCreative.materialGroupId"
placeholder=
"选择素材组"
style=
"width: 200px"
>
<el-option
v-for=
"item in materialList"
:key=
"item.id"
:label=
"item.materialGroupName"
:value=
"item.id"
>
</el-option>
</el-select>
</el-form-item>
<ImageUploader
@
change=
"handleUploadChange"
/>
</el-form>
</div>
</div>
...
...
@@ -721,6 +708,7 @@ import TextTextarea from "./childComponents/TextTextarea";
import
CountrySelector
from
"./childComponents/CountrySelector"
;
import
LanguageSelector
from
"./childComponents/LanguageSelector"
;
import
TextInputList
from
"./childComponents/TextInputList"
;
import
ImageUploader
from
"./childComponents/ImageUploader.vue"
;
import
{
stepList
}
from
"./childComponents/util"
;
...
...
@@ -736,6 +724,7 @@ export default {
TextTextarea
,
CountrySelector
,
TextInputList
,
ImageUploader
},
// 注册
data
()
{
...
...
@@ -2019,6 +2008,10 @@ export default {
filterAccount
(
query
,
item
){
query
=
query
.
toLowerCase
()
return
item
.
key
.
toString
().
toLowerCase
().
includes
(
query
)
||
item
.
label
.
toString
().
toLowerCase
().
includes
(
query
)
},
handleUploadChange
(
files
){
console
.
log
(
"aabbccddeeff"
,
files
)
this
.
putinTask
.
imageAssets
=
files
}
},
};
...
...
src/views/createMaterial/AdMaterialManager.vue
0 → 100644
View file @
e3c503d5
<
template
>
<div
class=
"ad-material-manager"
>
<el-container>
<!-- 左侧目录树 -->
<el-aside
width=
"250px"
class=
"aside"
>
<div
class=
"directory-header"
>
<span>
素材目录
</span>
<el-button
type=
"text"
size=
"small"
@
click=
"showNewDirDialog(null)"
>
<i
class=
"el-icon-plus"
></i>
新建目录
</el-button>
</div>
<el-tree
ref=
"directoryTree"
:data=
"directories"
:props=
"defaultProps"
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
class=
"directory-actions"
>
<el-button
type=
"text"
size=
"mini"
@
click
.
stop=
"showNewDirDialog(data.id)"
>
<i
class=
"el-icon-plus"
></i>
</el-button>
<el-button
type=
"text"
size=
"mini"
@
click
.
stop=
"handleDeleteDirectory(node, data)"
>
<i
class=
"el-icon-delete"
></i>
</el-button>
</span>
</span>
</el-tree>
</el-aside>
<!-- 右侧内容区 -->
<el-main
class=
"main"
>
<div
class=
"content-header"
>
<div
class=
"current-path"
>
当前位置:
{{
currentPath
}}
</div>
<el-upload
ref=
"upload"
:action=
"uploadUrl"
:data=
"
{ directoryId: currentDirectory }"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:before-upload="beforeUpload"
multiple
:show-file-list="false"
>
<el-button
type=
"primary"
:disabled=
"!currentDirectory"
>
<i
class=
"el-icon-upload"
></i>
上传文件
</el-button>
</el-upload>
</div>
<!-- 文件列表 -->
<el-table
v-loading=
"loading"
:data=
"materials"
style=
"width: 100%"
>
<el-table-column
label=
"预览"
width=
"120"
>
<template
slot-scope=
"scope"
>
<div
class=
"preview-container"
>
<img
v-if=
"scope.row.type === 'image'"
:src=
"scope.row.url"
class=
"preview-image"
>
<video
v-else-if=
"scope.row.type === 'video'"
:src=
"scope.row.url"
class=
"preview-video"
controls
>
</video>
</div>
</
template
>
</el-table-column>
<el-table-column
prop=
"name"
label=
"文件名"
></el-table-column>
<el-table-column
prop=
"type"
label=
"类型"
width=
"100"
>
<
template
slot-scope=
"scope"
>
{{
scope
.
row
.
type
===
'image'
?
'图片'
:
'视频'
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"size"
label=
"大小"
width=
"100"
>
<
template
slot-scope=
"scope"
>
{{
formatFileSize
(
scope
.
row
.
size
)
}}
</
template
>
</el-table-column>
<el-table-column
prop=
"createTime"
label=
"上传时间"
width=
"180"
>
<
template
slot-scope=
"scope"
>
{{
formatDate
(
scope
.
row
.
createTime
)
}}
</
template
>
</el-table-column>
<el-table-column
label=
"操作"
width=
"150"
>
<
template
slot-scope=
"scope"
>
<el-button
size=
"mini"
type=
"text"
@
click=
"showMoveDialog(scope.row)"
>
移动
</el-button>
<el-button
size=
"mini"
type=
"text"
@
click=
"handlePreview(scope.row)"
>
预览
</el-button>
<el-button
size=
"mini"
type=
"text"
class=
"delete-btn"
@
click=
"handleDelete(scope.row)"
>
删除
</el-button>
</
template
>
</el-table-column>
</el-table>
</el-main>
</el-container>
<!-- 新建目录弹窗 -->
<el-dialog
:title=
"parentId ? '新建子目录' : '新建目录'"
:visible
.
sync=
"newDirDialogVisible"
width=
"30%"
>
<el-form
:model=
"newDirForm"
ref=
"newDirForm"
:rules=
"dirFormRules"
>
<el-form-item
label=
"目录名称"
prop=
"name"
>
<el-input
v-model=
"newDirForm.name"
autocomplete=
"off"
></el-input>
</el-form-item>
</el-form>
<div
slot=
"footer"
>
<el-button
@
click=
"newDirDialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"createDirectory"
>
确定
</el-button>
</div>
</el-dialog>
<!-- 移动文件弹窗 -->
<el-dialog
title=
"移动到"
:visible
.
sync=
"moveDialogVisible"
width=
"30%"
>
<el-tree
ref=
"moveTree"
:data=
"directories"
:props=
"defaultProps"
node-key=
"id"
:default-expand-all=
"true"
@
node-click=
"handleMoveNodeClick"
></el-tree>
<div
slot=
"footer"
>
<el-button
@
click=
"moveDialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"confirmMove"
>
确定
</el-button>
</div>
</el-dialog>
<!-- 预览弹窗 -->
<el-dialog
:visible
.
sync=
"previewDialogVisible"
width=
"50%"
class=
"preview-dialog"
>
<div
class=
"preview-content"
>
<img
v-if=
"previewFile && previewFile.type === 'image'"
:src=
"previewFile.url"
class=
"preview-image"
>
<video
v-else-if=
"previewFile && previewFile.type === 'video'"
:src=
"previewFile.url"
controls
class=
"preview-video"
></video>
</div>
</el-dialog>
</div>
</template>
<
script
>
import
{
MockService
}
from
'./mockService'
export
default
{
name
:
'AdMaterialManager'
,
data
()
{
return
{
// 目录树数据
directories
:
[],
mockService
:
null
,
defaultProps
:
{
children
:
'children'
,
label
:
'name'
},
currentDirectory
:
null
,
// 当前选中的目录ID
currentPath
:
'根目录'
,
// 当前路径
// 文件列表数据
materials
:
[],
loading
:
false
,
// 新建目录相关
newDirDialogVisible
:
false
,
newDirForm
:
{
name
:
''
,
parentId
:
null
},
dirFormRules
:
{
name
:
[
{
required
:
true
,
message
:
'请输入目录名称'
,
trigger
:
'blur'
},
{
min
:
1
,
max
:
20
,
message
:
'长度在 1 到 20 个字符'
,
trigger
:
'blur'
}
]
},
parentId
:
null
,
// 移动文件相关
moveDialogVisible
:
false
,
selectedMoveDirectory
:
null
,
currentMoveFile
:
null
,
// 预览相关
previewDialogVisible
:
false
,
previewFile
:
null
,
// 上传相关
uploadUrl
:
'/api/materials/upload'
}
},
created
()
{
this
.
mockService
=
new
MockService
()
this
.
fetchDirectories
()
},
methods
:
{
// 获取目录树数据
async
fetchDirectories
()
{
try
{
const
directories
=
await
this
.
mockService
.
getDirectories
()
this
.
directories
=
directories
}
catch
(
error
)
{
this
.
$message
.
error
(
'获取目录失败'
)
console
.
error
(
'获取目录失败:'
,
error
)
}
},
// 获取当前目录下的文件
async
fetchMaterials
(
directoryId
)
{
this
.
loading
=
true
try
{
const
materials
=
await
this
.
mockService
.
getMaterials
(
directoryId
)
this
.
materials
=
materials
}
catch
(
error
)
{
this
.
$message
.
error
(
'获取文件列表失败'
)
console
.
error
(
'获取文件列表失败:'
,
error
)
}
finally
{
this
.
loading
=
false
}
},
// 处理目录点击
handleNodeClick
(
data
)
{
this
.
currentDirectory
=
data
.
id
this
.
currentPath
=
this
.
getNodePath
(
data
)
this
.
fetchMaterials
(
data
.
id
)
},
// 获取节点路径
getNodePath
(
node
)
{
const
path
=
[]
let
currentNode
=
node
while
(
currentNode
)
{
path
.
unshift
(
currentNode
.
name
)
currentNode
=
currentNode
.
parent
}
return
path
.
join
(
' / '
)
},
// 显示新建目录弹窗
showNewDirDialog
(
parentId
)
{
this
.
parentId
=
parentId
this
.
newDirForm
.
name
=
''
this
.
newDirForm
.
parentId
=
parentId
this
.
newDirDialogVisible
=
true
},
// 创建目录
async
createDirectory
()
{
this
.
$refs
.
newDirForm
.
validate
(
async
(
valid
)
=>
{
if
(
valid
)
{
try
{
await
this
.
mockService
.
createDirectory
({
name
:
this
.
newDirForm
.
name
,
parentId
:
this
.
newDirForm
.
parentId
})
this
.
$message
.
success
(
'创建成功'
)
this
.
newDirDialogVisible
=
false
await
this
.
fetchDirectories
()
// 重新获取目录树
}
catch
(
error
)
{
this
.
$message
.
error
(
'创建失败'
)
console
.
error
(
'创建目录失败:'
,
error
)
}
}
})
},
// 删除目录
handleDeleteDirectory
(
node
,
data
)
{
this
.
$confirm
(
'确认删除该目录吗?其下的所有文件将被移动到根目录'
,
'提示'
,
{
type
:
'warning'
}).
then
(
async
()
=>
{
try
{
await
this
.
mockService
.
deleteDirectory
(
data
.
id
)
this
.
$message
.
success
(
'删除成功'
)
await
this
.
fetchDirectories
()
// 重新获取目录树
// 如果删除的是当前选中的目录,清空文件列表
if
(
data
.
id
===
this
.
currentDirectory
)
{
this
.
materials
=
[]
this
.
currentDirectory
=
null
this
.
currentPath
=
'根目录'
}
}
catch
(
error
)
{
this
.
$message
.
error
(
'删除失败'
)
console
.
error
(
'删除目录失败:'
,
error
)
}
}).
catch
(()
=>
{})
},
// 上传前验证
beforeUpload
(
file
)
{
const
isImage
=
file
.
type
.
startsWith
(
'image/'
)
const
isVideo
=
file
.
type
.
startsWith
(
'video/'
)
const
isLt100M
=
file
.
size
/
1024
/
1024
<
100
if
(
!
isImage
&&
!
isVideo
)
{
this
.
$message
.
error
(
'只能上传图片或视频文件!'
)
return
false
}
if
(
!
isLt100M
)
{
this
.
$message
.
error
(
'文件大小不能超过 100MB!'
)
return
false
}
return
true
},
// 处理上传成功
async
handleUploadSuccess
(
response
,
file
,
fileList
)
{
try
{
const
result
=
await
this
.
mockService
.
uploadFile
(
file
.
raw
,
this
.
currentDirectory
)
if
(
result
.
code
===
0
)
{
this
.
$message
.
success
(
'上传成功'
)
await
this
.
fetchMaterials
(
this
.
currentDirectory
)
// 重新获取文件列表
}
else
{
this
.
$message
.
error
(
result
.
message
||
'上传失败'
)
}
}
catch
(
error
)
{
this
.
$message
.
error
(
'上传失败'
)
console
.
error
(
'上传文件失败:'
,
error
)
}
},
// 处理上传失败
handleUploadError
(
err
)
{
this
.
$message
.
error
(
'上传失败'
)
console
.
error
(
'上传文件失败:'
,
err
)
},
// 显示移动文件弹窗
showMoveDialog
(
file
)
{
this
.
currentMoveFile
=
file
this
.
selectedMoveDirectory
=
null
this
.
moveDialogVisible
=
true
},
// 处理移动目标目录选择
handleMoveNodeClick
(
data
)
{
this
.
selectedMoveDirectory
=
data
.
id
},
// 确认移动文件
async
confirmMove
()
{
if
(
!
this
.
selectedMoveDirectory
)
{
this
.
$message
.
warning
(
'请选择目标目录'
)
return
}
try
{
await
this
.
mockService
.
moveFile
(
this
.
currentMoveFile
.
id
,
this
.
selectedMoveDirectory
)
this
.
$message
.
success
(
'移动成功'
)
this
.
moveDialogVisible
=
false
await
this
.
fetchMaterials
(
this
.
currentDirectory
)
// 重新获取当前目录的文件列表
}
catch
(
error
)
{
this
.
$message
.
error
(
'移动失败'
)
console
.
error
(
'移动文件失败:'
,
error
)
}
},
// 删除文件
handleDelete
(
file
)
{
this
.
$confirm
(
'确认删除该文件吗?'
,
'提示'
,
{
type
:
'warning'
}).
then
(
async
()
=>
{
try
{
await
this
.
mockService
.
deleteFile
(
file
.
id
)
this
.
$message
.
success
(
'删除成功'
)
await
this
.
fetchMaterials
(
this
.
currentDirectory
)
// 重新获取文件列表
}
catch
(
error
)
{
this
.
$message
.
error
(
'删除失败'
)
console
.
error
(
'删除文件失败:'
,
error
)
}
}).
catch
(()
=>
{})
},
// 预览文件
handlePreview
(
file
)
{
this
.
previewFile
=
file
this
.
previewDialogVisible
=
true
},
// 工具方法 - 格式化文件大小
formatFileSize
(
size
)
{
if
(
size
<
1024
)
{
return
size
+
' B'
}
else
if
(
size
<
1024
*
1024
)
{
return
(
size
/
1024
).
toFixed
(
2
)
+
' KB'
}
else
{
return
(
size
/
1024
/
1024
).
toFixed
(
2
)
+
' MB'
}
},
// 工具方法 - 格式化日期
formatDate
(
date
)
{
return
new
Date
(
date
).
toLocaleString
()
}
}
}
</
script
>
<
style
scoped
>
.ad-material-manager
{
height
:
100%
;
min-height
:
500px
;
}
.el-container
{
height
:
100%
;
}
.aside
{
background-color
:
#f5f7fa
;
border-right
:
1px
solid
#e6e6e6
;
}
.directory-header
{
padding
:
15px
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
border-bottom
:
1px
solid
#e6e6e6
;
}
.main
{
padding
:
20px
;
}
.content-header
{
margin-bottom
:
20px
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.current-path
{
font-size
:
14px
;
color
:
#606266
;
}
.custom-tree-node
{
flex
:
1
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
font-size
:
14px
;
padding-right
:
8px
;
}
.directory-actions
{
opacity
:
0
;
transition
:
opacity
0.2s
;
}
.custom-tree-node
:hover
.directory-actions
{
opacity
:
1
;
}
.preview-container
{
width
:
80px
;
height
:
80px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
overflow
:
hidden
;
border-radius
:
4px
;
border
:
1px
solid
#e6e6e6
;
}
.preview-image
,
.preview-video
{
max-width
:
100%
;
max-height
:
100%
;
object-fit
:
contain
;
}
.delete-btn
{
color
:
#f56c6c
;
}
.delete-btn
:hover
{
color
:
#f78989
;
}
.preview-dialog
.preview-content
{
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
}
.preview-dialog
.preview-image
{
max-width
:
100%
;
max-height
:
70vh
;
}
.preview-dialog
.preview-video
{
max-width
:
100%
;
max-height
:
70vh
;
}
</
style
>
src/views/createMaterial/index.vue
View file @
e3c503d5
...
...
@@ -10,9 +10,6 @@
<el-tab-pane
label=
"文案素材"
name=
"copywriting"
>
<copywritinglibrary
v-if=
"isCopywriting"
></copywritinglibrary>
</el-tab-pane>
<el-tab-pane
label=
"素材推荐"
name=
"materialRecommend"
>
<material-recommend
v-if=
"isRecommend"
/>
</el-tab-pane>
</el-tabs>
</div>
</
template
>
...
...
@@ -30,7 +27,6 @@ export default {
materialGroup
,
copywritinglibrary
,
materiallibrary
,
MaterialRecommend
,
},
data
()
{
return
{
...
...
src/views/createMaterial/mockService.js
0 → 100644
View file @
e3c503d5
// 生成唯一ID
const
generateId
=
()
=>
Math
.
random
().
toString
(
36
).
substr
(
2
,
9
);
// 初始化本地存储数据
const
initLocalStorage
=
()
=>
{
if
(
!
localStorage
.
getItem
(
'directories'
))
{
localStorage
.
setItem
(
'directories'
,
JSON
.
stringify
([
{
id
:
'root'
,
name
:
'根目录'
,
parentId
:
null
,
children
:
[
{
id
:
'dir1'
,
name
:
'2024投放素材'
,
parentId
:
'root'
,
children
:
[
{
id
:
'dir1-1'
,
name
:
'春节活动'
,
parentId
:
'dir1'
,
children
:
[]
}
]
},
{
id
:
'dir2'
,
name
:
'产品图库'
,
parentId
:
'root'
,
children
:
[]
}
]
}
]));
}
if
(
!
localStorage
.
getItem
(
'materials'
))
{
localStorage
.
setItem
(
'materials'
,
JSON
.
stringify
([
{
id
:
generateId
(),
name
:
'示例图片1.jpg'
,
type
:
'image'
,
size
:
1024
*
1024
*
2
,
// 2MB
url
:
'https://picsum.photos/400/300'
,
directoryId
:
'dir1-1'
,
createTime
:
new
Date
().
toISOString
()
},
{
id
:
generateId
(),
name
:
'示例视频1.mp4'
,
type
:
'video'
,
size
:
1024
*
1024
*
10
,
// 10MB
url
:
'https://www.w3schools.com/html/mov_bbb.mp4'
,
directoryId
:
'dir2'
,
createTime
:
new
Date
().
toISOString
()
}
]));
}
};
// Mock Service 类
export
class
MockService
{
constructor
()
{
initLocalStorage
();
}
// 获取目录树
async
getDirectories
()
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
directories
=
JSON
.
parse
(
localStorage
.
getItem
(
'directories'
));
resolve
(
directories
);
},
300
);
});
}
// 创建目录
async
createDirectory
({
name
,
parentId
})
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
directories
=
JSON
.
parse
(
localStorage
.
getItem
(
'directories'
));
const
newDir
=
{
id
:
generateId
(),
name
,
parentId
,
children
:
[]
};
const
addToParent
=
(
dirs
)
=>
{
for
(
let
dir
of
dirs
)
{
if
(
dir
.
id
===
parentId
)
{
dir
.
children
.
push
(
newDir
);
return
true
;
}
if
(
dir
.
children
&&
dir
.
children
.
length
)
{
if
(
addToParent
(
dir
.
children
))
return
true
;
}
}
return
false
;
};
if
(
parentId
)
{
addToParent
(
directories
);
}
else
{
directories
.
push
(
newDir
);
}
localStorage
.
setItem
(
'directories'
,
JSON
.
stringify
(
directories
));
resolve
(
newDir
);
},
300
);
});
}
// 删除目录
async
deleteDirectory
(
id
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
directories
=
JSON
.
parse
(
localStorage
.
getItem
(
'directories'
));
const
materials
=
JSON
.
parse
(
localStorage
.
getItem
(
'materials'
));
const
removeDir
=
(
dirs
)
=>
{
for
(
let
i
=
0
;
i
<
dirs
.
length
;
i
++
)
{
if
(
dirs
[
i
].
id
===
id
)
{
dirs
.
splice
(
i
,
1
);
return
true
;
}
if
(
dirs
[
i
].
children
&&
dirs
[
i
].
children
.
length
)
{
if
(
removeDir
(
dirs
[
i
].
children
))
return
true
;
}
}
return
false
;
};
// 将目录下的文件移动到根目录
const
updatedMaterials
=
materials
.
map
(
material
=>
{
if
(
material
.
directoryId
===
id
)
{
return
{
...
material
,
directoryId
:
'root'
};
}
return
material
;
});
removeDir
(
directories
);
localStorage
.
setItem
(
'directories'
,
JSON
.
stringify
(
directories
));
localStorage
.
setItem
(
'materials'
,
JSON
.
stringify
(
updatedMaterials
));
resolve
({
success
:
true
});
},
300
);
});
}
// 获取文件列表
async
getMaterials
(
directoryId
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
materials
=
JSON
.
parse
(
localStorage
.
getItem
(
'materials'
));
const
filtered
=
materials
.
filter
(
m
=>
m
.
directoryId
===
directoryId
);
resolve
(
filtered
);
},
300
);
});
}
// 上传文件
async
uploadFile
(
file
,
directoryId
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
reader
=
new
FileReader
();
reader
.
onload
=
(
e
)
=>
{
const
materials
=
JSON
.
parse
(
localStorage
.
getItem
(
'materials'
));
const
newMaterial
=
{
id
:
generateId
(),
name
:
file
.
name
,
type
:
file
.
type
.
startsWith
(
'image/'
)
?
'image'
:
'video'
,
size
:
file
.
size
,
url
:
file
.
type
.
startsWith
(
'image/'
)
?
'https://picsum.photos/400/300?'
+
new
Date
().
getTime
()
// 模拟新的图片URL
:
'https://www.w3schools.com/html/mov_bbb.mp4'
,
// 模拟视频URL
directoryId
,
createTime
:
new
Date
().
toISOString
()
};
materials
.
push
(
newMaterial
);
localStorage
.
setItem
(
'materials'
,
JSON
.
stringify
(
materials
));
resolve
({
code
:
0
,
message
:
'success'
,
url
:
newMaterial
.
url
});
};
reader
.
readAsDataURL
(
file
);
},
1000
);
// 模拟上传耗时
});
}
// 移动文件
async
moveFile
(
fileId
,
targetDirectoryId
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
materials
=
JSON
.
parse
(
localStorage
.
getItem
(
'materials'
));
const
updatedMaterials
=
materials
.
map
(
material
=>
{
if
(
material
.
id
===
fileId
)
{
return
{
...
material
,
directoryId
:
targetDirectoryId
};
}
return
material
;
});
localStorage
.
setItem
(
'materials'
,
JSON
.
stringify
(
updatedMaterials
));
resolve
({
success
:
true
});
},
300
);
});
}
// 删除文件
async
deleteFile
(
fileId
)
{
return
new
Promise
((
resolve
)
=>
{
setTimeout
(()
=>
{
const
materials
=
JSON
.
parse
(
localStorage
.
getItem
(
'materials'
));
const
index
=
materials
.
findIndex
(
m
=>
m
.
id
===
fileId
);
if
(
index
>
-
1
)
{
materials
.
splice
(
index
,
1
);
localStorage
.
setItem
(
'materials'
,
JSON
.
stringify
(
materials
));
}
resolve
({
success
:
true
});
},
300
);
});
}
}
\ No newline at end of file
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