Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
A
aitools
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
周成波
aitools
Commits
5decc3ef
Commit
5decc3ef
authored
Oct 11, 2025
by
Administrator
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
wm_nano_banana
parent
a98e92f0
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
687 additions
and
0 deletions
+687
-0
index.ts
src/router/index.ts
+6
-0
common.ts
src/views/wm_nano_banana/compositions/common.ts
+45
-0
customerInfo.ts
src/views/wm_nano_banana/compositions/customerInfo.ts
+101
-0
fileUpload.ts
src/views/wm_nano_banana/compositions/fileUpload.ts
+105
-0
process.ts
src/views/wm_nano_banana/compositions/process.ts
+218
-0
index.css
src/views/wm_nano_banana/index.css
+87
-0
index.vue
src/views/wm_nano_banana/index.vue
+125
-0
No files found.
src/router/index.ts
View file @
5decc3ef
import
{
createRouter
,
createWebHashHistory
}
from
'vue-router'
import
HomeView
from
'../views/home/index.vue'
import
SampleHandleView
from
'../views/sample_handle/index.vue'
import
WmNanoBananaView
from
'../views/wm_nano_banana/index.vue'
const
router
=
createRouter
({
history
:
createWebHashHistory
(
import
.
meta
.
env
.
BASE_URL
),
...
...
@@ -19,6 +20,11 @@ const router = createRouter({
path
:
'/sample_handle'
,
name
:
'sample_handle'
,
component
:
SampleHandleView
},
{
path
:
'/wm_nano_banana'
,
name
:
'wm_nano_banana'
,
component
:
WmNanoBananaView
}
]
})
...
...
src/views/wm_nano_banana/compositions/common.ts
0 → 100644
View file @
5decc3ef
import
aitoolsService
from
'@/api/service/aitoolsService'
// 检测设备类型
export
const
detectDeviceType
=
()
=>
{
const
userAgent
=
navigator
.
userAgent
.
toLowerCase
()
let
source
=
''
if
(
userAgent
.
match
(
/mobile/i
)
||
userAgent
.
match
(
/android/i
)
||
userAgent
.
match
(
/iphone/i
)
||
userAgent
.
match
(
/ipad/i
))
{
source
=
'mobile'
// if (userAgent.match(/iphone/i) || userAgent.match(/ipad/i)) {
// source = 'ios'
// } else if (userAgent.match(/android/i)) {
// source = 'android'
// }
if
(
userAgent
.
match
(
/micromessenger/i
))
{
source
=
'wechat'
}
}
else
{
source
=
'pc'
}
console
.
log
(
'设备类型:'
,
source
)
return
source
}
// 记录用户操作
export
const
trackUserAction
=
async
(
source
:
string
,
user_id
:
string
,
content
:
string
,
action
:
string
)
=>
{
let
param
:
any
=
{
source
:
source
,
user_id
:
user_id
,
content
:
content
,
action
:
action
,
}
aitoolsService
.
commonApi
(
'提交用户轨迹数据'
,
'track'
,
param
)
.
then
((
response
)
=>
{
if
(
response
==
'ok'
)
{
console
.
log
(
action
,
'上报成功'
)
}
else
{
console
.
log
(
action
,
'上报失败'
)
}
})
.
catch
((
error
)
=>
{
console
.
log
(
action
,
'上报失败:'
,
error
)
})
}
src/views/wm_nano_banana/compositions/customerInfo.ts
0 → 100644
View file @
5decc3ef
import
aitoolsService
from
'@/api/service/aitoolsService'
;
import
{
trackUserAction
}
from
'./common'
;
import
{
ElMessage
,
ElMessageBox
}
from
'element-plus'
;
import
utils
from
'@/utils/utils'
;
import
{
reactive
,
ref
}
from
'vue'
;
// 客户留资
export
const
myCustomerInfo
=
(
form
:
any
,
title
:
any
)
=>
{
const
customer
=
reactive
({
name
:
''
,
mobile
:
''
,
company
:
''
,
note
:
''
,
})
const
customerInfoVisible
=
ref
(
false
)
const
onCustomerInfoSubmit
=
async
()
=>
{
if
(
!
customer
.
name
||
!
customer
.
mobile
)
{
ElMessage
({
message
:
'请填写姓名和手机号'
,
type
:
'warning'
})
return
}
if
(
!
utils
.
isPhone
(
customer
.
mobile
))
{
ElMessage
({
message
:
'手机号格式不正确'
,
type
:
'warning'
})
return
}
try
{
console
.
log
(
customer
)
let
param
:
any
=
{
name
:
customer
.
name
,
mobile
:
customer
.
mobile
,
company
:
customer
.
company
,
source
:
form
.
source
,
note
:
customer
.
note
,
}
await
aitoolsService
.
commonApi
(
'记录客户信息'
,
'take_customer_info'
,
param
);
// 统计
trackUserAction
(
form
.
source
,
form
.
user_id
,
title
.
value
,
'客户留资'
)
customerInfoVisible
.
value
=
false
// 防止重复提交
localStorage
.
setItem
(
"isSubmitCustomerInfo"
,
'yes'
);
// 新开一个浏览器窗口,打开一个链接
openNewWindow
();
}
catch
(
error
)
{
ElMessage
({
message
:
String
(
error
),
type
:
'error'
})
}
}
const
openNewWindow
=
()
=>
{
if
(
form
.
source
===
'pc'
)
{
window
.
open
(
form
.
report_url
,
'_blank'
);
}
else
{
if
(
form
.
source
===
'wechat'
)
{
ElMessageBox
.
confirm
(
'请先<span style="color: red;">「点击确认」</span>查看完整报告,如需保存报告到手机,再至右上角选择<span style="color: red;">「默认浏览器」</span>打开'
,
'微信提示'
,
{
confirmButtonText
:
'确认'
,
type
:
'info'
,
center
:
true
,
showClose
:
false
,
showCancelButton
:
false
,
dangerouslyUseHTMLString
:
true
,
closeOnClickModal
:
false
,
closeOnPressEscape
:
false
,
}
).
then
(()
=>
{
window
.
location
.
href
=
form
.
report_url
;
})
}
else
{
window
.
location
.
href
=
form
.
report_url
;
}
}
}
const
onClickDownloadPDF
=
()
=>
{
const
isSubmitCustomerInfo
=
localStorage
.
getItem
(
"isSubmitCustomerInfo"
);
console
.
log
(
'isSubmitCustomerInfo ='
,
isSubmitCustomerInfo
);
if
(
isSubmitCustomerInfo
===
'yes'
)
{
customerInfoVisible
.
value
=
false
// 直接打开链接
openNewWindow
();
}
else
{
customerInfoVisible
.
value
=
true
}
}
return
{
customer
,
customerInfoVisible
,
onCustomerInfoSubmit
,
onClickDownloadPDF
,
}
}
src/views/wm_nano_banana/compositions/fileUpload.ts
0 → 100644
View file @
5decc3ef
import
{
ref
}
from
'vue'
import
{
ElMessage
,
genFileId
,
type
UploadInstance
,
type
UploadProps
,
type
UploadRawFile
,
type
UploadFile
,
}
from
'element-plus'
export
const
myFileUpload
=
(
FileSizeLimitM
:
number
,
form
:
any
,
loading
:
any
)
=>
{
// ############ 处理上传文件 begin #############
const
upload
=
ref
<
UploadInstance
>
()
const
actionUrl
=
ref
(
import
.
meta
.
env
.
MODE
===
'production'
?
'/file'
:
import
.
meta
.
env
.
VITE_APP_BASE_API
+
'/file'
)
const
handleBeforeUpload
=
async
(
file
:
any
)
=>
{
const
isLimit
=
file
.
size
/
1024
/
1024
<=
FileSizeLimitM
if
(
!
isLimit
)
{
ElMessage
.
error
(
'上传文件大小不能超过 '
+
FileSizeLimitM
+
'MB!'
)
return
false
}
}
const
handleUploadSuccess
=
(
val
:
Wm
.
UploadResult
,
file
:
UploadFile
)
=>
{
// console.log(val)
if
(
val
.
code
==
0
)
{
form
.
file_path
=
val
.
data
[
0
].
path
console
.
log
(
'form.file_path ='
,
form
.
file_path
)
form
.
file_path_domain
=
val
.
data
[
0
].
url
console
.
log
(
'form.file_path_domain ='
,
form
.
file_path_domain
)
// console.log('file =', file.name, file.size, file.url)
if
(
file
.
name
&&
file
.
size
!==
undefined
)
{
form
.
file_name
=
file
.
name
form
.
file_size
=
file
.
size
}
ElMessage
({
message
:
'上传成功'
,
type
:
'success'
})
}
else
{
ElMessage
({
message
:
'上传失败:'
+
val
.
message
,
type
:
'error'
})
}
loading
.
value
=
false
}
const
handleUploadExceed
:
UploadProps
[
'onExceed'
]
=
(
files
)
=>
{
// 清除已上传的文件
upload
.
value
!
.
clearFiles
()
// 获取超出限制的第一个文件
const
file
=
files
[
0
]
as
UploadRawFile
// 给文件分配一个新的唯一标识
file
.
uid
=
genFileId
()
// 手动触发文件上传
upload
.
value
!
.
handleStart
(
file
)
// 提交上传
upload
.
value
!
.
submit
()
}
const
handleUploadError
=
(
error
:
Error
)
=>
{
ElMessage
({
message
:
String
(
error
.
message
),
type
:
'error'
})
loading
.
value
=
false
}
const
handleUploadProgress
=
(
event
:
ProgressEvent
,
file
:
UploadFile
)
=>
{
file
.
percentage
=
Math
.
round
(
event
.
loaded
/
event
.
total
*
100
)
console
.
log
(
'上传进度:'
,
file
.
percentage
)
file
.
url
=
'/video-icon.png'
// 根据进度控制 loading 状态
if
(
file
.
percentage
<=
100
)
{
loading
.
value
=
true
}
// if (file.percentage == 100) {
// loading.value = false
// }
}
const
handleRemoveFile
=
()
=>
{
// 清除已上传的文件
// upload.value!.clearFiles()
form
.
file_path
=
""
form
.
file_path_domain
=
""
form
.
file_name
=
""
form
.
file_size
=
0
console
.
log
(
'文件列表移除File, form.file_path ='
,
form
.
file_path
,
', form.file_path_domain ='
,
form
.
file_path_domain
)
}
// ############ 处理上传文件 end #############
return
{
loading
,
upload
,
actionUrl
,
handleBeforeUpload
,
handleUploadSuccess
,
handleUploadExceed
,
handleUploadError
,
handleUploadProgress
,
handleRemoveFile
,
}
}
src/views/wm_nano_banana/compositions/process.ts
0 → 100644
View file @
5decc3ef
import
aitoolsService
from
'@/api/service/aitoolsService'
import
utils
from
'@/utils/utils'
import
{
ElMessage
,
ElLoading
,
ElMessageBox
}
from
'element-plus'
import
type
{
Action
}
from
'element-plus'
export
const
onProcessing
=
(
form
:
any
,
steps_active
:
any
,
process_loading
:
any
,
result_loading
:
any
)
=>
{
const
fileToClassify
=
async
()
=>
{
if
(
!
form
.
file_path
||
form
.
file_path
.
length
==
0
)
{
ElMessage
({
message
:
'请先上传文件'
,
type
:
'warning'
})
return
}
// 进入第二页
from_first_to_second
();
// 清除结果
form
.
sample_result
=
{
root_path
:
''
,
data
:
[{
dir_name
:
''
,
images
:
[],
}]
};
// 提交处理请求
try
{
let
param
:
any
=
{
task_id
:
form
.
task_id
,
file_path
:
form
.
file_path
,
crop_range
:
JSON
.
stringify
(
form
.
crop_range
),
box_range
:
JSON
.
stringify
(
form
.
box_range
),
output_type
:
"dir"
,
classes_select
:
""
,
target_fps
:
form
.
target_fps
,
// 目标帧率
}
process_loading
.
value
=
true
;
// 发起请求
aitoolsService
.
commonApi
(
'提交处理'
,
'gen_sample_from_video'
,
param
)
.
then
((
response
)
=>
{
// console.log(form)
console
.
log
(
`接口返回:
${
response
}
`
);
form
.
sample_result
=
response
;
// 设置默认值
form
.
classes_select
=
{}
form
.
classes_select2
=
{}
response
.
data
.
forEach
((
item
:
{
dir_name
:
string
;
images
:
string
[]
})
=>
{
form
.
classes_select
[
item
.
dir_name
]
=
'0'
;
form
.
classes_select2
[
item
.
dir_name
]
=
''
;
});
process_loading
.
value
=
false
;
})
.
catch
((
error
)
=>
{
ElMessage
({
message
:
error
,
type
:
'error'
});
// // 重置task_id
// form.task_id = utils.genDateTimeStr();
// console.log('重置 task_id =', form.task_id);
process_loading
.
value
=
false
;
})
}
catch
(
error
:
any
)
{
ElMessage
({
message
:
error
,
type
:
'error'
});
process_loading
.
value
=
false
;
}
}
function
isAllDataClassified
():
boolean
{
let
is_ok
=
false
;
if
(
!
form
.
sample_result
?.
data
||
!
form
.
classes_select
)
{
ElMessage
({
message
:
'数据未正确初始化'
,
type
:
'error'
});
}
form
.
sample_result
.
data
.
every
((
item
:
{
dir_name
:
string
;
images
:
string
[]
})
=>
{
if
(
!
form
.
classes_select
.
hasOwnProperty
(
item
.
dir_name
))
{
ElMessage
({
message
:
`请为
${
item
.
dir_name
}
分类`
,
type
:
'error'
});
}
else
{
is_ok
=
true
}
});
return
is_ok
;
}
const
classifyToDownload
=
async
()
=>
{
if
(
!
isAllDataClassified
())
{
return
}
// 进入第3页
from_second_to_third
();
// 清除结果
form
.
sample_path
=
''
;
// 提交处理请求
try
{
let
param
:
any
=
{
task_id
:
form
.
task_id
,
file_path
:
form
.
file_path
,
crop_range
:
JSON
.
stringify
(
form
.
crop_range
),
box_range
:
JSON
.
stringify
(
form
.
box_range
),
output_type
:
"zip"
,
classes_select
:
JSON
.
stringify
(
form
.
classes_select
),
target_fps
:
form
.
target_fps
,
// 目标帧率
}
result_loading
.
value
=
true
;
// 发起请求
aitoolsService
.
commonApi
(
'提交处理'
,
'gen_sample_from_video'
,
param
)
.
then
((
response
)
=>
{
// console.log(form)
console
.
log
(
`接口返回:
${
response
}
`
);
form
.
sample_path
=
response
.
sample_path
;
result_loading
.
value
=
false
;
})
.
catch
((
error
)
=>
{
ElMessage
({
message
:
error
,
type
:
'error'
});
// // 重置task_id
// form.task_id = utils.genDateTimeStr();
// console.log('重置 task_id =', form.task_id);
result_loading
.
value
=
false
;
})
}
catch
(
error
:
any
)
{
ElMessage
({
message
:
error
,
type
:
'error'
});
result_loading
.
value
=
false
;
}
}
const
from_first_to_second
=
()
=>
{
steps_active
.
value
=
1
}
const
back_to_first
=
()
=>
{
steps_active
.
value
=
0
;
form
.
task_id
=
utils
.
genDateTimeStr
();
console
.
log
(
'返回首页,task_id ='
,
form
.
task_id
);
}
const
from_second_to_third
=
()
=>
{
steps_active
.
value
=
2
;
}
const
back_to_second
=
()
=>
{
steps_active
.
value
=
1
;
form
.
classes_select2
=
JSON
.
parse
(
JSON
.
stringify
(
form
.
classes_select
));
}
function
downloadFile
(
url
:
string
,
filename
?:
string
)
{
const
a
=
document
.
createElement
(
'a'
)
a
.
href
=
url
if
(
filename
)
{
a
.
download
=
filename
// 指定下载文件名
}
a
.
target
=
'_blank'
document
.
body
.
appendChild
(
a
)
a
.
click
()
document
.
body
.
removeChild
(
a
)
}
function
deleteOneSample
(
sample_dir
:
string
)
{
ElMessageBox
.
alert
(
`确认删除
${
sample_dir
}
这行样本吗?`
,
'删除样本'
,
{
// autofocus: false,
confirmButtonText
:
'删除'
,
callback
:
(
action
:
Action
)
=>
{
if
(
action
===
'confirm'
)
{
// 删除 form.sample_result.data
const
index
=
form
.
sample_result
.
data
.
findIndex
((
item
:
any
)
=>
item
.
dir_name
===
sample_dir
);
form
.
sample_result
.
data
.
splice
(
index
,
1
);
// 删除 form.classes_select[sample_dir]
delete
form
.
classes_select
[
sample_dir
];
// 删除 form.classes_select2[sample_dir]
delete
form
.
classes_select2
[
sample_dir
];
ElMessage
({
type
:
'success'
,
message
:
'删除成功'
})
}
},
})
}
return
{
downloadFile
,
process_loading
,
result_loading
,
deleteOneSample
,
}
}
\ No newline at end of file
src/views/wm_nano_banana/index.css
0 → 100644
View file @
5decc3ef
.home-container
{
width
:
100%
;
}
.title
{
:is(span)
{
font-size
:
25px
;
font-weight
:
bold
;
color
:
#181818
;
}
text-align
:
center
;
margin
:
20px
0
0
0
;
}
.subtitle
{
:is(span)
{
font-size
:
13px
;
color
:
#181818
;
}
text-align
:
center
;
margin
:
0
0
20px
0
;
}
.progress
{
margin
:
10px
50px
;
text-align
:
center
;
}
.content-container
{
margin
:
20px
;
display
:
flex
;
flex-direction
:
row
;
/* 默认横向排列 */
justify-content
:
center
;
/* 水平居中 */
/* box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
border-radius
:
8px
;
/* 可选:添加圆角 */
/* background-color: #fff; /* 可选:添加背景色 */
@media
(
max-width
:
768px
)
{
/* 当屏幕宽度小于768px时变为竖向排列 */
flex-direction
:
column
;
}
}
/* 几个页面的公共样式 */
.upload-section
{
padding
:
20px
;
/* 标题 */
.section-title
{
font-size
:
18px
;
font-weight
:
bold
;
color
:
#181818
;
}
/* 描述 */
.section-desc
{
font-size
:
12px
;
}
/* 上传区域 */
.upload-div
{
margin
:
20px
0
;
/* border: 2px dashed #dddfe5; */
border-radius
:
4px
;
}
/* 已上传文件 */
.uploaded-div
{
margin
:
20px
0
;
padding
:
20px
10px
;
border-radius
:
4px
;
border
:
1px
dashed
#dddfe5
;
/* 添加边框样式 */
.uploaded-file-info
{
position
:
relative
;
margin
:
0
auto
;
>img
{
height
:
300px
;
}
}
}
/* 按钮 */
.button
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
flex-start
;
.next
{
align-self
:
flex-end
;
}
}
}
src/views/wm_nano_banana/index.vue
0 → 100644
View file @
5decc3ef
<
script
setup
lang=
"ts"
>
import
utils
from
'@/utils/utils'
import
{
onUpdated
,
onMounted
,
reactive
,
ref
}
from
'vue'
import
{
detectDeviceType
}
from
'./compositions/common'
import
{
myFileUpload
}
from
'./compositions/fileUpload'
import
{
onProcessing
}
from
'./compositions/process'
import
{
Check
,
Delete
,
Edit
,
Message
,
Search
,
Star
,
}
from
'@element-plus/icons-vue'
const
title
=
ref
(
'元芒数字'
)
const
form
=
reactive
({
source
:
''
,
task_id
:
''
,
file_path
:
''
,
file_path_domain
:
''
,
file_name
:
''
,
file_size
:
0
,
})
const
steps_active
=
ref
(
0
)
const
FileSizeLimitM
=
5
const
file_loading
=
ref
(
false
)
const
my_file_upload
=
myFileUpload
(
FileSizeLimitM
,
form
,
file_loading
)
const
upload
=
my_file_upload
.
upload
const
actionUrl
=
my_file_upload
.
actionUrl
onMounted
(
async
()
=>
{
// 检测设备类型
form
.
source
=
detectDeviceType
();
// 设置页面标题
document
.
title
=
title
.
value
;
// 生成task_id
form
.
task_id
=
utils
.
genDateTimeStr
();
console
.
log
(
'页面加载,task_id ='
,
form
.
task_id
);
})
</
script
>
<
template
>
<main
class=
"home-container"
>
<!-- 标题 -->
<div
class=
"title"
><el-text>
WM Nano Banana
</el-text></div>
<div
class=
"subtitle"
><el-text>
WM Nano Banana
</el-text></div>
<!-- 步骤条 -->
<div>
<el-steps
:active=
"steps_active"
align-center
finish-status=
"success"
>
<el-step
title=
"上传图片"
/>
<el-step
title=
"显示结果"
/>
</el-steps>
</div>
<div
class=
"content-container"
>
<!-- 文件页 -->
<div
class=
"upload-section"
v-if=
"steps_active === 0"
>
<div><el-text
class=
"section-title"
>
上传文件
</el-text></div>
<!--
<div><el-text
class=
"section-desc"
>
上传文件
</el-text></div>
-->
<div
class=
"upload-div"
v-loading=
"file_loading"
v-if=
"!form.file_path"
>
<el-upload
ref=
"upload"
:show-file-list=
"false"
:limit=
"1"
drag
accept=
".png,.jpg,.jpeg"
:action=
"actionUrl"
:on-success=
"my_file_upload.handleUploadSuccess"
:on-exceed=
"my_file_upload.handleUploadExceed"
:on-error=
"my_file_upload.handleUploadError"
:on-remove=
"my_file_upload.handleRemoveFile"
:before-upload=
"my_file_upload.handleBeforeUpload"
:on-progress=
"my_file_upload.handleUploadProgress"
list-type=
"picture"
>
<el-icon
class=
"el-icon--upload"
><upload-filled
/></el-icon>
<div
class=
"el-upload__text"
>
拖拽文件到这里 或
<em>
浏览文件
</em></div>
<div
class=
"el-upload__tip"
slot=
"tip"
>
文件格式 png/jpg, 大小限制
{{
FileSizeLimitM
}}
M
</div>
</el-upload>
</div>
<div
class=
"uploaded-div"
v-if=
"form.file_path"
>
<div
class=
"uploaded-file-info"
>
<img
:src=
"form.file_path_domain"
/>
</div>
</div>
<div
class=
"button"
>
<el-button
class=
"next"
color=
"#181818"
size=
"large"
>
下载
</el-button>
</div>
</div>
</div>
</main>
</
template
>
<!-- 样式 只在当前页面生效,优先级比组件样式低 -->
<
style
lang=
"scss"
scoped
src=
"./index.css"
></
style
>
<!-- 全局样式,如果要改组件样式,得在这里改,但为了避免所有组件都改,这里可以设置class层级 -->
<
style
lang=
"scss"
>
.progress-section
{
.classifications
{
.class-select-1
{
.el-select__wrapper
{
background-color
:
aquamarine
;
}
}
.class-select-2
{
.el-select__wrapper
{
background-color
:
rgb
(
121
,
160
,
245
);
}
}
.class-select-3
{
.el-select__wrapper
{
background-color
:
rgb
(
164
,
139
,
246
);
}
}
}
}
</
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