From ce5d4a0d224aaab33b4d8ddd7b4af170882d406e Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期二, 04 三月 2025 14:30:28 +0800 Subject: [PATCH] 预览业务表格 --- src/components/table/colFilter/ColFilter.vue | 6 + src/components/chat/components/playBar/PlayBar.vue | 26 ++++- src/components/chat/user/index.vue | 18 +++ src/components/chat/components/playBar/businessTable/index.vue | 59 ++++++++--- src/components/chat/components/playBar/businessTablePreview/index.vue | 153 ++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+), 25 deletions(-) diff --git a/src/components/chat/components/playBar/PlayBar.vue b/src/components/chat/components/playBar/PlayBar.vue index 104d981..f1874bd 100644 --- a/src/components/chat/components/playBar/PlayBar.vue +++ b/src/components/chat/components/playBar/PlayBar.vue @@ -25,12 +25,14 @@ </el-button> </div> - <div class="set-input "> + <div class="set-input"> <div v-if="attachList?.length > 0" class="flex gap-3.5 w-full overflow-x-auto px-2 pb-2"> <div v-for="(item, index) in attachList" :key="index" class="flex items-center gap-2 bg-[#f5f5f5] px-2 py-3 rounded-lg w-[220px] relative group" + :class="{ 'cursor-pointer': item.type === 'table' }" + @click="openAttachPreview(item)" > <template v-if="item.type === 'file'"> <el-image @@ -56,7 +58,7 @@ </template> <div class="group-hover:visible invisible absolute right-0.5 top-0.5 bg-red-500 flex-center rounded-full p-0.5 cursor-pointer" - @click="deleteAttachInIndex(index)" + @click.stop="deleteAttachInIndex(index)" > <span class="ywifont ywicon-guanbi text-white !text-[10px] font-bold"></span> </div> @@ -142,6 +144,7 @@ @updateInput="updateInputValue" /> <BusinessTable v-model="businessTableIsShow" @submit="submitBusinessTable" /> + <BusinessTablePreview :data="attachPreviewData" v-model="attachPreviewIsShow" @submit="submitBusinessTable" /> </div> </div> </template> @@ -149,15 +152,18 @@ <script setup lang="ts"> import type { InputInstance } from 'element-plus'; import { nextTick, ref } from 'vue'; +import BusinessTable from './businessTable/index.vue'; +import BusinessTablePreview from './businessTablePreview/index.vue'; +import type { Attach } from './hook/useAttach'; +import { useAttach } from './hook/useAttach'; import { useInputEvent } from './hook/useInputEvent'; +import { useUploadFile } from './hook/useUploadFile'; import InputTip from './inputTip/index.vue'; import CommonPhrases from './phrase/CommonPhrases.vue'; import SceneSwitch from './SceneSwitch.vue'; import VoicePage from './voicePage/VoicePage.vue'; import { useCompRef } from '/@/utils/types'; -import { useUploadFile } from './hook/useUploadFile'; -import BusinessTable from './businessTable/index.vue'; -import { useAttach } from './hook/useAttach'; + const emits = defineEmits(['sendClick', 'stopGenClick']); const props = defineProps({ isTalking: Boolean, @@ -269,6 +275,16 @@ businessTableIsShow.value = true; }; //#endregion + +//#region ====================== 闄勪欢棰勮 ====================== +const attachPreviewIsShow = ref(false); +const attachPreviewData = ref<Attach>(); +const openAttachPreview = (item: Attach) => { + if (item.type === 'file') return; + attachPreviewIsShow.value = true; + attachPreviewData.value = item; +}; +//#endregion defineExpose({ addPhrase, showSyncTip, diff --git a/src/components/chat/components/playBar/businessTable/index.vue b/src/components/chat/components/playBar/businessTable/index.vue index e886004..369b1d3 100644 --- a/src/components/chat/components/playBar/businessTable/index.vue +++ b/src/components/chat/components/playBar/businessTable/index.vue @@ -46,19 +46,19 @@ </template> <template #main> <div class="w100 h100"> - <div class="h-full"> + <div class="h-full flex-column"> + <ColFilter class="flex-0 ml-auto mb-2" :column-list="tableCheckData" /> <el-table v-loading="tableLoading" ref="draggableTableRef" - class="h100" + class="flex-auto" border - :row-class-name="isDragStatus ? 'cursor-move' : 'cursor-pointer'" :data="tableData" highlight-current-row @sort-change="handleSortChange" > <el-table-column - v-for="item in tableColumns" + v-for="item in visibleTableColumns" :prop="item.name" sortable="custom" :key="item.name" @@ -89,6 +89,7 @@ import { useCompRef } from '/@/utils/types'; import { convertListToTree, debounce, travelTree } from '/@/utils/util'; import { ElMessage } from 'element-plus'; +import ColFilter from '/@/components/table/colFilter/ColFilter.vue'; const dialogIsShow = defineModel({ type: Boolean, }); @@ -106,13 +107,18 @@ submitLoading.value = false; }); const tables = checkedList.map((item) => { + const indexMapItem = getIndexMapItem(item.columns); return { title: item.title, - columns: item.columns.map((item) => { - return item.title; - }), + columns: item.columns + .filter((item) => item.isShow) + .map((item) => { + return item.title; + }), values: item.tableData.map((item) => { - return Object.values(item); + return Object.values(item).filter((item, index) => { + return indexMapItem.get(index).isShow; + }); }), }; }); @@ -121,7 +127,6 @@ }; const submitFormValue = async () => { const data = await getSubmitData(); - emit('submit', data); dialogIsShow.value = false; }; @@ -169,6 +174,7 @@ ...item, // 鍒濆鏌ヨ鍊� values: [''], + isShow: true, }; }); } @@ -221,10 +227,28 @@ const tableData = computed(() => { return currentNode.value?.tableData || []; }); -const isDragStatus = ref(false); const tableColumns = computed(() => { return currentNode.value?.columns || []; +}); + +const tableCheckData = computed(() => { + return tableColumns.value.map((item) => { + return { + prop: item.name, + label: item.title, + get isShow() { + return item.isShow; + }, + set isShow(value) { + item.isShow = value; + }, + }; + }); +}); + +const visibleTableColumns = computed(() => { + return tableColumns.value.filter((item) => item.isShow); }); const getTableData = async () => { // allTableData.value = (res.values || []).map((item) => { @@ -249,16 +273,9 @@ ); }; -const indexMapItem = computed<Map<number, any>>(() => { - return new Map( - tableColumns.value.map((item, index) => { - return [index, item]; - }) - ); -}); //#region ====================== 鏌ヨ ====================== const getFilterColumns = (node) => { - return node?.columns?.filter((item) => item.filter) as any[]; + return node?.columns?.filter((item) => item.filter && item.isShow) as any[]; }; const filterColumns = computed(() => { return getFilterColumns(currentNode.value); @@ -432,4 +449,10 @@ height: calc(90vh - 111px) !important; } } + +.yw-layout-main { + .el-card__body { + padding: 10px; + } +} </style> diff --git a/src/components/chat/components/playBar/businessTablePreview/index.vue b/src/components/chat/components/playBar/businessTablePreview/index.vue new file mode 100644 index 0000000..3e2dcc6 --- /dev/null +++ b/src/components/chat/components/playBar/businessTablePreview/index.vue @@ -0,0 +1,153 @@ +<template> + <el-dialog + class="limit-height" + :destroy-on-close="true" + v-model="dialogIsShow" + width="70%" + :close-on-click-modal="false" + @closed="closeDialog" + > + <template #header> + <div style="color: #fff"> + <SvgIcon name="ele-Document" :size="16" style="margin-right: 3px; display: inline; vertical-align: middle" /> + <span>鏌ョ湅涓氬姟琛�</span> + </div> + </template> + <div class="w100 h100"> + <div class="h-full flex-column"> + <ColFilter class="flex-0 ml-auto mb-2" :column-list="tableCheckData" /> + <el-table v-loading="tableLoading" ref="draggableTableRef" class="flex-auto" border :data="tableData" highlight-current-row> + <el-table-column + v-for="item in visibleTableColumns" + :prop="item.name" + sortable + :key="item.name" + :label="item.title" + show-overflow-tooltip + > + </el-table-column> + </el-table> + </div> + </div> + </el-dialog> +</template> + +<script setup lang="ts" name="BusinessTablePreview"> +import type { PropType } from 'vue'; +import { computed, ref, watch } from 'vue'; +// import TableSearch from './search/index.vue'; +import type { Attach } from '../hook/useAttach'; +import ColFilter from '/@/components/table/colFilter/ColFilter.vue'; +const dialogIsShow = defineModel({ + type: Boolean, +}); +const props = defineProps({ + data: { + type: Object as PropType<Attach>, + default: () => {}, + }, +}); +const closeDialog = () => { + dialogIsShow.value = false; + tableData.value = []; + tableColumns.value = []; +}; + +//#region ====================== 鎸囨爣绠$悊琛ㄦ牸鏁版嵁锛宼able init ====================== +const tableLoading = ref(false); +const tableData = ref([]); + +const tableColumns = ref([]); + +const tableCheckData = computed(() => { + return tableColumns.value.map((item) => { + return { + prop: item.name, + label: item.title, + get isShow() { + return item.isShow; + }, + set isShow(value) { + item.isShow = value; + }, + }; + }); +}); + +const visibleTableColumns = computed(() => { + return tableColumns.value.filter((item) => item.isShow); +}); + +//#endregion + +const getIndexMapItem = (columns) => { + return new Map<number, any>( + columns.map((item, index) => { + return [index, item]; + }) + ); +}; + +//#region ====================== 鏌ヨ ====================== + +const parseRecordData = (values, columns) => { + const indexMapItem = getIndexMapItem(columns); + return (values || []).map((item, index) => { + const row = {} as any; + item?.forEach((item, index) => { + row[indexMapItem.get(index).name] = item; + }); + return row; + }); +}; + +//#endregion +watch( + () => dialogIsShow.value, + (val) => { + if (!val) return; + tableColumns.value = (props.data?.model?.columns || []).map((item) => { + return { + name: item, + title: item, + isShow: true, + }; + }); + tableData.value = parseRecordData(props.data?.model?.values || [], tableColumns.value); + } +); +</script> +<style scoped lang="scss"> +.set-permission { + padding-block: 16px; + padding-block-end: 0; +} +.set-form { + display: block; + box-sizing: border-box; + height: 100%; + padding-block: 16px; + .set-form-item { + font-weight: 500; + color: #667085; + font-size: 14px; + } +} + +:deep(.el-dialog__body) { + height: calc(90vh - 111px) !important; +} +</style> +<style lang="scss"> +.limit-height { + .el-dialog__body { + height: calc(90vh - 111px) !important; + } +} + +.yw-layout-main { + .el-card__body { + padding: 10px; + } +} +</style> diff --git a/src/components/chat/user/index.vue b/src/components/chat/user/index.vue index 3162078..f07fa3a 100644 --- a/src/components/chat/user/index.vue +++ b/src/components/chat/user/index.vue @@ -11,6 +11,8 @@ v-for="(item, index) in msg.attachList" :key="index" class="flex items-center gap-2 bg-[#e9e9e9] px-2 py-3 rounded-lg w-[220px] relative group" + @click="openAttachPreview(item)" + :class="{ 'cursor-pointer': item.type === 'table' }" > <template v-if="item.type === 'file'"> <el-image @@ -42,6 +44,7 @@ </div> </div> </div> + <BusinessTablePreview :data="attachPreviewData" v-model="attachPreviewIsShow" /> <!-- #endregion --> <div class="flex px-4 rounded-lg relative flex-row-reverse items-center" :key="`${msg.historyId}_${msg.role}`"> @@ -103,10 +106,13 @@ </template> <script setup lang="ts" name="UserMsg"> +import { useClipboard } from '@vueuse/core'; import { ElMessage } from 'element-plus'; import { AnswerState, answerTypeMapCom, roleImageMap, type ChatMessage } from '../model/types'; import { isSharePage } from '/@/stores/chatRoom'; -import { onClickOutside, useClipboard } from '@vueuse/core'; +import { ref } from 'vue'; +import BusinessTablePreview from '../components/playBar/businessTablePreview/index.vue'; +import { Attach } from '../components/playBar/hook/useAttach'; const emit = defineEmits<{ (event: 'copyMsg', msgObj: ChatMessage): void; @@ -135,5 +141,15 @@ const shareClick = (msg) => { emit('shareClick', msg); }; + +//#region ====================== 闄勪欢棰勮 ====================== +const attachPreviewIsShow = ref(false); +const attachPreviewData = ref<Attach>(); +const openAttachPreview = (item: Attach) => { + if (item.type === 'file') return; + attachPreviewIsShow.value = true; + attachPreviewData.value = item; +}; +//#endregion </script> <style scoped lang="scss"></style> diff --git a/src/components/table/colFilter/ColFilter.vue b/src/components/table/colFilter/ColFilter.vue index 315b1ac..9ded84c 100644 --- a/src/components/table/colFilter/ColFilter.vue +++ b/src/components/table/colFilter/ColFilter.vue @@ -1,7 +1,7 @@ <template> <el-dropdown trigger="click" placement="bottom-end" :hideOnClick="false"> <span - title="鐐瑰嚮閫夋嫨鏄剧ず鍒�" + :title="title" class="rounded-full p-1.5 border-[1.5px] hover:text-blue-400 border-gray-200 border-solid cursor-pointer ywifont ywicon-grid !text-[13px] h-fit" ></span> @@ -35,6 +35,10 @@ type: Boolean, default: true, }, + title: { + type: String, + default: '鐐瑰嚮閫夋嫨鏄剧ず鍒�', + }, }); const emit = defineEmits<{ -- Gitblit v1.9.3