Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mengchangaigc
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
姚珂
mengchangaigc
Commits
310cf38d
Commit
310cf38d
authored
Aug 31, 2026
by
黄同智
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
完成商品中的选择商品和商品地址查询的弹窗
parent
a1f3aea0
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1144 additions
and
125 deletions
+1144
-125
indedx.vue
apps/web-vue/src/components/Table/indedx.vue
+17
-4
AgentProductImageDialog.vue
...ue/src/views/agent/components/AgentProductImageDialog.vue
+393
-0
AgentProductInfoDialog.vue
...vue/src/views/agent/components/AgentProductInfoDialog.vue
+123
-0
AgentPromptTools.vue
apps/web-vue/src/views/agent/components/AgentPromptTools.vue
+567
-0
index.vue
apps/web-vue/src/views/agent/index.vue
+41
-117
VideoItem.vue
apps/web-vue/src/views/resource/components/VideoItem.vue
+2
-3
tsconfig.json
apps/web-vue/tsconfig.json
+1
-1
No files found.
apps/web-vue/src/components/Table/indedx.vue
View file @
310cf38d
...
...
@@ -2,7 +2,8 @@
<div
class=
"common-table-wrapper"
>
<!-- 1. 表格主体 -->
<el-table
:data=
"tableData"
style=
"width: 100%"
:border=
"border"
:size=
"size"
:stripe=
"stripe"
:row-key=
"rowKey || undefined"
v-loading=
"loading"
@
selection-change=
"handleSelectionChange"
>
:height=
"height || undefined"
:row-key=
"rowKey || undefined"
v-loading=
"loading"
@
selection-change=
"handleSelectionChange"
>
<!-- 默认插槽:让父组件自定义列 -->
<slot></slot>
...
...
@@ -15,8 +16,8 @@
<!-- 2. 分页组件 -->
<div
v-if=
"showPagination"
class=
"pagination-container"
>
<el-pagination
v-model:current-page=
"currentPage"
v-model:page-size=
"pageSize"
:size=
"size"
:page-sizes=
"
[10, 20, 50, 100]
"
:background=
"true"
layout=
"total, sizes, prev, pager, next, jumper"
<el-pagination
v-model:current-page=
"currentPage"
v-model:page-size=
"pageSize"
:size=
"
p
size"
:page-sizes=
"
pageSizes
"
:background=
"true"
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"total"
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
/>
</div>
</div>
...
...
@@ -59,6 +60,10 @@ const props = defineProps({
type
:
String
,
default
:
''
},
psize
:
{
type
:
String
,
default
:
''
},
showPagination
:
{
type
:
Boolean
,
default
:
true
,
...
...
@@ -66,7 +71,15 @@ const props = defineProps({
rowKey
:
{
type
:
[
String
,
Function
],
default
:
''
,
}
},
height
:
{
type
:
[
String
,
Number
],
default
:
''
,
},
pageSizes
:
{
type
:
Array
,
default
:
()
=>
[
10
,
20
,
50
,
100
],
},
});
// 2. 定义 Emits(向父组件传递事件)
...
...
apps/web-vue/src/views/agent/components/AgentProductImageDialog.vue
0 → 100644
View file @
310cf38d
<
template
>
<Dialog
v-model=
"dialogVisible"
bg-color=
"#fff"
width=
"1152"
>
<div
class=
"flex-col bg-fff round-8 hidden"
>
<header
class=
"dialog-header flex-items-center h-56 px-20"
>
<h3
class=
"fs-14 fw-700 color-000"
>
添加商品图
</h3>
</header>
<section
class=
"dialog-body flex-1 flex-col hidden px-20 py-14"
>
<div
class=
"tab-row flex-items-center mb-20"
>
<div
v-for=
"item in tabs"
:key=
"item.value"
class=
"tab-item h-34 lh-34 px-16 fs-12 fw-700 pointer"
:class=
"
{ active: activeTab === item.value }" @click="activeTab = item.value">
{{
item
.
label
}}
</div>
</div>
<div
v-if=
"activeTab === 'library'"
class=
"library-panel flex-1 flex-col hidden"
>
<div
class=
"filter-row flex-between gap-8 mb-12"
>
<div
class=
"flex-items-center flex-wrap gap-8 flex-1"
>
<el-select
v-model=
"firstCategory"
class=
"filter-select"
placeholder=
"全部一级分类"
>
<el-option
v-for=
"item in firstCategories"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
<el-select
v-model=
"secondCategory"
class=
"filter-select"
placeholder=
"全部二级分类"
>
<el-option
v-for=
"item in secondCategories"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
<el-select
v-model=
"tagValue"
class=
"filter-select"
placeholder=
"全部标签"
>
<el-option
v-for=
"item in tags"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
<el-select
v-model=
"statusValue"
class=
"filter-select"
placeholder=
"全部状态"
>
<el-option
v-for=
"item in statuses"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
<el-select
v-model=
"uploaderValue"
class=
"uploader-select"
placeholder=
"全部上传人"
>
<el-option
v-for=
"item in uploaders"
:key=
"item"
:label=
"item"
:value=
"item"
/>
</el-select>
<el-input
v-model=
"searchKeyword"
class=
"search-input flex-1"
placeholder=
"搜索图片名称或 ID"
>
<template
#
prefix
>
<el-icon>
<Search
/>
</el-icon>
</
template
>
</el-input>
<el-checkbox
v-model=
"onlyMine"
class=
"mine-check"
>
仅看我的
</el-checkbox>
</div>
</div>
<SeTable
class=
"material-table flex-1"
:border=
"false"
:data-list=
"currentPageImageRows"
:total=
"imageRows.length"
:default-page-size=
"pageSize"
:page-sizes=
"[6, 10, 20, 50]"
:height=
"tableHeight"
row-key=
"id"
psize=
"small"
@
update:page=
"handleImagePageChange"
@
selection-change=
"handleImageSelectionChange"
>
<el-table-column
type=
"selection"
reserve-selection
width=
"46"
/>
<el-table-column
label=
"图片缩略图"
width=
"122"
>
<
template
#
default=
"{ row }"
>
<img
:src=
"row.image"
:alt=
"row.name"
class=
"thumb-img cover round-4"
/>
</
template
>
</el-table-column>
<el-table-column
label=
"文件名称 / ID"
min-width=
"240"
>
<
template
#
default=
"{ row }"
>
<div
class=
"flex-col"
>
<span
class=
"fs-12 fw-700 color-333 ellipsis"
>
{{
row
.
name
}}
</span>
<span
class=
"fs-10 color-999 mt-4"
>
{{
row
.
id
}}
</span>
</div>
</
template
>
</el-table-column>
<el-table-column
label=
"状态"
width=
"118"
>
<
template
#
default=
"{ row }"
>
<span
class=
"status-tag h-22 lh-22 px-10 round-4 fs-10"
>
{{
row
.
status
}}
</span>
</
template
>
</el-table-column>
<el-table-column
label=
"分类 / 标签"
min-width=
"190"
>
<
template
#
default=
"{ row }"
>
<div
class=
"flex-col"
>
<span
class=
"fs-12 fw-700 color-333 ellipsis"
>
{{
row
.
category
}}
</span>
<span
class=
"fs-10 color-999 mt-4 ellipsis"
>
{{
row
.
tags
}}
</span>
</div>
</
template
>
</el-table-column>
<el-table-column
prop=
"uploader"
label=
"上传人"
width=
"112"
/>
<el-table-column
prop=
"resolution"
label=
"分辨率"
width=
"118"
/>
<el-table-column
prop=
"size"
label=
"大小"
width=
"72"
/>
</SeTable>
</div>
<div
v-else
class=
"flex-1 round-8"
>
<UploadFile
v-model=
"localFiles"
list-type=
"drap"
:multiple=
"true"
:limit=
"6"
>
<
template
#
trigger
>
<div>
<el-icon
size=
"20"
>
<Upload
/>
</el-icon>
</div>
<p
class=
"fs-12 color-888 mt-6"
>
点击选择本地图片
</p>
</
template
>
</UploadFile>
<div
class=
"color-888 fs-12 text-center"
>
支持上传本地图片文件,图片格式:jpeg、 png、 webp、 bmp、 tiff、 gif,单张图片大小≤30MB。
</div>
<div
class=
"color-888 fs-12 text-center"
>
图片宽高比需在 (0.4, 2.5) 之间,宽高像素需在 (300px, 6000px) 之间。
</div>
<div
class=
"color-888 fs-12 text-center"
>
仅支持上传非人脸图,请仔细查看要求并确保上传素材为您原创或已取得合法授权。
</div>
</div>
</section>
<footer
class=
"dialog-footer flex-between px-20 py-12"
>
<div
class=
"fs-12 color-666"
>
已选
<span
class=
"color-main fw-700"
>
{{ selectedImageIds.length }}
</span>
张,还可添加
{{ restCount }} 张
</div>
<div
class=
"flex-items-center gap-8"
>
<el-button
class=
"h-34 px-18"
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
class=
"confirm-btn h-34 px-20 border-none"
@
click=
"confirmImages"
>
确认选择
</el-button>
</div>
</footer>
</div>
</Dialog>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
type
{
UploadUserFile
}
from
'element-plus'
;
import
{
Search
,
Upload
}
from
'@element-plus/icons-vue'
;
import
Dialog
from
'@/components/Dialog/index.vue'
;
import
UploadFile
from
'@/components/Upload/index.vue'
;
import
productImg
from
'@/static/image/huaban.png'
;
import
peopleImg
from
'@/static/image/meinv.png'
;
type
TabValue
=
'library'
|
'local'
;
type
ImageRow
=
{
id
:
string
;
name
:
string
;
image
:
string
;
status
:
string
;
category
:
string
;
tags
:
string
;
uploader
:
string
;
resolution
:
string
;
size
:
string
;
};
const
props
=
defineProps
<
{
modelValue
:
boolean
;
}
>
();
const
emit
=
defineEmits
<
{
'update:modelValue'
:
[
value
:
boolean
];
confirm
:
[
ids
:
string
[]];
}
>
();
const
dialogVisible
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
value
)
=>
emit
(
'update:modelValue'
,
value
),
});
const
tabs
:
{
label
:
string
;
value
:
TabValue
}[]
=
[
{
label
:
'图片管理'
,
value
:
'library'
},
{
label
:
'本地上传'
,
value
:
'local'
},
];
const
firstCategories
=
[
'全部一级分类'
,
'美妆护肤'
,
'服饰内衣'
,
'日用百货'
,
'食品饮料'
];
const
secondCategories
=
[
'全部二级分类'
,
'商品主图'
,
'模特展示'
,
'效果对比'
,
'资质证明'
];
const
tags
=
[
'全部标签'
,
'产品实拍'
,
'成分党'
,
'清凉感'
,
'对比实测'
];
const
statuses
=
[
'全部状态'
,
'审核通过'
,
'待审核'
,
'未审核'
];
const
uploaders
=
[
'全部上传人'
,
'致上互娱'
,
'汤小真'
,
'徐振'
,
'美妆设计组'
];
const
activeTab
=
ref
<
TabValue
>
(
'library'
);
const
firstCategory
=
ref
(
'全部一级分类'
);
const
secondCategory
=
ref
(
'全部二级分类'
);
const
tagValue
=
ref
(
'全部标签'
);
const
statusValue
=
ref
(
'全部状态'
);
const
uploaderValue
=
ref
(
'全部上传人'
);
const
searchKeyword
=
ref
(
''
);
const
onlyMine
=
ref
(
false
);
const
selectedImageIds
=
ref
<
string
[]
>
([]);
const
localFiles
=
ref
<
UploadUserFile
[]
>
([]);
const
currentPage
=
ref
(
1
);
const
pageSize
=
ref
(
10
);
const
tableHeight
=
500
;
const
imageRows
:
ImageRow
[]
=
[
{
id
:
'img-1'
,
name
:
'防晒植物提取精华液展图.jpg'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 商品主图'
,
tags
:
'产品实拍、成分党'
,
uploader
:
'致上互娱'
,
resolution
:
'1080x1440'
,
size
:
'2.4 MB'
,
},
{
id
:
'img-2'
,
name
:
'无痕防晒冰丝丝袜场景模特图.png'
,
image
:
productImg
,
status
:
'待审核'
,
category
:
'服饰内衣 / 模特展示'
,
tags
:
'模特出镜、清凉冰丝'
,
uploader
:
'汤小真'
,
resolution
:
'800x1200'
,
size
:
'1.8 MB'
,
},
{
id
:
'img-3'
,
name
:
'夏日爆款产品盒装主图.jpg'
,
image
:
productImg
,
status
:
'审核通过'
,
category
:
'日用百货 / 宣发主图'
,
tags
:
'爆款、活动促销'
,
uploader
:
'致上互娱'
,
resolution
:
'1920x1080'
,
size
:
'4.5 MB'
,
},
{
id
:
'img-4'
,
name
:
'草本护肤成分拆解对比展图.png'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 成分展示'
,
tags
:
'对比实测'
,
uploader
:
'汤小真'
,
resolution
:
'1080x1080'
,
size
:
'3.1 MB'
,
},
{
id
:
'img-5'
,
name
:
'防晒霜SPF50权威检测图.jpg'
,
image
:
productImg
,
status
:
'未审核'
,
category
:
'美妆护肤 / 资质证明'
,
tags
:
'成分党、检测报告'
,
uploader
:
'致上互娱'
,
resolution
:
'1080x1920'
,
size
:
'1.2 MB'
,
},
{
id
:
'img-6'
,
name
:
'补水面膜水分提升对比实验图.jpg'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 效果对比'
,
tags
:
'对比实测'
,
uploader
:
'徐振'
,
resolution
:
'1080x1440'
,
size
:
'2.9 MB'
,
},
{
id
:
'img-7'
,
name
:
'夏日清凉草本展示图.jpg'
,
image
:
productImg
,
status
:
'待审核'
,
category
:
'食品饮料 / 商品主图'
,
tags
:
'清凉夏日、产品实拍'
,
uploader
:
'美妆设计组'
,
resolution
:
'2000x2000'
,
size
:
'5.4 MB'
,
},
{
id
:
'img-8'
,
name
:
'高奢护肤瓶身渲染特写.jpg'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 商品主图'
,
tags
:
'高端质感、3D渲染'
,
uploader
:
'汤小真'
,
resolution
:
'1440x1920'
,
size
:
'3.8 MB'
,
},
{
id
:
'img-9'
,
name
:
'高奢护肤瓶身渲染特写.jpg'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 商品主图'
,
tags
:
'高端质感、3D渲染'
,
uploader
:
'汤小真'
,
resolution
:
'1440x1920'
,
size
:
'3.8 MB'
,
},
{
id
:
'img-10'
,
name
:
'高奢护肤瓶身渲染特写.jpg'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 商品主图'
,
tags
:
'高端质感、3D渲染'
,
uploader
:
'汤小真'
,
resolution
:
'1440x1920'
,
size
:
'3.8 MB'
,
},
{
id
:
'img-11'
,
name
:
'高奢护肤瓶身渲染特写.jpg'
,
image
:
peopleImg
,
status
:
'审核通过'
,
category
:
'美妆护肤 / 商品主图'
,
tags
:
'高端质感、3D渲染'
,
uploader
:
'汤小真'
,
resolution
:
'1440x1920'
,
size
:
'3.8 MB'
,
},
];
const
restCount
=
computed
(()
=>
Math
.
max
(
0
,
6
-
selectedImageIds
.
value
.
length
));
const
currentPageImageRows
=
computed
(()
=>
{
const
start
=
(
currentPage
.
value
-
1
)
*
pageSize
.
value
;
return
imageRows
.
slice
(
start
,
start
+
pageSize
.
value
);
});
const
handleImagePageChange
=
(
page
:
number
,
size
:
number
)
=>
{
currentPage
.
value
=
page
;
pageSize
.
value
=
size
;
};
const
handleImageSelectionChange
=
(
selection
:
ImageRow
[])
=>
{
selectedImageIds
.
value
=
selection
.
map
((
item
)
=>
item
.
id
);
};
const
confirmImages
=
()
=>
{
emit
(
'confirm'
,
selectedImageIds
.
value
);
dialogVisible
.
value
=
false
;
};
</
script
>
<
style
scoped
lang=
"scss"
>
.dialog-header
{
flex
:
0
0
56px
;
border-bottom
:
1px
solid
#edf2f7
;
}
.dialog-body
,
.library-panel
,
.material-table
{
min-height
:
0
;
}
.dialog-footer
{
flex
:
0
0
58px
;
border-top
:
1px
solid
#edf2f7
;
background
:
#f8fafc
;
}
.tab-row
{
border-bottom
:
1px
solid
#dfe7f2
;
}
.tab-item
{
color
:
#536883
;
border-bottom
:
2px
solid
transparent
;
&
.active
{
color
:
#7d28ff
;
border-bottom-color
:
#7d28ff
;
}
}
.filter-select
{
width
:
130px
;
}
.uploader-select
{
width
:
124px
;
}
.search-input
{
min-width
:
220px
;
}
.mine-check
{
white-space
:
nowrap
;
}
.thumb-img
{
width
:
48px
;
height
:
48px
;
}
.status-tag
{
color
:
#61738c
;
background
:
#eef3f8
;
}
.confirm-btn
{
background
:
#7d28ff
;
}
:deep
(
.el-select__wrapper
),
:deep
(
.el-input__wrapper
)
{
border-radius
:
4px
;
box-shadow
:
0
0
0
1px
#dce6f2
inset
;
}
:deep
(
.el-checkbox__input.is-checked
.el-checkbox__inner
)
{
border-color
:
#7d28ff
;
background
:
#7d28ff
;
}
</
style
>
apps/web-vue/src/views/agent/components/AgentProductInfoDialog.vue
0 → 100644
View file @
310cf38d
<
template
>
<Dialog
v-model=
"dialogVisible"
bg-color=
"#fff"
width=
"768"
>
<div
class=
"product-info-dialog bg-fff round-8 hidden"
>
<header
class=
"dialog-header flex-items-center h-56 px-20"
>
<h3
class=
"fs-14 fw-700 color-000"
>
输入商品信息
</h3>
</header>
<section
class=
"dialog-body px-20 py-20"
>
<div
class=
"fs-12 fw-700 color-333 mb-8"
>
抖音商品链接或商品 ID
</div>
<div
class=
"flex gap-10"
>
<el-input
v-model=
"productKeyword"
class=
"flex-1"
size=
"small"
placeholder=
"粘贴抖音商品链接或输入商品 ID"
/>
<el-button
:disabled=
"productKeyword === ''"
type=
"primary"
class=
"search-btn fs-12 h-34 px-16"
>
搜索
</el-button>
</div>
<div
v-for=
"item in productOptions"
:key=
"item.id"
class=
"product-card flex-items-center gap-12 mt-16 p-12 round-6 pointer"
:class=
"
{ active: selectedProductId === item.id }" @click="selectedProductId = item.id">
<img
:src=
"item.image"
:alt=
"item.name"
class=
"product-cover wh-60 round-4 cover"
/>
<div
class=
"flex-1"
>
<div
class=
"fs-14 fw-700 color-000 lh-22"
>
{{
item
.
name
}}
</div>
<div
class=
"fs-12 color-666 lh-20"
>
商品 ID:
{{
item
.
id
}}
</div>
</div>
<span
v-if=
"selectedProductId === item.id"
class=
"checked-icon flex-center wh-18 round"
>
<el-icon
class=
"fs-12"
>
<Check
/>
</el-icon>
</span>
</div>
</section>
<footer
class=
"dialog-footer flex justify-end gap-8 px-20 py-12"
>
<el-button
class=
"px-18"
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
class=
"confirm-btn px-18 border-none"
@
click=
"confirmProductInfo"
>
确认
</el-button>
</footer>
</div>
</Dialog>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'vue'
;
import
{
Check
}
from
'@element-plus/icons-vue'
;
import
Dialog
from
'@/components/Dialog/index.vue'
;
import
productImg
from
'@/static/image/huaban.png'
;
const
props
=
defineProps
<
{
modelValue
:
boolean
;
}
>
();
const
emit
=
defineEmits
<
{
'update:modelValue'
:
[
value
:
boolean
];
confirm
:
[
value
:
string
];
}
>
();
const
productKeyword
=
ref
(
''
);
const
selectedProductId
=
ref
(
''
);
const
productOptions
=
[
{
id
:
'353829104771'
,
name
:
'Bestore 负离子高速吹风机'
,
image
:
productImg
,
},
{
id
:
'353829104772'
,
name
:
'负离子高速吹风机卡卡的撒'
,
image
:
productImg
,
},
];
const
dialogVisible
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
value
)
=>
emit
(
'update:modelValue'
,
value
),
});
const
confirmProductInfo
=
()
=>
{
emit
(
'confirm'
,
selectedProductId
.
value
||
productKeyword
.
value
);
dialogVisible
.
value
=
false
;
};
</
script
>
<
style
scoped
lang=
"scss"
>
.dialog-header
,
.dialog-footer
{
border-bottom
:
1px
solid
#edf2f7
;
}
.dialog-footer
{
border-top
:
1px
solid
#edf2f7
;
border-bottom
:
0
;
background
:
#f8fafc
;
}
:deep
(
.el-input__wrapper
)
{
// height: 40px;
// border-radius: 6px;
box-shadow
:
0
0
0
1px
#dce6f2
inset
;
}
:deep
(
.el-input__inner
)
{
font-size
:
12px
;
}
.product-card
{
border
:
1px
solid
#dce6f2
;
background
:
#fff
;
transition
:
border-color
.2s
,
background
.2s
;
&
.active
{
border-color
:
#9b6cff
;
background
:
#f4f0ff
;
}
}
.product-cover
{
background
:
#f3f6fb
;
}
.checked-icon
{
color
:
#7d28ff
;
border
:
1px
solid
#7d28ff
;
}
</
style
>
apps/web-vue/src/views/agent/components/AgentPromptTools.vue
0 → 100644
View file @
310cf38d
<
template
>
<div
class=
"flex-items-center flex-wrap"
>
<el-popover
v-model:visible=
"productPopoverVisible"
placement=
"bottom"
:width=
"150"
trigger=
"hover"
:hide-after=
"0"
>
<template
#
reference
>
<el-button
class=
"m-2 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Box"
/>
</el-icon>
<span>
商品
</span>
</el-button>
</
template
>
<div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
@
click=
"openProductInfoDialog"
>
<el-icon>
<component
:is=
"Link"
/>
</el-icon>
<span>
输入商品信息
</span>
</div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
@
click=
"openProductImageDialog"
>
<el-icon>
<component
:is=
"Link"
/>
</el-icon>
<span>
添加商品图
</span>
</div>
</div>
</el-popover>
<el-popover
placement=
"bottom"
:width=
"150"
trigger=
"hover"
:hide-after=
"0"
>
<
template
#
reference
>
<el-button
class=
"m-2 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"VideoCamera"
/>
</el-icon>
<span>
参考
</span>
</el-button>
</
template
>
<div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Clock"
/>
</el-icon>
<span>
历史投放素材
</span>
</div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Upload"
/>
</el-icon>
<span>
本地上传
</span>
</div>
</div>
</el-popover>
<el-popover
placement=
"bottom"
:width=
"120"
trigger=
"hover"
popper-class=
"agent-prompt-tools-popper"
:hide-after=
"0"
>
<
template
#
reference
>
<el-button
class=
"m-2 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Document"
/>
</el-icon>
<span>
脚本/原料
</span>
</el-button>
</
template
>
<div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Document"
/>
</el-icon>
<span>
添加脚本
</span>
</div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Film"
/>
</el-icon>
<span>
添加原料
</span>
</div>
</div>
</el-popover>
<el-popover
placement=
"bottom"
:width=
"378"
trigger=
"click"
popper-class=
"agent-param-popper"
:hide-after=
"0"
>
<
template
#
reference
>
<el-button
class=
"flex-center gap-6 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Setting"
/>
</el-icon>
<span
class=
"mr-6"
>
生成参数
</span>
<el-icon
size=
"12"
>
<component
:is=
"ArrowDownBold"
/>
</el-icon>
</el-button>
</
template
>
<div
class=
"param-panel p-12 round-4 bg-fff"
>
<div
class=
"switch-row flex h-36 p-4 round-4 mb-8"
>
<el-button
v-for=
"item in mediaTypes"
:key=
"item.value"
class=
"switch-btn flex-1 h-28 round-4 border-none fs-14"
:class=
"{ active: mediaType === item.value }"
text
@
click=
"setMediaType(item.value)"
>
<el-icon>
<component
:is=
"item.icon"
/>
</el-icon>
<span>
{{ item.label }}
</span>
</el-button>
</div>
<div
class=
"switch-row flex h-36 p-4 round-4"
>
<el-button
v-for=
"item in paramTabs"
:key=
"item.value"
class=
"switch-btn flex-1 h-28 round-4 border-none fs-14"
:class=
"{ active: paramTab === item.value }"
text
@
click=
"setParamTab(item.value)"
>
{{ item.label }}
</el-button>
</div>
<
template
v-if=
"mediaType === 'image' && paramTab === 'frame'"
>
<div
class=
"flex-between mt-14 mb-10"
>
<span
class=
"fs-12 color-888"
>
比例
</span>
<span
class=
"fs-12 color-000"
>
{{
imageRatio
}}
</span>
</div>
<div
class=
"option-list flex flex-wrap gap-6"
>
<div
v-for=
"item in imageRatios"
:key=
"item.value"
class=
"option-btn flex-center gap-6 h-34 px-12 round-6 pointer fs-12"
:class=
"
{ active: imageRatio === item.value }" text @click="imageRatio = item.value">
<el-icon
v-if=
"item.icon"
>
<component
:is=
"item.icon"
/>
</el-icon>
<span
v-else
class=
"ratio-icon round-2"
:class=
"item.iconClass"
></span>
<span>
{{
item
.
label
}}
</span>
</div>
</div>
<el-button
class=
"custom-size flex-center gap-8 w-full h-34 mt-8 round-6 bg-fff fs-12"
>
<el-icon>
<FullScreen
/>
</el-icon>
<span>
自定义像素尺寸
</span>
</el-button>
</
template
>
<
template
v-if=
"mediaType === 'image' && paramTab === 'output'"
>
<div
class=
"fs-12 color-888 mt-14 mb-10"
>
画质
</div>
<div
class=
"option-list flex gap-6"
>
<div
v-for=
"item in qualities"
:key=
"item"
class=
"option-btn flex-center flex-1 fs-12 pointer h-34 round-6 border-none"
:class=
"
{ active: imageQuality === item }" text @click="imageQuality = item">
{{
item
}}
</div>
</div>
<div
class=
"fs-12 color-888 mt-14 mb-10"
>
数量
</div>
<div
class=
"option-list flex gap-6"
>
<div
v-for=
"item in quantities"
:key=
"item"
class=
"option-btn flex-1 h-34 round-6 flex-center fs-12 pointer"
:class=
"
{ active: imageQuantity === item }" text @click="imageQuantity = item">
{{
item
}}
</div>
</div>
</
template
>
<
template
v-if=
"mediaType === 'video' && paramTab === 'frame'"
>
<div
class=
"fs-12 color-888 mt-14 mb-10"
>
参考方式
</div>
<div
class=
"option-list flex gap-6"
>
<div
v-for=
"item in referenceModes"
:key=
"item"
class=
"option-btn flex-1 h-34 round-6 flex-center pointer fs-12"
:class=
"
{ active: referenceMode === item }" text @click="referenceMode = item">
{{
item
}}
</div>
</div>
<div
class=
"flex-between mt-14 mb-10"
>
<span
class=
"fs-12 color-888"
>
比例
</span>
<span
class=
"fs-12 color-888"
>
智能
</span>
</div>
<div
class=
"option-list flex flex-wrap gap-6"
>
<div
v-for=
"item in videoRatios"
:key=
"item.value"
class=
"option-btn flex-center gap-6 h-34 px-12 round-6 flex-center pointer fs-12"
:class=
"
{ active: videoRatio === item.value }" text @click="videoRatio = item.value">
<el-icon
v-if=
"item.icon"
>
<component
:is=
"item.icon"
/>
</el-icon>
<span
v-else
class=
"ratio-icon round-2"
:class=
"item.iconClass"
></span>
<span>
{{
item
.
label
}}
</span>
</div>
</div>
<el-button
class=
"custom-size flex-center gap-8 w-full h-34 mt-8 round-10 bg-fff"
>
<el-icon>
<FullScreen
/>
</el-icon>
<span>
自定义像素尺寸
</span>
</el-button>
</
template
>
<
template
v-if=
"mediaType === 'video' && paramTab === 'output'"
>
<div
class=
"fs-12 color-888 mt-14 mb-10"
>
清晰度
</div>
<div
class=
"option-list flex gap-6"
>
<div
v-for=
"item in resolutions"
:key=
"item"
class=
"option-btn flex-1 h-32 round-6 flex-center pointer fs-12"
:class=
"
{ active: resolution === item }" text @click="resolution = item">
{{
item
}}
</div>
</div>
<div
class=
"custom-input flex-between h-32 px-10 mt-6 round-6"
>
<el-input
v-model=
"customResolution"
class=
"param-input flex-1"
placeholder=
"自定义,例如 2160 或 4K"
/>
<span
class=
"fs-12 color-888"
>
建议值可直接选择
</span>
</div>
<div
class=
"fs-12 color-888 mt-14 mb-10"
>
数量
</div>
<div
class=
"option-list flex gap-6"
>
<div
v-for=
"item in quantities"
:key=
"item"
class=
"option-btn flex-1 h-32 round-4 flex-center pointer fs-12"
:class=
"
{ active: videoQuantity === item }" text @click="videoQuantity = item">
{{
item
}}
</div>
<el-input
v-model=
"customDuration"
class=
"w-96"
placeholder=
"自定义"
/>
</div>
<div
class=
"fs-12 color-888 mt-14 mb-10"
>
时长
</div>
<div
class=
"option-list flex gap-6"
>
<div
v-for=
"item in durations"
:key=
"item"
class=
"option-btn flex-1 h-32 round-6 flex-center pointer fs-12"
:class=
"
{ active: duration === item }" text @click="duration = item">
{{
item
}}
</div>
<div
class=
"duration-input flex-items-center h-32 round-6 pl-10"
>
<el-input
v-model=
"customDuration"
class=
"param-input w-96"
placeholder=
"自定义"
/>
<span
class=
"unit flex-center h-full px-10 fs-12"
>
秒
</span>
</div>
</div>
<div
class=
"switch-setting flex-between h-44 px-14 mt-16 round-6"
>
<span
class=
"fs-12 color-888"
>
生成声音
</span>
<el-switch
v-model=
"withVoice"
/>
<span
class=
"fs-12 color-888 ml-18"
>
添加水印
</span>
<el-switch
v-model=
"withWatermark"
/>
</div>
</
template
>
</div>
</el-popover>
<el-popover
placement=
"bottom"
:width=
"232"
trigger=
"click"
popper-class=
"agent-style-popper"
:hide-after=
"0"
>
<
template
#
reference
>
<el-button
class=
"flex-center gap-6 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Position"
/>
</el-icon>
<span
class=
"mr-6"
>
风格
</span>
<el-icon
size=
"12"
>
<component
:is=
"ArrowDownBold"
/>
</el-icon>
</el-button>
</
template
>
<div
class=
"flex bg-fff hidden"
>
<div
class=
"style-tabs w-100 py-8"
>
<div
v-for=
"item in styleTabs"
:key=
"item.value"
class=
"flex-between h-38 px-12 fs-14 pointer"
:class=
"{ active: activeStyleTab === item.value }"
@
click=
"activeStyleTab = item.value"
>
<span>
{{ item.label }}-{{ item.count }}
</span>
<el-icon
v-if=
"activeStyleTab === item.value"
size=
"12"
>
<ArrowRightBold
/>
</el-icon>
</div>
</div>
<div
class=
"style-options flex-1 py-8 over-y-auto"
>
<el-checkbox-group
v-model=
"currentStyleChecked"
class=
"flex-col"
>
<el-checkbox
v-for=
"item in currentStyleOptions"
:key=
"item.value"
:value=
"item.value"
class=
"style-check flex-items-center h-42 px-16"
>
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</el-popover>
<AgentProductInfoDialog
v-model=
"productInfoVisible"
/>
<AgentProductImageDialog
v-model=
"productImageVisible"
/>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
,
type
Component
}
from
'vue'
;
import
{
ArrowDownBold
,
ArrowRightBold
,
Box
,
Clock
,
Document
,
Film
,
FullScreen
,
Link
,
MagicStick
,
Picture
,
Position
,
Setting
,
Upload
,
VideoCamera
,
}
from
'@element-plus/icons-vue'
;
import
AgentProductImageDialog
from
'./AgentProductImageDialog.vue'
;
import
AgentProductInfoDialog
from
'./AgentProductInfoDialog.vue'
;
type
MediaType
=
'image'
|
'video'
;
type
ParamTab
=
'frame'
|
'output'
;
type
StyleTab
=
'style'
|
'element'
;
type
SwitchOption
<
T
extends
string
=
string
>
=
{
label
:
string
;
value
:
T
;
icon
?:
Component
;
};
type
IconSwitchOption
<
T
extends
string
=
string
>
=
SwitchOption
<
T
>
&
{
icon
:
Component
;
};
type
RatioOption
=
SwitchOption
&
{
iconClass
?:
string
;
};
const
mediaType
=
ref
<
MediaType
>
(
'image'
);
const
paramTab
=
ref
<
ParamTab
>
(
'frame'
);
const
imageRatio
=
ref
(
'smart'
);
const
videoRatio
=
ref
(
'smart'
);
const
imageQuality
=
ref
(
'智能'
);
const
imageQuantity
=
ref
(
'1份'
);
const
videoQuantity
=
ref
(
'1份'
);
const
referenceMode
=
ref
(
'智能参考'
);
const
resolution
=
ref
(
'智能'
);
const
duration
=
ref
(
'5秒'
);
const
customResolution
=
ref
(
''
);
const
customDuration
=
ref
(
''
);
const
withVoice
=
ref
(
true
);
const
withWatermark
=
ref
(
false
);
const
productPopoverVisible
=
ref
(
false
);
const
productInfoVisible
=
ref
(
false
);
const
productImageVisible
=
ref
(
false
);
const
activeStyleTab
=
ref
<
StyleTab
>
(
'style'
);
const
checkedStyleValues
=
ref
([
'english'
,
'revenge'
]);
const
checkedElementValues
=
ref
([
'contrast'
]);
const
setMediaType
=
(
value
:
MediaType
)
=>
{
mediaType
.
value
=
value
;
};
const
setParamTab
=
(
value
:
ParamTab
)
=>
{
paramTab
.
value
=
value
;
};
const
openProductInfoDialog
=
()
=>
{
productPopoverVisible
.
value
=
false
;
productInfoVisible
.
value
=
true
;
};
const
openProductImageDialog
=
()
=>
{
productPopoverVisible
.
value
=
false
;
productImageVisible
.
value
=
true
;
};
const
mediaTypes
:
IconSwitchOption
<
MediaType
>
[]
=
[
{
label
:
'图片'
,
value
:
'image'
,
icon
:
Picture
},
{
label
:
'视频'
,
value
:
'video'
,
icon
:
VideoCamera
},
];
const
paramTabs
:
SwitchOption
<
ParamTab
>
[]
=
[
{
label
:
'画面'
,
value
:
'frame'
},
{
label
:
'输出'
,
value
:
'output'
},
];
const
imageRatios
:
RatioOption
[]
=
[
{
label
:
'智能'
,
value
:
'smart'
,
icon
:
MagicStick
},
{
label
:
'1:1'
,
value
:
'1:1'
,
iconClass
:
'square'
},
{
label
:
'16:9'
,
value
:
'16:9'
,
iconClass
:
'wide'
},
{
label
:
'4:3'
,
value
:
'4:3'
,
iconClass
:
'standard'
},
{
label
:
'3:2'
,
value
:
'3:2'
,
iconClass
:
'wide'
},
{
label
:
'2:3'
,
value
:
'2:3'
,
iconClass
:
'portrait'
},
{
label
:
'3:4'
,
value
:
'3:4'
,
iconClass
:
'portrait'
},
{
label
:
'9:16'
,
value
:
'9:16'
,
iconClass
:
'phone'
},
];
const
videoRatios
:
RatioOption
[]
=
[
{
label
:
'智能'
,
value
:
'smart'
,
icon
:
MagicStick
},
{
label
:
'21:9'
,
value
:
'21:9'
,
iconClass
:
'cinema'
},
{
label
:
'16:9'
,
value
:
'16:9'
,
iconClass
:
'wide'
},
{
label
:
'4:3'
,
value
:
'4:3'
,
iconClass
:
'standard'
},
{
label
:
'1:1'
,
value
:
'1:1'
,
iconClass
:
'square'
},
{
label
:
'3:4'
,
value
:
'3:4'
,
iconClass
:
'portrait'
},
{
label
:
'9:16'
,
value
:
'9:16'
,
iconClass
:
'phone'
},
];
const
qualities
=
[
'智能'
,
'高'
,
'中'
,
'低'
];
const
quantities
=
[
'1份'
,
'2份'
,
'3份'
,
'4份'
];
const
referenceModes
=
[
'智能参考'
,
'首帧'
,
'首尾帧'
];
const
resolutions
=
[
'智能'
,
'480P'
,
'720P'
,
'1080P'
];
const
durations
=
[
'5s'
,
'10s'
];
const
styleOptions
=
[
{
label
:
'英文虐渣'
,
value
:
'english'
},
{
label
:
'打脸复仇'
,
value
:
'revenge'
},
{
label
:
'强势碾压'
,
value
:
'power'
},
{
label
:
'沙雕搞笑'
,
value
:
'funny'
},
{
label
:
'幽默风趣'
,
value
:
'humor'
},
{
label
:
'玩梗搞笑'
,
value
:
'meme'
},
];
const
elementOptions
=
[
{
label
:
'痛点对比'
,
value
:
'contrast'
},
{
label
:
'使用过程'
,
value
:
'process'
},
{
label
:
'产品展示'
,
value
:
'product'
},
];
const
styleTabs
=
computed
(()
=>
[
{
label
:
'风格'
,
value
:
'style'
as
const
,
count
:
checkedStyleValues
.
value
.
length
},
{
label
:
'元素'
,
value
:
'element'
as
const
,
count
:
checkedElementValues
.
value
.
length
},
]);
const
currentStyleOptions
=
computed
(()
=>
activeStyleTab
.
value
===
'style'
?
styleOptions
:
elementOptions
);
const
currentStyleChecked
=
computed
({
get
:
()
=>
activeStyleTab
.
value
===
'style'
?
checkedStyleValues
.
value
:
checkedElementValues
.
value
,
set
:
(
value
:
string
[])
=>
{
if
(
activeStyleTab
.
value
===
'style'
)
{
checkedStyleValues
.
value
=
value
;
return
;
}
checkedElementValues
.
value
=
value
;
},
});
</
script
>
<
style
scoped
lang=
"scss"
>
.hover
{
&
:hover
{
color
:
rgba
(
var
(
--
main-color
)
,
0
.8
);
}
}
.param-panel
{
box-shadow
:
0
8px
24px
rgba
(
23
,
38
,
64
,
.12
);
}
.switch-row
,
.option-btn
,
.switch-setting
{
background
:
#f1f3f5
;
}
.switch-btn
{
color
:
#1f2f45
;
}
.switch-btn.active
{
color
:
#1f2f45
;
background
:
#fff
;
box-shadow
:
0
1px
6px
rgba
(
12
,
24
,
44
,
.14
);
}
.option-btn
{
color
:
#1f2f45
;
}
.option-btn.active
{
color
:
#1f2f45
;
background
:
#e8f0f4
;
}
.option-list
.option-btn
{
flex
:
1
0
calc
((
100%
-
24px
)
/
5
);
}
.custom-size
{
border
:
1px
dashed
#d9e4f2
;
color
:
#1f2f45
;
}
.ratio-icon
{
display
:
inline-block
;
border
:
1px
solid
currentColor
;
}
.ratio-icon.square
{
width
:
12px
;
height
:
12px
;
}
.ratio-icon.wide
{
width
:
16px
;
height
:
9px
;
}
.ratio-icon.standard
{
width
:
14px
;
height
:
10px
;
}
.ratio-icon.portrait
{
width
:
9px
;
height
:
14px
;
}
.ratio-icon.phone
{
width
:
8px
;
height
:
16px
;
}
.ratio-icon.cinema
{
width
:
18px
;
height
:
8px
;
}
.custom-input
,
.duration-input
{
border
:
1px
solid
#dfe7f2
;
background
:
#fff
;
}
:deep
(
.param-input
.el-input__wrapper
)
{
padding
:
0
;
box-shadow
:
none
;
background
:
transparent
;
}
:deep
(
.param-input
.el-input__inner
)
{
height
:
30px
;
font-size
:
12px
;
}
.unit
{
border-left
:
1px
solid
#dfe7f2
;
color
:
#1f2f45
;
}
.style-tabs
{
border-right
:
1px
solid
#eee
;
}
.style-options
{
max-height
:
268px
;
&
:
:-
webkit-scrollbar
{
width
:
6px
;
}
&
:
:-
webkit-scrollbar-thumb
{
background
:
#6a6a6a
;
border-radius
:
6px
;
}
}
.style-check
{
margin-right
:
0
;
&
:hover
{
background
:
#eee
;
}
}
:deep
(
.style-check
.el-checkbox__label
)
{
font-size
:
14px
;
}
</
style
>
<
style
>
.agent-prompt-tools-popper
{
min-width
:
120px
!important
;
}
.agent-param-popper
{
padding
:
0
!important
;
border
:
0
!important
;
border-radius
:
6px
!important
;
overflow
:
hidden
;
}
.agent-style-popper
{
padding
:
0
!important
;
/* border: 0 !important; */
border-radius
:
6px
!important
;
overflow
:
hidden
;
background
:
transparent
!important
;
}
</
style
>
apps/web-vue/src/views/agent/index.vue
View file @
310cf38d
<
template
>
<div
class=
"h-full bgmr relative hidden"
>
<section
v-if=
"hasMessages"
>
<div
v-if=
"hasMessages"
class=
"fixed top-20 pl-20 flex-items-center gap-6 pointer"
@
click=
"hasMessages = false"
>
<el-icon
class=
"fs-16"
>
<Back
/>
</el-icon>
<span
class=
"fs-12 fw-600"
>
返回
</span>
</div>
<section
v-if=
"!hasMessages"
>
<div
class=
"mt-60 flex-col items-center"
>
<div
class=
"flex-center wh-44 round-8 color-fff bg-jb"
>
<el-icon
class=
"fs-24"
>
...
...
@@ -12,93 +18,8 @@
<div
class=
"flex-col bg-fff round-16 model2 p-20 boxshaw"
>
<el-input
v-model=
"prompt"
:rows=
"10"
type=
"textarea"
resize=
"none"
placeholder=
"发送给 Agent"
/>
<div
class=
"flex-between pt-16"
>
<div
class=
"flex-items-center flex-wrap"
>
<el-popover
placement=
"top-start"
:width=
"150"
trigger=
"hover"
>
<template
#
reference
>
<el-button
class=
"m-2 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Box"
/>
</el-icon>
<span>
商品
</span>
</el-button>
</
template
>
<div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Link"
/>
</el-icon>
<span>
输入商品信息
</span>
</div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Link"
/>
</el-icon>
<span>
添加商品图
</span>
</div>
</div>
</el-popover>
<el-popover
placement=
"top-start"
:width=
"150"
trigger=
"hover"
>
<
template
#
reference
>
<el-button
class=
"m-2 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"VideoCamera"
/>
</el-icon>
<span>
参考
</span>
</el-button>
</
template
>
<div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Clock"
/>
</el-icon>
<span>
历史投放素材
</span>
</div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Upload"
/>
</el-icon>
<span>
本地上传
</span>
</div>
</div>
</el-popover>
<el-popover
placement=
"top-start"
:width=
"120"
trigger=
"hover"
popper-class=
"xxx"
>
<
template
#
reference
>
<el-button
class=
"m-2 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Document"
/>
</el-icon>
<span>
脚本/原料
</span>
</el-button>
</
template
>
<div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Document"
/>
</el-icon>
<span>
添加脚本
</span>
</div>
<div
class=
"flex-items-center gap-6 h-30 border-none pointer hover"
>
<el-icon>
<component
:is=
"Film"
/>
</el-icon>
<span>
添加原料
</span>
</div>
</div>
</el-popover>
<el-button
class=
"flex-center gap-6 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Setting"
/>
</el-icon>
<span>
45秒 · 9:16
</span>
</el-button>
<el-button
class=
"flex-center gap-6 h-30 border-none px-6"
>
<el-icon>
<component
:is=
"Position"
/>
</el-icon>
<span>
风格
</span>
</el-button>
</div>
<div
class=
"border-eee flex bg-fff round-100 p-6 gap-10 pl-16 minw-110 pointer hoverbth"
>
<AgentPromptTools
/>
<div
class=
"border-eee flex bg-fff round-100 p-6 gap-10 pl-16 minw-110 pointer hoverbth"
@
click=
"hasMessages = true"
>
开始创作
<div
class=
"wh-24 color-fff round flex-center bg-jb"
>
<el-icon>
...
...
@@ -161,25 +82,17 @@
<span>
回到底部
</span>
</el-button>
</div>
<div
class=
"composer-box flex-col bg-fff round-16 border-eee boxshaw"
>
<div
ref=
"composerBoxRef"
class=
"composer-box flex-col bg-fff round-16 border-eee boxshaw"
@
focusin=
"handleComposerActive"
@
pointerdown=
"handleComposerActive"
>
<div
class=
"input-row flex gap-20 px-14 pt-14"
>
<UploadFile
:width=
"isComposerCompact ? 40 : 44"
/>
<el-input
v-model=
"prompt"
class=
"flex-1 boxno"
type=
"textarea"
resize=
"none"
:rows=
"3"
:placeholder=
"isComposerCompact ? '输入你的创作想法' : '输入你的创作想法、脚本或画面要求'"
@
focus=
"handleInputFocus"
@
blur=
"handleInputBlur"
/>
:placeholder=
"isComposerCompact ? '输入你的创作想法' : '输入你的创作想法、脚本或画面要求'"
@
focus=
"handleInputFocus"
@
blur=
"handleInputBlur"
/>
</div>
<div
class=
"composer-actions flex-between px-14"
>
<div
class=
"flex-items-center flex-wrap"
>
<el-button
v-for=
"item in agentControls"
:key=
"item.label"
class=
"flex-center gap-6 h-32 px-12 fs-12 round-6"
>
<el-icon>
<component
:is=
"item.icon"
/>
</el-icon>
<span>
{{ item.label }}
</span>
<el-icon
v-if=
"item.hasArrow"
>
<ArrowDown
/>
</el-icon>
</el-button>
<AgentPromptTools
/>
</div>
<div
class=
"send-btn flex-center wh-40 p-0 color-fff pointer border-none round fs-20 mb-10 bg-jb"
@
click=
"sendMessage"
>
...
...
@@ -195,10 +108,11 @@
</
template
>
<
script
setup
lang=
"ts"
>
import
{
computed
,
nextTick
,
onMounted
,
ref
}
from
'vue'
;
import
{
computed
,
nextTick
,
on
BeforeUnmount
,
on
Mounted
,
ref
}
from
'vue'
;
import
type
{
Component
}
from
'vue'
;
import
{
ArrowDown
,
B
ox
,
Coin
,
CopyDocument
,
Document
,
Link
,
MagicStick
,
Position
,
Setting
,
Top
,
VideoCamera
,
Clock
,
Upload
,
Film
}
from
'@element-plus/icons-vue'
;
import
{
ArrowDown
,
B
ack
,
Box
,
Coin
,
CopyDocument
,
Link
,
MagicStick
,
Position
,
Top
}
from
'@element-plus/icons-vue'
;
import
UploadFile
from
'@/components/Upload/index.vue'
import
AgentPromptTools
from
'./components/AgentPromptTools.vue'
;
type
ToolItem
=
{
label
:
string
;
...
...
@@ -209,11 +123,13 @@ type ToolItem = {
const
prompt
=
ref
(
''
);
const
submittedTopic
=
ref
(
'美颜'
);
const
messagesCount
=
ref
(
0
);
const
hasMessages
=
computed
(()
=>
messagesCount
.
value
>
0
);
const
hasMessages
=
ref
(
false
)
const
chatScrollRef
=
ref
<
HTMLElement
>
();
const
composerBoxRef
=
ref
<
HTMLElement
>
();
const
isInputFocused
=
ref
(
false
);
const
isComposerActive
=
ref
(
false
);
const
isNearBottom
=
ref
(
true
);
const
isComposerCompact
=
computed
(()
=>
!
isNearBottom
.
value
&&
!
isInputFocused
.
value
);
const
isComposerCompact
=
computed
(()
=>
!
isNearBottom
.
value
&&
!
isInputFocused
.
value
&&
!
isComposerActive
.
value
);
const
agentControls
:
ToolItem
[]
=
[
{
label
:
'Agent 模式'
,
icon
:
Position
,
hasArrow
:
true
},
...
...
@@ -268,9 +184,27 @@ const handleInputBlur = () => {
nextTick
(
handleChatScroll
);
};
const
handleComposerActive
=
()
=>
{
isComposerActive
.
value
=
true
;
};
const
handleDocumentPointerDown
=
(
event
:
PointerEvent
)
=>
{
const
target
=
event
.
target
;
if
(
!
(
target
instanceof
Node
))
{
return
;
}
isComposerActive
.
value
=
Boolean
(
composerBoxRef
.
value
?.
contains
(
target
));
nextTick
(
handleChatScroll
);
};
onMounted
(()
=>
{
document
.
addEventListener
(
'pointerdown'
,
handleDocumentPointerDown
);
nextTick
(
handleChatScroll
);
});
onBeforeUnmount
(()
=>
{
document
.
removeEventListener
(
'pointerdown'
,
handleDocumentPointerDown
);
});
</
script
>
<
style
scoped
lang=
"scss"
>
...
...
@@ -289,12 +223,6 @@ onMounted(() => {
}
}
.hover
{
&
:hover
{
color
:
rgba
(
var
(
--
main-color
)
,
0
.8
);
}
}
.boxshaw
{
box-shadow
:
0
0
10px
#000000
09
;
...
...
@@ -384,7 +312,7 @@ onMounted(() => {
padding
:
0
8px
0
0
;
}
.chat-composer-wrap.is-compact
.composer-actions
>
.flex-items-center
:first-child
{
.chat-composer-wrap.is-compact
.composer-actions
>
.flex-items-center
:first-child
{
display
:
none
;
}
...
...
@@ -406,13 +334,9 @@ onMounted(() => {
.send-btn
{
opacity
:
0
.8
;
&
:hover
{
opacity
:
1
;
}
}
</
style
>
<
style
>
.xxx
{
min-width
:
120px
!important
;
}
</
style
>
apps/web-vue/src/views/resource/components/VideoItem.vue
View file @
310cf38d
...
...
@@ -28,7 +28,7 @@
</div>
</div>
<div
v-if=
"!isCheck"
class=
"absolute top-0 left-0 bg-main w-full h-full video-main"
>
<video
src=
"
../../../../../public
/aaa.mp4"
height=
"100%"
controls
autoplay
disablePictureInPicture
></video>
<video
src=
"/aaa.mp4"
height=
"100%"
controls
autoplay
disablePictureInPicture
></video>
<div
class=
"absolute top-10 right-10 flex gap-4"
>
<div
class=
"bg-333 w-24 h-24 round-6 pointer"
></div>
<div
class=
"bg-333 w-24 h-24 round-6 pointer"
></div>
...
...
@@ -79,4 +79,4 @@ const props = defineProps({
.round-bl
{
border-bottom-left-radius
:
6px
;
}
</
style
>
\ No newline at end of file
</
style
>
apps/web-vue/tsconfig.json
View file @
310cf38d
...
...
@@ -12,7 +12,7 @@
"lib"
:
[
"ES2020"
,
"DOM"
],
"skipLibCheck"
:
true
,
"baseUrl"
:
"."
,
"ignoreDeprecations"
:
"
6
.0"
,
"ignoreDeprecations"
:
"
5
.0"
,
"paths"
:
{
"@/*"
:
[
"src/*"
]
},
...
...
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