提交 3c16b961 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 添加数字人视频生成功能和相关组件

- 新增 AIDigitalHumanModal 组件用于生成数字人视频 - 引入数字人和配音数据 - 实现视频生成和插入功能 - 更新 API 代理路径为 /api/volcano_file - 优化相关样式和逻辑
上级 e793befe
import axios from 'axios'
import { message } from 'antd'
const appId = 'i-ri1jxut3edfmz'
const appKey = 'z3d69f164i698e3nph68'
/**
* 使用 Web Crypto API 实现 HMAC-SHA256
* 由于 HmacSHA256 通常是同步的,但 Web Crypto 是异步的,
* 我们需要调整拦截器以支持异步签名生成。
*/
const hmacSHA256 = async (data, key) => {
const encoder = new TextEncoder()
const keyData = encoder.encode(key)
const msgData = encoder.encode(data)
const cryptoKey = await window.crypto.subtle.importKey(
'raw',
keyData,
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
)
const signature = await window.crypto.subtle.sign('HMAC', cryptoKey, msgData)
return Array.from(new Uint8Array(signature))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
}
const buildSign = async () => {
// 生成当前时间 + 1小时后的时间戳
const time = new Date(new Date().getTime() + 60 * 60 * 3000).toISOString()
// 使用 Web Crypto API 生成签名
const signature = await hmacSHA256(appId + time, appKey)
// 返回Authorization头格式: appId/signature/time
return `${appId}/${signature}/${time}`
}
const request = axios.create({ baseURL: '/api/xiling' })
request.interceptors.request.use(async (config) => {
config.headers.Authorization = await buildSign()
return config
})
request.interceptors.response.use((response) => {
if (response.data.code == 0) {
return response.data
} else {
const errorMsg = response.data.message?.global || '请求失败'
message.error(errorMsg)
return Promise.reject(response)
}
}, (error) => {
message.error('网络请求错误')
return Promise.reject(error)
})
/**
* 文件上传
* https://cloud.baidu.com/doc/AI_DH/s/5m11r7clu
*/
export const upload = async (data, options = {}) => {
const response = await request.post('/api/digitalhuman/open/v1/file/upload', data, {
headers: { 'Content-Type': 'multipart/form-data' },
...options,
})
return response.result
}
// 获取数字人列表
export const getDigitalHumanList = async (params, options) => {
const { systemFigure, item = 'VIDEO-DH_2D', pageSize = 1000 } = params
const response = await request.get('/api/digitalhuman/open/v1/figure/query', {
params: { systemFigure, item, pageSize },
...options,
})
return response.result?.result || []
}
/**
* 提交生成式数字人形象定制任务
*/
export const submitGenerativeFigure = async (params, options = {}) => {
return request.post('/api/digitalhuman/open/v1/figure/generative/train', params, options)
}
/**
* 查询生成式数字人形象定制状态
*/
export const queryGenerativeFigure = async (params, options = {}) => {
return request.get('/api/digitalhuman/open/v1/figure/lite2d/query', { params, ...options })
}
/**
* 提交123数字人视频任务
*/
export const submitVideoFast = async (params, options = {}) => {
return request.post('/api/digitalhuman/open/v1/video/submit/fast', params, options)
}
/**
* 提交基础视频合成任务
*/
export const submitVideo = async (params, options = {}) => {
return request.post('/api/digitalhuman/open/v1/video/submit', params, options)
}
/**
* 查询视频任务状态
*/
export const getVideoTask = async (params, options = {}) => {
return request.get('/api/digitalhuman/open/v1/video/task', { params, ...options })
}
/**
* 提交高级视频合成任务
*/
export const submitVideoAdvanced = async (params, options = {}) => {
return request.post('/api/digitalhuman/open/v1/video/advanced/submit', params, options)
}
/**
* 查询高级视频任务状态
*/
export const getVideoAdvancedTask = async (params, options = {}) => {
return request.get('/api/digitalhuman/open/v1/video/advanced/task', { params, ...options })
}
@image-link-url: '/src/assets/images/editor/icon_link_editor.png';
.wrap-phone-privew {
.ant-modal {
.ant-modal-close {
inset-inline-end: 17px;
top: 0px;
right: 0;
}
.ant-modal-content {
background: url('@/assets/images/phone_6.7.png') no-repeat;
background-size: 100% 100%;
box-shadow: none;
height: 1003px;
border-radius: 48px;
padding: 36px 32px 34px 32px;
}
.priview-modal {
padding-top: 40px;
width: 430px;
height: 932px;
position: relative;
}
}
.phone-body {
flex: 1;
height: 100%;
overflow: hidden;
box-sizing: border-box;
overflow: hidden;
width: 430px;
border-radius: 30px;
}
.previee-container {
height: 100%;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
border-radius: 0 0 40px;
.chapter-head-title {
height: 44px;
line-height: 44px;
border-bottom: 1px solid #efefef;
h2 {
font-size: 18px;
text-align: left;
height: 44px;
white-space: nowrap;
text-overflow: ellipsis;
font-weight: 600;
overflow: hidden;
padding-right: 10px;
align-items: center;
&.tree {
display: flex;
justify-content: space-between;
.no {
display: block;
flex: 1;
max-width: calc(100% - 50px);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.menu {
flex: 0 0 40px;
display: block;
text-align: right;
}
}
.ant-btn {
padding-right: 0;
padding: 0 5px;
margin-left: 5px;
}
.ant-space {
width: 98%;
overflow: hidden;
.ant-space-item:nth-child(2) {
width: 98%;
overflow: hidden;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.in {
}
}
.no {
padding-left: 20px;
}
.menu {
cursor: pointer;
}
}
}
.preview-content-it {
flex: 1;
position: relative;
height: calc(100% - 45px);
overflow: hidden;
.preview-content-show {
overflow-x: hidden;
overflow-y: auto;
height: calc(100% - 85px);
}
.preview-page-number {
height: 40px;
line-height: 40px;
text-align: center;
font-size: 14px;
color: #999;
border-top: 1px solid #efefef;
background: #fff;
flex-shrink: 0;
}
* {
line-height: 1.5;
font-family: '黑体';
}
.preview-content-html {
padding-bottom: 50px;
.chapter-practice,
.chapter-expand,
.chapter-item-section,
.chapter-item-header,
.chapter-gallery-container {
margin-top: 15px;
margin-bottom: 15px;
&.chapter-expand-inline,
&.chapter-gallery-inline {
margin-top: 0;
margin-bottom: 0;
margin-top: -3px !important;
}
}
.tooltip-icon {
margin-top: -3px !important;
}
}
.table-container {
padding: 10px;
}
span {
vertical-align: middle;
}
table {
border-collapse: collapse;
margin-left: 10px;
margin-right: 10px;
width: calc(100% - 20px) !important;
}
table th {
font-weight: 700;
background-color: #f5f2f0;
}
table th,
table td {
border: 1px solid #ddd;
padding: 5px;
font-size: 18px;
}
div[data-w-e-type='video'] {
margin: 10px;
}
p[data-slate-node]:not(:empty) {
margin: 15px 10px;
}
p {
line-height: 1.5;
font-size: 18px;
font-family: '黑体';
margin: 15px 10px;
img {
height: auto;
max-width: 100%;
}
> img {
vertical-align: middle;
+ span {
vertical-align: middle;
padding-top: 4px;
line-height: 1.5;
}
}
}
// 预览题库
.chapter-practice {
margin: 0 10px;
position: relative;
cursor: pointer;
}
.practice-insert-topic {
padding: 10px 10px 30px;
.topic_style {
h3 {
font-size: 18px;
color: #333;
line-height: 20px;
margin-bottom: 10px;
}
}
.practice-item {
padding: 10px 0;
.topic-choose-title {
display: flex;
align-items: first baseline;
margin-bottom: 10px;
.index {
display: block;
flex: 0 0 18px;
line-height: 24px;
}
p {
display: inline;
margin: 0;
}
img {
max-height: 60px !important;
max-width: 100% !important;
width: auto !important;
height: auto !important;
vertical-align: middle;
}
}
.topic_choose-list {
.topic_choose-item {
padding: 3px 3px 3px 10px;
display: flex;
.choose-ico {
flex: 0 0 20px;
align-items: baseline;
padding-top: 2px;
.ico {
display: inline-block;
width: 18px;
height: 18px;
vertical-align: text-top;
background-size: cover;
background-repeat: no-repeat;
&.checkbox {
background-image: url('@/assets/images/editor/icon_checkbox_normal.png');
}
&.radio {
background-image: url('@/assets/images/editor/icon_radio_normal.png');
}
}
}
}
.topic_content {
margin-left: 5px;
display: flex;
align-items: first baseline;
flex: 1;
.correct {
flex: 0 0 18px;
display: block;
}
p {
margin: 0;
display: inline;
vertical-align: top;
img {
max-width: 50% !important;
height: auto;
}
}
}
}
}
}
pre {
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
line-height: 20px;
font-size: 14px;
padding: 10px;
color: #333;
background-color: #f5f2f0;
margin: 15px 0;
}
blockquote {
padding: 10px;
line-height: 20px;
font-size: 14px;
background-color: #f5f2f0;
border-left: 5px solid #b4d5ff;
margin: 15px 0;
}
a {
color: #aa1941;
&:link,
&:visited,
&:hover {
color: #aa1941;
}
span {
color: #aa1941 !important;
}
&::after {
content: '';
background-size: cover;
background-image: url(@image-link-url); // 使用 Less 变量
width: 14px;
height: 14px;
display: inline-block;
margin: 0 5px;
vertical-align: text-bottom;
}
}
h1 {
line-height: 40px;
font-size: 24px;
}
h2 {
line-height: 34px;
font-size: 20px;
}
h3 {
line-height: 28px;
font-size: 18px;
}
h4 {
line-height: 22px;
font-size: 16px;
}
h5 {
line-height: 18px;
font-size: 14px;
}
h6 {
line-height: 16px;
font-size: 12px;
}
video,
audio {
width: 100%;
}
.chapter-image-pic {
img {
width: 100%;
height: auto;
}
}
.w-e-image-container {
img {
&:not([style]) {
width: auto;
}
width: 100%;
}
}
// 画廊
.chapter-gallery-container {
cursor: pointer;
.chapter-gallery-item {
width: 50%;
padding: 10px;
box-sizing: border-box;
text-align: center;
&.one {
width: 100%;
}
p {
}
img {
height: auto;
width: 100%;
}
&.one {
img {
max-width: 100%;
height: auto;
width: 100%;
}
}
}
&.chapter-gallery-inline {
display: inline-block;
vertical-align: middle;
cursor: pointer;
}
}
.chapter-item-tooltip,
.chapter-item-link,
.chapter-gallery-inline,
.chapter-expand-inline {
text-indent: 0;
svg {
text-indent: 0;
}
}
.chapter-item-link {
display: inline !important;
}
// 画廊展示
.gallery-prview-container {
.gallery-img {
max-height: 300px;
height: 300px;
width: 100%;
text-align: center;
display: flex;
align-items: center;
position: relative;
text-align: center;
justify-content: center;
&.noData {
text-align: center;
padding-top: 40px;
color: #999;
font-size: 18px;
}
.opa {
position: absolute;
top: 50%;
background-repeat: no-repeat;
background-size: cover;
width: 32px;
height: 32px;
margin-top: -16px;
cursor: pointer;
&.prev {
left: 10px;
background-image: url('@/assets/images/editor/icon_preview_prev.png');
}
&.next {
right: 10px;
background-image: url('@/assets/images/editor/icon_preview_next.png');
}
}
img {
max-width: 100%;
max-height: 300px;
// width: 100%;
// height: auto;
}
}
.controll {
padding: 20px;
display: flex;
justify-content: space-between;
}
.steps {
padding: 0 10px;
text-align: right;
color: #999;
}
.title {
padding: 12px 10px;
font-size: 20px;
line-height: 28px;
margin-bottom: 5px;
border-bottom: 1px solid #f1f1f1;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
}
.desc {
padding: 0 10px;
font-size: 16px;
line-height: 28px;
color: #999;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
}
}
.chapter-expand {
margin: 0 10px;
}
// 扩展内容
.expand-content {
padding-bottom: 25px;
}
.img-preview {
text-align: center;
padding-top: 50px;
img {
width: 100%;
}
}
li,
dd,
dt,
blockquote {
font-size: 18px;
font-family: '黑体';
line-height: 1.5;
margin: 15px 0;
}
ol {
margin: 0 10px 0 28px;
li {
padding-left: 5px;
span {
vertical-align: initial;
}
}
}
ul {
margin: 0px 10px 0 30px;
li {
span {
vertical-align: initial;
}
}
}
.preview-content-other {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
background-color: #fff;
}
}
// 气泡
.tooltip {
position: absolute;
width: 70%;
max-width: 80%;
// min-width: 50%;
top: 10%;
left: 00%;
background-color: #fff;
border: 1px solid #d2d2d2;
border-radius: 5px;
// opacity: 0;
transition: 0.2s opacity linear;
z-index: -1;
box-shadow: 0 0 15px 1px rgba(122, 122, 122, 0.8);
.square {
width: 0px;
height: 0px;
position: absolute;
left: 0;
z-index: 10002;
&::before {
width: 0px;
height: 0px;
position: absolute;
border: 9px solid transparent;
content: '';
padding: 0;
}
&::after {
width: 0px;
height: 0px;
position: absolute;
content: '';
border: 10px solid transparent;
}
&.square_top {
top: 0px;
&::before {
top: -17px;
left: 3px;
border-color: transparent transparent #fff transparent;
z-index: 12;
}
&::after {
top: -20px;
left: 2px;
border-color: transparent transparent #d2d2d2 transparent;
z-index: 10;
}
}
&.square_bottom {
bottom: 0px;
&::before {
top: -1px;
left: -19px;
border-color: #fff transparent transparent transparent;
z-index: 12;
}
&::after {
top: 0px;
left: -20px;
border-color: #d2d2d2 transparent transparent transparent;
z-index: 10;
}
}
}
.tooltip-content-container {
padding: 5px;
}
.tooltip-content {
.content {
line-height: 22px;
min-height: 22px;
overflow-x: hidden;
overflow-y: auto;
max-height: 140px;
padding: 5px;
p {
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
margin: 0;
}
}
.content-opa {
border-top: 1px solid #ebebeb;
padding-top: 5px;
display: flex;
justify-content: flex-end;
align-items: center;
.c-link {
max-width: 60px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
height: 18px;
line-height: 18px;
}
a:link,
a:visited,
a:hover {
font-size: 14px;
display: inline-block;
margin-left: 10px;
color: #aa1941;
}
a {
img {
width: 18px;
height: 18px;
}
}
}
}
}
}
}
.priview-drawer-container {
height: 932px;
top: 42px;
left: 0px;
width: 430px;
border-radius: 15px 0 40px 40px;
overflow: hidden;
.priview-drawer-header {
padding: 10px;
}
.priview-drawer-body {
padding: 10px;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
.ant-tree .ant-tree-treenode {
position: relative;
// padding-right: 40px;
.ant-tree-node-content-wrapper {
overflow: hidden;
}
}
.tree-node-title {
display: inline-flex;
align-items: center;
width: 100%;
.tree-node-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tree-node-page {
// position: absolute;
// right: 0;
font-size: 12px;
color: #999;
// min-width: 32px;
text-align: right;
background: #f0f0f0;
border-radius: 4px;
padding: 0 6px;
line-height: 20px;
}
}
}
}
@image-link-url: '/src/assets/images/editor/icon_link_editor.png';
.wrap-phone-privew {
.ant-modal {
.ant-modal-close {
inset-inline-end: 17px;
top: 0px;
right: 0;
}
.ant-modal-content {
background: url('@/assets/images/phone_6.7.png') no-repeat;
background-size: 100% 100%;
box-shadow: none;
height: 1003px;
border-radius: 48px;
padding: 36px 32px 34px 32px;
}
.priview-modal {
padding-top: 40px;
width: 430px;
height: 932px;
position: relative;
}
}
.phone-body {
flex: 1;
height: 100%;
overflow: hidden;
box-sizing: border-box;
overflow: hidden;
width: 430px;
border-radius: 30px;
}
.previee-container {
height: 100%;
overflow: hidden;
position: relative;
display: flex;
flex-direction: column;
border-radius: 0 0 40px;
.chapter-head-title {
height: 44px;
line-height: 44px;
border-bottom: 1px solid #efefef;
h2 {
font-size: 18px;
text-align: left;
height: 44px;
white-space: nowrap;
text-overflow: ellipsis;
font-weight: 600;
overflow: hidden;
padding-right: 10px;
align-items: center;
&.tree {
display: flex;
justify-content: space-between;
.no {
display: block;
flex: 1;
max-width: calc(100% - 50px);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.menu {
flex: 0 0 40px;
display: block;
text-align: right;
}
}
.ant-btn {
padding-right: 0;
padding: 0 5px;
margin-left: 5px;
}
.ant-space {
width: 98%;
overflow: hidden;
.ant-space-item:nth-child(2) {
width: 98%;
overflow: hidden;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.no {
padding-left: 20px;
}
.menu {
cursor: pointer;
}
}
}
.preview-content-it {
flex: 1;
position: relative;
height: calc(100% - 45px);
overflow: hidden;
.preview-content-show {
overflow-x: hidden;
overflow-y: auto;
height: calc(100% - 85px);
}
.preview-page-number {
height: 40px;
line-height: 40px;
text-align: center;
font-size: 14px;
color: #999;
border-top: 1px solid #efefef;
background: #fff;
flex-shrink: 0;
}
* {
line-height: 1.5;
font-family: '黑体';
}
.preview-content-html {
padding-bottom: 50px;
.chapter-practice,
.chapter-expand,
.chapter-item-section,
.chapter-item-header,
.chapter-gallery-container {
margin-top: 15px;
margin-bottom: 15px;
&.chapter-expand-inline,
&.chapter-gallery-inline {
margin-top: 0;
margin-bottom: 0;
margin-top: -3px !important;
}
}
.tooltip-icon {
margin-top: -3px !important;
}
}
.table-container {
padding: 10px;
}
span {
vertical-align: middle;
}
table {
border-collapse: collapse;
margin-left: 10px;
margin-right: 10px;
width: calc(100% - 20px) !important;
}
table th {
font-weight: 700;
background-color: #f5f2f0;
}
table th,
table td {
border: 1px solid #ddd;
padding: 5px;
font-size: 18px;
}
div[data-w-e-type='video'] {
margin: 10px;
}
p[data-slate-node]:not(:empty) {
margin: 15px 10px;
}
p {
line-height: 1.5;
font-size: 18px;
font-family: '黑体';
margin: 15px 10px;
img {
height: auto;
max-width: 100%;
}
> img {
vertical-align: middle;
+ span {
vertical-align: middle;
padding-top: 4px;
line-height: 1.5;
}
}
}
// 插入图标(img + 文本节点)需要整体按行居中
p:has(> img[alt='icon-inline']) {
display: flex;
align-items: center;
gap: 6px;
}
p:has(> img[alt='icon-inline']) > img[alt='icon-inline'] {
flex: 0 0 auto;
}
// 预览题库
.chapter-practice {
margin: 0 10px;
position: relative;
cursor: pointer;
}
.practice-insert-topic {
padding: 10px 10px 30px;
.topic_style {
h3 {
font-size: 18px;
color: #333;
line-height: 20px;
margin-bottom: 10px;
}
}
.practice-item {
padding: 10px 0;
.topic-choose-title {
display: flex;
align-items: first baseline;
margin-bottom: 10px;
.index {
display: block;
flex: 0 0 18px;
line-height: 24px;
}
p {
display: inline;
margin: 0;
}
img {
max-height: 60px !important;
max-width: 100% !important;
width: auto !important;
height: auto !important;
vertical-align: middle;
}
}
.topic_choose-list {
.topic_choose-item {
padding: 3px 3px 3px 10px;
display: flex;
.choose-ico {
flex: 0 0 20px;
align-items: baseline;
padding-top: 2px;
.ico {
display: inline-block;
width: 18px;
height: 18px;
vertical-align: text-top;
background-size: cover;
background-repeat: no-repeat;
&.checkbox {
background-image: url('@/assets/images/editor/icon_checkbox_normal.png');
}
&.radio {
background-image: url('@/assets/images/editor/icon_radio_normal.png');
}
}
}
}
.topic_content {
margin-left: 5px;
display: flex;
align-items: first baseline;
flex: 1;
.correct {
flex: 0 0 18px;
display: block;
}
p {
margin: 0;
display: inline;
vertical-align: top;
img {
max-width: 50% !important;
height: auto;
}
}
}
}
}
}
pre {
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
line-height: 20px;
font-size: 14px;
padding: 10px;
color: #333;
background-color: #f5f2f0;
margin: 15px 0;
}
blockquote {
padding: 10px;
line-height: 20px;
font-size: 14px;
background-color: #f5f2f0;
border-left: 5px solid #b4d5ff;
margin: 15px 0;
}
a {
color: #aa1941;
&:link,
&:visited,
&:hover {
color: #aa1941;
}
span {
color: #aa1941 !important;
}
&::after {
content: '';
background-size: cover;
background-image: url(@image-link-url); // 使用 Less 变量
width: 14px;
height: 14px;
display: inline-block;
margin: 0 5px;
vertical-align: text-bottom;
}
}
h1 {
line-height: 40px;
font-size: 24px;
}
h2 {
line-height: 34px;
font-size: 20px;
}
h3 {
line-height: 28px;
font-size: 18px;
}
h4 {
line-height: 22px;
font-size: 16px;
}
h5 {
line-height: 18px;
font-size: 14px;
}
h6 {
line-height: 16px;
font-size: 12px;
}
video,
audio {
width: 100%;
}
.chapter-image-pic {
img {
width: 100%;
height: auto;
}
}
.w-e-image-container {
img {
&:not([style]) {
width: auto;
}
width: 100%;
}
}
// 画廊
.chapter-gallery-container {
cursor: pointer;
.chapter-gallery-item {
width: 50%;
padding: 10px;
box-sizing: border-box;
text-align: center;
&.one {
width: 100%;
}
img {
height: auto;
width: 100%;
}
&.one {
img {
max-width: 100%;
height: auto;
width: 100%;
}
}
}
&.chapter-gallery-inline {
display: inline-block;
vertical-align: middle;
cursor: pointer;
}
}
.chapter-item-tooltip,
.chapter-item-link,
.chapter-gallery-inline,
.chapter-expand-inline {
text-indent: 0;
svg {
text-indent: 0;
}
}
.chapter-item-link {
display: inline !important;
}
// 画廊展示
.gallery-prview-container {
.gallery-img {
max-height: 300px;
height: 300px;
width: 100%;
text-align: center;
display: flex;
align-items: center;
position: relative;
text-align: center;
justify-content: center;
&.noData {
text-align: center;
padding-top: 40px;
color: #999;
font-size: 18px;
}
.opa {
position: absolute;
top: 50%;
background-repeat: no-repeat;
background-size: cover;
width: 32px;
height: 32px;
margin-top: -16px;
cursor: pointer;
&.prev {
left: 10px;
background-image: url('@/assets/images/editor/icon_preview_prev.png');
}
&.next {
right: 10px;
background-image: url('@/assets/images/editor/icon_preview_next.png');
}
}
img {
max-width: 100%;
max-height: 300px;
// width: 100%;
// height: auto;
}
}
.controll {
padding: 20px;
display: flex;
justify-content: space-between;
}
.steps {
padding: 0 10px;
text-align: right;
color: #999;
}
.title {
padding: 12px 10px;
font-size: 20px;
line-height: 28px;
margin-bottom: 5px;
border-bottom: 1px solid #f1f1f1;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
}
.desc {
padding: 0 10px;
font-size: 16px;
line-height: 28px;
color: #999;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
}
}
.chapter-expand {
margin: 0 10px;
}
// 扩展内容
.expand-content {
padding-bottom: 25px;
}
.img-preview {
text-align: center;
padding-top: 50px;
img {
width: 100%;
}
}
li,
dd,
dt,
blockquote {
font-size: 18px;
font-family: '黑体';
line-height: 1.5;
margin: 15px 0;
}
ol {
margin: 0 10px 0 28px;
li {
padding-left: 5px;
span {
vertical-align: initial;
}
}
}
ul {
margin: 0px 10px 0 30px;
li {
span {
vertical-align: initial;
}
}
}
.preview-content-other {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
background-color: #fff;
}
}
// 气泡
.tooltip {
position: absolute;
width: 70%;
max-width: 80%;
// min-width: 50%;
top: 10%;
left: 00%;
background-color: #fff;
border: 1px solid #d2d2d2;
border-radius: 5px;
// opacity: 0;
transition: 0.2s opacity linear;
z-index: -1;
box-shadow: 0 0 15px 1px rgba(122, 122, 122, 0.8);
.square {
width: 0px;
height: 0px;
position: absolute;
left: 0;
z-index: 10002;
&::before {
width: 0px;
height: 0px;
position: absolute;
border: 9px solid transparent;
content: '';
padding: 0;
}
&::after {
width: 0px;
height: 0px;
position: absolute;
content: '';
border: 10px solid transparent;
}
&.square_top {
top: 0px;
&::before {
top: -17px;
left: 3px;
border-color: transparent transparent #fff transparent;
z-index: 12;
}
&::after {
top: -20px;
left: 2px;
border-color: transparent transparent #d2d2d2 transparent;
z-index: 10;
}
}
&.square_bottom {
bottom: 0px;
&::before {
top: -1px;
left: -19px;
border-color: #fff transparent transparent transparent;
z-index: 12;
}
&::after {
top: 0px;
left: -20px;
border-color: #d2d2d2 transparent transparent transparent;
z-index: 10;
}
}
}
.tooltip-content-container {
padding: 5px;
}
.tooltip-content {
.content {
line-height: 22px;
min-height: 22px;
overflow-x: hidden;
overflow-y: auto;
max-height: 140px;
padding: 5px;
p {
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-all;
margin: 0;
}
}
.content-opa {
border-top: 1px solid #ebebeb;
padding-top: 5px;
display: flex;
justify-content: flex-end;
align-items: center;
.c-link {
max-width: 60px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
height: 18px;
line-height: 18px;
}
a:link,
a:visited,
a:hover {
font-size: 14px;
display: inline-block;
margin-left: 10px;
color: #aa1941;
}
a {
img {
width: 18px;
height: 18px;
}
}
}
}
}
}
}
.priview-drawer-container {
height: 932px;
top: 42px;
left: 0px;
width: 430px;
border-radius: 15px 0 40px 40px;
overflow: hidden;
.priview-drawer-header {
padding: 10px;
}
.priview-drawer-body {
padding: 10px;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
.ant-tree .ant-tree-treenode {
position: relative;
// padding-right: 40px;
.ant-tree-node-content-wrapper {
overflow: hidden;
}
}
.tree-node-title {
display: inline-flex;
align-items: center;
width: 100%;
.tree-node-name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tree-node-page {
// position: absolute;
// right: 0;
font-size: 12px;
color: #999;
// min-width: 32px;
text-align: right;
background: #f0f0f0;
border-radius: 4px;
padding: 0 6px;
line-height: 20px;
}
}
}
}
@image-url: '/src/assets/images/editor/icon_link_editor.png';
.wangeditor-customer-container {
display: flex;
justify-content: space-between;
height: 100%;
.editor-content-container {
height: 100%;
overflow-y: hidden;
flex: 3;
padding: 10px;
border: 1px solid #e5e5e5;
border-radius: 6px;
.w-e-scroll {
line-height: 1.5;
font-family: '黑体';
}
.w-e-scroll div[role='textarea'] > div {
margin: 15px 0 !important;
font-size: 18px;
line-height: 1.65;
font-family: '黑体';
span {
font-size: 18px;
}
}
.w-e-text-container [data-slate-editor] h1.chapter-item-header,
.w-e-text-container [data-slate-editor] h2.chapter-item-section {
margin: 0 !important;
}
.ant-spin-nested-loading,
.ant-spin-container,
.w-e-text-container {
height: 100%;
}
.ant-spin-container > div,
#w-e-textarea-1 {
min-height: 99%;
}
.w-e-text-container [data-slate-editor] p:not(:empty) {
margin: 15px 0;
span[data-slate-node='text'] {
padding-top: 0px;
}
}
.w-e-text-container [data-slate-editor] p {
line-height: 1.5;
font-size: 18px;
font-family: '黑体';
margin-block: 0;
span[data-slate-node='text'],
span[data-slate-node='element'] {
vertical-align: middle;
height: 100%;
line-height: 100%;
}
span[data-slate-node='text'] {
padding-top: 3px;
}
}
.w-e-text-container [data-slate-editor] .w-e-image-container {
vertical-align: middle;
display: inline-block;
+ span {
padding-top: 8px;
}
}
p {
span[data-slate-zero-width] {
display: inline-block;
width: 2px;
}
> span:last-child {
span[data-slate-zero-width] {
display: inline-block;
width: 2px;
// &:not(:empty) {
// display: inline;
// }
}
}
> span:first-child {
span[data-slate-zero-width] {
display: inline;
}
}
}
.w-e-text-container [data-slate-editor] a,
.w-e-text-container [data-slate-editor] .chapter-item-link {
position: relative;
&:link,
&:hover,
&:visited {
color: #aa1941;
span {
color: #aa1941 !important;
}
}
&::after {
content: '';
background-size: cover;
background-image: url(@image-url); // 使用 Less 变量
width: 14px;
height: 14px;
display: inline-block;
margin: 0 5px;
vertical-align: text-bottom;
}
img {
max-width: 14px;
max-height: 14px;
min-width: 14px;
min-height: 14px;
width: 14px;
height: 14px;
}
}
.w-e-text-container [data-slate-editor] .chapter-item-link {
&::after {
content: '';
background-size: cover;
background-image: none; // 使用 Less 变量
width: 0px;
height: 0px;
display: inline-block;
margin: 0;
vertical-align: text-bottom;
}
}
video {
max-width: 100%;
}
li,
dd,
dt,
blockquote {
font-size: 18px;
font-family: '黑体';
line-height: 1.5;
margin: 15px 0;
}
table {
td,
th {
font-size: 18px;
line-height: 1.5;
}
}
.w-e-text-container [data-slate-editor] .chapter-gallery-inline {
margin: 0 4px;
height: 18px;
cursor: pointer;
img {
min-width: 18px;
min-height: 18px;
}
}
.title-head {
text-align: center;
font-size: 18px;
color: #333333;
line-height: 40px;
display: flex;
justify-content: space-between;
height: 52px;
padding-bottom: 10px;
.ant-divider {
margin-top: 0;
}
.left {
flex: 1;
width: calc(100% - 480px);
h4 {
font-size: 16px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: left;
padding-right: 10px;
}
}
.right {
display: flex;
right: 0;
top: 0;
align-items: flex-start;
flex: 0 0 480px;
justify-content: flex-end;
.save-time {
display: flex;
flex-direction: column;
justify-content: flex-end;
.img {
color: #ab1941;
font-size: 12px;
display: flex;
align-items: center;
height: 22px;
line-height: 22px;
text-align: right;
display: block;
img {
width: 20px;
margin-right: 8px;
vertical-align: middle;
}
}
.time {
font-size: 12px;
color: #999;
line-height: 14px;
}
}
.view {
&:disabled {
color: #666666 !important;
}
}
.history {
color: #666666;
&:hover {
.ant-btn-icon {
color: #ab1941;
}
}
}
}
}
}
.editor-toolbar-container {
margin: 10px;
.w-e-bar {
padding: 0;
svg {
width: 18px;
height: 18px;
}
}
.w-e-bar-divider {
margin: 0;
padding-top: 10px;
width: 100%;
height: auto;
background: #fafafa;
font-size: 14px;
font-weight: 600;
line-height: 40px;
}
.w-e-bar-item {
height: auto;
button {
svg:nth-child(2) {
display: none;
}
&.has-title {
width: 100%;
height: 100%;
padding: 10px;
flex-direction: column;
align-items: center;
.title {
margin: 5px 0 0 0;
}
}
}
}
}
.ant-spin-nested-loading,
.ant-spin-container {
width: 100%;
}
.toolbar-customer {
flex: 1;
}
.menu-tabs-key {
flex: 0 0 306px;
background: #fafafa;
border: 1px solid #e5e5e5;
min-width: 300px;
margin-left: 10px;
border-radius: 6px;
height: 100%;
overflow: hidden;
.tabs {
height: 50px;
display: flex;
width: 100%;
justify-content: center;
.tabs-item {
flex: 1;
text-align: center;
border-bottom: 1px solid #e5e5e5;
color: #333;
font-size: 16px;
line-height: 50px;
box-sizing: border-box;
cursor: pointer;
position: relative;
&.active {
span {
width: 60px;
display: block;
height: 2px;
background-color: #ab1941;
position: absolute;
left: 50%;
margin-left: -30px;
bottom: -1px;
}
}
}
}
.menu-tabs-content {
overflow-y: auto;
overflow-x: hidden;
max-height: calc(100% - 50px);
.styletem {
padding: 0 11px;
p {
font-weight: 600;
color: #333333;
line-height: 22px;
font-size: 16px;
margin-top: 24px;
margin-bottom: 34px;
}
ul {
padding-left: 0;
}
ul li {
display: flex;
border-bottom: 1px solid #cccccc;
padding: 10px 0;
justify-content: space-between;
align-items: center;
.left {
flex: 1;
display: flex;
}
img {
width: 23px;
margin-right: 20px;
vertical-align: middle;
}
.color {
display: inline-block;
width: 22px;
height: 22px;
border-radius: 3px;
&.color1 {
background-color: #ab1941;
}
&.color2 {
background-color: #2970f6;
}
&.color3 {
background-color: #2ad882;
}
&.color4 {
background-color: #eb3351;
}
}
.type {
font-size: 14px;
color: #333333;
line-height: 20px;
vertical-align: middle;
}
b {
font-weight: 600;
font-size: 14px !important;
margin-left: 15px;
}
.use {
flex: 0 0 60px;
text-align: right;
color: #1672ec;
}
}
}
.toolbox-parent {
position: relative;
.custom-bar-box-input {
position: absolute;
z-index: 101;
height: 48px;
padding: 10px 15px;
width: 298px;
top: 165px;
box-sizing: border-box;
.box {
justify-content: space-around;
display: flex;
width: 268px;
}
.customer-box-input-item {
box-sizing: border-box;
text-align: center;
width: 60px;
p {
width: 60px;
text-align: center;
}
.ant-input-number-outlined {
border: none;
outline: none;
width: 60px;
&:hover,
&:focus {
border: none;
outline: none;
}
}
input {
width: 60px;
color: #333;
text-align: center;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 30px;
line-height: 30px;
background-color: #fff;
&:hover,
&:focus {
border-color: #b83956;
}
}
.text {
color: #999;
font-size: 12px;
margin-top: 4px;
}
}
}
}
}
}
}
/*
['bold', 'underline', 'italic', 'through', 'code', 'sub', 'sup', 'clearStyle', 'color', 'bgColor', 'fontSize', 'fontFamily', 'indent', 'delIndent', 'justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify', 'lineHeight', 'insertImage', 'deleteImage', 'editImage', 'viewImageLink', 'imageWidth30', 'imageWidth50', 'imageWidth100', 'divider', 'emotion', 'insertLink', 'editLink', 'unLink', 'viewLink', 'codeBlock', 'blockquote', 'headerSelect', 'header1', 'header2', 'header3', 'header4', 'header5', 'todo', 'redo', 'undo', 'fullScreen', 'enter', 'bulletedList', 'numberedList', 'insertTable', 'deleteTable', 'insertTableRow', 'deleteTableRow', 'insertTableCol', 'deleteTableCol', 'tableHeader', 'tableFullWidth', 'insertVideo', 'uploadVideo', 'editVideoSize', 'uploadImage', 'codeSelectLang']
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'deleteImage', 'editImage', 'viewImageLink', 'imageWidth30', 'imageWidth50', 'imageWidth100', '', '', '', 'editLink', 'unLink', 'viewLink', '', '', 'headerSelect', 'header1', 'header2', 'header3', 'header4', 'header5', '', '', '', '', '', '', '', '', 'deleteTable', 'insertTableRow', 'deleteTableRow', 'insertTableCol', 'deleteTableCol', 'tableHeader', 'tableFullWidth', '', 'codeSelectLang', 'editVideoSize', '', '']
*/
@image-url: '/src/assets/images/editor/icon_link_editor.png';
.wangeditor-customer-container {
display: flex;
justify-content: space-between;
height: 100%;
.editor-content-container {
height: 100%;
overflow-y: hidden;
flex: 3;
padding: 10px;
border: 1px solid #e5e5e5;
border-radius: 6px;
.w-e-scroll {
line-height: 1.5;
font-family: '黑体';
}
.w-e-scroll div[role='textarea'] > div {
margin: 15px 0 !important;
font-size: 18px;
line-height: 1.65;
font-family: '黑体';
span {
font-size: 18px;
}
}
.w-e-text-container [data-slate-editor] h1.chapter-item-header,
.w-e-text-container [data-slate-editor] h2.chapter-item-section {
margin: 0 !important;
}
.ant-spin-nested-loading,
.ant-spin-container,
.w-e-text-container {
height: 100%;
}
.ant-spin-container > div,
#w-e-textarea-1 {
min-height: 99%;
}
.w-e-text-container [data-slate-editor] p:not(:empty) {
margin: 15px 0;
span[data-slate-node='text'] {
padding-top: 0px;
}
}
.w-e-text-container [data-slate-editor] p {
line-height: 1.5;
font-size: 18px;
font-family: '黑体';
margin-block: 0;
span[data-slate-node='text'],
span[data-slate-node='element'] {
vertical-align: middle;
height: 100%;
line-height: 100%;
}
span[data-slate-node='text'] {
padding-top: 3px;
}
}
.w-e-text-container [data-slate-editor] .w-e-image-container {
vertical-align: middle;
display: inline-block;
+ span {
padding-top: 8px;
}
}
.w-e-text-container [data-slate-editor] img[alt='icon-inline'] {
vertical-align: middle;
display: inline-block;
}
// 插入图标后(图片节点 + 文本节点)强制整行居中
.w-e-text-container [data-slate-editor] p:has(.w-e-image-container img[alt='icon-inline']) {
display: flex;
align-items: center;
gap: 6px;
}
.w-e-text-container [data-slate-editor] p:has(.w-e-image-container img[alt='icon-inline'])
.w-e-image-container {
flex: 0 0 auto;
}
.w-e-text-container [data-slate-editor] p:has(.w-e-image-container img[alt='icon-inline'])
.w-e-image-container
+ span {
padding-top: 0;
}
p {
span[data-slate-zero-width] {
display: inline-block;
width: 2px;
}
> span:last-child {
span[data-slate-zero-width] {
display: inline-block;
width: 2px;
// &:not(:empty) {
// display: inline;
// }
}
}
> span:first-child {
span[data-slate-zero-width] {
display: inline;
}
}
}
.w-e-text-container [data-slate-editor] a,
.w-e-text-container [data-slate-editor] .chapter-item-link {
position: relative;
&:link,
&:hover,
&:visited {
color: #aa1941;
span {
color: #aa1941 !important;
}
}
&::after {
content: '';
background-size: cover;
background-image: url(@image-url); // 使用 Less 变量
width: 14px;
height: 14px;
display: inline-block;
margin: 0 5px;
vertical-align: text-bottom;
}
img {
max-width: 14px;
max-height: 14px;
min-width: 14px;
min-height: 14px;
width: 14px;
height: 14px;
}
}
.w-e-text-container [data-slate-editor] .chapter-item-link {
&::after {
content: '';
background-size: cover;
background-image: none; // 使用 Less 变量
width: 0px;
height: 0px;
display: inline-block;
margin: 0;
vertical-align: text-bottom;
}
}
video {
max-width: 100%;
}
li,
dd,
dt,
blockquote {
font-size: 18px;
font-family: '黑体';
line-height: 1.5;
margin: 15px 0;
}
table {
td,
th {
font-size: 18px;
line-height: 1.5;
}
}
.w-e-text-container [data-slate-editor] .chapter-gallery-inline {
margin: 0 4px;
height: 18px;
cursor: pointer;
img {
min-width: 18px;
min-height: 18px;
}
}
.title-head {
text-align: center;
font-size: 18px;
color: #333333;
line-height: 40px;
display: flex;
justify-content: space-between;
height: 52px;
padding-bottom: 10px;
.ant-divider {
margin-top: 0;
}
.left {
flex: 1;
width: calc(100% - 480px);
h4 {
font-size: 16px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: left;
padding-right: 10px;
}
}
.right {
display: flex;
right: 0;
top: 0;
align-items: flex-start;
flex: 0 0 480px;
justify-content: flex-end;
.save-time {
display: flex;
flex-direction: column;
justify-content: flex-end;
.img {
color: #ab1941;
font-size: 12px;
display: flex;
align-items: center;
height: 22px;
line-height: 22px;
text-align: right;
display: block;
img {
width: 20px;
margin-right: 8px;
vertical-align: middle;
}
}
.time {
font-size: 12px;
color: #999;
line-height: 14px;
}
}
.view {
&:disabled {
color: #666666 !important;
}
}
.history {
color: #666666;
&:hover {
.ant-btn-icon {
color: #ab1941;
}
}
}
}
}
}
.editor-toolbar-container {
margin: 10px;
.w-e-bar {
padding: 0;
svg {
width: 18px;
height: 18px;
}
}
.w-e-bar-divider {
margin: 0;
padding-top: 10px;
width: 100%;
height: auto;
background: #fafafa;
font-size: 14px;
font-weight: 600;
line-height: 40px;
}
.w-e-bar-item {
height: auto;
button {
svg:nth-child(2) {
display: none;
}
&.has-title {
width: 100%;
height: 100%;
padding: 10px;
flex-direction: column;
align-items: center;
.title {
margin: 5px 0 0 0;
}
}
}
}
}
.ant-spin-nested-loading,
.ant-spin-container {
width: 100%;
}
.toolbar-customer {
flex: 1;
}
.menu-tabs-key {
flex: 0 0 306px;
background: #fafafa;
border: 1px solid #e5e5e5;
min-width: 300px;
margin-left: 10px;
border-radius: 6px;
height: 100%;
overflow: hidden;
.tabs {
height: 50px;
display: flex;
width: 100%;
justify-content: center;
.tabs-item {
flex: 1;
text-align: center;
border-bottom: 1px solid #e5e5e5;
color: #333;
font-size: 16px;
line-height: 50px;
box-sizing: border-box;
cursor: pointer;
position: relative;
&.active {
span {
width: 60px;
display: block;
height: 2px;
background-color: #ab1941;
position: absolute;
left: 50%;
margin-left: -30px;
bottom: -1px;
}
}
}
}
.menu-tabs-content {
overflow-y: auto;
overflow-x: hidden;
max-height: calc(100% - 50px);
.styletem {
padding: 0 11px;
p {
font-weight: 600;
color: #333333;
line-height: 22px;
font-size: 16px;
margin-top: 24px;
margin-bottom: 34px;
}
ul {
padding-left: 0;
}
ul li {
display: flex;
border-bottom: 1px solid #cccccc;
padding: 10px 0;
justify-content: space-between;
align-items: center;
.left {
flex: 1;
display: flex;
}
img {
width: 23px;
margin-right: 20px;
vertical-align: middle;
}
.color {
display: inline-block;
width: 22px;
height: 22px;
border-radius: 3px;
&.color1 {
background-color: #ab1941;
}
&.color2 {
background-color: #2970f6;
}
&.color3 {
background-color: #2ad882;
}
&.color4 {
background-color: #eb3351;
}
}
.type {
font-size: 14px;
color: #333333;
line-height: 20px;
vertical-align: middle;
}
b {
font-weight: 600;
font-size: 14px !important;
margin-left: 15px;
}
.use {
flex: 0 0 60px;
text-align: right;
color: #1672ec;
}
}
}
.toolbox-parent {
position: relative;
.custom-bar-box-input {
position: absolute;
z-index: 101;
height: 48px;
padding: 10px 15px;
width: 298px;
top: 165px;
box-sizing: border-box;
.box {
justify-content: space-around;
display: flex;
width: 268px;
}
.customer-box-input-item {
box-sizing: border-box;
text-align: center;
width: 60px;
p {
width: 60px;
text-align: center;
}
.ant-input-number-outlined {
border: none;
outline: none;
width: 60px;
&:hover,
&:focus {
border: none;
outline: none;
}
}
input {
width: 60px;
color: #333;
text-align: center;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 30px;
line-height: 30px;
background-color: #fff;
&:hover,
&:focus {
border-color: #b83956;
}
}
.text {
color: #999;
font-size: 12px;
margin-top: 4px;
}
}
}
}
}
}
}
/*
['bold', 'underline', 'italic', 'through', 'code', 'sub', 'sup', 'clearStyle', 'color', 'bgColor', 'fontSize', 'fontFamily', 'indent', 'delIndent', 'justifyLeft', 'justifyRight', 'justifyCenter', 'justifyJustify', 'lineHeight', 'insertImage', 'deleteImage', 'editImage', 'viewImageLink', 'imageWidth30', 'imageWidth50', 'imageWidth100', 'divider', 'emotion', 'insertLink', 'editLink', 'unLink', 'viewLink', 'codeBlock', 'blockquote', 'headerSelect', 'header1', 'header2', 'header3', 'header4', 'header5', 'todo', 'redo', 'undo', 'fullScreen', 'enter', 'bulletedList', 'numberedList', 'insertTable', 'deleteTable', 'insertTableRow', 'deleteTableRow', 'insertTableCol', 'deleteTableCol', 'tableHeader', 'tableFullWidth', 'insertVideo', 'uploadVideo', 'editVideoSize', 'uploadImage', 'codeSelectLang']
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'deleteImage', 'editImage', 'viewImageLink', 'imageWidth30', 'imageWidth50', 'imageWidth100', '', '', '', 'editLink', 'unLink', 'viewLink', '', '', 'headerSelect', 'header1', 'header2', 'header3', 'header4', 'header5', '', '', '', '', '', '', '', '', 'deleteTable', 'insertTableRow', 'deleteTableRow', 'insertTableCol', 'deleteTableCol', 'tableHeader', 'tableFullWidth', '', 'codeSelectLang', 'editVideoSize', '', '']
*/
// Extend menu
class AIDigitalHuman {
constructor() {
this.title = '数字人'
this.iconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 28 28"><path fill="currentColor" d="M12 5.5a2 2 0 0 0 1.491 1.935c.337.053.68.053 1.018 0A2 2 0 1 0 12 5.5m-1.337 1.058a3.5 3.5 0 1 1 6.675 0l4.419-1.436a2.477 2.477 0 1 1 1.53 4.712L18 11.552v3.822c0 .16.03.32.091.468l2.728 6.752a2.477 2.477 0 0 1-4.594 1.856l-2.243-5.553l-2.232 5.56a2.46 2.46 0 0 1-3.21 1.362a2.477 2.477 0 0 1-1.364-3.215l2.734-6.812q.09-.224.09-.466v-3.774L4.712 9.834a2.477 2.477 0 0 1 1.531-4.712zm2.518 2.346a5 5 0 0 1-.649-.162L5.78 6.548a.977.977 0 0 0-.604 1.859l5.46 1.774c.515.168.864.648.864 1.189v3.957c0 .35-.067.698-.198 1.024l-2.734 6.811a.977.977 0 0 0 .538 1.267a.96.96 0 0 0 1.252-.531l2.463-6.136c.42-1.045 1.897-1.047 2.319-.003l2.476 6.129a.977.977 0 1 0 1.812-.732L16.7 16.404a2.8 2.8 0 0 1-.2-1.03V11.37c0-.541.349-1.021.864-1.189l5.46-1.774a.977.977 0 1 0-.604-1.859l-6.752 2.194q-.32.104-.649.162a3.5 3.5 0 0 1-1.639 0"/></svg>`
this.tag = 'button'
}
getValue() {
return 'hello, 音频'
}
isActive() {
return false
}
isDisabled() {
return true
}
exec() {
return
}
}
export default {
key: 'AIDigitalHuman',
factory() {
return new AIDigitalHuman()
}
}
import BaseModalMenu from './common/BaseModalMenu'
import AIDigitalHumanModal from './common/AIDigitalHumanModal'
class AIDigitalHuman extends BaseModalMenu {
constructor() {
super()
this.title = '数字人'
this.iconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 28 28"><path fill="currentColor" d="M12 5.5a2 2 0 0 0 1.491 1.935c.337.053.68.053 1.018 0A2 2 0 1 0 12 5.5m-1.337 1.058a3.5 3.5 0 1 1 6.675 0l4.419-1.436a2.477 2.477 0 1 1 1.53 4.712L18 11.552v3.822c0 .16.03.32.091.468l2.728 6.752a2.477 2.477 0 0 1-4.594 1.856l-2.243-5.553l-2.232 5.56a2.46 2.46 0 0 1-3.21 1.362a2.477 2.477 0 0 1-1.364-3.215l2.734-6.812q.09-.224.09-.466v-3.774L4.712 9.834a2.477 2.477 0 0 1 1.531-4.712zm2.518 2.346a5 5 0 0 1-.649-.162L5.78 6.548a.977.977 0 0 0-.604 1.859l5.46 1.774c.515.168.864.648.864 1.189v3.957c0 .35-.067.698-.198 1.024l-2.734 6.811a.977.977 0 0 0 .538 1.267a.96.96 0 0 0 1.252-.531l2.463-6.136c.42-1.045 1.897-1.047 2.319-.003l2.476 6.129a.977.977 0 1 0 1.812-.732L16.7 16.404a2.8 2.8 0 0 1-.2-1.03V11.37c0-.541.349-1.021.864-1.189l5.46-1.774a.977.977 0 1 0-.604-1.859l-6.752 2.194q-.32.104-.649.162a3.5 3.5 0 0 1-1.639 0"/></svg>`
}
getValue(editor) {
return <AIDigitalHumanModal key={Date.now()} editor={editor}></AIDigitalHumanModal>
}
}
export default {
key: 'AIDigitalHuman',
factory() {
return new AIDigitalHuman()
},
}
import { useState, useRef, useEffect } from 'react'
import { Modal, Input, Button, message, Spin, Row, Col, Space, Divider, Checkbox, Tabs, Empty } from 'antd'
import {
VideoCameraOutlined,
CheckOutlined,
DownloadOutlined,
PlayCircleOutlined,
PauseCircleOutlined,
UserOutlined,
} from '@ant-design/icons'
import { SlateTransforms } from '@wangeditor/editor'
import { saveAs } from 'file-saver'
import { uploadFileByUrl } from '@/utils/oss'
import useDigitalHuman from '@/hooks/useDigitalHuman'
import './AIDigitalHumanModal.less'
const { TextArea } = Input
export default function AIDigitalHumanModal(props) {
const { editor } = props
const [isModalOpen, setIsModalOpen] = useState(true)
// 核心选择状态
const [selectedFigure, setSelectedFigure] = useState(null)
const [selectedVoice, setSelectedVoice] = useState(null)
// 已移除字幕、透明背景和背景图片 URL 相关配置
const [textValue, setTextValue] = useState('')
const [playingVoiceId, setPlayingVoiceId] = useState(null)
const { figures, femaleVoices, maleVoices, loadingFigures, isGenerating, generatedVideo, generateVideo } =
useDigitalHuman()
// 初始化默认选择
useEffect(() => {
if (!selectedFigure && figures.length > 0) {
setSelectedFigure(figures[0])
}
if (!selectedVoice && femaleVoices.length > 0) {
setSelectedVoice(femaleVoices[0])
}
}, [figures, femaleVoices, selectedFigure, selectedVoice])
const handleVoicePlay = (voice) => {
if (playingVoiceId === voice.id) {
audioRef.current.pause()
setPlayingVoiceId(null)
} else {
audioRef.current.src = voice.previewUrl
audioRef.current.play()
setPlayingVoiceId(voice.id)
audioRef.current.onended = () => setPlayingVoiceId(null)
}
}
const handleGenerate = async () => {
if (!selectedFigure) return message.warning('请选择数字人形象')
if (!textValue.trim()) return message.warning('请输入播报文本')
if (!selectedVoice) return message.warning('请选择音色')
try {
await generateVideo({
figureId: selectedFigure.id,
figureName: selectedFigure.name,
driveType: 'TEXT',
text: textValue.trim(),
voiceId: selectedVoice?.id,
width: 720,
height: 1280,
})
} catch (error) {}
}
const handleInsert = async () => {
if (!generatedVideo) return
try {
message.loading({ content: '处理中...', key: 'inserting' })
const ossUrl = await uploadFileByUrl(generatedVideo.url)
if (editor) {
editor.restoreSelection()
const nodes = [
{ type: 'video', src: ossUrl, children: [{ text: '' }] },
{
type: 'paragraph',
textAlign: 'center',
fontSize: '14px',
children: [{ text: `${generatedVideo.figureName} - AI播报` }],
},
]
SlateTransforms.insertNodes(editor, nodes)
message.success({ content: '已插入文章', key: 'inserting' })
setIsModalOpen(false)
}
} catch (error) {
message.error({ content: '插入失败', key: 'inserting' })
}
}
return (
<Modal
title="数字人 AI 视频生成"
open={isModalOpen}
onCancel={() => setIsModalOpen(false)}
footer={null}
width={1000}
centered
destroyOnClose>
<div className="ai-digital-human">
<Row gutter={[24, 16]}>
<Col span={24}>
{/* 形象选择 */}
<div className="section-box">
<div className="section-title">1. 选择数字人形象</div>
{loadingFigures && figures.length === 0 ? (
<div style={{ textAlign: 'center', padding: '20px' }}>
<Spin tip="同步云端形象中..." />
</div>
) : figures.length > 0 ? (
<div className="figure-list">
{figures.map((fig) => (
<div
key={fig.id}
className={`figure-item ${selectedFigure?.id === fig.id ? 'active' : ''}`}
onClick={() => setSelectedFigure(fig)}>
<div className="avatar-wrapper">
{fig.avatar ? (
<img
src={fig.avatar}
className="figure-avatar"
onError={(e) => {
e.target.style.display = 'none'
e.target.nextSibling.style.display = 'flex'
}}
/>
) : null}
<div className="avatar-fallback" style={{ display: fig.avatar ? 'none' : 'flex' }}>
<UserOutlined />
</div>
</div>
<div className="figure-name">{fig.name}</div>
</div>
))}
</div>
) : (
<Empty description="暂无可用形象" />
)}
</div>
{/* 驱动配置 */}
<div className="section-box">
<div className="section-title">2. 配置播报内容与音色</div>
<div className="drive-content">
<TextArea
placeholder="请输入要播报的文本内容..."
rows={4}
value={textValue}
onChange={(e) => setTextValue(e.target.value)}
style={{ marginBottom: 12 }}
/>
<div className="voice-selection">
<div className="sub-title">精选配音:</div>
<Tabs
size="small"
items={[
{
key: 'female',
label: '女声',
children: (
<div className="voice-grid">
{femaleVoices.map((v) => (
<div
key={v.id}
className={`voice-item ${selectedVoice?.id === v.id ? 'active' : ''}`}
onClick={() => setSelectedVoice(v)}>
<div className="v-info">
<span className="v-name">{v.name}</span>
<span className="v-style">{v.style}</span>
</div>
<Button
type="text"
shape="circle"
icon={playingVoiceId === v.id ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
onClick={(e) => {
e.stopPropagation()
handleVoicePlay(v)
}}
/>
</div>
))}
</div>
),
},
{
key: 'male',
label: '男声',
children: (
<div className="voice-grid">
{maleVoices.map((v) => (
<div
key={v.id}
className={`voice-item ${selectedVoice?.id === v.id ? 'active' : ''}`}
onClick={() => setSelectedVoice(v)}>
<div className="v-info">
<span className="v-name">{v.name}</span>
<span className="v-style">{v.style}</span>
</div>
<Button
type="text"
shape="circle"
icon={playingVoiceId === v.id ? <PauseCircleOutlined /> : <PlayCircleOutlined />}
onClick={(e) => {
e.stopPropagation()
handleVoicePlay(v)
}}
/>
</div>
))}
</div>
),
},
]}
/>
</div>
</div>
{/* 已彻底移除背景图片 URL 相关输入 */}
</div>
</Col>
<Col span={24}>
<div className="preview-panel">
<div className="section-title">生成状态</div>
<div className="results-container">
{isGenerating ? (
<div className="generating-box">
<Spin size="large" />
<div className="gen-text">视频正在录制中...</div>
<div className="gen-hint">预计 1 分钟左右完成</div>
</div>
) : generatedVideo ? (
<div className="final-video-box">
<video src={generatedVideo.url} controls autoPlay />
<div className="video-actions">
<Button
block
size="large"
icon={<DownloadOutlined />}
onClick={() => saveAs(generatedVideo.url, 'ai_video.mp4')}>
下载视频
</Button>
<Button block type="primary" size="large" icon={<CheckOutlined />} onClick={handleInsert}>
插入编辑器
</Button>
</div>
</div>
) : (
<div className="empty-preview">
<VideoCameraOutlined style={{ fontSize: 64, opacity: 0.1 }} />
<p>等待配置完成后点击开始</p>
</div>
)}
</div>
<Divider style={{ margin: '12px 0' }} />
<Button
type="primary"
size="large"
block
icon={<VideoCameraOutlined />}
onClick={handleGenerate}
loading={isGenerating}
className="submit-btn">
开始生成 AI 视频
</Button>
</div>
</Col>
</Row>
</div>
</Modal>
)
}
.ai-digital-human {
.section-box {
margin-bottom: 14px;
padding: 12px;
background: #fff;
border-radius: 8px;
border: 1px solid #f0f0f0;
.section-title {
font-size: 15px;
font-weight: 600;
color: #333;
margin-bottom: 10px;
border-left: 4px solid #ab1941;
padding-left: 10px;
}
}
.figure-list {
display: flex;
overflow-x: auto;
gap: 12px;
padding: 6px 0;
&::-webkit-scrollbar {
height: 6px;
}
&::-webkit-scrollbar-thumb {
background: #e8e8e8;
border-radius: 3px;
}
.figure-item {
flex: 0 0 92px;
cursor: pointer;
text-align: center;
padding: 8px;
border: 2px solid transparent;
border-radius: 12px;
transition: all 0.3s;
&.active {
border-color: #ab1941;
background: rgba(171, 25, 65, 0.08);
}
.avatar-wrapper {
width: 72px;
height: 72px;
margin: 0 auto 6px;
position: relative;
.figure-avatar {
width: 72px;
height: 72px;
border-radius: 50%;
object-fit: cover;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
background: #f0f0f0;
}
.avatar-fallback {
width: 72px;
height: 72px;
border-radius: 50%;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
color: #bfbfbf;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
}
.figure-name {
font-size: 13px;
color: #555;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.voice-selection {
.sub-title {
font-size: 13px;
font-weight: 500;
margin-bottom: 8px;
color: #666;
}
.voice-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 6px;
max-height: 176px;
overflow-y: auto;
padding-right: 4px;
}
.voice-item {
display: flex;
align-items: center;
padding: 6px 10px;
border: 1px solid #e8e8e8;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
&.active {
border-color: #ab1941;
background: rgba(171, 25, 65, 0.08);
}
&:hover {
background: #fafafa;
}
.v-info {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
.v-name {
font-size: 13px;
font-weight: 500;
}
.v-style {
font-size: 11px;
color: #999;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
.upload-status {
margin-top: 10px;
color: #52c41a;
font-size: 13px;
}
.preview-panel {
.results-container {
min-height: 320px;
background: #f7f8fa;
border-radius: 12px;
border: 2px dashed #e1e4e8;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
.empty-preview {
text-align: center;
color: #abb2bb;
p {
font-size: 14px;
margin-top: 16px;
}
}
.generating-box {
text-align: center;
.gen-text {
font-weight: 600;
margin: 16px 0 8px;
color: #333;
}
.gen-hint {
font-size: 13px;
color: #999;
}
}
.final-video-box {
width: 100%;
video {
width: 100%;
border-radius: 8px;
background: #000;
max-height: 300px;
}
.video-actions {
margin-top: 14px;
display: flex;
flex-direction: column;
gap: 10px;
}
}
}
.submit-btn {
height: 44px;
font-size: 15px;
font-weight: 600;
border-radius: 8px;
margin-top: 16px;
}
}
}
......@@ -80,8 +80,8 @@ export default function IconModal(props) {
const iconNode = {
type: 'image',
src: selectedIcon.url,
alt: 'icon',
style: { width: '32px', height: '32px' },
alt: 'icon-inline',
style: { width: '32px', height: '32px', verticalAlign: 'middle' },
width: '32', // 双重保障,部分渲染器读这个
height: '32',
children: [{ text: '' }],
......
export const digitalHumans = [
{
id: 'A2A_V2-xinxin',
name: '梓欣',
gender: 'female',
avatar: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%2089_8dc1165.png',
},
{
id: 'A2A_V2-xixi',
name: '筱萱',
gender: 'female',
avatar: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%2090_2cae36d.png',
},
{
id: 'A2A_V2-xiaomeng2',
name: '乔雅',
gender: 'female',
avatar: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%2091_70a3d4d.png',
},
{
id: 'A2A_V2-aning',
name: '嘉睿',
gender: 'male',
avatar: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/%E5%98%89%E7%9D%BF-2_34e59dc.png',
},
{
id: 'A2A_V2-aning_red',
name: '嘉霖',
gender: 'male',
avatar: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%2094_10f09cc.png',
},
{
id: 'A2A_V2-gaoming',
name: '纪楚',
gender: 'male',
avatar: 'https://bce.bdstatic.com/doc/bce-doc/AI_DH/image%2095_ed96bc7.png',
},
]
export const femaleVoices = [
{
id: 'CAP_4146',
name: '度禧禧',
gender: '女声',
style: '温柔甜美',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/1e9d042c-f9d7-417f-88d3-4209f5516338/4146.wav',
},
{
id: 'CAP_6567',
name: '度小柔',
gender: '女声',
style: '知性大方',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/d00df619-70d5-458b-98e2-4d0e14595ace/6567.wav',
},
{
id: 'CAP_4189',
name: '度涵竹',
gender: '女声',
style: '自然生动',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/c3414454-fe66-4980-b653-b806da9616ed/4189.wav',
},
{
id: 'CAP_4194',
name: '度嫣然',
gender: '女声',
style: '温柔可爱',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/503224a8-98f3-4703-b953-795725546686/4194.wav',
},
{
id: 'CAP_4196',
name: '度清影',
gender: '女声',
style: '甜美可爱',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/d5e13abb-7a7e-4264-896c-4f9022fb6b78/4196.wav',
},
{
id: 'CAP_4197',
name: '度沁遥',
gender: '女声',
style: '温柔知性',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/e7e7274e-02df-4caa-9741-7d73b25d8ddd/4197.wav',
},
{
id: '5132',
name: '度小夏',
gender: '女声',
style: '知性大方',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/075ca6ce61d49629f62c520734b9e70e.wav',
},
{
id: '4100',
name: '度小雯',
gender: '女声',
style: '元气活力',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/b444cd975c8e5458ab0ab49644514a1a.wav',
},
{
id: '5116',
name: '度小希',
gender: '女声',
style: '元气活力',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/7015792b10a16e60753918c06869da2b.wav',
},
{
id: '5147',
name: '度常盈',
gender: '女声',
style: '亲和力强',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/1e6368f45761d7d65724bfc109cc5d4d.wav',
},
]
export const maleVoices = [
{
id: 'CAP_4193',
name: '度泽言-开朗',
gender: '男声',
style: '温柔青年',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/a545f018-54a1-4a89-a279-2c56a901bd5b/4193.wav',
},
{
id: 'CAP_4195',
name: '度怀安',
gender: '男声',
style: '磁性深情',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/029dd3eb-1bd9-455b-a5fe-3cc3d32f85c3/4195.wav',
},
{
id: 'CAP_4179',
name: '度泽言-温暖',
gender: '男声',
style: '温柔青年',
previewUrl: 'https://meta-human-editor-prd.cdn.bcebos.com/1a71e60c-bbe0-482b-81fb-4889524acbc3/ed2cc48e-db88-4a4d-91ba-e7f86935df03/4179.wav',
},
{
id: '4140',
name: '度小新',
gender: '男声',
style: '元气活力',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/a6bf500f7156b7e762ee2760288de98c.wav',
},
{
id: '5135',
name: '度星河',
gender: '男声',
style: '沉稳冷静',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/337482d4a3299ac70a4d01283ce9de9f.wav',
},
{
id: '4123',
name: '度小凯',
gender: '男声',
style: '激情饱满',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/17c68fd8042a35b667cd6a92ceef4dbf.wav',
},
{
id: '4003',
name: '度逍遥',
gender: '男声',
style: '权威靠谱/专业娴熟',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/f26b3d0c97543381f7a2ec3508eb96e8.wav',
},
{
id: '4129',
name: '度小彦',
gender: '男声',
style: '元气活力',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/f7e067b169ff8262f91ff69aa64a6be3.wav',
},
{
id: '4115',
name: '度小贤',
gender: '男声',
style: '权威靠谱/沉稳冷静',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/31aebec53b10a474019ba63e687c21f8.wav',
},
{
id: '4106',
name: '度博文',
gender: '男声',
style: '沉稳冷静',
previewUrl: 'https://digital-human-pipeline-output.cdn.bcebos.com/e192515285df0423911b14ef916cafbb.wav',
},
]
import { useState, useEffect, useRef, useCallback } from 'react'
import { message } from 'antd'
import { getDigitalHumanList, submitVideo, getVideoTask } from '@/api/xiling'
import { digitalHumans as staticFigures, femaleVoices, maleVoices } from '@/common/wangeditor-customer/menu/common/digital-human-data'
/**
* 数字人功能 Hook (简化版:仅文本驱动,移除底图)
*/
export default function useDigitalHuman() {
const [figures, setFigures] = useState(staticFigures)
const [loadingFigures, setLoadingFigures] = useState(false)
const [isGenerating, setIsGenerating] = useState(false)
const [generatedVideo, setGeneratedVideo] = useState(null)
const [error, setError] = useState(null)
const pollTimerRef = useRef(null)
const stopPolling = useCallback(() => {
if (pollTimerRef.current) {
clearInterval(pollTimerRef.current)
pollTimerRef.current = null
}
}, [])
const startPolling = useCallback((taskId, figureName) => {
stopPolling()
pollTimerRef.current = setInterval(async () => {
try {
const response = await getVideoTask({ taskId })
const { status, videoUrl } = response.result
if (status === 'SUCCESS' && videoUrl) {
stopPolling()
setGeneratedVideo({
url: videoUrl,
taskId: taskId,
figureName: figureName,
})
setIsGenerating(false)
message.success('数字人视频生成成功!')
} else if (status === 'FAILED') {
stopPolling()
setIsGenerating(false)
setError(response.result.failedMessage || '生成失败')
message.error(`视频生成失败: ${response.result.failedMessage || '未知错误'}`)
}
} catch (err) {
console.error('Polling Error:', err)
}
}, 5000)
}, [stopPolling])
// 处理获取列表
const fetchFigures = useCallback(async () => {
setLoadingFigures(true)
try {
const list = await getDigitalHumanList({ systemFigure: true })
if (list && list.length > 0) {
setFigures(prev => {
// 创建新数组,保留静态配置,并用 API 的数据补充或更新关键信息(如 avatar)
const combined = [...prev]
list.forEach(item => {
const existingIndex = combined.findIndex(c => String(c.id) === String(item.figureId))
const avatarUrl = item.figureImageUrl || item.figureVideoThumbnailUrl
if (existingIndex > -1) {
// 如果静态配置已存在,且 API 返回了有效的预览图,则更新它
if (avatarUrl) {
combined[existingIndex] = {
...combined[existingIndex],
avatar: avatarUrl
}
}
} else {
// 如果是全新的数字人,则添加
combined.push({
id: item.figureId,
name: item.name,
avatar: avatarUrl,
})
}
})
return combined
})
}
} catch (err) {
console.error('Fetch Figures Error:', err)
} finally {
setLoadingFigures(false)
}
}, [])
const generateVideo = useCallback(async (options) => {
const {
figureId,
figureName,
text,
voiceId,
width = 720,
height = 1280,
transparent = true,
enableSubtitle = false,
backgroundImageUrl
} = options
setIsGenerating(true)
setGeneratedVideo(null)
setError(null)
try {
const params = {
figureId,
driveType: 'TEXT',
text,
...(backgroundImageUrl ? { backgroundImageUrl } : {}),
ttsParams: {
person: voiceId || '5116',
speed: '5',
pitch: '5',
volume: '5'
},
videoParams: {
width: parseInt(width),
height: parseInt(height),
transparent: !!transparent
},
subtitleParams: {
enabled: !!enableSubtitle,
subtitlePolicy: 'SRT'
}
}
const response = await submitVideo(params)
if (response.result && response.result.taskId) {
startPolling(response.result.taskId, figureName)
return response.result.taskId
} else {
throw new Error('TaskId missing')
}
} catch (err) {
setIsGenerating(false)
const msg = err.response?.data?.message?.global || '提交失败'
setError(msg)
message.error(msg)
throw err
}
}, [startPolling])
useEffect(() => {
fetchFigures()
return () => stopPolling()
}, [fetchFigures, stopPolling])
return {
figures,
femaleVoices,
maleVoices,
loadingFigures,
isGenerating,
generatedVideo,
error,
generateVideo,
resetGeneratedVideo: () => setGeneratedVideo(null)
}
}
......@@ -77,7 +77,7 @@ export async function uploadFile(file) {
// 上传通过URL获取的文件
export async function uploadFileByUrl(url) {
try {
url = url.replace('https://ark-content-generation-cn-beijing.tos-cn-beijing.volces.com', '/api/ai_file')
url = url.replace('https://ark-content-generation-cn-beijing.tos-cn-beijing.volces.com', '/api/volcano_file')
const res = await axios.get(url, { responseType: 'blob' })
return await uploadFile(res.data)
} catch (error) {
......
......@@ -52,10 +52,10 @@ export default defineConfig(() => {
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/xiling/, ''),
},
'/api/ai_file': {
'/api/volcano_file': {
target: 'https://ark-content-generation-cn-beijing.tos-cn-beijing.volces.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/ai_file/, ''),
rewrite: (path) => path.replace(/^\/api\/volcano_file/, ''),
},
'/api': {
target: 'https://zijingebook.ezijing.com',
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论