Commit cc768501 authored by nick zheng's avatar nick zheng

chore: 草稿箱、我的作品联调和对话配置调整

parent bfc4695a
......@@ -40,7 +40,7 @@ export function deleteBackgroundImageById<T>(id: number) {
// 根据人物名称分页获取人物信息
export function fetchInfoByImageName<T>(imageName: string) {
return request.post<T>(`/bizDigitalHumanImageRest/findByImageName.json?imageName=${imageName}`, {
return request.post<T>(`/bizDigitalHumanImageRest/findDigitalHumanImageByImageName.json?imageName=${imageName}`, {
pagingInfo: { pageNo: 1, pageSize: 1000 },
})
}
......
......@@ -54,7 +54,7 @@ export function handleUploadBackgroundImageFile<T>(imageName: string, formData:
}
// 根据背景图id删除背景图
export function handleDeleteBackgroundImageById<T>(id: number) {
export function handleDeleteBackgroundImageById<T>(id: string) {
return request.post<T>(`/bizDigitalHumanMemberImageRest/deletedById.json?id=${id}`)
}
......
import { request } from '@/utils/request'
export function fetchDraftsList<T>(token: string, queryParams: { pagingInfo: any }) {
return request.post<T>(
'/bizDigitalHumanMemberDraftConfigRest/getList.json',
{},
{
headers: {
'X-Request-Token': token,
},
params: queryParams.pagingInfo,
},
)
// 获取草稿列表
export function fetchDraftsList<T>(payload: object) {
return request.post<T>(`/bizDigitalHumanMemberDraftConfigRest/getList.json`, payload)
}
// 保存更新用户草稿
export function fetchSaveDraftConfig<T>(payload: object) {
return request.post<T>('/bizDigitalHumanMemberDraftConfigRest/saveOrUpdate.json', payload)
}
// 通过id删除用户草稿
export function fetchDeleteDraftConfigById<T>(id: string) {
return request.post<T>(`/bizDigitalHumanMemberDraftConfigRest/deletedById.json?id=${id}`)
}
export function fetchRecentCreationList<T>(token: string) {
......
import { request } from '@/utils/request'
export function fetchOpusList<T>(token: string, queryParams: { pagingInfo: any }) {
return request.post<T>(
'/bizDigitalHumanMemberTaskStatusRest/getMemberTaskStatusList.json',
{},
{
headers: {
'X-Request-Token': token,
},
params: queryParams.pagingInfo,
},
)
export function fetchGetTaskList<T>(payload: object) {
return request.post<T>('/bizDigitalHumanMemberTaskStatusRest/getMemberTaskStatusList.json', payload)
}
......@@ -113,7 +113,7 @@ function handleDelete(id: number) {
positiveText: '確認',
negativeText: '取消',
onPositiveClick: async () => {
const res = await handleDeleteBackgroundImageById(id)
const res = await handleDeleteBackgroundImageById(`${id}`)
if (res.code === 0) {
window.$message.success('刪除成功')
......
......@@ -19,6 +19,7 @@ const twoDFewShotImageList = ref<DigitalImageItem[]>([])
const allImageList = ref<DigitalImageItem[]>([])
const showAll = ref(false)
const searchImageName = ref('')
const currentImageType = ref<ImageType>(ImageType.THREE_D)
onMounted(() => {
getDigitalImageList()
......@@ -38,7 +39,7 @@ async function getDigitalImageList() {
async function handleSearchImage() {
const res = await fetchInfoByImageName<DigitalImageItem[]>(searchImageName.value)
if (res.code === 0) {
allImageList.value = res.data
allImageList.value = res.data.filter((digitalImageItem) => digitalImageItem.imageType === currentImageType.value)
}
}
......@@ -47,6 +48,7 @@ function handleClickDigitalImage(figureId: string) {
}
function handleClickAll(imageType: ImageType) {
currentImageType.value = imageType
switch (imageType) {
case ImageType.THREE_D:
allImageList.value = threeDImageList.value
......@@ -119,13 +121,7 @@ function handleClickAll(imageType: ImageType) {
</template>
返回
</NButton>
<NInput
v-model:value="searchImageName"
round
placeholder="輸入名稱搜索"
@blur="handleSearchImage"
@keyup.enter="handleSearchImage"
>
<NInput v-model:value="searchImageName" round placeholder="輸入名稱搜索" @input="handleSearchImage">
<template #prefix>
<CustomIcon class="text-lg" icon="mingcute:search-line" />
</template>
......
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import PublishDigitalHumanDialogueModal from '../components/publish-digital-human-dialogue-modal.vue'
......@@ -45,6 +45,10 @@ onMounted(() => {
router.replace({ name: 'Root' })
})
onUnmounted(() => {
digitalHumanDialogueStore.resetDigitalHumanDialogue()
})
async function handleGetDigitalHumanDialogueConfig() {
fetchGetDigitalHumanDialogueConfigByConfigId<DigitalHumanDialogueConfig>(currentConfigId.value)
.then(async (res) => {
......
......@@ -38,7 +38,7 @@ export function createDiaglogueTableColumn(
},
{
title: '編輯時間',
title: '最後編輯時間',
key: 'modifiedTime',
align: 'left',
ellipsis: {
......@@ -75,7 +75,7 @@ export function createDiaglogueTableColumn(
value: row.isOpen === 'Y',
onUpdateValue: () => handleDiaglogueAction('updateOpen', row.configId, row),
},
{},
{ checked: () => '啓用', unchecked: () => '停用' },
)
},
},
......
......@@ -41,7 +41,7 @@ const showModal = computed({
watch(
() => props.configTitle,
(newValue) => {
digitalHumanDialogueFormData.title = newValue + '副本'
digitalHumanDialogueFormData.title = (newValue + '副本').slice(0, 30)
},
)
......@@ -60,6 +60,7 @@ function handleAddDigitalHumanDialogue() {
:title="modalTitle"
:width="448"
:height="220"
:btn-loading="btnLoading"
@confirm="handleAddDigitalHumanDialogue"
>
<template #content>
......
......@@ -66,6 +66,7 @@ async function handleGetDiaglogList() {
if (res.code === 0) {
digitalHumanDialogueList.value = res.data
pagingInfo.value = res.pagingInfo as PaginationInfo
checkedConfigIdList.value = []
dialogueTableLoading.value = false
}
}
......@@ -91,6 +92,7 @@ async function handleMultiDeleteDiaglogueConfig() {
function handleClearSearchQuery() {
searchQuery.value = ''
pagingInfo.value.pageNo = 1
handleGetDiaglogList()
}
......@@ -173,12 +175,15 @@ async function handleAddDigitalHumanDialogue(title: string) {
copyDiaglogueConfig.value!.baseInfo.configId = ''
copyDiaglogueConfig.value!.baseInfo.title = title
copyDiaglogueConfig.value!.baseInfo.publishStatus = 'N'
copyDiaglogueConfig.value!.baseInfo.isOpen = 'N'
const res = await fetchSaveDigitalHumanDialogueConfig(copyDiaglogueConfig.value!).finally(
() => (editDigitalHumanDialogueBtnLoading.value = false),
)
if (res.code === 0) {
window.$message.success('提交成功')
isShowEditDigitalHumanDialogueModal.value = false
await handleGetDiaglogList()
}
......@@ -219,7 +224,7 @@ function handleGetDialogueListUpdatePageSize(pageSize: number) {
</script>
<template>
<div ref="pageContentWrapRef" class="h-full w-full overflow-auto rounded-[16px] bg-white p-6">
<div ref="pageContentWrapRef" class="h-full w-full overflow-y-hidden rounded-[16px] bg-white p-6">
<p class="mb-6 select-none text-lg text-[#333]">我的對話</p>
<div class="flex justify-between">
......
import { h } from 'vue'
import { DataTableColumns, NEllipsis } from 'naive-ui'
import { formatDateTime } from '@/utils/date-formatter'
import { DraftConfig } from '@/store/types/creation'
export function createDarftTableColumns({
handleDeleteDraft,
handleEditDraft,
handleCreatCopyDraft,
}: {
handleDeleteDraft: (rowData: DraftConfig) => void
handleEditDraft: (rowData: DraftConfig) => void
handleCreatCopyDraft: (rowData: DraftConfig) => void
}): DataTableColumns<DraftConfig> {
return [
{
type: 'selection',
fixed: 'left',
},
{
title: '草稿名稱',
key: 'draftName',
align: 'left',
width: '400',
render(row) {
return h('div', { class: 'flex items-center' }, [
h(
'div',
{
style: { width: '64px', height: '36px', marginRight: '10px' },
class: 'flex items-center justify-center overflow-hidden relative',
},
[
row.coverUrl
? h('img', {
src: row.coverUrl,
alt: '作品圖',
class: 'h-full object-cover',
})
: h('div', { class: 'border w-full h-full text-xs flex items-center justify-center' }, '暫無圖片'),
],
),
h(NEllipsis, {}, { default: () => row.draftName || '--' }),
])
},
},
{
title: '類型',
key: 'type',
width: '180',
render(row) {
const typeMap = {
IMAGE_VIDEO: '照片數字人',
BASE_VIDEO: '數字人生成',
ADVANCED_VIDEO: '高級數字人',
}
return h('span', {}, typeMap[row.taskType] || '--')
},
},
{
title: '最後編輯時間',
key: 'modifiedTime',
width: 200,
render(row) {
return h('span', {}, row.modifiedTime ? formatDateTime(row.modifiedTime) : '--')
},
},
{
title: '操作',
key: 'operation',
width: 250,
render(row) {
return h('div', {}, [
h(
'span',
{
onClick: () => handleEditDraft(row),
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'編輯',
),
h(
'span',
{
onClick: () => handleCreatCopyDraft(row),
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'創建副本',
),
h(
h(
'span',
{
onClick: () => handleDeleteDraft(row),
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'删除',
),
),
])
},
},
]
}
<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue'
import { FormInst } from 'naive-ui'
import CustomModal from '@/components/custom-modal/custom-modal.vue'
interface Props {
isShowModal: boolean
draftName: string
btnLoading: boolean
modalTitle: string
}
interface Emits {
(e: 'update:isShowModal', value: boolean): void
(e: 'comfirm', value: string): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const digitalHumanDraftFormRef = ref<FormInst | null>(null)
const digitalHumanDraftFormData = reactive({
draftName: '',
})
const digitalHumanDraftFormRules = {
draftName: [{ required: true, message: '請輸入名稱', trigger: 'blur' }],
}
const showModal = computed({
get() {
return props.isShowModal
},
set(value: boolean) {
emit('update:isShowModal', value)
},
})
watch(
() => props.draftName,
(newValue) => {
digitalHumanDraftFormData.draftName = (newValue + '副本').slice(0, 30)
},
)
function handleAddDigitalHumanDialogue() {
digitalHumanDraftFormRef.value?.validate((errors) => {
if (!errors) {
emit('comfirm', digitalHumanDraftFormData.draftName)
}
})
}
</script>
<template>
<CustomModal
v-model:is-show="showModal"
:title="modalTitle"
:width="448"
:height="220"
:btn-loading="btnLoading"
@confirm="handleAddDigitalHumanDialogue"
>
<template #content>
<NForm
ref="digitalHumanDraftFormRef"
:model="digitalHumanDraftFormData"
:rules="digitalHumanDraftFormRules"
label-placement="left"
show-require-mark
label-width="80px"
class="pt-2"
>
<NFormItem label="名稱" path="draftName">
<NInput
v-model:value="digitalHumanDraftFormData.draftName"
placeholder="請輸入名稱"
:maxlength="30"
show-count
/>
</NFormItem>
</NForm>
</template>
</CustomModal>
</template>
<style lang="scss" scoped>
:deep(.n-input) {
border-radius: 4px;
.n-input-wrapper {
font-size: 12px;
}
}
</style>
<script setup lang="ts">
import { h, onMounted, reactive, Ref, ref, watch } from 'vue'
import { NButton, NEllipsis, NPopconfirm, useDialog, type DataTableColumns } from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Search, CloseOne } from '@icon-park/vue-next'
import { createData } from './draftsData'
import { fetchDraftsList } from '@/apis/drafts'
import { useUserStore } from '@/store/modules/user'
type draftType = 'IMAGE_VIDEO' | 'BASE_VIDEO' | 'ADVANCED_VIDEO'
interface draftRowData {
key: number
deaftsName: string
draftsImgUrl: string
type: draftType
LastEditTime: string
}
import useTableScrollY from '@/composables/useTableScrollY'
import CustomPagination, { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
import { fetchDeleteDraftConfigById, fetchDraftsList } from '@/apis/drafts'
import { createDarftTableColumns } from './columns'
import CreateCopyDraftModal from './components/create-copy-draft-modal.vue'
import { DraftConfig } from '@/store/types/creation'
import { saveDraftConfig } from '@/apis/digital-creation'
const dialog = useDialog()
const opusPagination = reactive({
page: 1,
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 20, 30],
prefix: () => h('div', [`共${filteredTemplateData.value.length}條數據`]),
onChange: (page: number) => {
opusPagination.page = page
},
onUpdatePageSize: (pageSize: number) => {
opusPagination.pageSize = pageSize
opusPagination.page = 1
},
})
const userStore = useUserStore()
const token = userStore.token
const templateClassify = [
{ value: 'All', label: '全部' },
const router = useRouter()
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 28 + 24 + 44 + 48 + 48 + 50)
const taskTypeList = [
{ value: '', label: '全部' },
{
value: 'IMAGE_VIDEO',
label: '照片數據人',
......@@ -48,212 +30,182 @@ const templateClassify = [
},
]
const opusSelectedTag = ref('All')
const checkedOpusKeys: Ref<number[]> = ref([])
const opusList = ref(createData())
const currentSelectedTaskType = ref('')
const checkedDraftIdList = ref<number[]>([])
const searchQuery = ref('')
const filteredTemplateData = ref(opusList.value)
const pagingInfo = ref({
pageNo: 0,
totalPages: 1,
const audioDraftTableLoading = ref(false)
const audioDraftList = ref<DraftConfig[]>([])
const isShowCreateCopyDraftModal = ref(false)
const createCopyDraftBtnLoading = ref(false)
const copyDraftConfig = ref<DraftConfig>()
const pagingInfo = ref<PaginationInfo>({
pageNo: 1,
pageSize: 10,
totalRows: 2,
totalPages: 0,
totalRows: 0,
})
const onCheckedOpusKeysChange = (newKeys: number[]) => {
checkedOpusKeys.value = newKeys
}
const isShowPagination = computed(() => {
return tableContentY.value > 0
})
const opusActionColumns = createOpusColumns({
handleOpusDeleteRow(rowData) {
window.$message.info(`删除 ${rowData.key}`)
},
handleDraftsEdit(rowData) {
window.$message.info(`編輯 ${rowData.key}`)
},
handleDraftsCreatCopy(rowData) {
window.$message.info(`創建副本 ${rowData.key}`)
},
onMounted(() => {
handleGetDraftList()
})
const handleMultiSelectDelete = () => {
dialog.warning({
title: '提示',
content: '確認刪除嗎?',
positiveText: '確認',
const darftTableColumns = createDarftTableColumns({
async handleDeleteDraft(rowData) {
window.$dialog.warning({
title: '請確認是否刪除',
content: '刪除後數據不可恢復',
negativeText: '取消',
onPositiveClick: () => {
opusList.value = opusList.value.filter((item) => !checkedOpusKeys.value.includes(item.key))
checkedOpusKeys.value = []
window.$message.success('删除成功')
},
onNegativeClick: () => {
window.$message.error('取消')
},
})
}
watch([opusSelectedTag], () => {
handleFilterOpusData()
})
function createOpusColumns({
handleOpusDeleteRow,
handleDraftsEdit,
handleDraftsCreatCopy,
}: {
handleOpusDeleteRow: (rowData: draftRowData) => void
handleDraftsEdit: (rowData: draftRowData) => void
handleDraftsCreatCopy: (rowData: draftRowData) => void
}): DataTableColumns<draftRowData> {
return [
{
type: 'selection',
fixed: 'left',
},
{
title: '草稿名稱',
key: 'deaftsName',
render(row) {
return h('div', { class: 'flex items-center w-[320px]' }, [
h(
'div',
{ class: 'w-[111px] h-[55px] mr-[10px] flex items-center justify-center overflow-hidden relative' },
[
h('img', {
src: row.draftsImgUrl,
alt: '作品圖片',
class: 'h-full object-cover',
}),
],
),
h(NEllipsis, { style: { maxWidth: '150px' } }, { default: () => row.deaftsName }),
])
},
},
{
title: '類型',
key: 'type',
render(row) {
const typeMap = {
IMAGE_VIDEO: '照片數字人',
BASE_VIDEO: '數字人生成',
ADVANCED_VIDEO: '高級數字人',
positiveText: '確認',
onPositiveClick: async () => {
const res = await fetchDeleteDraftConfigById(rowData!.id! + '')
if (res.code === 0) {
window.$message.success('刪除成功')
audioDraftList.value.length === 1 && (pagingInfo.value.pageNo = pagingInfo.value.pageNo - 1)
await handleGetDraftList()
}
return h('span', {}, typeMap[row.type] || '')
},
},
{
title: '最後編輯時間',
key: 'LastEditTime',
},
{
title: '操作',
key: 'operation',
render(row) {
return h('div', { class: 'operation-buttons' }, [
h(
'span',
{
onClick: () => handleDraftsEdit(row),
class: 'text-[#2468f2] cursor-pointer mr-[10px]',
},
'編輯',
),
h(
'span',
{
onClick: () => handleDraftsCreatCopy(row),
class: 'text-[#2468f2] cursor-pointer mr-[10px]',
},
'創建副本',
),
h(
NPopconfirm,
{
onPositiveClick: () => handleOpusDeleteRow(row),
onNegativeClick: () => {
window.$message.info('取消删除')
},
},
{
trigger: () =>
h(
'span',
{
class: 'text-[#2468f2] cursor-pointer',
})
},
'删除',
),
default: () => '確認要刪除這一行嗎?',
handleEditDraft(rowData) {
router.push({
name: 'Creation',
params: {
templateId: rowData.templateId,
draftId: rowData.id,
},
),
])
})
},
handleCreatCopyDraft(rowData) {
isShowCreateCopyDraftModal.value = true
copyDraftConfig.value = rowData
},
]
}
})
async function handleCreateCopyDraft(draftName: string) {
createCopyDraftBtnLoading.value = true
function handleFilterOpusData() {
let filteredData = opusList.value
const payload: DraftConfig = JSON.parse(JSON.stringify(copyDraftConfig.value))
if (opusSelectedTag.value !== 'All') {
filteredData = filteredData.filter((item) => item.type === opusSelectedTag.value)
payload.id = null
payload.draftName = draftName
const res = await saveDraftConfig<DraftConfig>(payload).finally(() => {
createCopyDraftBtnLoading.value = false
})
if (res.code === 0) {
window.$message.success('提交成功')
isShowCreateCopyDraftModal.value = false
await handleGetDraftList()
}
}
function handleFilterDraftListByTaskType(taskType: string) {
currentSelectedTaskType.value = taskType
pagingInfo.value.pageNo = 1
handleGetDraftList()
}
const handleMultiSelectDelete = () => {
window.$dialog.warning({
title: '提示',
content: '確認刪除嗎?',
positiveText: '確認',
negativeText: '取消',
onPositiveClick: async () => {
if (checkedDraftIdList.value.length > 0) {
const res = await fetchDeleteDraftConfigById(checkedDraftIdList.value.join(','))
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
filteredData = filteredData.filter((item) => item.deaftsName.toLowerCase().includes(query))
if (res.code === 0) {
checkedDraftIdList.value = []
window.$message.success('删除成功')
await handleGetDraftList()
}
}
},
})
}
filteredTemplateData.value = filteredData
function handleSearchDraftList() {
pagingInfo.value.pageNo = 1
handleGetDraftList()
}
function handleClearSearchQuery() {
searchQuery.value = ''
handleFilterOpusData()
pagingInfo.value.pageNo = 1
handleGetDraftList()
}
function handleUpdateCheckedDraftList(draftIdList: number[]) {
checkedDraftIdList.value = draftIdList
}
function getDraftsList() {
fetchDraftsList(token, { pagingInfo: pagingInfo.value }).then((res) => {
if (res.code !== 0) return ''
console.log('草稿箱list', res)
async function handleGetDraftList() {
audioDraftTableLoading.value = true
const res = await fetchDraftsList<DraftConfig[]>({
draftName: searchQuery.value,
taskType: currentSelectedTaskType.value,
pagingInfo: pagingInfo.value,
})
if (res.code === 0) {
audioDraftList.value = res.data
audioDraftTableLoading.value = false
checkedDraftIdList.value = []
pagingInfo.value = res.pagingInfo as PaginationInfo
}
}
onMounted(() => {
getDraftsList()
})
function handleGetDraftListUpdatePageNo(pageNo: number) {
pagingInfo.value.pageNo = pageNo
handleGetDraftList()
}
function handleGetDraftListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
handleGetDraftList()
}
</script>
<template>
<div ref="pageContentWrapRef" class="h-full w-full overflow-y-hidden rounded-[16px] bg-white p-6">
<div class="mb-[24px] cursor-default text-[18px] text-[#000]">草稿箱</div>
<div class="flex pb-[16px]">
<div
class="h-full min-h-[840px] min-w-[1160px] overflow-auto rounded-[16px] bg-white px-[24px] pb-[16px]"
style="scrollbar-width: none"
v-for="taskTypeItem in taskTypeList"
:key="taskTypeItem.value"
class="!h-[28px] cursor-pointer rounded-full !px-[12px] !leading-[28px] hover:text-[#5b647a]"
:class="taskTypeItem.value === currentSelectedTaskType ? 'bg-[#edeef7]' : ''"
@click="handleFilterDraftListByTaskType(taskTypeItem.value)"
>
<div class="overflow-auto bg-white pt-[24px]">
<div class="mb-[24px] cursor-default text-[18px] text-[#000]">草稿箱</div>
<div class="pb-[16px]">
<n-radio-group v-model:value="opusSelectedTag">
<n-radio-button
v-for="classify in templateClassify"
:key="classify.value"
class="!h-[28px] !px-[12px] !leading-[28px] hover:text-[#5b647a]"
:value="classify.value"
:label="classify.label"
/>
</n-radio-group>
{{ taskTypeItem.label }}
</div>
</div>
<div class="flex justify-between">
<div>
<NButton
class="!rounded-[4px]"
:class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedOpusKeys.length > 0 }"
:disabled="!checkedOpusKeys.length"
:class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedDraftIdList.length > 0 }"
:disabled="!checkedDraftIdList.length"
@click="handleMultiSelectDelete"
>删除</NButton
>
<span v-show="checkedOpusKeys.length" class="color-[#999] ml-[8px] text-[12px]"
>选择{{ checkedOpusKeys.length }}条记录</span
>
刪除
</NButton>
<span v-show="checkedDraftIdList.length" class="color-[#999] ml-[8px] text-[12px]">
選擇{{ checkedDraftIdList.length }}條記錄
</span>
</div>
<div
......@@ -264,7 +216,7 @@ onMounted(() => {
type="text"
class="mx-[11px] w-[183px] border-none outline-none"
placeholder="請輸入名稱進行蒐索"
@keyup.enter="handleFilterOpusData"
@keyup.enter="handleSearchDraftList"
/>
<CloseOne
v-show="searchQuery.length"
......@@ -274,106 +226,47 @@ onMounted(() => {
class="mr-[5px] cursor-pointer"
@click="handleClearSearchQuery"
/>
<Search theme="outline" size="16" fill="#00000040" class="cursor-pointer" @click="handleFilterOpusData" />
<Search theme="outline" size="16" fill="#00000040" class="cursor-pointer" @click="handleSearchDraftList" />
</div>
</div>
<div class="relative">
<n-data-table
<div :style="{ height: tableContentY + 48 + 'px' }">
<NDataTable
:loading="audioDraftTableLoading"
:bordered="false"
:single-line="false"
:columns="opusActionColumns"
:data="filteredTemplateData"
:checked-row-keys="checkedOpusKeys"
class="border-t-[1px]"
:pagination="opusPagination"
@update:checked-row-keys="onCheckedOpusKeysChange"
:row-key="(row: DraftConfig) => row.id"
:columns="darftTableColumns"
:data="audioDraftList"
:max-height="tableContentY"
:scroll-x="1086"
@update:checked-row-keys="handleUpdateCheckedDraftList"
>
</n-data-table>
<template #empty>
<div :style="{ height: tableContentY + 'px' }" class="flex items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div class="h-[123px] w-[156px] bg-[url('@/assets/images/empty-dialogue.png')] bg-[length:100%_100%]" />
<span class="mt-3 text-[#5b647a]">暫無數據</span>
</div>
</div>
</template>
</NDataTable>
</div>
</template>
<style lang="scss">
.n-radio-group {
--n-button-box-shadow-focus: none !important;
.n-radio-button {
border: none;
border-width: 0 !important;
border-radius: 15px !important;
&:first-child {
border-left: none !important;
}
&:last-child {
border-right: none !important;
}
&.n-radio-button--checked {
color: #091221 !important;
background-color: #edeef7 !important;
border: none !important;
}
&:not(.n-radio-button--disabled):hover:not(.n-radio-button--checked) {
color: #5b647a !important;
}
}
.n-radio-group__splitor {
width: 0 !important;
background-color: #fff !important;
}
}
.n-data-table {
border: none;
.n-data-table-th {
text-align: center !important;
background-color: #f6f7fb !important;
}
.n-data-table-td {
text-align: center !important;
}
.n-data-table-tbody {
font-size: 16px !important;
}
:not(.n-data-table--single-line) {
.n-data-table-th {
border-right: 2px solid #fff !important;
border-bottom: none !important;
}
.n-data-table-td {
border-right: 2px solid #fff0 !important;
border-bottom: none !important;
}
}
}
.v-binder-follower-content {
margin-left: 0 !important;
}
.n-popover__content {
font-size: 12px;
}
.n-popover:not(.n-popover--raw) {
color: #000;
background-color: #fff !important;
}
.n-card.n-card--bordered {
border: none !important;
}
<div v-show="isShowPagination" class="mt-5 flex w-full justify-end">
<CustomPagination
:paging-info="pagingInfo"
@update-page-no="handleGetDraftListUpdatePageNo"
@update-page-size="handleGetDraftListUpdatePageSize"
/>
</div>
</div>
.n-popover-shared .n-popover-arrow-wrapper .n-popover-arrow {
background-color: #fff !important ;
}
</style>
<CreateCopyDraftModal
v-model:is-show-modal="isShowCreateCopyDraftModal"
modal-title="編輯新草稿名稱"
:draft-name="copyDraftConfig?.draftName || ''"
:btn-loading="createCopyDraftBtnLoading"
@comfirm="handleCreateCopyDraft"
/>
</template>
......@@ -3,6 +3,13 @@ import { fetchUniversalCurrency } from '@/apis/user'
import { useUserStore } from '@/store/modules/user'
import { Right } from '@icon-park/vue-next'
import { onMounted, ref } from 'vue'
import CreateDigitalHumanDialogueModal, {
DigitalHumanDialogueForm,
} from '@/views/dialogue-detail/components/create-digital-human-dialogue-modal.vue'
import { useDigitalHumanDialogueStore } from '@/store/modules/digital-human-dialogue'
import { DigitalHumanDialogueConfig } from '@/store/types/digital-human-dialogue'
import { fetchSaveDigitalHumanDialogueConfig } from '@/apis/digital-human-dialogue'
import { useRouter } from 'vue-router'
const UniversalCurrency = ref()
const userStore = useUserStore()
......@@ -26,6 +33,30 @@ function handleRechargeCurrency() {
onMounted(() => {
getUniversalCurrency()
})
const digitalHumanDialogueStore = useDigitalHumanDialogueStore()
const router = useRouter()
const isShow = ref(false)
function handleShowModal() {
isShow.value = true
}
async function handleCreateDigitalHumanDialogue(digitalHumanDialogueForm: DigitalHumanDialogueForm) {
digitalHumanDialogueStore.resetDigitalHumanDialogue()
const payload: DigitalHumanDialogueConfig = JSON.parse(JSON.stringify(digitalHumanDialogueStore.$state))
payload.baseInfo.title = digitalHumanDialogueForm.title
payload.baseInfo.pageLayout = digitalHumanDialogueForm.pageLayout
const res = await fetchSaveDigitalHumanDialogueConfig<DigitalHumanDialogueConfig>(payload)
if (res.code === 0) {
router.push({ name: 'DialogueDetail', params: { configId: res.data.baseInfo.configId } })
isShow.value = false
}
}
</script>
<template>
......@@ -40,7 +71,7 @@ onMounted(() => {
</div>
<div
class="text-align-center mt-[20px] h-[35px] w-[90px] cursor-pointer rounded-[6px] bg-[#fff3] text-[14px] leading-[35px] text-[#fff] hover:bg-[#fff]/[.4]"
@click="handleGoToCreation"
@click="handleShowModal"
>
<span>立即創作</span>
</div>
......@@ -109,4 +140,11 @@ onMounted(() => {
</div>
</div>
</div>
<CreateDigitalHumanDialogueModal
v-model:is-show-modal="isShow"
modal-title="测试"
:btn-loading="false"
@comfirm="handleCreateDigitalHumanDialogue"
/>
</template>
import { formatDateTime } from '@/utils/date-formatter'
import { DataTableColumns, NEllipsis, NPopconfirm } from 'naive-ui'
import { h } from 'vue'
type TaskType = 'IMAGE_VIDEO' | 'BASE_VIDEO' | 'ADVANCED_VIDEO'
type TaskStateType = 'SUBMIT' | 'GENERATING' | 'SUCCESS' | 'FAILED'
export interface TaskItem {
id: number
taskCoverUrl: string
baiduTaskId: string
taskName: string
callbackUrl: string
taskType: TaskType
status: TaskStateType
videoUrl: string
duration: number
subtitleFileUrl: null
createTime: null
updateTime: string
modifiedTime: Date
}
export function createOpusColumns({
handleOpusDownload,
handleOpusDeleteRow,
handleOpusExamine,
handleOpusEditing,
}: {
handleOpusDownload: (rowData: TaskItem) => void
handleOpusDeleteRow: (rowData: TaskItem) => void
handleOpusExamine: (rowData: TaskItem) => void
handleOpusEditing: (rowData: TaskItem) => void
}): DataTableColumns<TaskItem> {
return [
{
type: 'selection',
fixed: 'left',
},
{
title: '作品名稱',
key: 'opusName',
width: 320,
render(row) {
return h('div', { class: 'flex items-center' }, [
h(
'div',
{
style: { width: '111px', height: '55px' },
class: 'mr-[10px] flex items-center justify-center overflow-hidden relative',
},
[
h('img', {
src: row.taskCoverUrl,
alt: '作品圖片',
class: 'h-full object-cover',
}),
],
),
h(NEllipsis, {}, { default: () => row.taskName || '--' }),
])
},
},
{
title: '狀態',
key: 'state',
width: 150,
render(row) {
const stateMap = {
SUBMIT: { text: '待生成', color: '#BBBBBB' },
GENERATING: { text: '生成中', color: '#1684FC' },
SUCCESS: { text: '生成成功', color: '#26A50E' },
FAILED: { text: '生成失敗', color: '#CB5656' },
}
const state = stateMap[row.status] || { text: '', color: '#CCCCCC' } // 默认灰色
return h('div', { class: 'flex items-center ' }, [
h('div', {
class: 'w-[10px] h-[10px] rounded-[25px] mr-18px reading-[22px]',
style: { backgroundColor: state.color },
}),
h('span', {}, state.text),
])
},
},
{
title: '類型',
key: 'type',
width: 120,
render(row) {
const typeMap = {
IMAGE_VIDEO: '照片數字人',
BASE_VIDEO: '數字人生成',
ADVANCED_VIDEO: '高級數字人',
}
return h('span', {}, typeMap[row.taskType] || '')
},
},
{
title: '視頻時長',
key: 'videoDuration',
width: 120,
render(row) {
return h('span', {}, row.duration ? row.duration : '--')
},
},
{
title: '完成時間',
key: 'completionTime',
width: 180,
render(row) {
return h('span', {}, row.modifiedTime ? formatDateTime(row.modifiedTime) : '--')
},
},
{
title: '操作',
key: 'operation',
width: 240,
fixed: 'right',
render(row) {
return h('div', { class: 'operation-buttons' }, [
h(
'span',
{
onClick: () => handleOpusExamine(row),
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'查看',
),
h(
'span',
{
onClick: () => handleOpusDownload(row),
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'下載',
),
h(
'span',
{
onClick: () => handleOpusEditing(row),
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'繼續編輯',
),
h(
NPopconfirm,
{
onPositiveClick: () => handleOpusDeleteRow(row),
onNegativeClick: () => {
window.$message.info('取消删除')
},
},
{
trigger: () =>
h(
'span',
{
style: { marginRight: '20px' },
class: 'text-theme-color cursor-pointer hover:opacity-80',
},
'删除',
),
default: () => '確認要刪除這一行嗎?',
},
),
])
},
},
]
}
<script setup lang="ts">
import { h, onMounted, reactive, Ref, ref, watch } from 'vue'
import { NButton, NEllipsis, NPopconfirm, useDialog, type DataTableColumns } from 'naive-ui'
import { computed, onMounted, Ref, ref } from 'vue'
import { Search, CloseOne } from '@icon-park/vue-next'
import { createData } from './opusData'
import { useUserStore } from '@/store/modules/user'
import { fetchOpusList } from '@/apis/opus'
type opusType = 'IMAGE_VIDEO' | 'BASE_VIDEO' | 'ADVANCED_VIDEO'
type opusStateType = 'SUBMIT' | 'GENERATING' | 'SUCCESS' | 'FAILED'
import { fetchGetTaskList } from '@/apis/opus'
import { createOpusColumns, TaskItem } from './columns'
import useTableScrollY from '@/composables/useTableScrollY'
import { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
interface OpusRowData {
key: number
opusName: string
opusImgUrl: string
state: opusStateType
type: opusType
videoDuration: string
completionTime: string
}
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 28 + 24 + 44 + 48 + 48 + 50)
const dialog = useDialog()
const opusPagination = reactive({
page: 1,
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 20, 30],
prefix: () => h('div', [`共${filteredTemplateData.value.length}條數據`]),
onChange: (page: number) => {
opusPagination.page = page
},
onUpdatePageSize: (pageSize: number) => {
opusPagination.pageSize = pageSize
opusPagination.page = 1
},
})
const userStore = useUserStore()
const token = userStore.token
const templateClassify = [
{ value: 'All', label: '全部' },
const taskTypeList = [
{ value: '', label: '全部' },
{
value: 'IMAGE_VIDEO',
label: '照片數據人',
......@@ -52,11 +25,15 @@ const templateClassify = [
},
]
const opusSelectedTag = ref('All')
const isShowPagination = computed(() => {
return tableContentY.value > 0
})
const currentSelectedTaskType = ref('')
const checkedOpusKeys: Ref<number[]> = ref([])
const opusList = ref(createData())
const searchQuery = ref('')
const filteredTemplateData = ref(opusList.value)
const filteredTemplateData = ref<TaskItem[]>([])
const pagingInfo = ref({
pageNo: 0,
totalPages: 1,
......@@ -70,21 +47,21 @@ const onCheckedOpusKeysChange = (newKeys: number[]) => {
const opusActionColumns = createOpusColumns({
handleOpusDeleteRow(rowData) {
window.$message.info(`删除 ${rowData.key}`)
window.$message.info(`删除 ${rowData.id}`)
},
handleOpusExamine(rowData) {
window.$message.info(`查看 ${rowData.key}`)
window.$message.info(`查看 ${rowData.id}`)
},
handleOpusDownload(rowData) {
window.$message.info(`下载 ${rowData.key}`)
window.$message.info(`下载 ${rowData.id}`)
},
handleOpusEditing(rowData) {
window.$message.info(`编辑 ${rowData.key}`)
window.$message.info(`编辑 ${rowData.id}`)
},
})
const handleMultiSelectDelete = () => {
dialog.warning({
window.$dialog.warning({
title: '提示',
content: '確認刪除嗎?',
positiveText: '確認',
......@@ -99,187 +76,45 @@ const handleMultiSelectDelete = () => {
},
})
}
watch([opusSelectedTag], () => {
handleFilterOpusData()
})
function createOpusColumns({
handleOpusDownload,
handleOpusDeleteRow,
handleOpusExamine,
handleOpusEditing,
}: {
handleOpusDownload: (rowData: OpusRowData) => void
handleOpusDeleteRow: (rowData: OpusRowData) => void
handleOpusExamine: (rowData: OpusRowData) => void
handleOpusEditing: (rowData: OpusRowData) => void
}): DataTableColumns<OpusRowData> {
return [
{
type: 'selection',
fixed: 'left',
},
{
title: '作品名稱',
key: 'opusName',
render(row) {
return h('div', { class: 'flex items-center w-[320px]' }, [
h(
'div',
{ class: 'w-[111px] h-[55px] mr-[10px] flex items-center justify-center overflow-hidden relative' },
[
h('img', {
src: row.opusImgUrl,
alt: '作品圖片',
class: 'h-full object-cover',
}),
],
),
h(NEllipsis, { style: { maxWidth: '150px' } }, { default: () => row.opusName }),
])
},
},
{
title: '狀態',
key: 'state',
render(row) {
const stateMap = {
SUBMIT: { text: '待生成', color: '#BBBBBB' },
GENERATING: { text: '生成中', color: '#1684FC' },
SUCCESS: { text: '生成成功', color: '#26A50E' },
FAILED: { text: '生成失敗', color: '#CB5656' },
}
const state = stateMap[row.state] || { text: '', color: '#CCCCCC' } // 默认灰色
return h('div', { class: 'flex justify-center items-center ' }, [
h('div', {
class: 'w-[10px] h-[10px] rounded-[25px] mr-18px reading-[22px]',
style: { backgroundColor: state.color },
}),
h('span', {}, state.text),
])
},
},
{
title: '類型',
key: 'type',
render(row) {
const typeMap = {
IMAGE_VIDEO: '照片數字人',
BASE_VIDEO: '數字人生成',
ADVANCED_VIDEO: '高級數字人',
}
return h('span', {}, typeMap[row.type] || '')
},
},
{
title: '視頻時長',
key: 'videoDuration',
},
{
title: '完成時間',
key: 'completionTime',
},
{
title: '操作',
key: 'operation',
render(row) {
return h('div', { class: 'operation-buttons' }, [
h(
'span',
{
onClick: () => handleOpusExamine(row),
class: 'text-[#2468f2] cursor-pointer mr-[10px]',
},
'查看',
),
h(
'span',
{
onClick: () => handleOpusDownload(row),
class: 'text-[#2468f2] cursor-pointer mr-[10px]',
},
'下載',
),
h(
'span',
{
onClick: () => handleOpusEditing(row),
class: 'text-[#2468f2] cursor-pointer mr-[10px]',
},
'繼續編輯',
),
h(
NPopconfirm,
{
onPositiveClick: () => handleOpusDeleteRow(row),
onNegativeClick: () => {
window.$message.info('取消删除')
},
},
{
trigger: () =>
h(
'span',
{
class: 'text-[#2468f2] cursor-pointer',
},
'删除',
),
default: () => '確認要刪除這一行嗎?',
},
),
])
},
},
]
}
function handleFilterOpusData() {
let filteredData = opusList.value
if (opusSelectedTag.value !== 'All') {
filteredData = filteredData.filter((item) => item.type === opusSelectedTag.value)
}
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase()
filteredData = filteredData.filter((item) => item.opusName.toLowerCase().includes(query))
}
filteredTemplateData.value = filteredData
}
function handleClearSearchQuery() {
searchQuery.value = ''
handleFilterOpusData()
}
function getOpusList() {
fetchOpusList(token, { pagingInfo: pagingInfo.value }).then((res) => {
if (res.code !== 0) return ''
console.log('作品list', res)
})
async function handleGetTaskList() {
const res = await fetchGetTaskList<TaskItem[]>({ pagingInfo: pagingInfo.value })
if (res.code === 0) {
filteredTemplateData.value = res.data
pagingInfo.value = res.pagingInfo as PaginationInfo
}
}
onMounted(() => {
getOpusList()
handleGetTaskList()
})
function handleGetTaskListUpdatePageNo(pageNo: number) {
pagingInfo.value.pageNo = pageNo
}
function handleGetTaskListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
}
</script>
<template>
<div ref="pageContentWrapRef" class="h-full w-full overflow-y-hidden rounded-[16px] bg-white p-6">
<div class="mb-[24px] cursor-default text-[18px] text-[#000]">我的作品</div>
<div class="flex pb-[16px]">
<div
class="h-full min-w-[1160px] overflow-auto rounded-[16px] bg-white px-[24px] pb-[16px]"
style="scrollbar-width: none"
v-for="taskTypeItem in taskTypeList"
:key="taskTypeItem.value"
class="!h-[28px] cursor-pointer rounded-full !px-[12px] !leading-[28px] hover:text-[#5b647a]"
:class="taskTypeItem.value === currentSelectedTaskType ? 'bg-[#edeef7]' : ''"
>
<div class="overflow-auto bg-white pt-[24px]">
<div class="mb-[24px] cursor-default text-[18px] text-[#000]">我的作品</div>
<div class="pb-[16px]">
<n-radio-group v-model:value="opusSelectedTag">
<n-radio-button
v-for="classify in templateClassify"
:key="classify.value"
class="!h-[28px] !px-[12px] !leading-[28px] hover:text-[#5b647a]"
:value="classify.value"
:label="classify.label"
/>
</n-radio-group>
{{ taskTypeItem.label }}
</div>
</div>
<div class="flex justify-between">
<div>
......@@ -288,11 +123,12 @@ onMounted(() => {
:class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedOpusKeys.length > 0 }"
:disabled="!checkedOpusKeys.length"
@click="handleMultiSelectDelete"
>删除</NButton
>
<span v-show="checkedOpusKeys.length" class="color-[#999] ml-[8px] text-[12px]"
>选择{{ checkedOpusKeys.length }}条记录</span
>
刪除
</NButton>
<span v-show="checkedOpusKeys.length" class="color-[#999] ml-[8px] text-[12px]">
選擇{{ checkedOpusKeys.length }}條記錄
</span>
</div>
<div
......@@ -303,7 +139,6 @@ onMounted(() => {
type="text"
class="mx-[11px] w-[183px] border-none outline-none"
placeholder="請輸入名稱進行蒐索"
@keyup.enter="handleFilterOpusData"
/>
<CloseOne
v-show="searchQuery.length"
......@@ -313,106 +148,37 @@ onMounted(() => {
class="mr-[5px] cursor-pointer"
@click="handleClearSearchQuery"
/>
<Search theme="outline" size="16" fill="#00000040" class="cursor-pointer" @click="handleFilterOpusData" />
<Search theme="outline" size="16" fill="#00000040" class="cursor-pointer" />
</div>
</div>
<div class="relative">
<n-data-table
<div :style="{ height: tableContentY + 48 + 'px' }">
<NDataTable
:bordered="false"
:single-line="false"
:columns="opusActionColumns"
:data="filteredTemplateData"
:checked-row-keys="checkedOpusKeys"
class="border-t-[1px]"
:pagination="opusPagination"
:max-height="tableContentY"
:scroll-x="1286"
@update:checked-row-keys="onCheckedOpusKeysChange"
>
</n-data-table>
<template #empty>
<div :style="{ height: tableContentY + 'px' }" class="flex items-center justify-center">
<div class="flex flex-col items-center justify-center">
<div class="h-[123px] w-[156px] bg-[url('@/assets/images/empty-dialogue.png')] bg-[length:100%_100%]" />
<span class="mt-3 text-[#5b647a]">暫無數據</span>
</div>
</div>
</template>
</NDataTable>
</div>
</template>
<style lang="scss">
.n-radio-group {
--n-button-box-shadow-focus: none !important;
.n-radio-button {
border: none;
border-width: 0 !important;
border-radius: 15px !important;
&:first-child {
border-left: none !important;
}
&:last-child {
border-right: none !important;
}
&.n-radio-button--checked {
color: #091221 !important;
background-color: #edeef7 !important;
border: none !important;
}
&:not(.n-radio-button--disabled):hover:not(.n-radio-button--checked) {
color: #5b647a !important;
}
}
.n-radio-group__splitor {
width: 0 !important;
background-color: #fff !important;
}
}
.n-data-table {
border: none;
.n-data-table-th {
text-align: center !important;
background-color: #f6f7fb !important;
}
.n-data-table-td {
text-align: center !important;
}
.n-data-table-tbody {
font-size: 16px !important;
}
:not(.n-data-table--single-line) {
.n-data-table-th {
border-right: 2px solid #fff !important;
border-bottom: none !important;
}
.n-data-table-td {
border-right: 2px solid #fff0 !important;
border-bottom: none !important;
}
}
}
.v-binder-follower-content {
margin-left: 0 !important;
}
.n-popover__content {
font-size: 12px;
}
.n-popover:not(.n-popover--raw) {
color: #000;
background-color: #fff !important;
}
.n-card.n-card--bordered {
border: none !important;
}
.n-popover-shared .n-popover-arrow-wrapper .n-popover-arrow {
background-color: #fff !important ;
}
</style>
<div v-show="isShowPagination" class="mt-5 flex w-full justify-end">
<CustomPagination
:paging-info="pagingInfo"
@update-page-no="handleGetTaskListUpdatePageNo"
@update-page-size="handleGetTaskListUpdatePageSize"
/>
</div>
</div>
</template>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment