This commit is contained in:
吴红兵
2026-03-03 15:32:36 +08:00
parent 168e134e1b
commit 4c735f93a0
5 changed files with 131 additions and 41 deletions

View File

@@ -87,3 +87,42 @@ export function editObj(obj: any) {
}); });
} }
/**
* 下载导入模板
*/
export function downloadTemplate() {
return request({
url: '/purchase/purchasingcategory/import/template',
method: 'get',
responseType: 'blob'
});
}
/**
* 导入数据
* @param file 文件对象
*/
export function importData(file: File) {
const formData = new FormData();
formData.append('file', file);
return request({
url: '/purchase/purchasingcategory/import',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
/**
* 导出数据
*/
export function exportData() {
return request({
url: '/purchase/purchasingcategory/export',
method: 'get',
responseType: 'blob'
});
}

View File

@@ -67,7 +67,7 @@ export function usePurchaseRules() {
const thresholds: Record<string, number> = { const thresholds: Record<string, number> = {
deptPurchase: 50000, deptPurchase: 50000,
feasibility: 300000, feasibility: 300000,
publicSelect: 400000, publicSelect: 300000,
govPurchase: 1000000 govPurchase: 1000000
}; };

View File

@@ -32,6 +32,7 @@
</div> </div>
</el-row> </el-row>
<el-alert type="warning" :closable="false">既定规则请不要随意修改否则会出错</el-alert>
<el-table <el-table
:data="state.dataList" :data="state.dataList"
v-loading="state.loading" v-loading="state.loading"

View File

@@ -17,6 +17,31 @@
@click="formDialogRef.openDialog('add')"> @click="formDialogRef.openDialog('add')">
新增 新增
</el-button> </el-button>
<el-button
icon="Download"
type="success"
v-auth="'purchase_purchasingcategory_import'"
@click="handleDownloadTemplate">
下载模板
</el-button>
<el-upload
ref="uploadRef"
:show-file-list="false"
:before-upload="handleBeforeUpload"
:http-request="handleImport"
accept=".xlsx,.xls"
v-auth="'purchase_purchasingcategory_import'">
<el-button icon="Upload" type="warning" :loading="importLoading">
导入
</el-button>
</el-upload>
<el-button
icon="Download"
type="info"
v-auth="'purchase_purchasingcategory_export'"
@click="handleExport">
导出
</el-button>
<right-toolbar class="ml10" /> <right-toolbar class="ml10" />
</div> </div>
</div> </div>
@@ -93,20 +118,17 @@
<script setup lang="ts" name="PurchasingCategory"> <script setup lang="ts" name="PurchasingCategory">
import { ref, reactive, defineAsyncComponent } from 'vue' import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table"; import { BasicTableProps, useTable } from "/@/hooks/table";
import { getTreeRoots, getTreeChildren, delObj } from "/@/api/purchase/purchasingcategory"; import { getTreeRoots, getTreeChildren, delObj, downloadTemplate, importData, exportData } from "/@/api/purchase/purchasingcategory";
import { useMessage, useMessageBox } from "/@/hooks/message"; import { useMessage, useMessageBox } from "/@/hooks/message";
import { List, Document, DocumentCopy, EditPen } from '@element-plus/icons-vue' import { List, Document, DocumentCopy, EditPen } from '@element-plus/icons-vue'
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue')); const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const tableRef = ref() const tableRef = ref()
const formDialogRef = ref() const formDialogRef = ref()
const uploadRef = ref()
const importLoading = ref(false)
/**
* 查询树根节点(懒加载:首屏只加载根节点)
*/
const queryTreeRoots = () => { const queryTreeRoots = () => {
return getTreeRoots().then((res: any) => { return getTreeRoots().then((res: any) => {
const list = res?.data ?? []; const list = res?.data ?? [];
@@ -114,12 +136,6 @@ const queryTreeRoots = () => {
}); });
}; };
/**
* 懒加载子节点:展开某行时按需请求子节点
* @param row 当前行
* @param treeNode 树节点信息
* @param resolve 回调,传入子节点数组
*/
const loadTreeNode = (row: any, treeNode: any, resolve: (data: any[]) => void) => { const loadTreeNode = (row: any, treeNode: any, resolve: (data: any[]) => void) => {
const parentCode = row?.code; const parentCode = row?.code;
if (!parentCode) { if (!parentCode) {
@@ -134,33 +150,18 @@ const loadTreeNode = (row: any, treeNode: any, resolve: (data: any[]) => void) =
.catch(() => resolve([])); .catch(() => resolve([]));
}; };
/**
* 定义响应式表格数据
*/
const state: BasicTableProps = reactive<BasicTableProps>({ const state: BasicTableProps = reactive<BasicTableProps>({
pageList: queryTreeRoots, pageList: queryTreeRoots,
queryForm: {}, queryForm: {},
isPage: false, // 树形表格不分页 isPage: false,
}); });
/**
* 使用 useTable 定义表格相关操作
*/
const { getDataList, tableStyle } = useTable(state); const { getDataList, tableStyle } = useTable(state);
/**
* 计算行序号(考虑树形结构)
*/
const getRowIndex = (index: number, row: any) => { const getRowIndex = (index: number, row: any) => {
// 对于树形表格,序号需要根据实际显示的行来计算
// 这里简化处理,直接返回 index + 1
return index + 1; return index + 1;
}; };
/**
* 删除当前行
* @param row - 当前行数据
*/
const handleDelete = async (row: any) => { const handleDelete = async (row: any) => {
try { try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', { await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
@@ -173,7 +174,6 @@ const handleDelete = async (row: any) => {
} }
try { try {
// 使用 id 或 code 作为删除参数(根据后端接口决定)
const deleteId = row.id || row.code; const deleteId = row.id || row.code;
await delObj(deleteId); await delObj(deleteId);
useMessage().success('删除成功'); useMessage().success('删除成功');
@@ -182,6 +182,57 @@ const handleDelete = async (row: any) => {
useMessage().error(err.msg || '删除失败'); useMessage().error(err.msg || '删除失败');
} }
}; };
const handleDownloadTemplate = async () => {
try {
const res: any = await downloadTemplate();
downloadFile(res, '采购品目导入模板.xlsx');
} catch (err: any) {
useMessage().error(err.msg || '下载模板失败');
}
};
const handleBeforeUpload = (file: File) => {
const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
if (!isExcel) {
useMessage().error('请上传Excel文件');
return false;
}
return true;
};
const handleImport = async (options: any) => {
importLoading.value = true;
try {
const res: any = await importData(options.file);
useMessage().success(res.msg || '导入成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '导入失败');
} finally {
importLoading.value = false;
}
};
const handleExport = async () => {
try {
const res: any = await exportData();
downloadFile(res, '采购品目数据.xlsx');
} catch (err: any) {
useMessage().error(err.msg || '导出失败');
}
};
const downloadFile = (blob: Blob, fileName: string) => {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -188,12 +188,6 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="8" class="mb12" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'">
<el-form-item label="推荐供应商" prop="suppliers" class="mb16">
<el-input v-model="dataForm.suppliers" placeholder="请输入三家供应商名称,用逗号分隔" clearable />
<div class="template-note mt5"><el-text type="info" size="small">请输入三家供应商名称用逗号分隔</el-text></div>
</el-form-item>
</el-col> -->
<el-col :span="8" class="mb12" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'"> <el-col :span="8" class="mb12" v-if="showAutoInviteSelect && dataForm.hasSupplier === '1'">
<el-form-item label="服务商城项目需求模板(邀请比选)" prop="serviceInviteSelect" required> <el-form-item label="服务商城项目需求模板(邀请比选)" prop="serviceInviteSelect" required>
<upload-file v-model="dataForm.serviceInviteSelect" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('serviceInviteSelect')" /> <upload-file v-model="dataForm.serviceInviteSelect" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ fileType: FILE_TYPE_MAP.serviceInviteSelect }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="flowFieldDisabled('serviceInviteSelect')" />
@@ -227,7 +221,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8" class="mb12" > <el-col :span="8" class="mb12" >
<el-form-item label="采购方式" prop="purchaseType" :required="!isDeptPurchase"> <el-form-item label="采购方式" prop="purchaseType">
<el-select v-model="dataForm.purchaseType" placeholder="请选择采购方式" clearable :disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType') || !isPurchaseCenter)" style="width: 100%"> <el-select v-model="dataForm.purchaseType" placeholder="请选择采购方式" clearable :disabled="(isFlowEmbed && isPurchaseCenter) ? false : (isAutoSelectPurchaseTypeUnion || flowFieldDisabled('purchaseType') || !isPurchaseCenter)" style="width: 100%">
<el-option v-for="item in purchaseTypeUnionList" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in purchaseTypeUnionList" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
@@ -1153,8 +1147,13 @@ const dataRules = reactive({
purchaseType: [ purchaseType: [
{ {
validator: (_rule: any, value: string, callback: (e?: Error) => void) => { validator: (_rule: any, value: string, callback: (e?: Error) => void) => {
// 部门自行采购且采购途径为“委托采购中心采购”并且为申请阶段:此处不校验采购方式 // 学校统一采购:申请阶段不要求采购方式,由审批环节采购中心补充
if (isDeptPurchase.value && isEntrustCenterChannel.value && !isFlowEmbed.value) { if (!isDeptPurchase.value) {
callback();
return;
}
// 部门自行采购且采购途径为"委托采购中心采购"并且为申请阶段:此处不校验采购方式
if (isEntrustCenterChannel.value && !isFlowEmbed.value) {
callback(); callback();
return; return;
} }