Files
yihuiyong-ui/apps/web-ele/src/views/database/info/add/index.vue
2026-04-23 11:37:37 +08:00

346 lines
10 KiB
Vue

<script setup lang="ts">
import { createResourceBatch, getInfoPriceBook } from '#/api/database/info';
import { DbHst } from '#/components/db-hst';
import { DbTree } from '#/components/db-tree';
import { useUpload } from '#/components/upload/use-upload';
import { Page } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { useAccessStore } from '@vben/stores';
import { useElementSize } from '@vueuse/core';
import type { UploadProps } from 'element-plus';
import { ElButton, ElCard, ElDialog, ElImage, ElInput, ElMessage, ElSplitter, ElSplitterPanel, ElText, ElUpload } from 'element-plus';
import { computed, nextTick, onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { HistoryDialog } from '../components/history-dialog';
import { MorePriceDialog } from '../components/more-price-dialog';
import { useDbHst } from './dbHst';
import { useDbTree } from './dbTree';
const route = useRoute();
const accessStore = useAccessStore();
const { uploadUrl, httpRequest } = useUpload('info-price');
const bookId = ref<any>(null);
const currentCategoryTreeId = ref<any>(null);
const bookTitle = ref<string>('');
const bookInfo = ref<any>(null);
const containerRef = ref<HTMLElement | null>(null);
const { height: containerHeight } = useElementSize(containerRef);
const isCcompleted = computed(() => accessStore.tenantId != 1 && bookInfo.value?.publishStatus === 'completed');
const dbTreeRef = ref();
const hstRef = ref();
const previewImageUrl = ref<string>('');
const previewVisible = ref(false);
const uploadDialogVisible = ref(false);
const currentUploadRow = ref<number>(0);
const currentUploadCol = ref<number>(0);
const uploadImageUrl = ref<string>('');
const uploading = ref(false);
const morePriceDialogVisible = ref(false);
const currentPriceRow = ref<number>(0);
const selectRowPrice = ref<any>(null);
const historyDialogVisible = ref(false);
const categorySource = ref<{ label: string; value: any }[]>([]);
const search = ref();
const handlePhotoPreview = (value: string) => {
previewImageUrl.value = value;
previewVisible.value = true;
};
const handlePhotoUpload = (row: number, col: number, value: string) => {
currentUploadRow.value = row;
currentUploadCol.value = col;
uploadImageUrl.value = value;
uploadDialogVisible.value = true;
};
const handleShowMorePrice = (row: number, col: number, value: any) => {
currentPriceRow.value = row;
morePriceDialogVisible.value = true;
selectRowPrice.value = hstRef.value.hotInstance.getSourceDataAtRow(row);
console.log('查看更多单价:', { row, col, value }, selectRowPrice.value);
};
const callbacks = {
onPhotoPreview: handlePhotoPreview,
onPhotoUpload: handlePhotoUpload,
onShowMorePrice: handleShowMorePrice,
};
const {
columns,
contextMenuItems,
dbSettings,
loadResourcePage,
loadCategorySource,
updateCategoryColumnSource,
} = useDbHst(hstRef, bookId, currentCategoryTreeId, isCcompleted, callbacks);
const {
treeData,
rootMenus,
nodeMenus,
loadCategoryTree,
handleTreeNodeSelect,
handleTreeNodeEdit,
} = useDbTree(dbTreeRef, bookId, isCcompleted, currentCategoryTreeId, loadResourcePage);
const loadBookInfo = async () => {
if (!bookId.value) return;
try {
const res = await getInfoPriceBook(bookId.value);
if (res) {
bookInfo.value = res;
bookTitle.value = `${res.name} (${res.startTime} ~ ${res.endTime})`;
}
} catch (error) {
console.error('加载信息价册失败:', error);
}
};
const handleSelectPrice = async (row: any) => {
if (hstRef.value?.hotInstance && selectRowPrice.value) {
const sendData = {
...selectRowPrice.value,
priceTaxExcl: row.priceTaxExcl,
taxRate: row.taxRate,
priceTaxIncl: row.priceTaxIncl,
};
try {
await createResourceBatch([sendData]);
ElMessage.success('更新成功');
loadResourcePage();
} catch (error) {
console.error('更新失败:', error);
ElMessage.error('更新失败');
}
}
};
const handleHistoryConfirm = async (nodes: any[]) => {
if (!nodes || nodes.length === 0) {
ElMessage.warning('请选择要导入的数据');
return;
}
const updatedNodes = nodes.map(node => ({
...node,
categoryTreeId: currentCategoryTreeId.value,
resourceItemId: node.id,
id: null,
}));
const res = await createResourceBatch(updatedNodes);
if (hstRef.value?.hotInstance && res) {
loadResourcePage();
ElMessage.success(`成功导入 ${updatedNodes.length} 条数据`);
}
};
const handleBeforeUpload: UploadProps['beforeUpload'] = rawFile => {
if (!rawFile.type.startsWith('image/')) {
ElMessage.error('只能上传图片文件!');
return false;
}
if (rawFile.size / 1024 / 1024 > 5) {
ElMessage.error('图片大小不能超过 5MB!');
return false;
}
return true;
};
const handleHttpRequest = (options: any) => {
uploading.value = true;
return httpRequest(options.file);
};
const handleUploadSuccess: UploadProps['onSuccess'] = (response: any) => {
uploading.value = false;
const imageUrl = response?.url || response;
uploadImageUrl.value = imageUrl;
if (hstRef.value?.hotInstance) {
hstRef.value.hotInstance.setDataAtCell(currentUploadRow.value, currentUploadCol.value, imageUrl);
}
ElMessage.success('上传成功');
handleDialogClose();
};
const handleUploadError: UploadProps['onError'] = () => {
uploading.value = false;
ElMessage.error('上传失败,请重试');
};
const handleUploadChange: UploadProps['onChange'] = uploadFile => {
if (uploadFile.raw) {
uploadImageUrl.value = URL.createObjectURL(uploadFile.raw);
}
};
const handleDialogClose = () => {
uploadDialogVisible.value = false;
};
const handlePreviewClose = () => {
previewVisible.value = false;
previewImageUrl.value = '';
};
const handleQuery = () => {
console.log('handleQuery');
};
const handleOpenHistoryDialog = () => {
if (!currentCategoryTreeId.value) {
ElMessage.warning('请先选择左侧分类节点');
return;
}
historyDialogVisible.value = true;
};
onMounted(async () => {
bookId.value = route.params.id || route.query.id;
await loadBookInfo();
await loadCategoryTree();
categorySource.value = await loadCategorySource();
updateCategoryColumnSource(categorySource.value);
nextTick(() => {
if (hstRef.value?.hotInstance) {
if (accessStore.tenantId != 1) {
const editableColumns = ['priceTaxExcl', 'taxRate', 'priceTaxIncl'];
const updatedColumns = columns.map(col => ({
...col,
editor: editableColumns.includes(col.data) ? col.editor : false,
}));
hstRef.value.hotInstance.updateSettings({
columns: updatedColumns,
});
}
}
});
});
</script>
<template>
<Page auto-content-height>
<ElCard class="h-full w-full border-radius-0" body-class="!p-0 h-full w-full flex flex-col" header-class="!p-1">
<div class="!p-1">
<ElText class="mx-1">信息价名称: {{ bookTitle }}</ElText>
<ElInput
v-model="search"
style="width: 300px"
placeholder="请输入"
class="input-with-select"
size="small"
>
<template #append>
<ElButton @click="handleQuery" size="small"><IconifyIcon icon="ep:search" /></ElButton>
</template>
</ElInput>
<ElButton
@click="handleOpenHistoryDialog"
type="primary"
size="small"
style="float: right"
:disabled="isCcompleted"
>
调用历史信息
</ElButton>
</div>
<div class="h-full w-full" ref="containerRef">
<ElSplitter :style="{ height: containerHeight + 'px' }">
<ElSplitterPanel size="15%" :min="200">
<ElCard class="w-full h-full border-radius-0" body-class="!p-0 h-full w-full flex flex-col">
<DbTree
ref="dbTreeRef"
:tree-data="treeData"
:root-menus="rootMenus"
:node-menus="nodeMenus"
@node-edit="handleTreeNodeEdit"
@node-click="handleTreeNodeSelect"
:defaultExpandedLevel="4"
/>
</ElCard>
</ElSplitterPanel>
<ElSplitterPanel :min="200">
<ElCard class="w-full h-full border-radius-0" body-class="!p-0 h-full w-full flex flex-col">
<DbHst ref="hstRef" :settings="dbSettings" :contextMenuItems="contextMenuItems" />
</ElCard>
</ElSplitterPanel>
</ElSplitter>
</div>
</ElCard>
<HistoryDialog
v-model:visible="historyDialogVisible"
:bookInfo="bookInfo"
:categorySource="categorySource"
@confirm="handleHistoryConfirm"
/>
<ElDialog v-model="uploadDialogVisible" title="上传图片" width="500px" @close="handleDialogClose">
<div class="upload-container">
<ElUpload
class="upload-demo"
drag
:action="uploadUrl"
:http-request="handleHttpRequest"
:show-file-list="false"
:before-upload="handleBeforeUpload"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:on-change="handleUploadChange"
accept="image/*"
:disabled="uploading"
>
<div class="flex min-h-[200px] flex-col items-center justify-center py-8">
<span class="icon-[mdi--cloud-upload-outline] mb-4 text-6xl text-gray-400"></span>
<div class="text-base text-gray-600">点击或拖拽文件到此区域上传</div>
<div class="mt-2 text-sm text-gray-400">支持 .jpg.png.gif.webp 格式图片文件</div>
</div>
</ElUpload>
</div>
<template #footer>
<ElButton @click="handleDialogClose" :disabled="uploading">关闭</ElButton>
</template>
</ElDialog>
<ElDialog v-model="previewVisible" title="图片预览" width="800px" @close="handlePreviewClose">
<div class="preview-image-container">
<ElImage
:src="previewImageUrl"
fit="contain"
:preview-src-list="[previewImageUrl]"
:initial-index="0"
style="width: 100%; max-height: 600px"
/>
</div>
<template #footer>
<ElButton @click="handlePreviewClose">关闭</ElButton>
</template>
</ElDialog>
<MorePriceDialog
v-model:visible="morePriceDialogVisible"
:bookInfo="bookInfo"
:selectRowPrice="selectRowPrice"
@select="handleSelectPrice"
/>
</Page>
</template>
<style lang="css"></style>