Merge branch 'developer' of ssh://code.cyweb.top:30033/scj/zhxy/v3/cloud-ui into developer

This commit is contained in:
guochunsi
2026-01-29 11:56:47 +08:00
12 changed files with 6674 additions and 4237 deletions

View File

@@ -0,0 +1,128 @@
<template>
<el-dialog
:title="dataForm.id ? '编辑' : '新增'"
v-model="visible"
width="600px"
:close-on-click-modal="false"
draggable>
<el-form
ref="formRef"
:model="dataForm"
:rules="dataRules"
label-width="100px"
v-loading="loading">
<el-form-item label="代理名称" prop="agentName">
<el-input
v-model="dataForm.agentName"
placeholder="请输入代理名称"
clearable />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="dataForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="PurchaseAgentForm">
import { reactive, ref, nextTick } from 'vue'
import { getObj, addObj, editObj } from '/@/api/finance/purchaseagent';
import { useMessage } from '/@/hooks/message';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
// 定义变量内容
const formRef = ref();
const dataForm = reactive({
id: '',
agentName: '',
remark: '',
});
const visible = ref(false);
const loading = ref(false);
const dataRules = ref({
agentName: [
{ required: true, message: '请输入代理名称', trigger: 'blur' }
],
});
// 打开弹窗
const openDialog = async (type: string, rowData?: any) => {
visible.value = true;
dataForm.id = '';
dataForm.agentName = '';
dataForm.remark = '';
nextTick(() => {
formRef.value?.resetFields();
if (type === 'edit' && rowData?.id) {
// 编辑时,先获取详情数据
loading.value = true;
getObj(rowData.id).then((res: any) => {
if (res.data) {
Object.assign(dataForm, {
id: res.data.id || '',
agentName: res.data.agentName || '',
remark: res.data.remark || '',
});
}
loading.value = false;
}).catch((err: any) => {
useMessage().error(err.msg || '获取详情失败');
loading.value = false;
});
}
});
};
// 提交
const onSubmit = async () => {
// 立即设置 loading防止重复点击
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
if (dataForm.id) {
await editObj(dataForm);
useMessage().success('编辑成功');
} else {
await addObj(dataForm);
useMessage().success('新增成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '编辑失败' : '新增失败'));
} finally {
loading.value = false;
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,185 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="代理名称" prop="agentName">
<el-input
v-model="state.queryForm.agentName"
placeholder="请输入代理名称"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
招标代理管理
</span>
<div class="header-actions">
<el-button
icon="FolderAdd"
type="primary"
@click="formDialogRef.openDialog('add')">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
</el-table-column>
<el-table-column prop="agentName" label="代理名称" min-width="200" show-overflow-tooltip>
<template #header>
<el-icon><Document /></el-icon>
<span style="margin-left: 4px">代理名称</span>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="300" show-overflow-tooltip>
<template #header>
<el-icon><EditPen /></el-icon>
<span style="margin-left: 4px">备注</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" show-overflow-tooltip>
<template #header>
<el-icon><Clock /></el-icon>
<span style="margin-left: 4px">创建时间</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="150">
<template #default="scope">
<el-button
icon="Edit"
link
type="primary"
@click="formDialogRef.openDialog('edit', scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
link
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="state.total > 0"
:total="state.total"
v-model:page="state.page"
v-model:limit="state.limit"
@pagination="getDataList"
/>
</el-card>
</div>
<!-- 编辑新增表单对话框 -->
<FormDialog ref="formDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="PurchaseAgent">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj } from "/@/api/finance/purchaseagent";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { List, Document, EditPen, Clock, Search } from '@element-plus/icons-vue'
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const tableRef = ref()
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
/**
* 定义响应式表格数据
*/
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
agentName: '',
},
createdIsNeed: true
});
/**
* 使用 useTable 定义表格相关操作
*/
const { getDataList, tableStyle } = useTable(state);
/**
* 重置搜索表单
*/
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
/**
* 删除当前行
* @param row - 当前行数据
*/
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
await delObj(row.id);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,182 @@
<template>
<el-dialog
:title="dataForm.id ? '编辑' : '新增'"
v-model="visible"
width="600px"
:close-on-click-modal="false"
draggable>
<el-form
ref="formRef"
:model="dataForm"
:rules="dataRules"
label-width="100px"
v-loading="loading">
<el-form-item label="父级节点" prop="parentCode">
<el-tree-select
v-model="dataForm.parentCode"
:data="parentData"
:props="{ value: 'code', label: 'name', children: 'children' }"
class="w100"
clearable
check-strictly
:render-after-expand="false"
placeholder="请选择父级节点(不选则为根节点)"
/>
</el-form-item>
<el-form-item label="品目编码" prop="code">
<el-input
v-model="dataForm.code"
placeholder="请输入品目编码"
clearable
:disabled="!!dataForm.id" />
</el-form-item>
<el-form-item label="品目名称" prop="name">
<el-input
v-model="dataForm.name"
placeholder="请输入品目名称"
clearable />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="dataForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
clearable />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false"> </el-button>
<el-button type="primary" @click="onSubmit" :disabled="loading"> </el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="PurchasingCategoryForm">
import { reactive, ref, nextTick } from 'vue'
import { getTree, addObj, editObj } from '/@/api/finance/purchasingcategory';
import { useMessage } from '/@/hooks/message';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
// 定义变量内容
const formRef = ref();
const dataForm = reactive({
id: '',
parentCode: '',
code: '',
name: '',
remark: '',
isMallService: '',
isMallProject: '',
});
const parentData = ref<any[]>([]);
const visible = ref(false);
const loading = ref(false);
const dataRules = ref({
code: [
{ required: true, message: '请输入品目编码', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入品目名称', trigger: 'blur' }
],
});
// 打开弹窗
const openDialog = (type: string, rowData?: any) => {
visible.value = true;
dataForm.id = '';
dataForm.parentCode = '';
dataForm.code = '';
dataForm.name = '';
dataForm.remark = '';
dataForm.isMallService = '';
dataForm.isMallProject = '';
nextTick(() => {
formRef.value?.resetFields();
if (type === 'add' && rowData?.code) {
// 新增时rowData 是父节点数据,设置父级编码
dataForm.parentCode = rowData.code;
} else if (type === 'edit' && rowData) {
// 编辑时rowData 是当前行数据
Object.assign(dataForm, {
id: rowData.id || '',
parentCode: rowData.parentCode || '',
code: rowData.code || '',
name: rowData.name || '',
remark: rowData.remark || '',
isMallService: rowData.isMallService || '',
isMallProject: rowData.isMallProject || '',
});
}
});
getTreeData();
};
// 提交
const onSubmit = async () => {
// 立即设置 loading防止重复点击
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
if (dataForm.id) {
await editObj(dataForm);
useMessage().success('编辑成功');
} else {
await addObj(dataForm);
useMessage().success('新增成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '编辑失败' : '新增失败'));
} finally {
loading.value = false;
}
};
// 从后端获取树形数据
const getTreeData = async () => {
try {
const res = await getTree();
parentData.value = [];
const root = {
code: '',
name: '根节点',
children: [] as any[],
};
if (res.data && Array.isArray(res.data)) {
root.children = res.data;
}
parentData.value.push(root);
} catch (err: any) {
useMessage().error(err.msg || '获取树形数据失败');
parentData.value = [];
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped>
.w100 {
width: 100%;
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
采购品目管理
</span>
<div class="header-actions">
<el-button
icon="FolderAdd"
type="primary"
@click="formDialogRef.openDialog('add')">
新增
</el-button>
<right-toolbar class="ml10" />
</div>
</div>
</template>
<!-- 树形表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table"
row-key="code"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
<template #default="{ $index, row }">
{{ getRowIndex($index, row) }}
</template>
</el-table-column>
<el-table-column prop="code" label="品目编码" min-width="150" show-overflow-tooltip>
<template #header>
<el-icon><DocumentCopy /></el-icon>
<span style="margin-left: 4px">品目编码</span>
</template>
</el-table-column>
<el-table-column prop="name" label="品目名称" min-width="200" show-overflow-tooltip>
<template #header>
<el-icon><Document /></el-icon>
<span style="margin-left: 4px">品目名称</span>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="200" show-overflow-tooltip>
<template #header>
<el-icon><EditPen /></el-icon>
<span style="margin-left: 4px">备注</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="150">
<template #default="scope">
<el-button
icon="Edit"
link
type="primary"
@click="formDialogRef.openDialog('edit', scope.row)">
编辑
</el-button>
<el-button
icon="Delete"
link
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<!-- 编辑新增表单对话框 -->
<FormDialog ref="formDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="PurchasingCategory">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getTree, delObj } from "/@/api/finance/purchasingcategory";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { List, Document, DocumentCopy, EditPen } from '@element-plus/icons-vue'
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const tableRef = ref()
const formDialogRef = ref()
/**
* 查询树形数据方法
* @param params - 查询参数
* @returns Promise<any>
*/
const queryTree = (params?: any) => {
return getTree(params);
};
/**
* 定义响应式表格数据
*/
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: queryTree,
queryForm: {},
isPage: false, // 树形表格不分页
});
/**
* 使用 useTable 定义表格相关操作
*/
const { getDataList, tableStyle } = useTable(state);
/**
* 计算行序号(考虑树形结构)
*/
const getRowIndex = (index: number, row: any) => {
// 对于树形表格,序号需要根据实际显示的行来计算
// 这里简化处理,直接返回 index + 1
return index + 1;
};
/**
* 删除当前行
* @param row - 当前行数据
*/
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
// 使用 id 或 code 作为删除参数(根据后端接口决定)
const deleteId = row.id || row.code;
await delObj(deleteId);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -0,0 +1,434 @@
<template>
<el-dialog
:title="dialogTitle"
v-model="visible"
width="900px"
:close-on-click-modal="false"
draggable>
<el-form
ref="formRef"
:model="dataForm"
:rules="dataRules"
label-width="140px"
v-loading="loading">
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="采购项目名称" prop="projectName">
<el-input
v-model="dataForm.projectName"
placeholder="请输入采购项目名称"
clearable
:disabled="isView" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="项目类别" prop="projectType">
<el-select
v-model="dataForm.projectType"
placeholder="请选择项目类别"
clearable
:disabled="isView"
style="width: 100%">
<el-option label="货物" value="A" />
<el-option label="工程" value="B" />
<el-option label="服务" value="C" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="品目编码" prop="categoryCode">
<el-tree-select
v-model="dataForm.categoryCode"
:data="categoryTreeData"
:props="{ value: 'code', label: 'name', children: 'children' }"
placeholder="请选择品目编码"
clearable
check-strictly
:render-after-expand="false"
:disabled="isView"
style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="填报日期" prop="applyDate">
<el-date-picker
v-model="dataForm.applyDate"
type="date"
placeholder="请选择填报日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
:disabled="isView"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="采购内容" prop="projectContent" class="mb20">
<el-input
v-model="dataForm.projectContent"
type="textarea"
:rows="3"
placeholder="请输入采购内容"
clearable
:disabled="isView" />
</el-form-item>
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="资金来源" prop="fundSource">
<el-input
v-model="dataForm.fundSource"
placeholder="请输入资金来源"
clearable
:disabled="isView" />
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="预算金额(元)" prop="budget">
<el-input-number
v-model="dataForm.budget"
:min="0.01"
:precision="2"
placeholder="请输入预算金额"
:disabled="isView"
style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="是否集采" prop="isCentralized">
<el-select
v-model="dataForm.isCentralized"
placeholder="请选择是否集采"
clearable
:disabled="isView"
style="width: 100%">
<el-option label="否" value="0" />
<el-option label="是" value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="是否特殊情况" prop="isSpecial">
<el-select
v-model="dataForm.isSpecial"
placeholder="请选择是否特殊情况"
clearable
:disabled="isView"
style="width: 100%">
<el-option label="否" value="0" />
<el-option label="紧急" value="1" />
<el-option label="单一" value="2" />
<el-option label="进口" value="3" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="24">
<el-col :span="12" class="mb20">
<el-form-item label="采购形式" prop="purchaseMode">
<el-select
v-model="dataForm.purchaseMode"
placeholder="请选择采购形式"
clearable
:disabled="isView"
style="width: 100%">
<el-option label="部门自行采购" value="0" />
<el-option label="学校统一采购" value="2" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="mb20">
<el-form-item label="学校统一采购方式" prop="purchaseSchool">
<el-select
v-model="dataForm.purchaseSchool"
placeholder="请选择学校统一采购方式"
clearable
:disabled="isView || dataForm.purchaseMode !== '2'"
style="width: 100%">
<el-option label="无" value="0" />
<el-option label="政府采购" value="1" />
<el-option label="学校自主采购" value="2" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="采购方式" prop="purchaseType" class="mb20">
<el-input
v-model="dataForm.purchaseType"
placeholder="请输入采购方式"
clearable
:disabled="isView" />
</el-form-item>
<el-form-item label="附件" prop="fileIds" class="mb20">
<upload-file
v-model="dataForm.fileIds"
:limit="10"
:disabled="isView"
upload-file-url="/purchase/purchasingfiles/upload" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="dataForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
clearable
:disabled="isView" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="visible = false">{{ isView ? '关闭' : '取消' }}</el-button>
<template v-if="!isView">
<el-button v-if="dataForm.id && dataForm.status === '-1'" type="warning" @click="handleTempStore" :disabled="loading">暂存</el-button>
<el-button type="primary" @click="handleSubmit" :disabled="loading">{{ dataForm.id ? '保存' : '提交' }}</el-button>
</template>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="PurchasingRequisitionForm">
import { reactive, ref, nextTick, computed } from 'vue'
import { getObj, addObj, editObj, tempStore, submitObj } from '/@/api/finance/purchasingrequisition';
import { getTree } from '/@/api/finance/purchasingcategory';
import { useMessage } from '/@/hooks/message';
import UploadFile from '/@/components/Upload/index.vue';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
// 定义变量内容
const formRef = ref();
const dataForm = reactive({
id: '',
projectName: '',
projectType: '',
projectContent: '',
applyDate: '',
fundSource: '',
budget: null as number | null,
isCentralized: '',
isSpecial: '',
purchaseMode: '',
purchaseSchool: '',
purchaseType: '',
categoryCode: '',
fileIds: '',
remark: '',
status: '',
});
const categoryTreeData = ref<any[]>([]);
const visible = ref(false);
const loading = ref(false);
const dialogType = ref<'add' | 'edit' | 'view'>('add');
const isView = computed(() => dialogType.value === 'view');
const dialogTitle = computed(() => {
if (dialogType.value === 'view') return '查看采购申请';
if (dialogType.value === 'edit') return '编辑采购申请';
return '新增采购申请';
});
const dataRules = reactive({
projectContent: [
{ required: true, message: '采购内容不能为空', trigger: 'blur' }
],
budget: [
{ required: true, message: '预算金额不能为空', trigger: 'blur' },
{ type: 'number', min: 0.01, message: '预算金额必须大于0.01', trigger: 'blur' }
],
isCentralized: [
{ required: true, message: '请选择是否集采', trigger: 'change' }
],
isSpecial: [
{ required: true, message: '请选择是否特殊情况', trigger: 'change' }
],
});
// 打开弹窗
const openDialog = async (type: 'add' | 'edit' | 'view', rowData?: any) => {
dialogType.value = type;
visible.value = true;
// 重置表单
dataForm.id = '';
dataForm.projectName = '';
dataForm.projectType = '';
dataForm.projectContent = '';
dataForm.applyDate = '';
dataForm.fundSource = '';
dataForm.budget = null;
dataForm.isCentralized = '';
dataForm.isSpecial = '';
dataForm.purchaseMode = '';
dataForm.purchaseSchool = '';
dataForm.purchaseType = '';
dataForm.categoryCode = '';
dataForm.fileIds = '';
dataForm.remark = '';
dataForm.status = '';
await getCategoryTreeData();
nextTick(() => {
formRef.value?.resetFields();
if (type === 'edit' || type === 'view') {
loading.value = true;
getObj(rowData.id)
.then((res) => {
Object.assign(dataForm, {
id: res.data.id || '',
projectName: res.data.projectName || '',
projectType: res.data.projectType || '',
projectContent: res.data.projectContent || '',
applyDate: res.data.applyDate || '',
fundSource: res.data.fundSource || '',
budget: res.data.budget || null,
isCentralized: res.data.isCentralized || '',
isSpecial: res.data.isSpecial || '',
purchaseMode: res.data.purchaseMode || '',
purchaseSchool: res.data.purchaseSchool || '',
purchaseType: res.data.purchaseType || '',
categoryCode: res.data.categoryCode || '',
fileIds: res.data.fileIds ? (Array.isArray(res.data.fileIds) ? res.data.fileIds.join(',') : res.data.fileIds) : '',
remark: res.data.remark || '',
status: res.data.status || '',
});
})
.catch((err: any) => {
useMessage().error(err.msg || '获取详情失败');
})
.finally(() => {
loading.value = false;
});
}
});
};
// 获取品目树形数据
const getCategoryTreeData = async () => {
try {
const res = await getTree();
categoryTreeData.value = [];
if (res.data && Array.isArray(res.data)) {
categoryTreeData.value = res.data;
}
} catch (err: any) {
console.error('获取品目树形数据失败', err);
categoryTreeData.value = [];
}
};
// 处理文件ID字符串转数组
// 从URL中提取文件IDURL格式通常为: /admin/sys-file/show?fileName=xxx&id=xxx
const getFileIdsArray = (): string[] => {
if (!dataForm.fileIds) return [];
if (Array.isArray(dataForm.fileIds)) return dataForm.fileIds;
// 如果是逗号分隔的URL字符串需要从URL中提取文件ID
const urls = dataForm.fileIds.split(',').filter(url => url.trim());
const fileIds: string[] = [];
urls.forEach(url => {
try {
// 尝试从URL参数中提取id
const urlObj = new URL(url, window.location.origin);
const id = urlObj.searchParams.get('id') || urlObj.searchParams.get('fileName');
if (id) {
fileIds.push(id);
} else {
// 如果没有id参数尝试从路径中提取
const pathParts = urlObj.pathname.split('/');
const lastPart = pathParts[pathParts.length - 1];
if (lastPart) {
fileIds.push(lastPart);
}
}
} catch {
// 如果URL解析失败直接使用原始值
fileIds.push(url);
}
});
return fileIds;
};
// 提交(新增或编辑)
const handleSubmit = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value?.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData = {
...dataForm,
fileIds: getFileIdsArray(),
};
if (dataForm.id) {
// 编辑
await editObj(submitData);
useMessage().success('保存成功');
} else {
// 新增(暂存,不启动流程)
await addObj(submitData);
useMessage().success('提交成功');
}
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || (dataForm.id ? '保存失败' : '提交失败'));
} finally {
loading.value = false;
}
};
// 暂存
const handleTempStore = async () => {
if (loading.value) return;
loading.value = true;
try {
const valid = await formRef.value?.validate().catch(() => {});
if (!valid) {
loading.value = false;
return false;
}
const submitData = {
...dataForm,
fileIds: getFileIdsArray(),
};
await tempStore(submitData);
useMessage().success('暂存成功');
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg || '暂存失败');
} finally {
loading.value = false;
}
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped>
.mb20 {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,293 @@
<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="state.queryForm" ref="searchFormRef" :inline="true" @keyup.enter="getDataList" class="search-form">
<el-form-item label="采购编号" prop="purchaseNo">
<el-input
v-model="state.queryForm.purchaseNo"
placeholder="请输入采购编号"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="采购项目名称" prop="projectName">
<el-input
v-model="state.queryForm.projectName"
placeholder="请输入采购项目名称"
clearable
style="width: 200px" />
</el-form-item>
<el-form-item label="项目类别" prop="projectType">
<el-select
v-model="state.queryForm.projectType"
placeholder="请选择项目类别"
clearable
style="width: 200px">
<el-option label="货物" value="A" />
<el-option label="工程" value="B" />
<el-option label="服务" value="C" />
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="state.queryForm.status"
placeholder="请选择状态"
clearable
style="width: 200px">
<el-option label="撤回" value="-2" />
<el-option label="暂存" value="-1" />
<el-option label="运行中" value="0" />
<el-option label="完成" value="1" />
<el-option label="作废" value="2" />
<el-option label="终止" value="3" />
</el-select>
</el-form-item>
<el-form-item label="是否集采" prop="isCentralized">
<el-select
v-model="state.queryForm.isCentralized"
placeholder="请选择是否集采"
clearable
style="width: 200px">
<el-option label="否" value="0" />
<el-option label="是" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="getDataList">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Document /></el-icon>
采购申请管理
</span>
<div class="header-actions">
<el-button
icon="FolderAdd"
type="primary"
@click="formDialogRef.openDialog('add')">
新增
</el-button>
<right-toolbar v-model:showSearch="showSearch" class="ml10" @queryTable="getDataList" />
</div>
</div>
</template>
<!-- 表格 -->
<el-table
ref="tableRef"
:data="state.dataList"
v-loading="state.loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center">
<template #header>
<el-icon><List /></el-icon>
</template>
</el-table-column>
<el-table-column prop="code" label="编号" min-width="120" show-overflow-tooltip>
<template #header>
<el-icon><DocumentCopy /></el-icon>
<span style="margin-left: 4px">编号</span>
</template>
</el-table-column>
<el-table-column prop="purchaseNo" label="采购编号" min-width="120" show-overflow-tooltip>
<template #header>
<el-icon><DocumentCopy /></el-icon>
<span style="margin-left: 4px">采购编号</span>
</template>
</el-table-column>
<el-table-column prop="projectName" label="采购项目名称" min-width="200" show-overflow-tooltip>
<template #header>
<el-icon><Document /></el-icon>
<span style="margin-left: 4px">采购项目名称</span>
</template>
</el-table-column>
<el-table-column prop="projectType" label="项目类别" width="100" align="center">
<template #header>
<el-icon><Collection /></el-icon>
<span style="margin-left: 4px">项目类别</span>
</template>
<template #default="scope">
<el-tag v-if="scope.row.projectType === 'A'" type="success">货物</el-tag>
<el-tag v-else-if="scope.row.projectType === 'B'" type="warning">工程</el-tag>
<el-tag v-else-if="scope.row.projectType === 'C'" type="info">服务</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="budget" label="预算金额(元)" width="120" align="right">
<template #header>
<el-icon><Money /></el-icon>
<span style="margin-left: 4px">预算金额</span>
</template>
<template #default="scope">
{{ scope.row.budget ? Number(scope.row.budget).toLocaleString() : '-' }}
</template>
</el-table-column>
<el-table-column prop="isCentralized" label="是否集采" width="100" align="center">
<template #header>
<el-icon><CircleCheck /></el-icon>
<span style="margin-left: 4px">是否集采</span>
</template>
<template #default="scope">
<el-tag v-if="scope.row.isCentralized === '1'" type="success"></el-tag>
<el-tag v-else-if="scope.row.isCentralized === '0'" type="info"></el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #header>
<el-icon><InfoFilled /></el-icon>
<span style="margin-left: 4px">状态</span>
</template>
<template #default="scope">
<el-tag v-if="scope.row.status === '-2'" type="info">撤回</el-tag>
<el-tag v-else-if="scope.row.status === '-1'" type="warning">暂存</el-tag>
<el-tag v-else-if="scope.row.status === '0'" type="primary">运行中</el-tag>
<el-tag v-else-if="scope.row.status === '1'" type="success">完成</el-tag>
<el-tag v-else-if="scope.row.status === '2'" type="danger">作废</el-tag>
<el-tag v-else-if="scope.row.status === '3'" type="info">终止</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" show-overflow-tooltip>
<template #header>
<el-icon><Clock /></el-icon>
<span style="margin-left: 4px">创建时间</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="200">
<template #default="scope">
<el-button
icon="View"
link
type="primary"
@click="formDialogRef.openDialog('view', scope.row)">
查看
</el-button>
<el-button
v-if="scope.row.status === '-1'"
icon="Edit"
link
type="primary"
@click="formDialogRef.openDialog('edit', scope.row)">
编辑
</el-button>
<el-button
v-if="scope.row.status === '-1'"
icon="Delete"
link
type="danger"
@click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<pagination
v-show="state.total > 0"
:total="state.total"
v-model:page="state.page"
v-model:limit="state.limit"
@pagination="getDataList"
/>
</el-card>
</div>
<!-- 编辑新增表单对话框 -->
<FormDialog ref="formDialogRef" @refresh="getDataList" />
</div>
</template>
<script setup lang="ts" name="PurchasingRequisition">
import { ref, reactive, defineAsyncComponent } from 'vue'
import { BasicTableProps, useTable } from "/@/hooks/table";
import { getPage, delObj } from "/@/api/finance/purchasingrequisition";
import { useMessage, useMessageBox } from "/@/hooks/message";
import { List, Document, DocumentCopy, Search, Collection, Money, CircleCheck, InfoFilled, Clock } from '@element-plus/icons-vue'
// 引入组件
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
// 定义变量内容
const tableRef = ref()
const formDialogRef = ref()
const searchFormRef = ref()
const showSearch = ref(true)
/**
* 定义响应式表格数据
*/
const state: BasicTableProps = reactive<BasicTableProps>({
pageList: getPage,
queryForm: {
purchaseNo: '',
projectName: '',
projectType: '',
status: '',
isCentralized: '',
},
createdIsNeed: true
});
/**
* 使用 useTable 定义表格相关操作
*/
const { getDataList, tableStyle } = useTable(state);
/**
* 重置搜索表单
*/
const handleReset = () => {
searchFormRef.value?.resetFields();
getDataList();
};
/**
* 删除当前行
* @param row - 当前行数据
*/
const handleDelete = async (row: any) => {
try {
await useMessageBox().confirm('确定要删除该记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
} catch {
return;
}
try {
await delObj(row.id);
useMessage().success('删除成功');
getDataList();
} catch (err: any) {
useMessage().error(err.msg || '删除失败');
}
};
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
</style>

View File

@@ -19,7 +19,7 @@
clearable
filterable
style="width: 200px">
<el-option
<el-option
v-for="item in schoolYearList"
:key="item.year"
:label="item.year"