Commit cc768501 authored by nick zheng's avatar nick zheng

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

parent bfc4695a
...@@ -40,7 +40,7 @@ export function deleteBackgroundImageById<T>(id: number) { ...@@ -40,7 +40,7 @@ export function deleteBackgroundImageById<T>(id: number) {
// 根据人物名称分页获取人物信息 // 根据人物名称分页获取人物信息
export function fetchInfoByImageName<T>(imageName: string) { 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 }, pagingInfo: { pageNo: 1, pageSize: 1000 },
}) })
} }
......
...@@ -54,7 +54,7 @@ export function handleUploadBackgroundImageFile<T>(imageName: string, formData: ...@@ -54,7 +54,7 @@ export function handleUploadBackgroundImageFile<T>(imageName: string, formData:
} }
// 根据背景图id删除背景图 // 根据背景图id删除背景图
export function handleDeleteBackgroundImageById<T>(id: number) { export function handleDeleteBackgroundImageById<T>(id: string) {
return request.post<T>(`/bizDigitalHumanMemberImageRest/deletedById.json?id=${id}`) return request.post<T>(`/bizDigitalHumanMemberImageRest/deletedById.json?id=${id}`)
} }
......
import { request } from '@/utils/request' import { request } from '@/utils/request'
export function fetchDraftsList<T>(token: string, queryParams: { pagingInfo: any }) { // 获取草稿列表
return request.post<T>( export function fetchDraftsList<T>(payload: object) {
'/bizDigitalHumanMemberDraftConfigRest/getList.json', return request.post<T>(`/bizDigitalHumanMemberDraftConfigRest/getList.json`, payload)
{}, }
{
headers: { // 保存更新用户草稿
'X-Request-Token': token, export function fetchSaveDraftConfig<T>(payload: object) {
}, return request.post<T>('/bizDigitalHumanMemberDraftConfigRest/saveOrUpdate.json', payload)
params: queryParams.pagingInfo, }
},
) // 通过id删除用户草稿
export function fetchDeleteDraftConfigById<T>(id: string) {
return request.post<T>(`/bizDigitalHumanMemberDraftConfigRest/deletedById.json?id=${id}`)
} }
export function fetchRecentCreationList<T>(token: string) { export function fetchRecentCreationList<T>(token: string) {
......
import { request } from '@/utils/request' import { request } from '@/utils/request'
export function fetchOpusList<T>(token: string, queryParams: { pagingInfo: any }) { export function fetchGetTaskList<T>(payload: object) {
return request.post<T>( return request.post<T>('/bizDigitalHumanMemberTaskStatusRest/getMemberTaskStatusList.json', payload)
'/bizDigitalHumanMemberTaskStatusRest/getMemberTaskStatusList.json',
{},
{
headers: {
'X-Request-Token': token,
},
params: queryParams.pagingInfo,
},
)
} }
...@@ -113,7 +113,7 @@ function handleDelete(id: number) { ...@@ -113,7 +113,7 @@ function handleDelete(id: number) {
positiveText: '確認', positiveText: '確認',
negativeText: '取消', negativeText: '取消',
onPositiveClick: async () => { onPositiveClick: async () => {
const res = await handleDeleteBackgroundImageById(id) const res = await handleDeleteBackgroundImageById(`${id}`)
if (res.code === 0) { if (res.code === 0) {
window.$message.success('刪除成功') window.$message.success('刪除成功')
......
...@@ -19,6 +19,7 @@ const twoDFewShotImageList = ref<DigitalImageItem[]>([]) ...@@ -19,6 +19,7 @@ const twoDFewShotImageList = ref<DigitalImageItem[]>([])
const allImageList = ref<DigitalImageItem[]>([]) const allImageList = ref<DigitalImageItem[]>([])
const showAll = ref(false) const showAll = ref(false)
const searchImageName = ref('') const searchImageName = ref('')
const currentImageType = ref<ImageType>(ImageType.THREE_D)
onMounted(() => { onMounted(() => {
getDigitalImageList() getDigitalImageList()
...@@ -38,7 +39,7 @@ async function getDigitalImageList() { ...@@ -38,7 +39,7 @@ async function getDigitalImageList() {
async function handleSearchImage() { async function handleSearchImage() {
const res = await fetchInfoByImageName<DigitalImageItem[]>(searchImageName.value) const res = await fetchInfoByImageName<DigitalImageItem[]>(searchImageName.value)
if (res.code === 0) { 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) { ...@@ -47,6 +48,7 @@ function handleClickDigitalImage(figureId: string) {
} }
function handleClickAll(imageType: ImageType) { function handleClickAll(imageType: ImageType) {
currentImageType.value = imageType
switch (imageType) { switch (imageType) {
case ImageType.THREE_D: case ImageType.THREE_D:
allImageList.value = threeDImageList.value allImageList.value = threeDImageList.value
...@@ -119,13 +121,7 @@ function handleClickAll(imageType: ImageType) { ...@@ -119,13 +121,7 @@ function handleClickAll(imageType: ImageType) {
</template> </template>
返回 返回
</NButton> </NButton>
<NInput <NInput v-model:value="searchImageName" round placeholder="輸入名稱搜索" @input="handleSearchImage">
v-model:value="searchImageName"
round
placeholder="輸入名稱搜索"
@blur="handleSearchImage"
@keyup.enter="handleSearchImage"
>
<template #prefix> <template #prefix>
<CustomIcon class="text-lg" icon="mingcute:search-line" /> <CustomIcon class="text-lg" icon="mingcute:search-line" />
</template> </template>
......
<script setup lang="ts"> <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 { useRouter } from 'vue-router'
import CustomIcon from '@/components/custom-icon/custom-icon.vue' import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import PublishDigitalHumanDialogueModal from '../components/publish-digital-human-dialogue-modal.vue' import PublishDigitalHumanDialogueModal from '../components/publish-digital-human-dialogue-modal.vue'
...@@ -45,6 +45,10 @@ onMounted(() => { ...@@ -45,6 +45,10 @@ onMounted(() => {
router.replace({ name: 'Root' }) router.replace({ name: 'Root' })
}) })
onUnmounted(() => {
digitalHumanDialogueStore.resetDigitalHumanDialogue()
})
async function handleGetDigitalHumanDialogueConfig() { async function handleGetDigitalHumanDialogueConfig() {
fetchGetDigitalHumanDialogueConfigByConfigId<DigitalHumanDialogueConfig>(currentConfigId.value) fetchGetDigitalHumanDialogueConfigByConfigId<DigitalHumanDialogueConfig>(currentConfigId.value)
.then(async (res) => { .then(async (res) => {
......
...@@ -38,7 +38,7 @@ export function createDiaglogueTableColumn( ...@@ -38,7 +38,7 @@ export function createDiaglogueTableColumn(
}, },
{ {
title: '編輯時間', title: '最後編輯時間',
key: 'modifiedTime', key: 'modifiedTime',
align: 'left', align: 'left',
ellipsis: { ellipsis: {
...@@ -75,7 +75,7 @@ export function createDiaglogueTableColumn( ...@@ -75,7 +75,7 @@ export function createDiaglogueTableColumn(
value: row.isOpen === 'Y', value: row.isOpen === 'Y',
onUpdateValue: () => handleDiaglogueAction('updateOpen', row.configId, row), onUpdateValue: () => handleDiaglogueAction('updateOpen', row.configId, row),
}, },
{}, { checked: () => '啓用', unchecked: () => '停用' },
) )
}, },
}, },
......
...@@ -41,7 +41,7 @@ const showModal = computed({ ...@@ -41,7 +41,7 @@ const showModal = computed({
watch( watch(
() => props.configTitle, () => props.configTitle,
(newValue) => { (newValue) => {
digitalHumanDialogueFormData.title = newValue + '副本' digitalHumanDialogueFormData.title = (newValue + '副本').slice(0, 30)
}, },
) )
...@@ -60,6 +60,7 @@ function handleAddDigitalHumanDialogue() { ...@@ -60,6 +60,7 @@ function handleAddDigitalHumanDialogue() {
:title="modalTitle" :title="modalTitle"
:width="448" :width="448"
:height="220" :height="220"
:btn-loading="btnLoading"
@confirm="handleAddDigitalHumanDialogue" @confirm="handleAddDigitalHumanDialogue"
> >
<template #content> <template #content>
......
...@@ -66,6 +66,7 @@ async function handleGetDiaglogList() { ...@@ -66,6 +66,7 @@ async function handleGetDiaglogList() {
if (res.code === 0) { if (res.code === 0) {
digitalHumanDialogueList.value = res.data digitalHumanDialogueList.value = res.data
pagingInfo.value = res.pagingInfo as PaginationInfo pagingInfo.value = res.pagingInfo as PaginationInfo
checkedConfigIdList.value = []
dialogueTableLoading.value = false dialogueTableLoading.value = false
} }
} }
...@@ -91,6 +92,7 @@ async function handleMultiDeleteDiaglogueConfig() { ...@@ -91,6 +92,7 @@ async function handleMultiDeleteDiaglogueConfig() {
function handleClearSearchQuery() { function handleClearSearchQuery() {
searchQuery.value = '' searchQuery.value = ''
pagingInfo.value.pageNo = 1
handleGetDiaglogList() handleGetDiaglogList()
} }
...@@ -173,12 +175,15 @@ async function handleAddDigitalHumanDialogue(title: string) { ...@@ -173,12 +175,15 @@ async function handleAddDigitalHumanDialogue(title: string) {
copyDiaglogueConfig.value!.baseInfo.configId = '' copyDiaglogueConfig.value!.baseInfo.configId = ''
copyDiaglogueConfig.value!.baseInfo.title = title copyDiaglogueConfig.value!.baseInfo.title = title
copyDiaglogueConfig.value!.baseInfo.publishStatus = 'N'
copyDiaglogueConfig.value!.baseInfo.isOpen = 'N'
const res = await fetchSaveDigitalHumanDialogueConfig(copyDiaglogueConfig.value!).finally( const res = await fetchSaveDigitalHumanDialogueConfig(copyDiaglogueConfig.value!).finally(
() => (editDigitalHumanDialogueBtnLoading.value = false), () => (editDigitalHumanDialogueBtnLoading.value = false),
) )
if (res.code === 0) { if (res.code === 0) {
window.$message.success('提交成功')
isShowEditDigitalHumanDialogueModal.value = false isShowEditDigitalHumanDialogueModal.value = false
await handleGetDiaglogList() await handleGetDiaglogList()
} }
...@@ -219,7 +224,7 @@ function handleGetDialogueListUpdatePageSize(pageSize: number) { ...@@ -219,7 +224,7 @@ function handleGetDialogueListUpdatePageSize(pageSize: number) {
</script> </script>
<template> <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> <p class="mb-6 select-none text-lg text-[#333]">我的對話</p>
<div class="flex justify-between"> <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"> <script setup lang="ts">
import { h, onMounted, reactive, Ref, ref, watch } from 'vue' import { computed, onMounted, ref } from 'vue'
import { NButton, NEllipsis, NPopconfirm, useDialog, type DataTableColumns } from 'naive-ui' import { useRouter } from 'vue-router'
import { Search, CloseOne } from '@icon-park/vue-next' import { Search, CloseOne } from '@icon-park/vue-next'
import { createData } from './draftsData' import useTableScrollY from '@/composables/useTableScrollY'
import { fetchDraftsList } from '@/apis/drafts' import CustomPagination, { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
import { useUserStore } from '@/store/modules/user' import { fetchDeleteDraftConfigById, fetchDraftsList } from '@/apis/drafts'
type draftType = 'IMAGE_VIDEO' | 'BASE_VIDEO' | 'ADVANCED_VIDEO' import { createDarftTableColumns } from './columns'
import CreateCopyDraftModal from './components/create-copy-draft-modal.vue'
interface draftRowData { import { DraftConfig } from '@/store/types/creation'
key: number import { saveDraftConfig } from '@/apis/digital-creation'
deaftsName: string
draftsImgUrl: string
type: draftType
LastEditTime: string
}
const dialog = useDialog() const router = useRouter()
const opusPagination = reactive({
page: 1, const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 28 + 24 + 44 + 48 + 48 + 50)
pageSize: 10,
showSizePicker: true, const taskTypeList = [
pageSizes: [10, 20, 30], { value: '', label: '全部' },
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: '全部' },
{ {
value: 'IMAGE_VIDEO', value: 'IMAGE_VIDEO',
label: '照片數據人', label: '照片數據人',
...@@ -48,212 +30,182 @@ const templateClassify = [ ...@@ -48,212 +30,182 @@ const templateClassify = [
}, },
] ]
const opusSelectedTag = ref('All') const currentSelectedTaskType = ref('')
const checkedOpusKeys: Ref<number[]> = ref([]) const checkedDraftIdList = ref<number[]>([])
const opusList = ref(createData())
const searchQuery = ref('') const searchQuery = ref('')
const filteredTemplateData = ref(opusList.value) const audioDraftTableLoading = ref(false)
const pagingInfo = ref({ const audioDraftList = ref<DraftConfig[]>([])
pageNo: 0,
totalPages: 1, const isShowCreateCopyDraftModal = ref(false)
const createCopyDraftBtnLoading = ref(false)
const copyDraftConfig = ref<DraftConfig>()
const pagingInfo = ref<PaginationInfo>({
pageNo: 1,
pageSize: 10, pageSize: 10,
totalRows: 2, totalPages: 0,
totalRows: 0,
}) })
const onCheckedOpusKeysChange = (newKeys: number[]) => { const isShowPagination = computed(() => {
checkedOpusKeys.value = newKeys return tableContentY.value > 0
} })
const opusActionColumns = createOpusColumns({ onMounted(() => {
handleOpusDeleteRow(rowData) { handleGetDraftList()
window.$message.info(`删除 ${rowData.key}`)
},
handleDraftsEdit(rowData) {
window.$message.info(`編輯 ${rowData.key}`)
},
handleDraftsCreatCopy(rowData) {
window.$message.info(`創建副本 ${rowData.key}`)
},
}) })
const handleMultiSelectDelete = () => { const darftTableColumns = createDarftTableColumns({
dialog.warning({ async handleDeleteDraft(rowData) {
title: '提示', window.$dialog.warning({
content: '確認刪除嗎?', title: '請確認是否刪除',
positiveText: '確認', content: '刪除後數據不可恢復',
negativeText: '取消', negativeText: '取消',
onPositiveClick: () => { positiveText: '確認',
opusList.value = opusList.value.filter((item) => !checkedOpusKeys.value.includes(item.key)) onPositiveClick: async () => {
checkedOpusKeys.value = [] const res = await fetchDeleteDraftConfigById(rowData!.id! + '')
window.$message.success('删除成功')
}, if (res.code === 0) {
onNegativeClick: () => { window.$message.success('刪除成功')
window.$message.error('取消') audioDraftList.value.length === 1 && (pagingInfo.value.pageNo = pagingInfo.value.pageNo - 1)
}, await handleGetDraftList()
})
}
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: '高級數字人',
} }
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',
}, },
'删除', handleEditDraft(rowData) {
), router.push({
default: () => '確認要刪除這一行嗎?', 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() { const payload: DraftConfig = JSON.parse(JSON.stringify(copyDraftConfig.value))
let filteredData = opusList.value
if (opusSelectedTag.value !== 'All') { payload.id = null
filteredData = filteredData.filter((item) => item.type === opusSelectedTag.value) 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) { if (res.code === 0) {
const query = searchQuery.value.toLowerCase() checkedDraftIdList.value = []
filteredData = filteredData.filter((item) => item.deaftsName.toLowerCase().includes(query)) window.$message.success('删除成功')
await handleGetDraftList()
} }
}
},
})
}
filteredTemplateData.value = filteredData function handleSearchDraftList() {
pagingInfo.value.pageNo = 1
handleGetDraftList()
} }
function handleClearSearchQuery() { function handleClearSearchQuery() {
searchQuery.value = '' searchQuery.value = ''
handleFilterOpusData() pagingInfo.value.pageNo = 1
handleGetDraftList()
}
function handleUpdateCheckedDraftList(draftIdList: number[]) {
checkedDraftIdList.value = draftIdList
} }
function getDraftsList() { async function handleGetDraftList() {
fetchDraftsList(token, { pagingInfo: pagingInfo.value }).then((res) => { audioDraftTableLoading.value = true
if (res.code !== 0) return '' const res = await fetchDraftsList<DraftConfig[]>({
console.log('草稿箱list', res) 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(() => { function handleGetDraftListUpdatePageNo(pageNo: number) {
getDraftsList() pagingInfo.value.pageNo = pageNo
}) handleGetDraftList()
}
function handleGetDraftListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
handleGetDraftList()
}
</script> </script>
<template> <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 <div
class="h-full min-h-[840px] min-w-[1160px] overflow-auto rounded-[16px] bg-white px-[24px] pb-[16px]" v-for="taskTypeItem in taskTypeList"
style="scrollbar-width: none" :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]"> {{ taskTypeItem.label }}
<div class="mb-[24px] cursor-default text-[18px] text-[#000]">草稿箱</div> </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>
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
<NButton <NButton
class="!rounded-[4px]" class="!rounded-[4px]"
:class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedOpusKeys.length > 0 }" :class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedDraftIdList.length > 0 }"
:disabled="!checkedOpusKeys.length" :disabled="!checkedDraftIdList.length"
@click="handleMultiSelectDelete" @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>
<div <div
...@@ -264,7 +216,7 @@ onMounted(() => { ...@@ -264,7 +216,7 @@ onMounted(() => {
type="text" type="text"
class="mx-[11px] w-[183px] border-none outline-none" class="mx-[11px] w-[183px] border-none outline-none"
placeholder="請輸入名稱進行蒐索" placeholder="請輸入名稱進行蒐索"
@keyup.enter="handleFilterOpusData" @keyup.enter="handleSearchDraftList"
/> />
<CloseOne <CloseOne
v-show="searchQuery.length" v-show="searchQuery.length"
...@@ -274,106 +226,47 @@ onMounted(() => { ...@@ -274,106 +226,47 @@ onMounted(() => {
class="mr-[5px] cursor-pointer" class="mr-[5px] cursor-pointer"
@click="handleClearSearchQuery" @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> </div>
<div class="relative">
<n-data-table <div :style="{ height: tableContentY + 48 + 'px' }">
<NDataTable
:loading="audioDraftTableLoading"
:bordered="false" :bordered="false"
:single-line="false" :single-line="false"
:columns="opusActionColumns" :row-key="(row: DraftConfig) => row.id"
:data="filteredTemplateData" :columns="darftTableColumns"
:checked-row-keys="checkedOpusKeys" :data="audioDraftList"
class="border-t-[1px]" :max-height="tableContentY"
:pagination="opusPagination" :scroll-x="1086"
@update:checked-row-keys="onCheckedOpusKeysChange" @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>
</div> </div>
</template>
</NDataTable>
</div> </div>
</template>
<style lang="scss">
.n-radio-group {
--n-button-box-shadow-focus: none !important;
.n-radio-button { <div v-show="isShowPagination" class="mt-5 flex w-full justify-end">
border: none; <CustomPagination
border-width: 0 !important; :paging-info="pagingInfo"
border-radius: 15px !important; @update-page-no="handleGetDraftListUpdatePageNo"
@update-page-size="handleGetDraftListUpdatePageSize"
&:first-child { />
border-left: none !important; </div>
} </div>
&: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 { <CreateCopyDraftModal
background-color: #fff !important ; v-model:is-show-modal="isShowCreateCopyDraftModal"
} modal-title="編輯新草稿名稱"
</style> :draft-name="copyDraftConfig?.draftName || ''"
:btn-loading="createCopyDraftBtnLoading"
@comfirm="handleCreateCopyDraft"
/>
</template>
...@@ -3,6 +3,13 @@ import { fetchUniversalCurrency } from '@/apis/user' ...@@ -3,6 +3,13 @@ import { fetchUniversalCurrency } from '@/apis/user'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { Right } from '@icon-park/vue-next' import { Right } from '@icon-park/vue-next'
import { onMounted, ref } from 'vue' 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 UniversalCurrency = ref()
const userStore = useUserStore() const userStore = useUserStore()
...@@ -26,6 +33,30 @@ function handleRechargeCurrency() { ...@@ -26,6 +33,30 @@ function handleRechargeCurrency() {
onMounted(() => { onMounted(() => {
getUniversalCurrency() 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> </script>
<template> <template>
...@@ -40,7 +71,7 @@ onMounted(() => { ...@@ -40,7 +71,7 @@ onMounted(() => {
</div> </div>
<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]" 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> <span>立即創作</span>
</div> </div>
...@@ -109,4 +140,11 @@ onMounted(() => { ...@@ -109,4 +140,11 @@ onMounted(() => {
</div> </div>
</div> </div>
</div> </div>
<CreateDigitalHumanDialogueModal
v-model:is-show-modal="isShow"
modal-title="测试"
:btn-loading="false"
@comfirm="handleCreateDigitalHumanDialogue"
/>
</template> </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"> <script setup lang="ts">
import { h, onMounted, reactive, Ref, ref, watch } from 'vue' import { computed, onMounted, Ref, ref } from 'vue'
import { NButton, NEllipsis, NPopconfirm, useDialog, type DataTableColumns } from 'naive-ui'
import { Search, CloseOne } from '@icon-park/vue-next' import { Search, CloseOne } from '@icon-park/vue-next'
import { createData } from './opusData' import { createData } from './opusData'
import { useUserStore } from '@/store/modules/user' import { fetchGetTaskList } from '@/apis/opus'
import { fetchOpusList } from '@/apis/opus' import { createOpusColumns, TaskItem } from './columns'
type opusType = 'IMAGE_VIDEO' | 'BASE_VIDEO' | 'ADVANCED_VIDEO' import useTableScrollY from '@/composables/useTableScrollY'
type opusStateType = 'SUBMIT' | 'GENERATING' | 'SUCCESS' | 'FAILED' import { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
interface OpusRowData { const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 28 + 24 + 44 + 48 + 48 + 50)
key: number
opusName: string
opusImgUrl: string
state: opusStateType
type: opusType
videoDuration: string
completionTime: string
}
const dialog = useDialog() const taskTypeList = [
const opusPagination = reactive({ { value: '', label: '全部' },
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: '全部' },
{ {
value: 'IMAGE_VIDEO', value: 'IMAGE_VIDEO',
label: '照片數據人', label: '照片數據人',
...@@ -52,11 +25,15 @@ const templateClassify = [ ...@@ -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 checkedOpusKeys: Ref<number[]> = ref([])
const opusList = ref(createData()) const opusList = ref(createData())
const searchQuery = ref('') const searchQuery = ref('')
const filteredTemplateData = ref(opusList.value) const filteredTemplateData = ref<TaskItem[]>([])
const pagingInfo = ref({ const pagingInfo = ref({
pageNo: 0, pageNo: 0,
totalPages: 1, totalPages: 1,
...@@ -70,21 +47,21 @@ const onCheckedOpusKeysChange = (newKeys: number[]) => { ...@@ -70,21 +47,21 @@ const onCheckedOpusKeysChange = (newKeys: number[]) => {
const opusActionColumns = createOpusColumns({ const opusActionColumns = createOpusColumns({
handleOpusDeleteRow(rowData) { handleOpusDeleteRow(rowData) {
window.$message.info(`删除 ${rowData.key}`) window.$message.info(`删除 ${rowData.id}`)
}, },
handleOpusExamine(rowData) { handleOpusExamine(rowData) {
window.$message.info(`查看 ${rowData.key}`) window.$message.info(`查看 ${rowData.id}`)
}, },
handleOpusDownload(rowData) { handleOpusDownload(rowData) {
window.$message.info(`下载 ${rowData.key}`) window.$message.info(`下载 ${rowData.id}`)
}, },
handleOpusEditing(rowData) { handleOpusEditing(rowData) {
window.$message.info(`编辑 ${rowData.key}`) window.$message.info(`编辑 ${rowData.id}`)
}, },
}) })
const handleMultiSelectDelete = () => { const handleMultiSelectDelete = () => {
dialog.warning({ window.$dialog.warning({
title: '提示', title: '提示',
content: '確認刪除嗎?', content: '確認刪除嗎?',
positiveText: '確認', positiveText: '確認',
...@@ -99,187 +76,45 @@ const handleMultiSelectDelete = () => { ...@@ -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() { function handleClearSearchQuery() {
searchQuery.value = '' searchQuery.value = ''
handleFilterOpusData()
} }
function getOpusList() { async function handleGetTaskList() {
fetchOpusList(token, { pagingInfo: pagingInfo.value }).then((res) => { const res = await fetchGetTaskList<TaskItem[]>({ pagingInfo: pagingInfo.value })
if (res.code !== 0) return ''
console.log('作品list', res) if (res.code === 0) {
}) filteredTemplateData.value = res.data
pagingInfo.value = res.pagingInfo as PaginationInfo
}
} }
onMounted(() => { onMounted(() => {
getOpusList() handleGetTaskList()
}) })
function handleGetTaskListUpdatePageNo(pageNo: number) {
pagingInfo.value.pageNo = pageNo
}
function handleGetTaskListUpdatePageSize(pageSize: number) {
pagingInfo.value.pageNo = 1
pagingInfo.value.pageSize = pageSize
}
</script> </script>
<template> <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 <div
class="h-full min-w-[1160px] overflow-auto rounded-[16px] bg-white px-[24px] pb-[16px]" v-for="taskTypeItem in taskTypeList"
style="scrollbar-width: none" :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]"> {{ taskTypeItem.label }}
<div class="mb-[24px] cursor-default text-[18px] text-[#000]">我的作品</div> </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>
</div> </div>
<div class="flex justify-between"> <div class="flex justify-between">
<div> <div>
...@@ -288,11 +123,12 @@ onMounted(() => { ...@@ -288,11 +123,12 @@ onMounted(() => {
:class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedOpusKeys.length > 0 }" :class="{ '!border-[#2468f2] !bg-[#2468f2] !text-white': checkedOpusKeys.length > 0 }"
:disabled="!checkedOpusKeys.length" :disabled="!checkedOpusKeys.length"
@click="handleMultiSelectDelete" @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>
<div <div
...@@ -303,7 +139,6 @@ onMounted(() => { ...@@ -303,7 +139,6 @@ onMounted(() => {
type="text" type="text"
class="mx-[11px] w-[183px] border-none outline-none" class="mx-[11px] w-[183px] border-none outline-none"
placeholder="請輸入名稱進行蒐索" placeholder="請輸入名稱進行蒐索"
@keyup.enter="handleFilterOpusData"
/> />
<CloseOne <CloseOne
v-show="searchQuery.length" v-show="searchQuery.length"
...@@ -313,106 +148,37 @@ onMounted(() => { ...@@ -313,106 +148,37 @@ onMounted(() => {
class="mr-[5px] cursor-pointer" class="mr-[5px] cursor-pointer"
@click="handleClearSearchQuery" @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> </div>
<div class="relative"> <div :style="{ height: tableContentY + 48 + 'px' }">
<n-data-table <NDataTable
:bordered="false" :bordered="false"
:single-line="false" :single-line="false"
:columns="opusActionColumns" :columns="opusActionColumns"
:data="filteredTemplateData" :data="filteredTemplateData"
:checked-row-keys="checkedOpusKeys" :checked-row-keys="checkedOpusKeys"
class="border-t-[1px]" :max-height="tableContentY"
:pagination="opusPagination" :scroll-x="1286"
@update:checked-row-keys="onCheckedOpusKeysChange" @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>
</div> </div>
</template>
</NDataTable>
</div> </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 { <div v-show="isShowPagination" class="mt-5 flex w-full justify-end">
border: none; <CustomPagination
:paging-info="pagingInfo"
.n-data-table-th { @update-page-no="handleGetTaskListUpdatePageNo"
text-align: center !important; @update-page-size="handleGetTaskListUpdatePageSize"
background-color: #f6f7fb !important; />
} </div>
</div>
.n-data-table-td { </template>
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>
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