This commit is contained in:
2025-08-29 12:32:49 +08:00
63 changed files with 4696 additions and 1743 deletions

View File

@ -12,9 +12,7 @@ VITE_APP_BASE_API = 'http://192.168.110.209:8899'
# 罗成 # 罗成
# VITE_APP_BASE_API = 'http://192.168.110.213:8899' # VITE_APP_BASE_API = 'http://192.168.110.213:8899'
# 朱银 # 朱银
# VITE_APP_BASE_API = 'http://192.168.110.149:8899' # VITE_APP_BASE_API = 'http://192.168.110.180:8899'
#曾涛
# VITE_APP_BASE_API = 'http://192.168.110.171:8899'
# 无人机接口地址 # 无人机接口地址

Binary file not shown.

View File

@ -19,3 +19,11 @@ onMounted(() => {
}); });
}); });
</script> </script>
<style>
* {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard syntax */
}
</style>

View File

@ -27,3 +27,11 @@ export const systemUserList = (query) => {
params: query params: query
}); });
}; };
// 查询
export const desUserList = (query) => {
return request({
url: '/design/drawingreviewReceipts/desUser/list',
method: 'get',
params: query
});
};

View File

@ -56,7 +56,7 @@ export const fillOutTheDesignVerificationForm = (data) => {
export const drawingreviewReceipts = (data) => { export const drawingreviewReceipts = (data) => {
return request({ return request({
url: '/design/drawingreviewReceipts', url: '/design/drawingreviewReceipts',
method: 'post', method: 'put',
data data
}); });
}; };
@ -96,3 +96,10 @@ export const drawingreview = (id) => {
method: 'get' method: 'get'
}); });
}; };
// 获取单据
export const getDrawingreviewReceipts = (id) => {
return request({
url: '/design/drawingreviewReceipts/review/' + id,
method: 'get'
});
};

View File

@ -75,3 +75,12 @@ export const getWhetherItExists = (id: string | number): AxiosPromise<ListOfForm
} }
}); });
}; };
//模版新增
export const addFormalities = (data: any): AxiosPromise<ListOfFormalitiesVO> => {
return request({
url: '/formalities/formalitiesAreConsolidated/addFormalities',
method: 'post',
data
});
};

View File

@ -160,3 +160,13 @@ export const obtainTheVersion = (query: any) => {
params: query params: query
}); });
}; };
/**
* 获取到物资剩余量
*/
export const mrpBaseRemaining = (query: any) => {
return request({
url: '/cailiaoshebei/mrpBase/remaining',
method: 'get',
params: query
});
};

View File

@ -68,3 +68,10 @@ export const getMaterialName = (id: any) => {
method: 'get' method: 'get'
}); });
}; };
//获取出库记录
export const inventoryList = (id: any) => {
return request({
url: '/materials/materialIssue/inventory/list/' + id,
method: 'get'
});
};

View File

@ -3,9 +3,9 @@ import { AxiosPromise } from 'axios';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
// 获取路由 // 获取路由
export function getRouters(): AxiosPromise<RouteRecordRaw[]> { export function getRouters(id: string): AxiosPromise<RouteRecordRaw[]> {
return request({ return request({
url: '/system/menu/getRouters', url: '/system/menu/getRouters/' + id,
method: 'get' method: 'get'
}); });
} }

View File

@ -54,6 +54,14 @@ export interface ContractorForm extends BaseEntity {
* 主键id * 主键id
*/ */
id?: string | number; id?: string | number;
/**
* 供应商id
*/
supplierId?: string | number;
/**
* 供应商
*/
supplier?: string;
/** /**
* 主键id * 主键id

View File

@ -186,3 +186,14 @@ export const uploadProjectFile = (data: any) => {
data: data data: data
}); });
}; };
/**
* 切换项目
* @param id
*/
export const changeProject = (id: string | number) => {
return request({
url: '/project/project/changeProject/' + id,
method: 'get'
});
};

View File

@ -8,7 +8,7 @@ import { SupplierInputVO, SupplierInputForm, SupplierInputQuery } from '@/api/su
* @returns {*} * @returns {*}
*/ */
export const listSupplierInput = (query?: SupplierInputQuery): AxiosPromise<SupplierInputVO[]> => { export const listSupplierInput = (query?: any): AxiosPromise<SupplierInputVO[]> => {
return request({ return request({
url: '/supplierInput/supplierInput/list', url: '/supplierInput/supplierInput/list',
method: 'get', method: 'get',

View File

@ -68,3 +68,11 @@ export const delMenu = (menuId: string | number) => {
method: 'delete' method: 'delete'
}); });
}; };
// 获取所有路由
export const getAllRouters = () => {
return request({
url: '/system/menu/getAllRouters',
method: 'get'
});
};

View File

@ -75,3 +75,11 @@ export function getRoleList(deptId?: number | string): AxiosPromise<any[]> {
} }
}); });
} }
// 获取部门下的项目列表
export function getProjectByDeptId(deptId?: number | string): AxiosPromise<any[]> {
return request({
url: '/system/dept/projectIdList/' + deptId,
method: 'get'
});
}

View File

@ -60,19 +60,20 @@ export interface UserForm {
nickName?: string; nickName?: string;
password: string; password: string;
phonenumber?: string; phonenumber?: string;
projectRoles?: any[];
email?: string; email?: string;
sex?: string; sex?: string;
status: string; status: string;
remark?: string; remark?: string;
postIds: string[]; postIds: string[];
roleIds: string[];
filePath?: string; filePath?: string;
} }
export interface UserInfoVO { export interface UserInfoVO {
user: UserVO; user: UserVO;
roles: RoleVO[]; roles: RoleVO[];
roleIds: string[];
projectRoles: any[];
posts: PostVO[]; posts: PostVO[];
postIds: string[]; postIds: string[];
roleGroup: string; roleGroup: string;

View File

@ -185,6 +185,10 @@ const props = defineProps({
taskVariables: { taskVariables: {
type: Object as () => Record<string, any>, type: Object as () => Record<string, any>,
default: () => {} default: () => {}
},
businessId1: {
type: String,
default: ''
} }
}); });
//遮罩层 //遮罩层
@ -336,6 +340,9 @@ const handleCompleteTask = async () => {
} }
if (isDrawing.value) { if (isDrawing.value) {
isShowSubmit.value = true; isShowSubmit.value = true;
nextTick(() => {
detailFormTeRef.value.getInfo(props.businessId1);
});
return; return;
} }
await proxy?.$modal.confirm('是否确认提交?'); await proxy?.$modal.confirm('是否确认提交?');
@ -538,6 +545,9 @@ const handleTermination = async () => {
const handleTerminationTask = async () => { const handleTerminationTask = async () => {
if (isDrawing.value) { if (isDrawing.value) {
isShowTermination.value = true; isShowTermination.value = true;
nextTick(() => {
detailFormTeRef.value.getInfo(props.businessId);
});
return; return;
} }
const params = { const params = {

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="select-container"> <div class="select-container" v-loading.fullscreen.lock="fullscreenLoading">
<label for="projectSelect" class="select-label">项目列表:</label> <label for="projectSelect" class="select-label">项目列表:</label>
<el-select <el-select
id="projectSelect" id="projectSelect"
@ -19,12 +19,18 @@
import { ref, computed, watch } from 'vue'; import { ref, computed, watch } from 'vue';
import { useUserStore } from '@/store/modules/user'; import { useUserStore } from '@/store/modules/user';
import { getProjectTeam } from '@/utils/projectTeam'; import { getProjectTeam } from '@/utils/projectTeam';
import router, { resetRouter } from '@/router';
import usePermissionStore from '@/store/modules/permission';
import { isHttp } from '@/utils/validate';
import { changeProject } from '@/api/project/project';
const fullscreenLoading = ref(false);
const route = useRoute();
const userStore = useUserStore(); const userStore = useUserStore();
const projects = computed(() => [ const projects = computed(() => [
// { id: '', name: '全部工程项目' }, // 添加空选项 // { id: '', name: '全部工程项目' }, // 添加空选项
...userStore.projects ...userStore.projects
]); ]);
const proxy = getCurrentInstance()?.proxy as any;
const selectedProjectId = ref(userStore.selectedProject?.id || ''); const selectedProjectId = ref(userStore.selectedProject?.id || '');
@ -37,13 +43,66 @@ watch(
{ deep: true } { deep: true }
); );
const handleSelect = (projectId: string) => { /** 切换项目逻辑 */
const handleSelect = async (projectId: string) => {
proxy.$cache.local.setJSON('isCheckRole', 'true');
const userStore = useUserStore();
const permissionStore = usePermissionStore();
const selectedProject = projects.value.find((p) => p.id === projectId); const selectedProject = projects.value.find((p) => p.id === projectId);
if (selectedProject) { if (!selectedProject) return;
userStore.setSelectedProject(selectedProject); const loadingInstance = ElLoading.service({
console.log(userStore.selectedProject); // 打印选中的项目 lock: true,
text: '项目切换中...',
background: 'rgba(0, 0, 0, 0.7)'
});
setTimeout(() => {
if (loadingInstance && loadingInstance.visible) {
loadingInstance.close();
} }
}, 3000);
await changeProject(projectId);
console.log('切换项目', selectedProject);
// 更新项目 & 权限
userStore.setSelectedProject(selectedProject);
await userStore.setInfo();
await userStore.setRoles(); // 这里会刷新 permissions/roles
// 重新生成路由
permissionStore.generateRoutes().then((routeList) => {
const currentPath = router.currentRoute.value.fullPath;
const exist = currentPath == '/' || currentPath == '/index' ? true : routeExists(currentPath, routeList);
if (exist) return loadingInstance.close();
proxy?.$tab.closeAllPage();
router.push('/index');
loadingInstance.close();
// 刷新当前路由
});
}; };
function routeExists(fullPath: string, routes: any[], parentPath = ''): boolean {
for (const route of routes) {
// 拼接完整 path
let currentPath = route.path.startsWith('/') ? route.path : `${parentPath}/${route.path}`;
// 处理多余的 "//"
currentPath = currentPath.replace(/\/+/g, '/');
// 判断
if (currentPath === fullPath) {
return true;
}
// 递归子路由
if (route.children && route.children.length > 0) {
if (routeExists(fullPath, route.children, currentPath)) {
return true;
}
}
}
return false;
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -4,6 +4,7 @@
<el-menu-item v-if="index < visibleNumber" :key="index" :style="{ '--theme': theme }" :index="item.path"> <el-menu-item v-if="index < visibleNumber" :key="index" :style="{ '--theme': theme }" :index="item.path">
<svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta ? item.meta.icon : ''" /> <svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta ? item.meta.icon : ''" />
{{ item.meta?.title }} {{ item.meta?.title }}
<!-- <span class="bage" v-if="item.meta?.title == '我的任务' && total > 0">{{ total }}</span> -->
</el-menu-item> </el-menu-item>
</template> </template>
@ -26,20 +27,27 @@ import useAppStore from '@/store/modules/app';
import useSettingsStore from '@/store/modules/settings'; import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import useUserStore from '@/store/modules/user';
import useNoticeStore from '@/store/modules/notice';
import { pageByTaskWait } from '@/api/workflow/task';
const userStore = useUserStore();
const noticeStore = storeToRefs(useNoticeStore());
// 顶部栏初始数 // 顶部栏初始数
const visibleNumber = ref<number>(-1); const visibleNumber = ref<number>(-1);
// 当前激活菜单的 index // 当前激活菜单的 index
const currentIndex = ref<string>(); const currentIndex = ref<string>();
// 隐藏侧边栏路由 // 隐藏侧边栏路由
const hideList = ['/index', '/user/profile']; const hideList = ['/index', '/user/profile'];
const total = ref(1);
const appStore = useAppStore(); const appStore = useAppStore();
const settingsStore = useSettingsStore(); const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore(); const permissionStore = usePermissionStore();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
onMounted(() => {
console.log(33333);
getWaitingList();
});
// 主题颜色 // 主题颜色
const theme = computed(() => settingsStore.theme); const theme = computed(() => settingsStore.theme);
// 所有的路由信息 // 所有的路由信息
@ -158,6 +166,26 @@ onBeforeUnmount(() => {
onMounted(() => { onMounted(() => {
setVisibleNumber(); setVisibleNumber();
}); });
// 获取我的待办
//分页
const getWaitingList = () => {
pageByTaskWait({ pageNum: 1, pageSize: 10 }).then((resp) => {
console.log(resp);
total.value = resp.total;
});
};
//用深度监听 消息
watch(
() => noticeStore.state.value.notices,
(newVal) => {
let time = setTimeout(() => {
getWaitingList();
clearTimeout(time);
}, 500);
},
{ deep: true }
);
</script> </script>
<style lang="scss"> <style lang="scss">
@ -197,4 +225,31 @@ onMounted(() => {
.topmenu-container .svg-icon { .topmenu-container .svg-icon {
margin-right: 4px; margin-right: 4px;
} }
.bage {
position: absolute;
top: 6px;
right: -12px;
padding: 0 6px;
height: 16px;
line-height: 16px;
background: #ff7a7a;
border-radius: 8px;
font-size: 12px;
color: #fff;
white-space: nowrap;
box-sizing: border-box;
}
.el-menu-item .el-menu-item__content {
position: relative;
padding-right: 24px;
}
.menu-title {
display: inline-block;
max-width: calc(100% - 24px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style> </style>

View File

@ -6,6 +6,7 @@
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" /> <svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" />
<template #title> <template #title>
<span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span> <span class="menu-title" :title="hasTitle(onlyOneChild.meta.title)">{{ onlyOneChild.meta.title }}</span>
<span class="bage" v-if="onlyOneChild.meta?.title == '我的待办' && total > 0">{{ total }}</span>
</template> </template>
</el-menu-item> </el-menu-item>
</app-link> </app-link>
@ -15,6 +16,7 @@
<template v-if="item.meta" #title> <template v-if="item.meta" #title>
<svg-icon :icon-class="item.meta ? item.meta.icon : ''" /> <svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
<span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span> <span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
<!-- <span class="bage" v-if="item.meta?.title == '我的任务' && total >= 0">{{ total }}</span> -->
</template> </template>
<sidebar-item <sidebar-item
@ -34,7 +36,11 @@ import { isExternal } from '@/utils/validate';
import AppLink from './Link.vue'; import AppLink from './Link.vue';
import { getNormalPath } from '@/utils/ruoyi'; import { getNormalPath } from '@/utils/ruoyi';
import { RouteRecordRaw } from 'vue-router'; import { RouteRecordRaw } from 'vue-router';
import { pageByTaskWait } from '@/api/workflow/task';
import useUserStore from '@/store/modules/user';
import useNoticeStore from '@/store/modules/notice';
const userStore = useUserStore();
const noticeStore = storeToRefs(useNoticeStore());
const props = defineProps({ const props = defineProps({
item: { item: {
type: Object as PropType<RouteRecordRaw>, type: Object as PropType<RouteRecordRaw>,
@ -49,7 +55,22 @@ const props = defineProps({
default: '' default: ''
} }
}); });
const total = ref(0);
onMounted(() => {
if (onlyOneChild.value.meta?.title == '我的待办' || props.item.meta?.title == '我的任务') {
console.log(44444444);
getWaitingList();
}
});
// 获取我的待办
//分页
const getWaitingList = () => {
pageByTaskWait({ pageNum: 1, pageSize: 10 }).then((resp) => {
console.log(resp);
total.value = resp.total;
});
};
const onlyOneChild = ref<any>({}); const onlyOneChild = ref<any>({});
const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => { const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
@ -64,12 +85,12 @@ const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[])
return true; return true;
}); });
// When there is only one child router, the child router is displayed by default // 只有一个子路由时默认显示子路由
if (showingChildren.length === 1) { if (showingChildren.length === 1) {
return true; return true;
} }
// Show parent if there are no child router to display // 没有子路由可显示时显示父路由
if (showingChildren.length === 0) { if (showingChildren.length === 0) {
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }; onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
return true; return true;
@ -98,4 +119,49 @@ const hasTitle = (title: string | undefined): string => {
} }
return title; return title;
}; };
//用深度监听 消息
watch(
() => noticeStore.state.value.notices,
(newVal) => {
if (onlyOneChild.value.meta?.title == '我的待办') {
console.log(121212121);
let time = setTimeout(() => {
getWaitingList();
clearTimeout(time);
}, 500);
}
},
{ deep: true }
);
</script> </script>
<style lang="scss" scoped>
.bage {
position: absolute;
top: 6px;
right: 36px;
padding: 0 6px;
height: 16px;
line-height: 16px;
background: #ff7a7a;
border-radius: 8px;
font-size: 12px;
color: #fff;
white-space: nowrap;
box-sizing: border-box;
}
.el-menu-item .el-menu-item__content {
position: relative;
padding-right: 24px;
}
.menu-title {
display: inline-block;
max-width: calc(100% - 24px);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

View File

@ -1,3 +1,4 @@
import $cache from '@/plugins/cache';
import { to as tos } from 'await-to-js'; import { to as tos } from 'await-to-js';
import router from './router'; import router from './router';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
@ -9,6 +10,8 @@ import useUserStore from '@/store/modules/user';
import useSettingsStore from '@/store/modules/settings'; import useSettingsStore from '@/store/modules/settings';
import usePermissionStore from '@/store/modules/permission'; import usePermissionStore from '@/store/modules/permission';
let isFirst = false;
NProgress.configure({ showSpinner: false }); NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/register', '/social-callback', '/register*', '/register/*', '/materials/purchaseDoc/uploadCode']; const whiteList = ['/login', '/register', '/social-callback', '/register*', '/register/*', '/materials/purchaseDoc/uploadCode'];
@ -16,54 +19,64 @@ const isWhiteList = (path: string) => {
return whiteList.some((pattern) => isPathMatch(pattern, path)); return whiteList.some((pattern) => isPathMatch(pattern, path));
}; };
router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from) => {
NProgress.start(); NProgress.start();
if (to.path == '/indexEquipment' || to.path == '/materials/purchaseDoc/uploadCode' || to.path == '/codeDetail') {
next(); // 特殊页面放行
} else if (getToken()) { if (['/indexEquipment', '/materials/purchaseDoc/uploadCode', '/codeDetail'].includes(to.path)) {
to.meta.title && useSettingsStore().setTitle(to.meta.title); return true;
/* has token*/ }
// 已登录
if (getToken()) {
if (to.meta.title) useSettingsStore().setTitle(to.meta.title);
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' });
NProgress.done(); NProgress.done();
} else if (isWhiteList(to.path)) { return { path: '/' };
next(); }
} else {
if (useUserStore().roles.length === 0) { if (isWhiteList(to.path)) {
return true;
}
if ((!isFirst && useUserStore().roles.length === 0) || $cache.local.getJSON('isCheckRole') === 'true') {
isFirst = true;
isRelogin.show = true; isRelogin.show = true;
// 判断当前用户是否已拉取完user_info信息
const [err] = await tos(useUserStore().getInfo()); const [err] = await tos(useUserStore().getInfo());
if (err) { if (err) {
await useUserStore().logout(); await useUserStore().logout();
ElMessage.error(err); ElMessage.error(err);
next({ path: '/' }); NProgress.done();
} else { return { path: '/' };
}
isRelogin.show = false; isRelogin.show = false;
const accessRoutes = await usePermissionStore().generateRoutes(); const accessRoutes = await usePermissionStore().generateRoutes();
// 根据roles权限生成可访问的路由表
accessRoutes.forEach((route) => { accessRoutes.forEach((route) => {
if (!isHttp(route.path)) { if (!isHttp(route.path)) router.addRoute(route);
router.addRoute(route); // 动态添加可访问路由表
}
}); });
// @ts-expect-error hack方法 确保addRoutes已完成
next({ path: to.path, replace: true, params: to.params, query: to.query, hash: to.hash, name: to.name as string }); // hack方法 确保addRoutes已完成 $cache.local.remove('isCheckRole');
// 确保路由已添加后再跳转
return { ...to, replace: true };
} }
return true;
} else { } else {
next(); isFirst = false;
} }
}
} else { // 未登录
// 没有token
if (isWhiteList(to.path)) { if (isWhiteList(to.path)) {
// 在免登录白名单,直接进入 return true;
next(); }
} else {
const redirect = encodeURIComponent(to.fullPath || '/'); const redirect = encodeURIComponent(to.fullPath || '/');
next(`/login?redirect=${redirect}`); // 否则全部重定向到登录页
NProgress.done(); NProgress.done();
} return { path: `/login?redirect=${redirect}` };
}
}); });
router.afterEach(() => { router.afterEach(() => {

View File

@ -1,6 +1,7 @@
import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router'; import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router';
/* Layout */ /* Layout */
import Layout from '@/layout/index.vue'; import Layout from '@/layout/index.vue';
import usePermissionStore from '@/store/modules/permission';
/** /**
* Note: 路由配置项 * Note: 路由配置项
@ -65,11 +66,7 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import('@/views/register.vue'), component: () => import('@/views/register.vue'),
hidden: true hidden: true
}, },
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404.vue'),
hidden: true
},
{ {
path: '/401', path: '/401',
component: () => import('@/views/error/401.vue'), component: () => import('@/views/error/401.vue'),
@ -134,10 +131,16 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import('@/views/gis2D/index.vue'), component: () => import('@/views/gis2D/index.vue'),
hidden: true hidden: true
}, },
{ {
path: '/materials/purchaseDoc/uploadCode', path: '/materials/purchaseDoc/uploadCode',
component: () => import('@/views/materials/purchaseDoc/uploadCode.vue'), component: () => import('@/views/materials/purchaseDoc/uploadCode.vue'),
hidden: true hidden: true
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/error/404.vue'),
hidden: true
} }
]; ];
@ -231,3 +234,11 @@ const router = createRouter({
}); });
export default router; export default router;
export function resetRouter() {
router.getRoutes().forEach((route) => {
if (route.name && !constantRoutes.find((r) => r.name === route.name)) {
router.hasRoute(route.name) && router.removeRoute(route.name);
}
});
}

View File

@ -10,6 +10,7 @@ import ParentView from '@/components/ParentView/index.vue';
import InnerLink from '@/layout/components/InnerLink/index.vue'; import InnerLink from '@/layout/components/InnerLink/index.vue';
import { createCustomNameComponent } from '@/utils/createCustomNameComponent'; import { createCustomNameComponent } from '@/utils/createCustomNameComponent';
import { useUserStoreHook } from './user';
// 匹配views里面所有的.vue文件 // 匹配views里面所有的.vue文件
const modules = import.meta.glob('./../../views/**/*.vue'); const modules = import.meta.glob('./../../views/**/*.vue');
@ -44,7 +45,7 @@ export const usePermissionStore = defineStore('permission', () => {
sidebarRouters.value = routes; sidebarRouters.value = routes;
}; };
const generateRoutes = async (): Promise<RouteRecordRaw[]> => { const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
const res = await getRouters(); const res = await getRouters(useUserStoreHook().selectedProject?.id || '0');
const { data } = res; const { data } = res;
const sdata = JSON.parse(JSON.stringify(data)); const sdata = JSON.parse(JSON.stringify(data));
const rdata = JSON.parse(JSON.stringify(data)); const rdata = JSON.parse(JSON.stringify(data));

View File

@ -28,7 +28,6 @@ const getSelectedProjectFromStorage = () => {
const getProjectTeamListFromStorage = () => { const getProjectTeamListFromStorage = () => {
const stored = $cache.local.getJSON('ProjectTeamList'); const stored = $cache.local.getJSON('ProjectTeamList');
console.log('获取缓存的项目班组列表:', stored); console.log('获取缓存的项目班组列表:', stored);
return stored ? stored : null; return stored ? stored : null;
}; };
@ -41,7 +40,9 @@ export const useUserStore = defineStore('user', () => {
const deptId = ref<string | number>(''); const deptId = ref<string | number>('');
const avatar = ref(''); const avatar = ref('');
const roles = ref<Array<string>>([]); // 用户角色编码集合 → 判断路由权限 const roles = ref<Array<string>>([]); // 用户角色编码集合 → 判断路由权限
const permissions = ref<Array<string>>([]); // 用户权限编码集合 → 判断按钮权限 const permissions = ref<Array<any>>([]); // 用户权限编码集合 → 判断按钮权限
const permissionList = ref<Array<any>>([]); // 用户所有权限列表
const roleList = ref<Array<any>>([]); // 用户所有角色列表
const projects = ref<Array<{ id: string; name: string }>>([]); const projects = ref<Array<{ id: string; name: string }>>([]);
// 从localStorage获取缓存的项目如果没有则默认为null // 从localStorage获取缓存的项目如果没有则默认为null
@ -66,15 +67,32 @@ export const useUserStore = defineStore('user', () => {
// 获取用户信息 // 获取用户信息
const getInfo = async (): Promise<void> => { const getInfo = async (): Promise<void> => {
// **新增项目数据获取**
const [projectErr, projectRes] = await to(getUserProject());
if (projectRes?.data) {
const projectList = projectRes.data.map((p) => ({
id: p.projectId,
name: p.projectName || '未知项目'
}));
setProjects(projectList);
// 如果有缓存的选中项目,且该项目在当前项目列表中存在,则使用缓存的项目
const storedProject = getSelectedProjectFromStorage();
if (storedProject && projectList.some((p) => p.id === storedProject.id)) {
setSelectedProject(storedProject);
} else if (projectList.length > 0) {
// 否则默认选择第一个项目
setSelectedProject(projectList[0]);
}
}
const [err, res] = await to(getUserInfo()); const [err, res] = await to(getUserInfo());
if (res) { if (res) {
const data = res.data; const data = res.data;
const user = data.user; const user = data.user;
const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar; const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar;
if (data.roles && data.roles.length > 0) { if (data.roles && data.roles.length > 0) {
roles.value = data.roles; permissionList.value = data.permissions;
permissions.value = data.permissions; roleList.value = data.roles;
setRoles();
} else { } else {
roles.value = ['ROLE_DEFAULT']; roles.value = ['ROLE_DEFAULT'];
} }
@ -85,30 +103,38 @@ export const useUserStore = defineStore('user', () => {
tenantId.value = user.tenantId; tenantId.value = user.tenantId;
deptId.value = user.deptId; deptId.value = user.deptId;
// **新增项目数据获取**
const [projectErr, projectRes] = await to(getUserProject());
if (projectRes?.data) {
const projectList = projectRes.data.map((p) => ({
id: p.projectId,
name: p.projectName || '未知项目'
}));
setProjects(projectList);
// 如果有缓存的选中项目,且该项目在当前项目列表中存在,则使用缓存的项目
const storedProject = getSelectedProjectFromStorage();
if (storedProject && projectList.some((p) => p.id === storedProject.id)) {
setSelectedProject(storedProject);
} else if (projectList.length > 0) {
// 否则默认选择第一个项目
setSelectedProject(projectList[0]);
}
}
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject(err); return Promise.reject(err);
}; };
const setInfo = async () => {
const [err, res] = await to(getUserInfo());
if (res) {
const data = res.data;
const user = data.user;
const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar;
if (data.roles && data.roles.length > 0) {
permissionList.value = data.permissions;
roleList.value = data.roles;
setRoles();
} else {
roles.value = ['ROLE_DEFAULT'];
}
name.value = user.userName;
nickname.value = user.nickName;
avatar.value = profile;
userId.value = user.userId;
tenantId.value = user.tenantId;
deptId.value = user.deptId;
}
};
const setRoles = () => {
const projectRole = roleList.value.find((item) => item.projectId == selectedProject.value?.id)?.projectRoles || [];
roles.value = projectRole;
const projectPermissions = permissionList.value.find((item) => item.projectId == selectedProject.value?.id)?.projectPermissions || [];
permissions.value = projectPermissions;
};
// 注销 // 注销
const logout = async (): Promise<void> => { const logout = async (): Promise<void> => {
@ -158,7 +184,9 @@ export const useUserStore = defineStore('user', () => {
setProjectTeamList, setProjectTeamList,
projects, projects,
selectedProject, selectedProject,
ProjectTeamList ProjectTeamList,
setInfo,
setRoles
}; };
}); });

View File

@ -20,12 +20,10 @@ export const initSSE = (url: any) => {
}); });
watch(error, () => { watch(error, () => {
console.log('SSE connection error:', error.value);
error.value = null; error.value = null;
}); });
watch(data, () => { watch(data, () => {
console.log('🚀 ~ initSSE ~ data:', JSON.parse(data.value));
let label = ''; let label = '';
let route1 = ''; let route1 = '';
let detailId = ''; let detailId = '';
@ -33,6 +31,9 @@ export const initSSE = (url: any) => {
if (JSON.parse(data.value)) { if (JSON.parse(data.value)) {
const obj = JSON.parse(data.value); const obj = JSON.parse(data.value);
route1 = obj.type; route1 = obj.type;
if (obj.type == 'count') {
return;
}
label = obj.content; label = obj.content;
// detailId = obj.detailId; // detailId = obj.detailId;
data.value = null; data.value = null;

View File

@ -63,14 +63,7 @@
<el-table-column prop="quantity" label="数量" /> <el-table-column prop="quantity" label="数量" />
<el-table-column prop="remark" label="单价" align="center"> <el-table-column prop="remark" label="单价" align="center">
<template #default="scope"> <template #default="scope">
<el-input-number <span>{{ scope.row.unitPrice }}</span>
:model-value="scope.row.unitPrice"
@change="(val) => (scope.row.unitPrice = val)"
:precision="2"
:step="0.1"
:controls="false"
v-if="scope.row.quantity && scope.row.quantity != 0"
/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="price" label="总价" align="center"> <el-table-column prop="price" label="总价" align="center">

View File

@ -48,7 +48,7 @@
icon="view" icon="view"
@click="handleViewInfo" @click="handleViewInfo"
v-hasPermi="['bidding:biddingLimitList:getVersionDetail']" v-hasPermi="['bidding:biddingLimitList:getVersionDetail']"
v-if="versionObj.status != 'draft'" v-if="versionObj.status && versionObj.status != 'draft'"
>查看流程</el-button >查看流程</el-button
> >
</el-form-item> </el-form-item>
@ -60,13 +60,22 @@
<el-table-column prop="num" label="编号" /> <el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" /> <el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" /> <el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" /> <el-table-column prop="quantity" label="数量">
<template #default="scope">
{{ scope.row.children.length > 0 ? '' : scope.row.quantity }}
</template>
</el-table-column>
<el-table-column prop="remark" label="单价" align="center"> <el-table-column prop="remark" label="单价" align="center">
<template #default="scope"> <template #default="scope">
<el-input-number <el-input-number
:disabled="versionObj.status != 'draft'" :disabled="versionObj.status != 'draft'"
:model-value="scope.row.unitPrice" :model-value="scope.row.unitPrice"
@change="(val) => (scope.row.unitPrice = val)" @change="
(val) => {
scope.row.unitPrice = val;
changePrice(scope.row);
}
"
:precision="2" :precision="2"
:step="0.1" :step="0.1"
:controls="false" :controls="false"
@ -85,7 +94,7 @@
type="primary" type="primary"
size="small" size="small"
:disabled="versionObj.status != 'draft'" :disabled="versionObj.status != 'draft'"
@click="handleSave(scope.row)" @click="handleSave(scope.row, 'all')"
v-if="scope.row.quantity && scope.row.quantity != 0" v-if="scope.row.quantity && scope.row.quantity != 0"
v-hasPermi="['bidding:biddingLimitList:edit']" v-hasPermi="['bidding:biddingLimitList:edit']"
>确定</el-button >确定</el-button
@ -187,18 +196,21 @@ const getTableData = async () => {
} }
} }
}; };
//修改单价 const modifyPrice = new Map();
const handleSave = (row: any) => {
const changePrice = (row: any) => {
modifyPrice.set(row.id, row);
// if (!row.unitPrice) {
// modifyPrice.delete(row.id);
// }
};
//修改单价 biddingLimitListUpdate
const handleSave = (row?: any, type?: any) => {
try { try {
if (!row.unitPrice) { if (type == 'single') {
ElMessage({
message: '请输入单价',
type: 'warning'
});
return;
}
loading.value = true; loading.value = true;
biddingLimitListUpdate(row).then((res) => { const list = [{ ...row }];
biddingLimitListUpdate(list).then((res) => {
if (res.code == 200) { if (res.code == 200) {
ElMessage({ ElMessage({
message: '修改成功', message: '修改成功',
@ -207,11 +219,30 @@ const handleSave = (row: any) => {
getTableData(); getTableData();
} }
}); });
}
if (type == 'all') {
loading.value = true;
const list = [];
modifyPrice.forEach((item) => {
list.push({ ...item });
});
biddingLimitListUpdate(list).then((res) => {
if (res.code == 200) {
ElMessage({
message: '修改成功',
type: 'success'
});
getTableData();
}
});
}
} catch (error) { } catch (error) {
ElMessage({ ElMessage({
message: '修改失败', message: '修改失败',
type: 'error' type: 'error'
}); });
} finally {
loading.value = false;
} }
}; };
const tableRef = ref<any>(); const tableRef = ref<any>();

View File

@ -26,20 +26,39 @@
<el-table-column prop="name" label="名称" /> <el-table-column prop="name" label="名称" />
<el-table-column prop="content" label="内容" /> <el-table-column prop="content" label="内容" />
<el-table-column prop="price" label="限价" /> <el-table-column prop="price" label="限价" />
<el-table-column prop="plannedBiddingTime" label="计划招标时间" align="center"> <el-table-column prop="plannedBiddingTime" align="center">
<template #header> <span style="color: red">*</span>计划招标时间 </template>
<template #default="scope"> <template #default="scope">
<el-date-picker v-model="scope.row.plannedBiddingTime" type="date" value-format="YYYY-MM-DD" placeholder="选择时间" /> <el-date-picker v-model="scope.row.plannedBiddingTime" type="date" value-format="YYYY-MM-DD" placeholder="选择时间" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="price" label="操作" align="center"> <el-table-column prop="price" label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button type="warning" size="small" @click="handleDetail(scope.row)" v-hasPermi="['tender:segmentedIndicatorPlanning:getMore']" <el-button
type="warning"
icon="view"
size="small"
link
@click="handleDetail(scope.row)"
v-hasPermi="['tender:segmentedIndicatorPlanning:getMore']"
>详情</el-button >详情</el-button
> >
<el-button type="primary" size="small" @click="handleSave(scope.row)" v-hasPermi="['tender:segmentedIndicatorPlanning:edit']" <el-button
type="primary"
icon="edit"
size="small"
link
@click="handleSave(scope.row)"
v-hasPermi="['tender:segmentedIndicatorPlanning:edit']"
>确定</el-button >确定</el-button
> >
<el-button type="danger" size="small" @click="delHandle(scope.row)" v-hasPermi="['tender:segmentedIndicatorPlanning:remove']" <el-button
type="danger"
icon="delete"
size="small"
link
@click="delHandle(scope.row)"
v-hasPermi="['tender:segmentedIndicatorPlanning:remove']"
>删除</el-button >删除</el-button
> >
</template> </template>
@ -99,7 +118,8 @@
<el-table-column prop="name" label="工程或费用名称" /> <el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" /> <el-table-column prop="unit" label="单位" />
<!-- <el-table-column prop="quantity" label="数量" /> --> <!-- <el-table-column prop="quantity" label="数量" /> -->
<el-table-column prop="selectNum" label="选择数量" align="center"> <el-table-column prop="quantity" label="计划量" align="center" />
<el-table-column prop="selectNum" label="设计量" align="center">
<template #default="scope"> <template #default="scope">
<el-input-number <el-input-number
:model-value="scope.row.selectNum" :model-value="scope.row.selectNum"
@ -113,16 +133,36 @@
:step="1" :step="1"
:controls="false" :controls="false"
:max="Math.floor(scope.row.quantity)" :max="Math.floor(scope.row.quantity)"
v-if="scope.row.quantity && scope.row.quantity != 0" v-if="scope.row.quantity && scope.row.quantity != 0 && scope.row.unitPrice"
/> />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="unitPrice" label="单价" align="center" />
<!-- <el-table-column prop="price" label="总价" align="center"> <el-table-column prop="useQuantity" label="剩余量" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row.price }} {{
(scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) == 0
? ''
: (scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
}}
</template> </template>
</el-table-column> --> </el-table-column>
<el-table-column prop="unitPrice" label="单价" align="center" />
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
Number(scope.row.unitPrice) ==
0
? ''
: (
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
Number(scope.row.unitPrice)
).toFixed(2)
}}
</template>
</el-table-column>
</el-table> </el-table>
</el-col> </el-col>
</el-row> </el-row>

View File

@ -35,14 +35,15 @@
<el-form-item> <el-form-item>
<el-button type="primary" @click="handleExport()" v-hasPermi="['tender:billofquantitiesLimitList:export']">导出excel</el-button> <el-button type="primary" @click="handleExport()" v-hasPermi="['tender:billofquantitiesLimitList:export']">导出excel</el-button>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" v-if="reviewStatus == 'draft'" @click="clickApprovalSheet()">审核</el-button> <el-button type="primary" v-if="reviewStatus && reviewStatus == 'draft'" @click="clickApprovalSheet()">审核</el-button>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button <el-button
type="warning" type="warning"
icon="view" icon="view"
v-if="reviewStatus != 'draft'" v-if="reviewStatus && reviewStatus != 'draft'"
@click="clickApprovalSheet()" @click="clickApprovalSheet()"
v-hasPermi="['tender:tenderPlanLimitList:getVersionDetail']" v-hasPermi="['tender:tenderPlanLimitList:getVersionDetail']"
>查看流程</el-button >查看流程</el-button
@ -56,7 +57,11 @@
<el-table-column prop="num" label="编号" /> <el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" /> <el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" /> <el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" /> <el-table-column prop="quantity" label="数量">
<template #default="scope">
{{ scope.row.children.length > 0 ? '' : scope.row.quantity }}
</template>
</el-table-column>
<el-table-column prop="unitPrice" label="单价" align="center"> <el-table-column prop="unitPrice" label="单价" align="center">
<template #default="scope"> <template #default="scope">
<el-input-number <el-input-number
@ -65,6 +70,7 @@
@change=" @change="
(val) => { (val) => {
scope.row.unitPrice = val; scope.row.unitPrice = val;
changePrice(scope.row);
} }
" "
:precision="2" :precision="2"
@ -87,7 +93,7 @@
type="primary" type="primary"
size="small" size="small"
:disabled="reviewStatus != 'draft'" :disabled="reviewStatus != 'draft'"
@click="handleSave(scope.row)" @click="handleSave(scope.row, 'all')"
v-if="scope.row.quantity && scope.row.quantity != 0" v-if="scope.row.quantity && scope.row.quantity != 0"
v-hasPermi="['tender:billofquantitiesLimitList:edit']" v-hasPermi="['tender:billofquantitiesLimitList:edit']"
>确定</el-button >确定</el-button
@ -220,18 +226,18 @@ const getTableData = async () => {
loading.value = false; loading.value = false;
} }
}; };
const modifyPrice = new Map();
const changePrice = (row: any) => {
modifyPrice.set(row.id, row);
};
//修改单价 //修改单价
const handleSave = (row: any) => { const handleSave = (row?: any, type?: any) => {
try { try {
if (!row.unitPrice) { if (type == 'single') {
ElMessage({
message: '请输入单价',
type: 'warning'
});
return;
}
loading.value = true; loading.value = true;
updatePrice({ ...row, type: '1' }).then((res) => { const list = [{ ...row, type: '1' }];
updatePrice(list).then((res) => {
if (res.code == 200) { if (res.code == 200) {
ElMessage({ ElMessage({
message: '修改成功', message: '修改成功',
@ -240,11 +246,30 @@ const handleSave = (row: any) => {
getTableData(); getTableData();
} }
}); });
}
if (type == 'all') {
loading.value = true;
const list = [];
modifyPrice.forEach((item) => {
list.push({ ...item, type: '1' });
});
updatePrice(list).then((res) => {
if (res.code == 200) {
ElMessage({
message: '修改成功',
type: 'success'
});
getTableData();
}
});
}
} catch (error) { } catch (error) {
ElMessage({ ElMessage({
message: '修改失败', message: '修改失败',
type: 'error' type: 'error'
}); });
} finally {
loading.value = false;
} }
}; };
const tableRef = ref<any>(); const tableRef = ref<any>();

View File

@ -38,7 +38,14 @@
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['design:extract:query']">审核</el-button> <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['design:extract:query']">审核</el-button>
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)" v-hasPermi="['design:extract:export']">导出</el-button <el-button
link
type="primary"
v-if="scope.row.status != 'finish'"
icon="Download"
@click="handleDownload(scope.row)"
v-hasPermi="['design:extract:export']"
>导出</el-button
><el-button ><el-button
link link
type="warning" type="warning"
@ -65,15 +72,17 @@
<el-table :loading="loadingFlie" :data="fileList" style="width: 100%" border> <el-table :loading="loadingFlie" :data="fileList" style="width: 100%" border>
<el-table-column prop="fileName" label="文件名称" align="center"> <el-table-column prop="fileName" label="文件名称" align="center">
<template #default="scope"> <template #default="scope">
<el-link <!-- <el-link
:key="scope.row.fileId" :key="scope.row.fileId"
:href="scope.row.fileUrl" :href="scope.row.fileUrl"
target="_blank" target="_blank"
:type="scope.row.status == '1' ? 'primary' : 'info'" :type="scope.row.status == '1' ? 'primary' : 'info'"
:underline="false" :underline="false"
disabled
> >
{{ scope.row.fileName }} {{ scope.row.fileName }}
</el-link> </el-link> -->
<span>{{ scope.row.fileName }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="版本号" align="center" width="120" prop="version"> </el-table-column> <el-table-column label="版本号" align="center" width="120" prop="version"> </el-table-column>

View File

@ -445,7 +445,9 @@ const getMajor = async () => {
let res = await extractUserMajor({ userId: userId.value, projectId: currentProject.value?.id }); let res = await extractUserMajor({ userId: userId.value, projectId: currentProject.value?.id });
if (res.code == 200) { if (res.code == 200) {
des_user_major.value = res.data; des_user_major.value = res.data;
console.log(des_user_major.value); if (res.data.length > 0) {
form.user_major = res.data[0].userMajor;
}
} }
}; };
/** 回显表单数据(编辑/查看/审批场景) */ /** 回显表单数据(编辑/查看/审批场景) */

View File

@ -0,0 +1,804 @@
<template>
<div class="p-6 bg-gray-50">
<div class="appointment mx-auto bg-white rounded-xl shadow-sm overflow-hidden transition-all duration-300 hover:shadow-md">
<!-- 表单标题区域 -->
<div class="bg-gradient-to-r from-blue-500 to-blue-600 text-white p-6">
<h2 class="text-2xl font-bold flex items-center"><i class="el-icon-user-circle mr-3"></i>人员配置</h2>
<p class="text-blue-100 mt-2 opacity-90">请配置项目相关负责人员信息</p>
<el-button @click="disabledForm = false" class="px-8 py-2.5 transition-all duration-300 font-medium" v-if="disabledForm">
点击编辑
</el-button>
</div>
<!-- 表单内容区域 -->
<el-form ref="leaveFormRef" :model="form" :disabled="disabledForm" :rules="rules" label-width="120px" class="p-6 space-y-6">
<!-- 设计负责人 -->
<div class="fonts">
<el-row>
<el-col :span="8"
><el-form-item label="设计负责人" prop="designLeader" class="mb-4">
<el-select
v-model="form.designLeader"
placeholder="请选择设计负责人"
class="w-full transition-all duration-300 border-gray-300 focus:border-blue-400 focus:ring-1 focus:ring-blue-400"
>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</div>
<!-- 专业人员配置专业 + 设计人员 + 校审人员 横向排列 -->
<div class="border border-gray-200 rounded-lg p-5 transition-all duration-300 hover:shadow-md bg-gray-50">
<div class="flex justify-between items-center mb-5">
<h3 class="text-lg font-semibold text-gray-700 flex items-center"><i class="el-icon-users mr-2 text-blue-500"></i>专业人员配置</h3>
<div class="flex gap-3">
<!-- 新增专业按钮 -->
<el-button type="primary" size="small" :disabled="disabledForm" @click="addMajor">
<i class="el-icon-plus mr-1"></i>新增专业
</el-button>
</div>
</div>
<!-- 表头 -->
<el-row :gutter="20" class="mb-3 font-medium text-gray-700">
<el-col :span="6" :xs="24" :sm="8">专业</el-col>
<el-col :span="9" :xs="24" :sm="8">设计人员可多选</el-col>
<el-col :span="9" :xs="24" :sm="8">校审人员可多选</el-col>
</el-row>
<!-- 分割线 -->
<el-divider class="my-4" />
<!-- 专业配置行专业+ 设计人员+ 校审人员 横向排列 -->
<div
v-for="(majorConfig, configIndex) in combinedConfigs"
:key="configIndex"
style="background: aliceblue; border-radius: 10px"
class="mb-5 animate-fadeIn"
>
<el-row :gutter="20" class="items-top">
<!-- 左侧专业选择 -->
<el-col :span="6" :xs="24" :sm="8" class="mb-4 sm:mb-0" style="margin-top: 8px">
<el-form-item
:prop="`designers.${configIndex}.userMajor`"
:rules="{ required: true, message: '请选择专业', trigger: 'change' }"
class="mb-0"
label-width="80px"
label="专业"
>
<!-- 专业选择下拉框 -->
<el-select
v-model="form.designers[configIndex].userMajor"
placeholder="请选择专业"
class="w-full transition-all duration-300 border-gray-300"
@change="(val) => handleMajorChange(val, configIndex)"
>
<!-- 临时添加调试显示 -->
<template v-if="des_user_major && des_user_major.length > 0">
<el-option v-for="item in des_user_major" :key="item.value" :label="item.label" :value="item.value" />
</template>
<template v-else>
<el-option label="无专业数据" value="" disabled />
</template>
</el-select>
</el-form-item>
</el-col>
<!-- 中间设计人员 -->
<el-col :span="9" :xs="24" :sm="8" class="mb-4 sm:mb-0">
<div class="pl-0 sm:pl-4 border-l-0 sm:border-l-2 border-blue-200 py-0 sm:py-2">
<!-- 设计人员列表 -->
<div class="space-y-3">
<div v-for="(person, personIndex) in majorConfig.designPersons" :key="personIndex" class="flex items-center">
<el-form-item
:prop="`designers.${configIndex}.persons.${personIndex}.userId`"
:rules="{ required: true, message: '请选择人员', trigger: 'change' }"
class="flex-1 mr-3 mb-0"
label="设计人员"
label-width="80px"
>
<el-select
v-model="person.userId"
placeholder="请选择设计人员"
class="w-full transition-all duration-300 border-gray-300"
@change="() => checkDuplicate(person, 'designers', configIndex, personIndex)"
>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
</el-select>
</el-form-item>
<div>
<el-button
type="danger"
size="small"
@click="removePerson('designers', configIndex, personIndex)"
class="transition-all duration-300 hover:bg-red-600"
:disabled="majorConfig.designPersons.length <= 1 || disabledForm"
>
<el-icon :size="16">
<Delete />
</el-icon>
</el-button>
<el-button
type="success"
size="small"
@click="addPerson('designers', configIndex)"
class="transition-all duration-300 transform hover:scale-105"
:disabled="!majorConfig.userMajor || disabledForm"
>
<el-icon :size="16">
<Plus />
</el-icon>
</el-button>
</div>
</div>
</div>
<!-- 空状态提示 -->
<div
v-if="majorConfig.designPersons.length == 0"
class="text-gray-500 text-sm py-2 bg-gray-100 rounded-lg border border-dashed border-gray-200 mt-1"
>
暂无设计人员请点击"添加设计人员"
</div>
</div>
</el-col>
<!-- 右侧校审人员 -->
<el-col :span="9" :xs="24" :sm="8">
<div class="pl-0 sm:pl-4 border-l-0 sm:border-l-2 border-green-200 py-0 sm:py-2">
<!-- 校审人员列表 -->
<div class="space-y-3">
<div v-for="(person, personIndex) in majorConfig.reviewPersons" :key="personIndex" class="flex items-center">
<el-form-item
:prop="`reviewers.${configIndex}.persons.${personIndex}.userId`"
:rules="{ required: true, message: '请选择人员', trigger: 'change' }"
class="flex-1 mr-3 mb-0"
label="校审人员"
label-width="80px"
>
<el-select
v-model="person.userId"
placeholder="请选择校审人员"
class="w-full transition-all duration-300 border-gray-300"
@change="() => checkDuplicate(person, 'reviewers', configIndex, personIndex)"
>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId" />
</el-select>
</el-form-item>
<div>
<el-button
type="danger"
size="small"
@click="removePerson('reviewers', configIndex, personIndex)"
class="transition-all duration-300 hover:bg-red-600"
:disabled="majorConfig.reviewPersons.length <= 1 || disabledForm"
>
<el-icon :size="16">
<Delete />
</el-icon>
</el-button>
<el-button
type="success"
size="small"
@click="addPerson('reviewers', configIndex)"
class="transition-all duration-300 transform hover:scale-105"
:disabled="!majorConfig.userMajor || disabledForm"
>
<el-icon :size="16">
<Plus />
</el-icon>
</el-button>
</div>
</div>
</div>
<!-- 空状态提示 -->
<div
v-if="majorConfig.reviewPersons.length == 0"
class="text-gray-500 text-sm py-2 bg-gray-100 rounded-lg border border-dashed border-gray-200 mt-1"
>
暂无校审人员请点击"添加校审人员"
</div>
</div>
</el-col>
</el-row>
<!-- 删除专业配置行 -->
<el-row class="mt-2">
<el-col :span="24" class="text-right pr-4">
<el-button
type="text"
class="text-red-500 hover:text-red-700 transition-colors"
@click="removeMajor(configIndex)"
:disabled="combinedConfigs.length <= 1 || disabledForm"
>
<i class="el-icon-delete mr-1"></i>删除专业
</el-button>
</el-col>
</el-row>
</div>
</div>
<!-- 提交按钮区域 -->
<div class="flex justify-center space-x-6 mt-8 pt-6 border-t border-gray-100">
<el-button
type="primary"
size="large"
v-hasPermi="['design:user:batch']"
@click="submitForm"
class="px-8 py-2.5 transition-all duration-300 transform hover:scale-105 bg-blue-500 hover:bg-blue-600 text-white font-medium"
:disabled="disabledForm"
>
<i class="el-icon-check mr-2"></i>确认提交
</el-button>
<el-button
size="large"
@click="resetForm"
class="px-8 py-2.5 transition-all duration-300 border-gray-300 hover:bg-gray-100 font-medium"
:disabled="disabledForm"
>
<i class="el-icon-refresh mr-2"></i>重置
</el-button>
</div>
</el-form>
</div>
</div>
</template>
<script setup name="PersonnelForm" lang="ts">
import { ref, reactive, computed, onMounted, toRefs, watch, WatchStopHandle } from 'vue';
import { getCurrentInstance } from 'vue';
import type { ComponentInternalInstance } from 'vue';
import { useUserStoreHook } from '@/store/modules/user';
import { ElMessage, ElLoading } from 'element-plus';
import { Delete, Plus } from '@element-plus/icons-vue'; // 修复添加Plus图标导入
import { designUserAdd, designUserList, systemUserList } from '@/api/design/appointment';
// 获取当前实例
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store
const userStore = useUserStoreHook();
// 从 store 中获取当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
// 专业字典数据 - 增加默认空数组避免undefined
const { des_user_major = ref([]) } = toRefs<any>(proxy?.useDict('des_user_major') || {});
// 调试:打印专业数据
onMounted(() => {
console.log('专业数据:', des_user_major.value);
});
// 表单数据:保持原有数据结构不变
interface MajorGroup {
userMajor: string | null; // 专业
persons: Array<{ userId: number | null }>; // 该专业下的多个人员
}
const form = reactive({
projectId: currentProject.value?.id,
designLeader: null, // 设计负责人
designers: [] as MajorGroup[], // 设计人员:按专业分组,每组含多个人员
reviewers: [] as MajorGroup[] // 校审人员:按专业分组,每组含多个人员
});
// 组合配置用于视图展示(专业+设计人员+校审人员)
const combinedConfigs = computed(() => {
// 确保designers和reviewers数组长度一致
const maxLength = Math.max(form.designers.length, form.reviewers.length);
while (form.designers.length < maxLength) {
form.designers.push(createEmptyMajorGroup());
}
while (form.reviewers.length < maxLength) {
form.reviewers.push(createEmptyMajorGroup());
}
// 组合数据用于视图展示
return form.designers.map((designerGroup, index) => ({
userMajor: designerGroup.userMajor,
designPersons: designerGroup.persons,
reviewPersons: form.reviewers[index].persons
}));
});
// 表单验证规则
const rules = reactive({
designLeader: [{ required: true, message: '请选择设计负责人', trigger: 'change' }]
});
// 用户列表
const userList = ref([]);
// 表单引用
const leaveFormRef = ref();
const disabledForm = ref(false); //控制提交按钮状态
/** 查询当前部门的所有用户 */
const getDeptAllUser = async (deptId: any) => {
try {
const res = await systemUserList({ deptId });
userList.value = res.rows;
} catch (error) {
ElMessage.error('获取用户列表失败');
}
};
/** 查询当前表单数据并回显 */
const designUser = async () => {
if (!currentProject.value?.id) return;
const loading = ElLoading.service({
lock: true,
text: '加载配置数据中...',
background: 'rgba(255, 255, 255, 0.7)'
});
try {
const res = await designUserList({ projectId: currentProject.value?.id });
// 清空现有数据
form.designLeader = null;
form.designers = [];
form.reviewers = [];
if (res.code == 200 && res.rows && res.rows.length > 0) {
disabledForm.value = true;
// 1. 分类整理数据(按用户类型)
const designLeader = res.rows.find((item) => item.userType == 1);
const designerItems = res.rows.filter((item) => item.userType == 2);
const reviewerItems = res.rows.filter((item) => item.userType == 3);
// 2. 回显设计负责人
if (designLeader) form.designLeader = designLeader.userId;
// 3. 回显设计人员(按专业分组)
form.designers = groupPersonByMajor(designerItems);
// 4. 回显校审人员(按专业分组)
form.reviewers = groupPersonByMajor(reviewerItems);
}
// 补全默认空项至少1个专业分组每组至少1个人员
if (form.designers.length == 0) form.designers.push(createEmptyMajorGroup());
if (form.reviewers.length == 0) form.reviewers.push(createEmptyMajorGroup());
} catch (error) {
ElMessage.error('获取配置数据失败');
// 异常时初始化默认空项
form.designers = [createEmptyMajorGroup()];
form.reviewers = [createEmptyMajorGroup()];
} finally {
loading.close();
}
};
/** 辅助函数创建空的专业分组含1个空人员 */
const createEmptyMajorGroup = (): MajorGroup => ({
userMajor: null,
persons: [{ userId: null }]
});
/** 辅助函数:按专业分组整理人员数据(用于回显) */
const groupPersonByMajor = (items: any[]): MajorGroup[] => {
const groupMap: Record<string, MajorGroup> = {};
items.forEach((item) => {
const major = item.userMajor || '未分类';
// 不存在该专业分组则创建
if (!groupMap[major]) {
groupMap[major] = { userMajor: item.userMajor, persons: [] };
}
// 添加当前人员到专业分组
groupMap[major].persons.push({ userId: item.userId });
});
// 处理空分组确保每组至少1个人员
Object.values(groupMap).forEach((group) => {
if (group.persons.length == 0) group.persons.push({ userId: null });
});
return Object.values(groupMap);
};
/** 新增专业配置行 */
const addMajor = () => {
form.designers.push(createEmptyMajorGroup());
form.reviewers.push(createEmptyMajorGroup());
// 滚动到新增的专业配置行
setTimeout(() => {
const groups = document.querySelectorAll(`[data-v-${proxy?.$options.__scopeId}] .animate-fadeIn`);
if (groups.length > 0) {
groups[groups.length - 1].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}, 100);
};
/** 删除专业配置行 */
const removeMajor = (configIndex: number) => {
if (form.designers.length <= 1) {
ElMessage.warning('至少保留一个专业配置');
return;
}
form.designers.splice(configIndex, 1);
form.reviewers.splice(configIndex, 1);
};
/** 给指定专业配置行添加人员 */
const addPerson = (type: 'designers' | 'reviewers', configIndex: number) => {
form[type][configIndex].persons.push({ userId: null });
// 滚动到新增的人员选择框
setTimeout(() => {
const personSelects = document.querySelectorAll(`[data-v-${proxy?.$options.__scopeId}] .el-select`);
if (personSelects.length > 0) {
personSelects[personSelects.length - 1].scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}, 100);
};
/** 从指定专业配置行删除人员 */
const removePerson = (type: 'designers' | 'reviewers', configIndex: number, personIndex: number) => {
const targetGroup = form[type][configIndex];
if (targetGroup.persons.length <= 1) {
ElMessage.warning(`该专业至少保留一个${type == 'designers' ? '设计' : '校审'}人员`);
return;
}
targetGroup.persons.splice(personIndex, 1);
};
/** 专业变更时:清空当前专业下的人员(避免专业与人员不匹配) */
const handleMajorChange = (newMajor: string, configIndex: number) => {
// 直接修改原始数据源,确保响应式生效
form.designers[configIndex].userMajor = newMajor;
form.reviewers[configIndex].userMajor = newMajor;
form.designers[configIndex].persons = [{ userId: null }];
form.reviewers[configIndex].persons = [{ userId: null }];
// ElMessage.info(`已重置「${getMajorLabel(newMajor)}」专业下的人员,请重新选择`);
};
// ========== 核心:重复校验逻辑 ==========
/**
* 校验同一角色内(设计/校审)的「专业+人员」组合唯一性
*/
const checkDuplicate = (current: { userId: number | null }, role: 'designers' | 'reviewers', configIndex: number, personIndex: number) => {
console.log(`校验触发 - 角色: ${role}, 专业索引: ${configIndex}, 人员索引: ${personIndex}, 人员ID: ${current.userId}`);
console.log(form);
const currentGroup = form[role][configIndex];
// 未选专业/人员时不校验
if (!currentGroup.userMajor || !current.userId) return;
// 生成当前「专业+人员」唯一标识
const currentKey = `${currentGroup.userMajor}-${current.userId}`;
let duplicateItem = null;
// 1. 检查当前专业配置行内是否有重复人员
duplicateItem = currentGroup.persons.find((item, idx) => {
return idx !== personIndex && item.userId == current.userId;
});
if (duplicateItem) {
ElMessage.warning(`当前专业下「${getUserName(current.userId)}」已存在,请重新选择`);
current.userId = null;
return;
}
// 2. 检查同一角色内其他专业配置行是否有重复(专业+人员唯一)
form[role].forEach((group, gIdx) => {
if (gIdx == configIndex) return; // 跳过当前配置行
group.persons.forEach((item) => {
if (`${group.userMajor}-${item.userId}` == currentKey) {
duplicateItem = item;
}
});
});
if (duplicateItem) {
ElMessage.warning(`${getMajorLabel(currentGroup.userMajor)}+${getUserName(current.userId)}」组合已存在,请重新选择`);
current.userId = null;
}
};
/** 辅助函数:通过专业值获取专业名称 */
const getMajorLabel = (majorValue: string | null) => {
if (!majorValue || !des_user_major.value) return '';
const major = des_user_major.value.find((item: any) => item.value == majorValue);
return major ? major.label : majorValue;
};
/** 辅助函数通过用户ID获取用户名 */
const getUserName = (userId: number | null) => {
if (!userId || !userList.value.length) return '';
const user = userList.value.find((item: any) => item.userId == userId);
return user ? user.nickName : userId;
};
/** 提交表单(保持原有数据结构) */
const submitForm = async () => {
if (!leaveFormRef.value) return;
try {
// 1. 基础表单验证
await leaveFormRef.value.validate();
// 2. 提交前二次校验:「专业+人员」组合唯一性
let hasDuplicate = false;
const allKeys: string[] = [];
// 收集所有「专业+人员」组合(设计+校审分开校验)
const collectKeys = (roleGroups: MajorGroup[], roleName: string) => {
roleGroups.forEach((group) => {
if (!group.userMajor) return;
group.persons.forEach((person) => {
if (!person.userId) return;
const key = `${group.userMajor}-${person.userId}`;
if (allKeys.includes(key)) {
hasDuplicate = true;
ElMessage.error(`${roleName}中存在重复的「专业+人员」组合,请检查`);
}
allKeys.push(key);
});
});
};
// 校验设计人员
collectKeys(form.designers, '设计人员');
if (hasDuplicate) return;
// 清空临时数组,校验校审人员(不校验设计与校审之间)
allKeys.length = 0;
collectKeys(form.reviewers, '校审人员');
if (hasDuplicate) return;
// 3. 构建提交数据(适配后端原有数据格式)
const submitData = {
projectId: form.projectId,
personnel: [
// 设计负责人
{
userId: form.designLeader,
userType: 'designLeader',
userMajor: null
},
// 设计人员:展开专业分组,每个人员单独作为一条数据
...form.designers.flatMap((group) =>
group.persons.map((person) => ({
userId: person.userId,
userType: 'designer',
userMajor: group.userMajor
}))
),
// 校审人员:展开专业分组,每个人员单独作为一条数据
...form.reviewers.flatMap((group) =>
group.persons.map((person) => ({
userId: person.userId,
userType: 'reviewer',
userMajor: group.userMajor
}))
)
]
};
// 4. 数据处理(保持原有逻辑不变)
const arr = [];
userList.value.forEach((item) => {
submitData.personnel.forEach((item1) => {
if (item1.userId == item.userId) {
let userType = 1;
if (item1.userType == 'designer') userType = 2;
else if (item1.userType == 'reviewer') userType = 3;
arr.push({
userName: item.nickName,
projectId: submitData.projectId,
userId: item1.userId,
userType: userType,
userMajor: item1.userMajor
});
}
});
});
// 5. 提交到后端(保持原有逻辑不变)
const loading = ElLoading.service({ text: '提交中...', background: 'rgba(255,255,255,0.7)' });
const res = await designUserAdd({
list: arr,
projectId: currentProject.value?.id
});
if (res.code == 200) {
disabledForm.value = true;
loading.close();
ElMessage.success('提交成功');
} else {
ElMessage.error(res.msg || '提交失败');
}
} catch (error) {
ElMessage.error('请完善表单信息后再提交');
} finally {
// ElLoading.service().close();
}
};
/** 重置表单(适配新数据结构) */
const resetForm = () => {
if (leaveFormRef.value) {
leaveFormRef.value.resetFields();
// 重置为默认空状态1个专业分组每组1个空人员
form.designers = [createEmptyMajorGroup()];
form.reviewers = [createEmptyMajorGroup()];
ElMessage.info('表单已重置');
}
};
// 监听项目ID刷新数据
const listeningProject: WatchStopHandle = watch(
() => currentProject.value?.id,
() => {
getDeptAllUser(userStore.deptId).then(() => {
designUser();
});
}
);
// 页面生命周期
onUnmounted(() => {
listeningProject();
});
onMounted(() => {
getDeptAllUser(userStore.deptId).then(() => {
designUser();
});
});
</script>
<style lang="scss" scoped>
.appointment {
width: 70vw;
max-width: 1600px;
.el-select__wrapper {
width: 100% !important;
}
.el-button--small {
margin-bottom: 0;
}
.fonts {
.el-form-item--default .el-form-item__label {
font-size: 18px !important;
}
}
}
// 自定义动画
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fadeIn {
animation: fadeIn 0.3s ease-out forwards;
}
// 表单样式优化
::v-deep .el-form {
--el-form-item-margin-bottom: 0;
}
::v-deep .el-form-item {
margin-bottom: 0;
&__label {
font-weight: 500;
color: #4e5969;
}
&__content {
padding: 0;
}
}
::v-deep .el-select {
width: 100%;
.el-input__inner {
border-radius: 6px;
transition: all 0.3s ease;
}
&:hover .el-input__inner {
border-color: #66b1ff;
}
&.el-select-focus .el-input__inner {
border-color: #409eff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
}
}
::v-deep .el-button {
border-radius: 6px;
padding: 8px 16px;
&--primary {
background-color: #409eff;
border-color: #409eff;
&:hover {
background-color: #66b1ff;
border-color: #66b1ff;
}
}
&--success {
background-color: #67c23a;
border-color: #67c23a;
&:hover {
background-color: #85ce61;
border-color: #85ce61;
}
&:disabled {
background-color: #b3e099;
border-color: #b3e099;
}
}
&--danger {
background-color: #f56c6c;
border-color: #f56c6c;
&:hover {
background-color: #f78989;
border-color: #f78989;
}
&:disabled {
background-color: #ffcccc;
border-color: #ffbbbb;
cursor: not-allowed;
}
}
&--text {
color: #f56c6c;
&:hover {
color: #f78989;
background-color: rgba(245, 108, 108, 0.05);
}
}
}
// 响应式网格布局
.grid {
display: grid;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
.md\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.gap-4 {
gap: 1rem;
}
// 适配小屏幕小于768px时垂直排列
@media (max-width: 768px) {
.appWidth {
width: 95vw;
}
::v-deep .el-form {
padding: 4px;
}
::v-deep .el-form-item__label {
width: 100px;
}
// 小屏幕下各列上下间距
::v-deep .el-col-xs-24 + .el-col-xs-24 {
margin-top: 12px;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@
<el-table-column label="卷册号" align="center" prop="volumeNo" width="150" /> <el-table-column label="卷册号" align="center" prop="volumeNo" width="150" />
<el-table-column label="流程状态" align="center"> <el-table-column label="流程状态" align="center">
<template #default="scope"> <template #default="scope">
<dict-tag v-if="scope.row.fileId != null" :options="wf_business_status" :value="scope.row.status" /> <dict-tag v-if="scope.row.costEstimation > 0" :options="wf_business_status" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="变更文件" align="center" width="150"> <el-table-column label="变更文件" align="center" width="150">
@ -64,19 +64,16 @@
<dict-tag :options="design_change_reason_type" :value="scope.row.changeReason ? scope.row.changeReason.split(',') : []" /> <dict-tag :options="design_change_reason_type" :value="scope.row.changeReason ? scope.row.changeReason.split(',') : []" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="图纸状态" align="center">
<template #default="scope">
<dict-tag v-if="scope.row.fileId != null" :options="wf_business_status" :value="scope.row.auditStatus" />
</template>
</el-table-column>
<el-table-column label="变更内容" align="center" prop="changeContent" width="150" /> <el-table-column label="变更内容" align="center" prop="changeContent" width="150" />
<el-table-column label="创建时间" align="center" prop="createTime" width="150" /> <el-table-column label="创建时间" align="center" prop="createTime" width="150" />
<el-table-column label="备注" align="center" prop="remark" width="200" /> <el-table-column label="备注" align="center" prop="remark" width="200" />
<el-table-column label="操作" align="center" fixed="right" width="300"> <el-table-column label="操作" align="center" fixed="right" width="300">
<template #default="scope"> <template #default="scope">
<el-button
type="primary"
link
icon="Upload"
@click="handleAddChange(scope.row)"
v-if="(scope.row.status == 'finish' || scope.row.costEstimation == '0') && scope.row.auditStatus == 'draft'"
>上传</el-button
>
<el-button <el-button
type="success" type="success"
link link
@ -86,8 +83,31 @@
@click="handleViewInfo(scope.row)" @click="handleViewInfo(scope.row)"
>查看</el-button >查看</el-button
> >
<el-button type="success" link icon="View" v-hasPermi="['design:designChange:query']" @click="handleViewDetail(scope.row)" <el-button
>通知单</el-button type="primary"
v-if="scope.row.status == 'draft' && scope.row.costEstimation > 0"
link
icon="plus"
v-hasPermi="['design:designChange:query']"
@click="handleViewUpdate(scope.row)"
>审核通知单</el-button
>
<el-button
v-if="scope.row.status != 'draft'"
type="success"
link
icon="View"
v-hasPermi="['design:designChange:query']"
@click="handleViewDetail(scope.row)"
>查看通知单</el-button
>
<el-button
type="primary"
link
icon="Upload"
@click="handleAddChange(scope.row)"
v-if="(scope.row.status == 'finish' || scope.row.costEstimation == '0') && scope.row.auditStatus == 'draft'"
>上传图纸</el-button
> >
<el-button <el-button
type="warning" type="warning"
@ -210,6 +230,17 @@ const handleAdd = () => {
}); });
}; };
/** 查看详情 */ /** 查看详情 */
const handleViewUpdate = (row) => {
proxy.$tab.closePage(proxy.$route);
proxy.$router.push({
path: `/approval/designChange/indexEdit`,
query: {
id: row.id,
type: 'update'
}
});
};
/** 查看详情 */
const handleViewDetail = (row) => { const handleViewDetail = (row) => {
proxy.$tab.closePage(proxy.$route); proxy.$tab.closePage(proxy.$route);
proxy.$router.push({ proxy.$router.push({

View File

@ -36,7 +36,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="工程名称" prop="projectName"> <el-form-item label="工程名称" prop="projectName">
<el-input v-model="form.projectName" placeholder="请输入工程名称" /> </el-form-item <el-input v-model="form.projectName" disabled placeholder="请输入工程名称" /> </el-form-item
></el-col> ></el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="原卷册号" prop="volumeNo"> <el-form-item label="原卷册号" prop="volumeNo">
@ -145,7 +145,7 @@
></el-col> ></el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="费用" prop="costEstimation"> <el-form-item label="费用" prop="costEstimation">
<el-input v-model="form.costEstimation" type="number" placeholder="请输入费用" /> </el-form-item <el-input min="0" v-model="form.costEstimation" type="number" placeholder="请输入费用" /> </el-form-item
></el-col> ></el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="变更费用估算表" label-width="110px" prop="costEstimationFile"> <el-form-item label="变更费用估算表" label-width="110px" prop="costEstimationFile">
@ -254,7 +254,7 @@ const initFormData = {
id: undefined, id: undefined,
projectId: currentProject.value?.id, projectId: currentProject.value?.id,
formNo: undefined, formNo: undefined,
projectName: undefined, projectName: currentProject.value?.name,
submitUnit: undefined, submitUnit: undefined,
specialty: undefined, specialty: undefined,
specialtyName: undefined, specialtyName: undefined,
@ -348,8 +348,11 @@ const getInfo = () => {
loading.value = true; loading.value = true;
buttonLoading.value = false; buttonLoading.value = false;
nextTick(async () => { nextTick(async () => {
const res = await getDesignChange(routeParams.value.id); let id = routeParams.value.id.split('_')[0];
const res = await getDesignChange(id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
console.log(form.value);
if (form.value.changeReason.length > 0) { if (form.value.changeReason.length > 0) {
form.value.changeReason = form.value.changeReason.split(','); form.value.changeReason = form.value.changeReason.split(',');
} }
@ -374,7 +377,6 @@ const submitForm = (status1: string) => {
if (form.value.saveFile && form.value.saveFile.length > 0) { if (form.value.saveFile && form.value.saveFile.length > 0) {
saveFile = form.value.saveFile.join(','); saveFile = form.value.saveFile.join(',');
} }
}
leaveFormRef.value?.validate(async (valid: boolean) => { leaveFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
@ -392,6 +394,9 @@ const submitForm = (status1: string) => {
} }
} }
}); });
} else {
submit(status.value, form.value);
}
}; };
const submitFlow = async () => { const submitFlow = async () => {
@ -429,14 +434,15 @@ const submitCallback = async () => {
}; };
//审批 //审批
const approvalVerifyOpen = async () => { const approvalVerifyOpen = async () => {
submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId); // submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId);
// submitVerifyRef.value.openDialog(routeParams.value.taskId); submitVerifyRef.value.openDialog(routeParams.value.taskId);
}; };
// 图纸上传成功之后 开始提交 // 图纸上传成功之后 开始提交
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const submit = async (status, data) => { const submit = async (status, data) => {
form.value = data; form.value = data;
form.value.id = form.value.id + '_audit';
if (status === 'draft') { if (status === 'draft') {
buttonLoading.value = false; buttonLoading.value = false;
proxy?.$modal.msgSuccess('暂存成功'); proxy?.$modal.msgSuccess('暂存成功');

View File

@ -36,7 +36,7 @@
</div> </div>
</el-card> </el-card>
<!-- 提交组件 --> <!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" /> <submitVerify ref="submitVerifyRef" :businessId1="form.id" :task-variables="taskVariables" @submit-callback="submitCallback" />
<approvalRecord ref="approvalRecordRef"></approvalRecord> <approvalRecord ref="approvalRecordRef"></approvalRecord>
<!-- 流程选择对话框 --> <!-- 流程选择对话框 -->
<el-dialog <el-dialog
@ -253,13 +253,16 @@ const submitCallback = async () => {
}; };
//审批 //审批
const approvalVerifyOpen = async () => { const approvalVerifyOpen = async () => {
// 判断是否还需要设计验证
if (form.value.isWindow) {
submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId); submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId);
// submitVerifyRef.value.openDialog(routeParams.value.taskId); } else {
submitVerifyRef.value.openDialog(routeParams.value.taskId);
}
}; };
// 图纸上传成功之后 开始提交 // 图纸上传成功之后 开始提交
const submit = async (status, data) => { const submit = async (status, data) => {
form.value = data; form.value = data;
form.value.id = routeParams.value.type == 'add' ? form.value.id + '_' : form.value.id.split('_')[0];
form.value.status = data.auditStatus ? data.auditStatus : data.status; form.value.status = data.auditStatus ? data.auditStatus : data.status;
if (status === 'draft') { if (status === 'draft') {
buttonLoading.value = false; buttonLoading.value = false;

View File

@ -11,12 +11,17 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="编号" prop="num"> <el-form-item label="编号" prop="num">
<!-- prop="num" 需与 rules 中键名一致 --> <!-- prop="num" 需与 rules 中键名一致 -->
<el-input v-model="formData.num" placeholder="请输入编号" /> <el-input v-model="formData.num" disabled placeholder="请输入编号" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="专业" prop="professional"> <el-form-item label="专业" prop="professionalName">
<el-input v-model="formData.professional" placeholder="请输入专业" /> <el-input v-model="formData.professionalName" disabled placeholder="请输入专业" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="卷册" prop="volume">
<el-input v-model="formData.volume" disabled placeholder="请输入卷册" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -24,14 +29,8 @@
<el-input v-model="formData.stage" placeholder="请输入设计阶段" /> <el-input v-model="formData.stage" placeholder="请输入设计阶段" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item label="卷册" prop="volume">
<el-input v-model="formData.volume" placeholder="请输入卷册" />
</el-form-item>
</el-col>
</el-row> </el-row>
</div> </div>
<!-- 项目信息区域 --> <!-- 项目信息区域 -->
<div class="form-section"> <div class="form-section">
<div class="section-title"> <div class="section-title">
@ -54,7 +53,6 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<!-- 人员信息区域 --> <!-- 人员信息区域 -->
<div class="form-section"> <div class="form-section">
<div class="section-title"> <div class="section-title">
@ -65,56 +63,77 @@
<el-row :gutter="20" class="section-content"> <el-row :gutter="20" class="section-content">
<el-col :span="12"> <el-col :span="12">
<el-form-item label="设计人" prop="designer"> <el-form-item label="设计人" prop="designer">
<el-input v-model="formData.designer" placeholder="请输入设计人" /> <el-input disabled v-model="formData.designerName" placeholder="请输入设计人" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"></el-col> <el-col :span="12"></el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="校审人员" prop="proofreading"> <el-form-item label="校审人员" prop="proofreading">
<el-input v-model="formData.proofreading" placeholder="请输入校审人员" /> <el-input disabled v-model="formData.proofreading" placeholder="请输入校审人员" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="12">
<el-form-item label="校审人员ID" prop="proofreadingId">
<el-input v-model="formData.proofreadingId" placeholder="请输入校审人员ID" />
</el-form-item>
</el-col> -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="校审时间" prop="proofreadingDate"> <el-form-item label="校审时间" prop="proofreadingDate">
<el-date-picker v-model="formData.proofreadingDate" type="date" placeholder="选择校审时间" format="YYYY-MM-DD" <el-date-picker
value-format="YYYY-MM-DD" /> v-model="formData.proofreadingDate"
type="date"
disabled
placeholder="选择校审时间"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="审核人员" prop="audit"> <el-form-item label="审核人员" prop="audit">
<el-input v-model="formData.audit" placeholder="请输入审核人员" /> <el-input disabled v-model="formData.audit" placeholder="请输入审核人员" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="12">
<el-form-item label="审核人员ID" prop="auditId">
<el-input v-model="formData.auditId" placeholder="请输入审核人员ID" />
</el-form-item>
</el-col> -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="审核时间" prop="auditDate"> <el-form-item label="审核时间" prop="auditDate">
<el-date-picker v-model="formData.auditDate" type="date" placeholder="选择审核时间" format="YYYY-MM-DD" <el-date-picker
value-format="YYYY-MM-DD" /> disabled
v-model="formData.auditDate"
type="date"
placeholder="选择审核时间"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="审定人员" prop="approve">
<el-input disabled v-model="formData.approve" placeholder="请输入审定人员" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="审定时间" prop="approveDate">
<el-date-picker
disabled
v-model="formData.approveDate"
type="date"
placeholder="选择审定时间"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="执行人员" prop="executor"> <el-form-item label="执行人员" prop="executor">
<el-input v-model="formData.executor" placeholder="请输入执行人员" /> <el-select
v-model="formData.executorId"
@change="changeExecutor"
placeholder="选择执行人员"
class="w-full transition-all duration-300 border-gray-300"
>
<el-option v-for="item in userList" :key="`user-${item.userId}`" :label="item.userName" :value="item.userId" />
</el-select>
<!-- <el-input v-model="formData.executor" placeholder="请输入执行人员" /> -->
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- <el-col :span="12">
<el-form-item label="执行人员ID" prop="executorId">
<el-input v-model="formData.executorId" placeholder="请输入执行人员ID" />
</el-form-item>
</el-col> -->
<el-col :span="12"> <el-col :span="12">
<el-form-item label="执行时间" prop="executorDate"> <el-form-item label="执行时间" prop="executorDate">
<el-date-picker v-model="formData.executorDate" type="date" placeholder="选择执行时间" format="YYYY-MM-DD" <el-date-picker v-model="formData.executorDate" type="date" placeholder="选择执行时间" format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
value-format="YYYY-MM-DD" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -151,17 +170,20 @@
<script setup name="ExamineForm" lang="ts"> <script setup name="ExamineForm" lang="ts">
import { ref, watch, reactive } from 'vue'; import { ref, watch, reactive } from 'vue';
import { fillOutTheDesignVerificationForm, drawingreviewReceipts } from '@/api/design/drawingreview'; import { fillOutTheDesignVerificationForm, drawingreviewReceipts } from '@/api/design/drawingreview';
import type { FormInstance, FormRules } from 'element-plus'; import { dayjs, type FormInstance, type FormRules } from 'element-plus';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { computed } from 'vue'; import { computed } from 'vue';
import { subProjectListAll } from '@/api/design/drawingreview'; import { subProjectListAll, getDrawingreviewReceipts } from '@/api/design/drawingreview';
import { desUserList } from '@/api/design/appointment';
// 获取用户 store // 获取用户 store
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
const userList = ref([]);
const userMap = new Map();
// 从 store 中获取当前选中的项目 // 从 store 中获取当前选中的项目
const currentProject = computed(() => userStore.selectedProject); const currentProject = computed(() => userStore.selectedProject);
console.log(currentProject.value);
const subProjectList = ref([]); const subProjectList = ref([]);
const Drawingreview = ref({});
let subProjectMap = new Map(); let subProjectMap = new Map();
// 定义表单数据类型 // 定义表单数据类型
interface FormData { interface FormData {
@ -193,9 +215,9 @@ const rules: FormRules = {
num: [{ required: true, message: '请输入编号', trigger: 'blur' }], num: [{ required: true, message: '请输入编号', trigger: 'blur' }],
professional: [{ required: true, message: '请输入专业', trigger: 'blur' }] professional: [{ required: true, message: '请输入专业', trigger: 'blur' }]
}; };
const userName = userStore.nickname;
// 表单数据 - 直接在组件内定义不再通过Props接收 // 表单数据 - 直接在组件内定义不再通过Props接收
const formData = reactive<FormData>({ const formData = ref({
num: '', num: '',
professional: '', professional: '',
stage: '', stage: '',
@ -228,8 +250,8 @@ watch(
(newVal) => { (newVal) => {
if (newVal) { if (newVal) {
// 根据实际项目结构调整赋值字段 // 根据实际项目结构调整赋值字段
formData.projectId = newVal.id || ''; formData.value.projectId = newVal.id || '';
formData.projectName = newVal.name || ''; formData.value.projectName = newVal.name || '';
} }
}, },
{ immediate: true, deep: true } { immediate: true, deep: true }
@ -259,12 +281,12 @@ const resetFields = () => {
// 获取表单数据 // 获取表单数据
const getFormData = (): FormData => { const getFormData = (): FormData => {
return { ...formData }; return { ...formData.value };
}; };
// 设置表单数据 // 设置表单数据
const setFormData = (data: Partial<FormData>) => { const setFormData = (data: Partial<FormData>) => {
Object.assign(formData, data); Object.assign(formData.value, data);
}; };
// 提交表单 // 提交表单
@ -279,12 +301,10 @@ const submit = async (businessId, cb) => {
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}); });
formData.subprojectName = subProjectMap.get(formData.subprojectId); formData.value.subprojectName = subProjectMap.get(formData.value.subprojectId);
// formData.drawingreviewId = businessId;
console.log(businessId); console.log(businessId);
// businessId 设置 如果有下滑线去掉后面及下划线 formData.value.drawingreviewId = businessId.replace(/_/g, '');
formData.drawingreviewId = businessId.replace(/_/g, ''); const res = await drawingreviewReceipts(formData.value);
const res = await drawingreviewReceipts(formData);
if (res.code === 200) { if (res.code === 200) {
// 提交成功处理逻辑 // 提交成功处理逻辑
console.log('提交成功'); console.log('提交成功');
@ -292,6 +312,52 @@ const submit = async (businessId, cb) => {
// 关闭 // 关闭
ElLoading.service().close(); ElLoading.service().close();
}; };
// 获取单据
const getInfo = async (id) => {
// 获取单据
console.log(id);
await getDeptAllUser();
let res = await getDrawingreviewReceipts(id);
console.log(res);
formData.value = res.data;
console.log(formData);
// 设计人 名称 designerName iddesigner
// 校审人员 名称 proofreading id: proofreadingId 校审时间 proofreadingDate
// 审定人员 名称 approve idapproveId 审定时间 approveDate
// 审核人员 名称 audit idauditId 审核时间 auditDate
// userStore.nickname //用户名
// userStore.userId //用户id
if (formData.value.approve) {
} else if (formData.value.audit) {
// 说明流程在第三步
formData.value.approve = userStore.nickname;
formData.value.approveId = userStore.userId;
formData.value.approveDate = dayjs().format('YYYY-MM-DD');
} else if (formData.value.proofreading) {
// 说明流程在第二步
formData.value.audit = userStore.nickname;
formData.value.auditId = userStore.userId;
formData.value.auditDate = dayjs().format('YYYY-MM-DD');
} else if (formData.value.designerName) {
// 说明流程在第一步
formData.value.proofreading = userStore.nickname;
formData.value.proofreadingId = userStore.userId;
formData.value.proofreadingDate = dayjs().format('YYYY-MM-DD');
}
};
/** 获取当前设计用户 */
const getDeptAllUser = async () => {
const res = await desUserList({ projectId: currentProject.value?.id, userType: '2' });
userList.value = res.data || [];
for (let i = 0; i < userList.value.length; i++) {
userMap.set(userList.value[i].userId, userList.value[i].userName);
}
};
const changeExecutor = (val) => {
formData.value.executor = userMap.get(val);
formData.value.executorDate = dayjs().format('YYYY-MM-DD');
};
onMounted(() => { onMounted(() => {
getSubProject(); getSubProject();
}); });
@ -301,7 +367,8 @@ defineExpose({
resetFields, resetFields,
getFormData, getFormData,
setFormData, setFormData,
submit submit,
getInfo
}); });
</script> </script>

View File

@ -434,7 +434,7 @@ const onLoad = async () => {
return; return;
} }
try { try {
proxy?.download('design/collect/exportWord', { id: form.id }, `收资清单_${new Date().getTime()}.zip`); proxy?.download('design/collect/exportWord', { id: form.id }, `收资清单_${new Date().getTime()}.doc`);
} catch (error) { } catch (error) {
ElMessage.error('导出失败,请重试'); ElMessage.error('导出失败,请重试');
console.error('文件导出错误:', error); console.error('文件导出错误:', error);

View File

@ -39,7 +39,7 @@
</div> </div>
</el-card> </el-card>
<!-- 提交组件 --> <!-- 提交组件 -->
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" /> <submitVerify ref="submitVerifyRef" :businessId1="form.id" :task-variables="taskVariables" @submit-callback="submitCallback" />
<approvalRecord ref="approvalRecordRef"></approvalRecord> <approvalRecord ref="approvalRecordRef"></approvalRecord>
<!-- 流程选择对话框 --> <!-- 流程选择对话框 -->
<el-dialog <el-dialog
@ -202,8 +202,14 @@ const submitCallback = async () => {
}; };
//审批 //审批
const approvalVerifyOpen = async () => { const approvalVerifyOpen = async () => {
// 图纸评审验证 // 图纸评审验证 判断是否需要设计验证
if (form.value.isWindow) {
console.log(routeParams.value.businessId);
submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId); submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId);
} else {
submitVerifyRef.value.openDialog(routeParams.value.taskId);
}
}; };
const submit = async (status, data) => { const submit = async (status, data) => {
form.value = data; form.value = data;

View File

@ -55,7 +55,9 @@
<el-table-column label="子项名称" align="center" prop="designSubitem" /> <el-table-column label="子项名称" align="center" prop="designSubitem" />
<el-table-column label="设计状态" align="center" prop="designState"> <el-table-column label="设计状态" align="center" prop="designState">
<template #default="scope"> <template #default="scope">
<dict-tag :options="design_state" :value="scope.row.designState" /> <el-tag type="primary" v-if="scope.row.designState == '2'">未出图</el-tag>
<el-tag type="success" v-if="scope.row.designState == '1'">已出图</el-tag>
<!-- <dict-tag :options="design_state" :value="scope.row.designState" /> -->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="专业" align="center" prop="specialtyName"> </el-table-column> <el-table-column label="专业" align="center" prop="specialtyName"> </el-table-column>
@ -539,7 +541,6 @@ const onSubmit = async () => {
buttonLoading.value = false; buttonLoading.value = false;
} }
}; };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: VolumeCatalogVO) => { const handleDelete = async (row?: VolumeCatalogVO) => {
const _ids = row?.design || ids.value; const _ids = row?.design || ids.value;

View File

@ -20,16 +20,12 @@
</div> </div>
<div class="p-6"> <div class="p-6">
<div class="grid grid-cols-1 gap-4"> <div class="grid grid-cols-1 gap-4">
<el-form <el-form ref="leaveFormRef" :model="form" :rules="rules" label-width="100px" class="space-y-4">
ref="leaveFormRef"
:disabled="routeParams.type === 'view' || form.auditStatus == 'waiting'"
:model="form"
:rules="rules"
label-width="100px"
class="space-y-4"
>
<el-form-item label="图纸文件" prop="fileId" class="mb-2 md:col-span-2"> <el-form-item label="图纸文件" prop="fileId" class="mb-2 md:col-span-2">
<el-input v-model="form.fileName" disabled placeholder="图纸名称" /> <!-- <el-input v-model="form.fileName" disabled placeholder="图纸名称" /> -->
<el-link :href="form.fileUrl" target="_blank" type="primary" :underline="false">
{{ form.fileName }}
</el-link>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
@ -199,7 +195,7 @@ const submitCallback = async () => {
//审批 //审批
const approvalVerifyOpen = async () => { const approvalVerifyOpen = async () => {
// 图纸评审验证 // 图纸评审验证
submitVerifyRef.value.openDialog(routeParams.value.taskId, true, routeParams.value.businessId); submitVerifyRef.value.openDialog(routeParams.value.taskId);
}; };
const submit = async (status, data) => { const submit = async (status, data) => {
form.value = data; form.value = data;

View File

@ -48,6 +48,20 @@
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['formalities:listOfFormalities:add']">新增</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['formalities:listOfFormalities:add']">新增</el-button>
<span style="margin-left: 10px"
><el-tooltip class="box-item" effect="dark" content="从原有模板列表选择新增" placement="top">
<el-icon color="#409efc"><WarningFilled /></el-icon> </el-tooltip
></span>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="addTemplate()" v-hasPermi="['formalities:formalitiesAreConsolidated:addFormalities']"
>新增数据</el-button
>
<span style="margin-left: 10px">
<el-tooltip class="box-item" effect="dark" content="创建新模板并添加数据" placement="top">
<el-icon color="#409efc"><WarningFilled /></el-icon>
</el-tooltip>
</span>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button <el-button
@ -67,7 +81,7 @@
<el-table v-loading="loading" :data="formalitiesAreConsolidatedList" @selection-change="handleSelectionChange" row-key="id" default-expand-all> <el-table v-loading="loading" :data="formalitiesAreConsolidatedList" @selection-change="handleSelectionChange" row-key="id" default-expand-all>
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<!-- <el-table-column label="手续办理清单模板父级" align="center" prop="formalitiesPname" /> --> <!-- <el-table-column label="手续办理清单模板父级" align="center" prop="formalitiesPname" /> -->
<el-table-column label="手续办理清单模板" align="center" prop="formalitiesName" /> <el-table-column label="手续办理清单" align="center" prop="formalitiesName" />
<el-table-column label="计划开始时间" align="center" prop="planTheStartTime" width="180"> <el-table-column label="计划开始时间" align="center" prop="planTheStartTime" width="180">
<template #default="scope"> <template #default="scope">
<span>{{ parseTime(scope.row.planTheStartTime, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.planTheStartTime, '{y}-{m}-{d}') }}</span>
@ -230,6 +244,11 @@
} }
}" }"
/> />
<div style="margin-left: 10px; display: flex; justify-content: center; align-items: center">
<el-tooltip class="box-item" effect="dark" content="列表上已选择得模版不可再选" placement="top">
<el-icon><WarningFilled /></el-icon>
</el-tooltip>
</div>
</el-form-item> </el-form-item>
<template #footer> <template #footer>
<span> <span>
@ -238,6 +257,27 @@
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<!-- 添加或修改手续办理清单模板对话框 -->
<el-dialog title="添加手续办理清单" v-model="tempDialogVisible" width="500px" append-to-body @close="templateCancel">
<el-form ref="listOfFormalitiesFormRef" :model="formTemplate" label-width="80px">
<el-form-item label="父级" prop="formalitiesPid" :rules="[{ required: true, message: '请选择父级', trigger: 'blur' }]">
<el-select v-model="formTemplate.formalitiesPid" placeholder="请选择父级">
<el-option label="根目录" value="0" />
<el-option v-for="item in listOfFormalitiesList" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="名称" prop="formalitiesName" :rules="[{ required: true, message: '请输入名称', trigger: 'blur' }]">
<el-input v-model="formTemplate.formalitiesName" placeholder="请输入名称" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitFormTemplate"> </el-button>
<el-button @click="templateCancel"> </el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -253,12 +293,15 @@ import {
editStatus, editStatus,
getTemplateTreeList getTemplateTreeList
} from '@/api/formalities/formalitiesAreConsolidated'; } from '@/api/formalities/formalitiesAreConsolidated';
import { listListOfFormalities, addFormalities } from '@/api/formalities/listOfFormalities';
import { import {
FormalitiesAreConsolidatedVO, FormalitiesAreConsolidatedVO,
FormalitiesAreConsolidatedQuery, FormalitiesAreConsolidatedQuery,
FormalitiesAreConsolidatedForm FormalitiesAreConsolidatedForm
} from '@/api/formalities/formalitiesAreConsolidated/types'; } from '@/api/formalities/formalitiesAreConsolidated/types';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { WarningFilled } from '@element-plus/icons-vue';
const fileVisible = ref(false); const fileVisible = ref(false);
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store // 获取用户 store
@ -287,6 +330,7 @@ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const file = ref(null); const file = ref(null);
const fileParams = reactive({ const fileParams = reactive({
pageNum: 1, pageNum: 1,
@ -404,7 +448,39 @@ const handleAdd = async () => {
tempTreeList.value = res.data; tempTreeList.value = res.data;
templateVisbile.value = true; templateVisbile.value = true;
}; };
const tempDialogVisible = ref(false);
const formTemplate: any = ref({
formalitiesPid: '',
formalitiesName: ''
});
const listOfFormalitiesList: any = ref([]);
//新增模版
const addTemplate = async () => {
tempDialogVisible.value = true;
const res = await listListOfFormalities();
listOfFormalitiesList.value = res.data;
};
//确定信息
const submitFormTemplate = async () => {
const params = {
projectId: currentProject.value.id,
addBusFormalitiesAreConsolidatedBo: {
...formTemplate.value
}
};
const res = await addFormalities(params);
if (res.code == 200) {
proxy?.$modal.msgSuccess('操作成功');
templateCancel();
getList();
}
};
//取消
const templateCancel = () => {
tempDialogVisible.value = false;
formTemplate.value.formalitiesPid = '';
formTemplate.value.formalitiesName = '';
};
// 选择模板 // 选择模板
const setTemp = async () => { const setTemp = async () => {
// form.value.formalitiesPid = tempValue.value[tempValue.value.length - 1]; // form.value.formalitiesPid = tempValue.value[tempValue.value.length - 1];

View File

@ -1,6 +1,5 @@
<template> <template>
<formalitiesAreConsolidated ref="formalitiesAreConsolidatedRef" class="overlay" v-if="showFormalitiesAreConsolidated" /> <formalitiesAreConsolidated ref="formalitiesAreConsolidatedRef" class="overlay" v-if="showFormalitiesAreConsolidated" />
<div class="p-2" v-else> <div class="p-2" v-else>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">

View File

@ -86,6 +86,7 @@ import { LoginData, TenantVO } from '@/api/types';
import { to } from 'await-to-js'; import { to } from 'await-to-js';
import { HttpStatus } from '@/enums/RespEnum'; import { HttpStatus } from '@/enums/RespEnum';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { getAllRouters } from '@/api/system/menu';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -153,6 +154,7 @@ const handleLogin = () => {
if (!err) { if (!err) {
const redirectUrl = redirect.value || '/'; const redirectUrl = redirect.value || '/';
await router.push(redirectUrl); await router.push(redirectUrl);
loading.value = false; loading.value = false;
} else { } else {
loading.value = false; loading.value = false;

View File

@ -6,8 +6,8 @@
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<el-row :gutter="10" class="mb8"> <el-row :gutter="10" class="mb8">
<el-col :span="1.5" :offset="0" <el-col :span="1.5" :offset="0">
><el-button <el-button
type="primary" type="primary"
v-hasPermi="['cailiaoshebei:materialbatchdemandplan:add']" v-hasPermi="['cailiaoshebei:materialbatchdemandplan:add']"
size="default" size="default"
@ -15,10 +15,10 @@
icon="FolderAdd" icon="FolderAdd"
plain plain
>新增</el-button >新增</el-button
></el-col
> >
<el-col :span="1.5" :offset="0" </el-col>
><el-button <el-col :span="1.5" :offset="0">
<el-button
type="danger" type="danger"
size="default" size="default"
v-hasPermi="['cailiaoshebei:materialbatchdemandplan:remove']" v-hasPermi="['cailiaoshebei:materialbatchdemandplan:remove']"
@ -27,8 +27,8 @@
icon="FolderDelete" icon="FolderDelete"
plain plain
>删除</el-button >删除</el-button
></el-col
> >
</el-col>
</el-row> </el-row>
</template> </template>
@ -70,19 +70,17 @@
>修改</el-button >修改</el-button
> >
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5" v-if="form.mrpBaseBo.status == 'draft'">
<el-button plain type="warning" icon="Finished" @click="handleAudit()" v-hasPermi="['cailiaoshebei:materialbatchdemandplan:query']" <el-button plain type="primary" icon="Finished" @click="handleAudit()">审核</el-button>
>审核</el-button </el-col>
> <el-col :span="1.5" v-if="form.mrpBaseBo.status != 'draft'">
<el-button type="warning" icon="View" @click="handleAudit()">查看流程</el-button>
</el-col> </el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table v-loading="loading" :data="cailiaoshebeiList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="cailiaoshebeiList" @selection-change="handleSelectionChange">
<!-- <el-table-column type="selection" width="55" align="center" /> -->
<!-- <el-table-column label="供货商ID" align="center" prop="supplierId" /> -->
<el-table-column label="物资名称" align="center" prop="name" /> <el-table-column label="物资名称" align="center" prop="name" />
<el-table-column label="质量标准" align="center" prop="qs" /> <el-table-column label="质量标准" align="center" prop="qs" />
<el-table-column label="规格型号" align="center" prop="specification" /> <el-table-column label="规格型号" align="center" prop="specification" />
@ -103,7 +101,7 @@
</el-row> </el-row>
<!-- 添加或修改物资-材料设备对话框 --> <!-- 添加或修改物资-材料设备对话框 -->
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="1300px" append-to-body> <el-dialog :close-on-click-modal="false" draggable :title="dialog.title" v-model="dialog.visible" width="1500px" append-to-body>
<el-form :model="form" ref="cailiaoshebeiFormRef" :rules="rules" label-width="80px" :inline="false"> <el-form :model="form" ref="cailiaoshebeiFormRef" :rules="rules" label-width="80px" :inline="false">
<el-divider>基础信息</el-divider> <el-divider>基础信息</el-divider>
<el-row :gutter="20"> <el-row :gutter="20">
@ -125,63 +123,93 @@
</el-row> </el-row>
<el-divider>主要信息</el-divider> <el-divider>主要信息</el-divider>
<el-table :data="form.planList"> <!-- 表格添加border属性优化视觉体验 -->
<el-table-column prop="name" align="center" label="版本号 " width="150"> <el-table :data="form.planList" border>
<!-- 版本号列 -->
<el-table-column prop="batchNumber" align="center" label="版本号 " width="200">
<template #default="scope"> <template #default="scope">
<el-select v-model="scope.row.versions" placeholder="请选择" @change="(val) => selectNameVersion(val, scope.row)"> <el-select v-model="scope.row.batchNumber" placeholder="请选择" @change="(val) => selectNameVersion(val, scope.row, scope.$index)">
<el-option v-for="item in versionList" :key="item.versions" :label="item.versions" :value="item.versions" /> <el-option v-for="item in versionList" :key="item.versions" :label="item.versions" :value="item.versions" />
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="name" align="center" label="物资名称"> <!-- 物资名称列 -->
<el-table-column prop="name" align="center" label="物资名称" width="160">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.name" v-if="scope.row.mrpBaseId" placeholder="请输入物资名称" disabled />
<el-select <el-select
:disabled="!scope.row.versions" v-else
:disabled="!scope.row.batchNumber"
v-model="scope.row.suppliespriceId" v-model="scope.row.suppliespriceId"
placeholder="请选择" placeholder="请选择"
@change="(val) => selectName(val, scope.row)" @change="(val) => selectName(val, scope.row, scope.$index)"
> >
<el-option v-for="item in nameList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in nameList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="specification" align="center" label="规格型号" width="150"> <!-- 剩余量列 -->
<el-table-column align="center" label="剩余量" width="80">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.specification" placeholder="请输入规格型号" disabled /> <span>{{ scope.row.Remaining }}</span>
<!-- <el-input disabled v-model="scope.row.Remaining" placeholder="剩余量" /> -->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="unit" align="center" label="单位" width="130"> <!-- 规格型号列 -->
<el-table-column prop="specification" align="center" label="规格型号" width="100">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.unit" placeholder="请输入单位" disabled /> <span>{{ scope.row.specification }}</span>
<!-- <el-input v-model="scope.row.specification" placeholder="请输入规格型号" disabled /> -->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="demandQuantity" align="center" label="数量" width="130"> <!-- 单位列 -->
<el-table-column prop="unit" align="center" label="单位" width="80">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.demandQuantity" placeholder="请输入数量" type="number" min="0" disabled /> <span>{{ scope.row.unit }}</span>
<!-- <el-input v-model="scope.row.unit" placeholder="请输入单位" disabled /> -->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="qs" align="center" label="质量标准" width="150"> <!-- 数量列新增错误提示展示 -->
<template #header> <span class="text-red">*</span> 质量标准 </template> <el-table-column prop="demandQuantity" align="center" label="数量" width="140">
<template #default="scope">
<el-input
v-model.number="scope.row.demandQuantity"
@input="validateDemandQuantity(scope.row, scope.$index)"
@blur="validateDemandQuantity(scope.row, scope.$index)"
:max="scope.row.Remaining"
placeholder="请输入数量"
type="number"
min="0"
/>
<!-- 数量错误提示红色小字体 -->
<div v-if="scope.row.quantityError" class="text-red-500 text-xs mt-1">{{ scope.row.quantityError }}</div>
</template>
</el-table-column>
<!-- 质量标准列 -->
<el-table-column prop="qs" align="center" label="质量标准" width="140">
<template #header> <span class="text-red-500">*</span> 质量标准 </template>
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.qs" placeholder="请输入质量标准" /> <el-input v-model="scope.row.qs" placeholder="请输入质量标准" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="arrivalTime" align="center" label="需求到货时间"> <!-- 需求到货时间列 -->
<template #header> <span class="text-red">*</span> 需求到货时间 </template> <el-table-column prop="arrivalTime" align="center" label="需求到货时间" width="180">
<template #header> <span class="text-red-500">*</span> 需求到货时间 </template>
<template #default="scope"> <template #default="scope">
<el-date-picker v-model="scope.row.arrivalTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" style="width: 140px" /> <el-date-picker v-model="scope.row.arrivalTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择" style="width: 140px" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="remark" align="center" label="备注" width="150"> <!-- 备注 -->
<el-table-column prop="remark" align="center" label="备注">
<template #default="scope"> <template #default="scope">
<el-input v-model="scope.row.remark" placeholder="请输入备注" disabled /> <el-input v-model="scope.row.remark" placeholder="请输入备注" disabled />
</template> </template>
</el-table-column> </el-table-column>
<!-- 操作列 -->
<el-table-column prop="remark" align="center" label="操作" width="150"> <el-table-column prop="remark" align="center" label="操作" width="150">
<template #default="scope"> <template #default="scope">
<el-button @click="addRow" type="success" icon="Plus" circle size="small" /> <el-button @click="addRow" type="primary" icon="Plus" size="small" />
<el-button @click="delRow(scope.$index)" type="danger" icon="Delete" circle size="small" /> <el-button @click="delRow(scope.$index)" type="danger" icon="Delete" size="small" />
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -206,10 +234,48 @@ import {
listSelectCailiaoshebei, listSelectCailiaoshebei,
obtainTheVersion, obtainTheVersion,
getDictList, getDictList,
coryEngineeringList coryEngineeringList,
mrpBaseRemaining
} from '@/api/materials/batchPlan'; } from '@/api/materials/batchPlan';
import { CailiaoshebeiVO, CailiaoshebeiQuery, CailiaoshebeiForm } from '@/api/materials/batchPlan/types'; import { CailiaoshebeiVO, CailiaoshebeiQuery, CailiaoshebeiForm } from '@/api/materials/batchPlan/types';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { getCurrentInstance, ComponentInternalInstance, watch, onMounted, onUnmounted } from 'vue';
import type { ElFormInstance } from 'element-plus';
// 类型定义补充
interface DialogOption {
visible: boolean;
title: string;
}
interface PlanListItem {
id?: number | undefined;
name?: string | undefined;
specification?: string | undefined;
unit?: string | undefined;
suppliespriceId?: number | undefined;
demandQuantity?: number | undefined;
qs?: string | undefined;
arrivalTime?: string | undefined;
remark?: string | undefined;
Remaining: number; // 剩余量(必存在)
quantityError: string; // 数量错误提示
batchNumber?: string | undefined; // 版本号
duplicateError: string; // 重复错误提示
mrpBaseId?: number | undefined;
}
interface FormData {
mrpBaseBo: {
id?: number | undefined;
preparedDate?: string | undefined;
planCode?: string | undefined;
matCat?: string | undefined;
status?: string | undefined;
projectId?: number | undefined;
};
planList: PlanListItem[];
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store // 获取用户 store
@ -227,17 +293,17 @@ const multiple = ref(true);
const total = ref(0); const total = ref(0);
const mainTotal = ref(0); const mainTotal = ref(0);
const batchOptions = ref<any[]>([]); const batchOptions = ref<any[]>([]);
const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status')); const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status') || {});
const route = useRoute(); const route = useRoute();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const cailiaoshebeiFormRef = ref<ElFormInstance>(); const cailiaoshebeiFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
const initFormData: any = { // 初始化表单数据(补充版本号、重复错误提示字段)
const initFormData: FormData = {
mrpBaseBo: { mrpBaseBo: {
id: undefined, id: undefined,
preparedDate: undefined, preparedDate: undefined,
@ -246,7 +312,6 @@ const initFormData: any = {
status: undefined, status: undefined,
projectId: currentProject.value?.id projectId: currentProject.value?.id
}, },
planList: [ planList: [
{ {
id: undefined, id: undefined,
@ -257,18 +322,23 @@ const initFormData: any = {
demandQuantity: undefined, demandQuantity: undefined,
qs: undefined, qs: undefined,
arrivalTime: undefined, arrivalTime: undefined,
remark: undefined remark: undefined,
Remaining: 0, // 初始化剩余量
quantityError: '', // 初始化数量错误提示
batchNumber: undefined, // 初始化版本号
duplicateError: '', // 初始化重复错误提示
mrpBaseId: undefined
} }
] ]
}; };
const data = reactive({ const data = reactive({
form: { ...initFormData }, form: { ...initFormData } as FormData,
queryParams: { queryParams: {
batchData: { batchData: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
planCode: undefined, planCode: undefined,
projectId: currentProject.value?.id projectId: currentProject.value?.id
}, },
mainData: { mainData: {
@ -285,77 +355,187 @@ const data = reactive({
'mrpBaseBo.matCat': [{ required: true, message: '物资分类不能为空', trigger: 'blur' }] 'mrpBaseBo.matCat': [{ required: true, message: '物资分类不能为空', trigger: 'blur' }]
} }
}); });
const batchNumber = ref(''); const batchNumber = ref('');
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
const nameList = ref([]); const nameList = ref<any[]>([]);
const versionList = ref([]); const versionList = ref<any[]>([]);
/** 查询物资-材料设备列表 */ /** 查询物资-材料设备列表 */
const getList = async (type?: string) => { const getList = async (type?: string) => {
loading.value = true; loading.value = true;
try {
const res = await listBatch(queryParams.value.batchData); const res = await listBatch(queryParams.value.batchData);
batchOptions.value = res.rows; batchOptions.value = res.rows || [];
if (res.rows && res.rows.length > 0 && !queryParams.value.mainData.mrpBaseId) { // 自动选中第一条数据(如果存在且未选中)
batchTreeRef.value.setCurrentKey(res.rows[0].id); if (batchOptions.value.length > 0 && !queryParams.value.mainData.mrpBaseId) {
queryParams.value.mainData.mrpBaseId = res.rows[0].id; batchTreeRef.value?.setCurrentKey(batchOptions.value[0].id);
form.value.mrpBaseBo.status = res.rows[0].status; queryParams.value.mainData.mrpBaseId = batchOptions.value[0].id;
form.value.mrpBaseBo.status = batchOptions.value[0].status;
} }
total.value = res.total; total.value = res.total || 0;
} catch (error) {
proxy?.$modal.msgError('获取批次列表失败');
} finally {
loading.value = false; loading.value = false;
if (type === 'search') return; // 非搜索场景下同步加载主列表
if (type !== 'search') {
getMainList(); getMainList();
}
}
}; };
const selectName = (val: any, row: any) => { /** 数量校验:必须≤剩余量,且为合法数字 */
const selected = nameList.value.find((item) => item.id === val); const validateDemandQuantity = (row: PlanListItem, index: number) => {
// 1. 清除之前的错误信息
row.quantityError = '';
// 2. 处理空值若需必填可补充row.quantityError = '数量不能为空'
if (row.demandQuantity === null || row.demandQuantity === undefined || row.demandQuantity === '') {
return;
}
// 3. 处理非数字
if (typeof row.demandQuantity !== 'number' || isNaN(row.demandQuantity)) {
row.quantityError = '请输入合法数字';
return;
}
// 4. 处理负数
if (row.demandQuantity < 0) {
row.quantityError = '数量不能为负数';
return;
}
// 5. 核心校验:数量≤剩余量
if (row.demandQuantity > row.Remaining) {
row.quantityError = `数量不能超过剩余量${row.Remaining}`;
}
};
/** 获取剩余量 */
const getMrpBaseRemaining = async (suppliespriceId: number, row: PlanListItem) => {
try {
const res = await mrpBaseRemaining({ suppliespriceId });
row.Remaining = res.data || 0;
// 剩余量更新后,重新校验当前数量
validateDemandQuantity(row, 0);
} catch (error) {
proxy?.$modal.msgError('获取剩余量失败');
row.Remaining = 0;
}
};
/** 校验重复数据:版本号+物资名称不能重复 */
const checkDuplicate = () => {
const planList = form.value.planList;
let hasDuplicate = false;
// 1. 清除所有重复错误提示
planList.forEach((item) => {
item.duplicateError = '';
});
// 2. 遍历校验重复(只校验版本号和物资名称都存在的行)
for (let i = 0; i < planList.length; i++) {
const current = planList[i];
// 跳过版本号或物资名称为空的行
if (!current.batchNumber || !current.suppliespriceId) continue;
for (let j = i + 1; j < planList.length; j++) {
const compare = planList[j];
if (!compare.batchNumber || !compare.suppliespriceId) continue;
// 版本号和物资ID都相同则判定为重复
if (current.batchNumber === compare.batchNumber && current.suppliespriceId === compare.suppliespriceId) {
current.duplicateError = `与第${j + 1}行重复(同版本+同物资)`;
compare.duplicateError = `与第${i + 1}行重复(同版本+同物资)`;
hasDuplicate = true;
}
}
}
return hasDuplicate;
};
/** 选择物资名称触发(新增索引参数,用于触发重复校验) */
const selectName = (val: number, row: PlanListItem, index: number) => {
console.log(row);
// 1. 获取剩余量并更新基础信息
getMrpBaseRemaining(val, row).then(() => {
const selected = nameList.value.find((item: any) => item.id === val);
if (selected) { if (selected) {
row.name = selected.name; row.name = selected.name;
row.specification = selected.specification; row.specification = selected.specification;
row.unit = selected.unit; row.unit = selected.unit;
row.qs = selected.qs; row.qs = selected.qs || '';
row.demandQuantity = selected.quantity; row.remark = selected.remark || '';
row.remark = selected.remark; row.arrivalTime = selected.arrivalTime || '';
row.arrivalTime = selected.arrivalTime;
} }
// 2. 触发重复校验
checkDuplicate();
});
}; };
/** 节点单击事件 */ /** 节点单击事件 */
const handleNodeClick = (data: any) => { const handleNodeClick = (data: any) => {
queryParams.value.mainData.mrpBaseId = data.id; queryParams.value.mainData.mrpBaseId = data.id;
form.value.mrpBaseBo.status = data.status; form.value.mrpBaseBo.status = data.status;
getMainList(); getMainList();
}; };
/** 获取主列表数据 */
const getMainList = async () => { const getMainList = async () => {
if (!queryParams.value.mainData.mrpBaseId) return; if (!queryParams.value.mainData.mrpBaseId) return;
loading.value = true;
try {
const res = await getBatch(queryParams.value.mainData); const res = await getBatch(queryParams.value.mainData);
cailiaoshebeiList.value = res.rows; cailiaoshebeiList.value = res.rows || [];
mainTotal.value = res.total; mainTotal.value = res.total || 0;
} catch (error) {
proxy?.$modal.msgError('获取物资列表失败');
} finally {
loading.value = false;
}
}; };
/** 搜索批次列表 */
const searchBatchList = async () => { const searchBatchList = async () => {
queryParams.value.batchData.planCode = batchNumber.value; queryParams.value.batchData.planCode = batchNumber.value;
getList('search'); getList('search');
}; };
//删除 /** 删除表格行 */
const delRow = (index: number) => { const delRow = (index: number) => {
if (form.value.planList.length <= 1) return proxy?.$modal.msgWarning('请至少保留一项'); if (form.value.planList.length <= 1) {
return proxy?.$modal.msgWarning('请至少保留一项物资数据');
}
form.value.planList.splice(index, 1); form.value.planList.splice(index, 1);
// 删除后重新校验重复(避免删除重复行后错误提示残留)
checkDuplicate();
}; };
//新增 /** 新增表格行 */
const addRow = () => { const addRow = () => {
form.value.planList.push({ const newRow: PlanListItem = {
name: undefined, name: undefined,
specification: undefined, specification: undefined,
unit: undefined, unit: undefined,
suppliespriceId: undefined,
demandQuantity: undefined, demandQuantity: undefined,
qs: undefined, qs: undefined,
arrivalTime: undefined, arrivalTime: undefined,
remark: undefined remark: undefined,
}); Remaining: 0,
quantityError: '',
batchNumber: undefined,
duplicateError: '',
mrpBaseId: undefined
};
form.value.planList.push(newRow);
}; };
/** 取消按钮 */ /** 取消按钮 */
@ -367,41 +547,22 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
const status = form.value.mrpBaseBo.status; const status = form.value.mrpBaseBo.status;
form.value = { ...initFormData, status }; // 重置但保留 // 重置表单(保留状态字段)
form.value = {
...JSON.parse(JSON.stringify(initFormData)),
mrpBaseBo: { ...initFormData.mrpBaseBo, status }
};
// 重置表单验证状态
cailiaoshebeiFormRef.value?.resetFields(); cailiaoshebeiFormRef.value?.resetFields();
// 重置项目ID
form.value.mrpBaseBo.projectId = currentProject.value?.id; form.value.mrpBaseBo.projectId = currentProject.value?.id;
form.value.planList = [
{
name: undefined,
specification: undefined,
unit: undefined,
suppliespriceId: undefined,
demandQuantity: undefined,
qs: undefined,
arrivalTime: undefined,
remark: undefined
}
];
}; };
// /** 搜索按钮操作 */
// const handleQuery = () => {
// queryParams.value.pageNum = 1;
// getList();
// };
// /** 重置按钮操作 */
// const resetQuery = () => {
// queryFormRef.value?.resetFields();
// handleQuery();
// };
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: CailiaoshebeiVO[]) => { const handleSelectionChange = (selection: CailiaoshebeiVO[]) => {
ids.value = selection.map((item) => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length != 1; single.value = selection.length !== 1;
multiple.value = !selection.length; multiple.value = selection.length === 0;
}; };
/** 新增按钮操作 */ /** 新增按钮操作 */
@ -411,93 +572,157 @@ const handleAdd = () => {
dialog.title = '新增物资-需求'; dialog.title = '新增物资-需求';
}; };
/** 修改按钮操作 */
const handleUpdata = () => { const handleUpdata = () => {
if (!queryParams.value.mainData.mrpBaseId) {
return proxy?.$modal.msgError('请先选择批次');
}
// 1. 获取对应版本的物资列表
reset(); reset();
getCailiaoshebei(queryParams.value.mainData.mrpBaseId).then((res: any) => { loading.value = true;
form.value.mrpBaseBo = res.data.mrpBaseBo; getCailiaoshebei(queryParams.value.mainData.mrpBaseId)
const allowedKeys = Object.keys(initFormData.planList[0]); .then((res: any) => {
form.value.planList = res.data.planList.map((item) => { // 1. 更新基础信息
return allowedKeys.reduce((obj, key) => { form.value.mrpBaseBo = res.data.mrpBaseBo || initFormData.mrpBaseBo;
obj[key] = item[key] ?? undefined;
return obj;
}, {});
});
console.log(form.value.planList); // 2. 更新表格数据(补充缺失字段)
}); form.value.planList = (res.data.planList || []).map((item: any) => ({
id: item.id,
name: item.name,
specification: item.specification,
unit: item.unit,
suppliespriceId: item.suppliespriceId,
demandQuantity: item.demandQuantity,
qs: item.qs,
arrivalTime: item.arrivalTime,
remark: item.remark,
Remaining: Number(item.remaining) || 0,
// remaining:
quantityError: '',
batchNumber: item.batchNumber,
duplicateError: '',
mrpBaseId: item.mrpBaseId
}));
// 3. 打开对话框
dialog.visible = true; dialog.visible = true;
dialog.title = '修改物资-需求'; dialog.title = '修改物资-需求';
})
.catch(() => {
proxy?.$modal.msgError('获取详情失败');
})
.finally(() => {
loading.value = false;
});
}; };
/** 提交数据 */ /** 提交数据(整合所有校验) */
const submitTransferForm = async () => { const submitTransferForm = async () => {
const result = validateAndClean(form.value.planList); // 1. 先校验重复数据
if (!result.valid) { const hasDuplicate = checkDuplicate();
proxy?.$modal.msgError(result.message); if (hasDuplicate) {
return; return proxy?.$modal.msgError('存在重复的版本号+物资组合,请修正后提交');
} }
// 2. 校验数量合法性(检查是否有数量错误)
const hasQuantityError = form.value.planList.some((row) => row.quantityError);
if (hasQuantityError) {
return proxy?.$modal.msgError('存在非法数量,请修正后提交');
}
// 3. 执行表单基础验证
const result = validateAndClean(form.value.planList);
if (!result.valid) {
return proxy?.$modal.msgError(result.message);
}
// 4. 表单组件验证
cailiaoshebeiFormRef.value?.validate(async (valid: boolean) => { cailiaoshebeiFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (!valid) return;
buttonLoading.value = true; buttonLoading.value = true;
form.value.planList = result.data; try {
await updateCailiaoshebei(form.value).finally(() => (buttonLoading.value = false)); // 5. 提交数据
await updateCailiaoshebei({
...form.value,
planList: result.data // 使用清洗后的数据
});
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
// 6. 刷新列表
await getList(); await getList();
} catch (error) {
proxy?.$modal.msgError('操作失败,请重试');
} finally {
buttonLoading.value = false;
} }
}); });
}; };
/** 删除批次 */ /** 删除批次 */
const handleDeleteBatch = async () => { const handleDeleteBatch = async () => {
const _ids = batchTreeRef.value.getCurrentNode()?.id; const _id = batchTreeRef.value?.getCurrentNode()?.id;
await proxy?.$modal.confirm('是否确认删除批次编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); if (!_id) {
await delBatch(_ids); return proxy?.$modal.msgError('请先选择批次');
}
try {
await proxy?.$modal.confirm('是否确认删除该批次?删除后不可恢复!');
await delBatch(_id);
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
// 重置选中状态并刷新列表
queryParams.value.mainData.mrpBaseId = undefined; queryParams.value.mainData.mrpBaseId = undefined;
form.value.mrpBaseBo.status = undefined;
await getList(); await getList();
} catch (error) {
// 取消确认时不提示错误
if (error !== 'cancel') {
proxy?.$modal.msgError('删除失败');
}
}
}; };
interface ValidateResult { /** 数据清洗与基础校验 */
valid: boolean; function validateAndClean(arr: PlanListItem[]) {
data: any[]; // 1. 过滤掉全空的数据项
errors: { index: number; field: string; message: string }[]; const cleanedArr = arr.filter((item) => !Object.values(item).every((v) => v === '' || v == null || v === undefined));
}
function validateAndClean(arr: any[]) { let hasFullItem = false; // 是否有至少一条完整数据
// 过滤掉全空的数据项
const cleanedArr = arr.filter((item) => !Object.values(item).every((v) => v === '' || v == null));
let hasFullItem = false; // 是否有至少一条全填数据
// 2. 逐行校验必填项
for (let idx = 0; idx < cleanedArr.length; idx++) { for (let idx = 0; idx < cleanedArr.length; idx++) {
const item = cleanedArr[idx]; const item = cleanedArr[idx];
const keys = Object.keys(item).filter((k) => k !== 'remark' && k !== 'id');
const allFilled = keys.every((k) => item[k] !== '' && item[k] != null); // 校验版本号
const allEmpty = Object.values(item).every((v) => v === '' || v == null); if (!item.batchNumber) {
return { valid: false, message: `${idx + 1}行:版本号不能为空`, data: cleanedArr };
}
// 单独检查 qs 和 arrivalTime // 校验物资选择
if (!item.suppliespriceId) {
return { valid: false, message: `${idx + 1}行:请选择物资名称`, data: cleanedArr };
}
// 校验质量标准
if (!item.qs) { if (!item.qs) {
return { valid: false, message: `${idx + 1}行:质量标准不能为空`, data: cleanedArr }; return { valid: false, message: `${idx + 1}行:质量标准不能为空`, data: cleanedArr };
} }
// 校验需求到货时间
if (!item.arrivalTime) { if (!item.arrivalTime) {
return { valid: false, message: `${idx + 1}行:需求到货时间不能为空`, data: cleanedArr }; return { valid: false, message: `${idx + 1}行:需求到货时间不能为空`, data: cleanedArr };
} }
// 检查其他字段是否部分填 // 校验数量必填且≥0
if (!allFilled && !allEmpty) { if (item.demandQuantity === null || item.demandQuantity === undefined || item.demandQuantity < 0) {
return { valid: false, message: `${idx + 1}行:主要信息存在部分字段缺失的数据项`, data: cleanedArr }; return { valid: false, message: `${idx + 1}行:请输入合法的需求数量`, data: cleanedArr };
} }
if (allFilled) {
hasFullItem = true; hasFullItem = true;
} }
}
// 检查是否至少有一条完整数据 // 3. 检查是否至少有一条完整数据
if (!hasFullItem) { if (!hasFullItem) {
return { valid: false, message: '至少需要一条完整的数据', data: cleanedArr }; return { valid: false, message: '至少需要填写一条完整的物资数据', data: cleanedArr };
} }
return { valid: true, message: '', data: cleanedArr }; return { valid: true, message: '', data: cleanedArr };
@ -506,54 +731,96 @@ function validateAndClean(arr: any[]) {
/** 审核按钮操作 */ /** 审核按钮操作 */
const handleAudit = async () => { const handleAudit = async () => {
if (!form.value.mrpBaseBo.status) { if (!form.value.mrpBaseBo.status) {
proxy?.$modal.msgError('请选择批次号'); return proxy?.$modal.msgError('请选择批次号');
return;
} }
if (!queryParams.value.mainData.mrpBaseId) {
return proxy?.$modal.msgError('请选择批次号');
}
// 关闭当前页并打开审核页
proxy?.$tab.closePage(route); proxy?.$tab.closePage(route);
proxy?.$tab.openPage('/approval/batchPlan/indexEdit', '审核物资设备批次需求计划', { proxy?.$tab.openPage('/approval/batchPlan/indexEdit', '审核物资设备批次需求计划', {
id: queryParams.value.mainData.mrpBaseId, id: queryParams.value.mainData.mrpBaseId,
status: form.value.mrpBaseBo.status + '_batchRequirements', status: `${form.value.mrpBaseBo.status}_batchRequirements`,
type: 'update' type: 'update'
}); });
}; };
const getNameList = (versions) => { /** 获取物资列表(按版本号筛选) */
coryEngineeringList({ projectId: currentProject.value?.id, versions }).then((res) => { const getNameList = (versions: string) => {
nameList.value = res.data; coryEngineeringList({
projectId: currentProject.value?.id,
versions
})
.then((res: any) => {
nameList.value = res.data || [];
})
.catch(() => {
nameList.value = [];
proxy?.$modal.msgError('获取物资列表失败');
}); });
}; };
// 获取版本号
const getVersion = () => {
obtainTheVersion({ projectId: currentProject.value?.id }).then((res) => {
versionList.value = res.data;
});
};
const selectNameVersion = (val, row) => {
row.suppliespriceId = undefined;
getNameList(val);
};
/** 获取版本号列表 */
const getVersion = () => {
obtainTheVersion({ projectId: currentProject.value?.id })
.then((res: any) => {
versionList.value = res.data || [];
})
.catch(() => {
versionList.value = [];
proxy?.$modal.msgError('获取版本号失败');
});
};
/** 选择版本号触发(新增索引参数,用于触发重复校验) */
const selectNameVersion = (val: string, row: PlanListItem, index: number) => {
row.batchNumber = val;
row.suppliespriceId = undefined; // 切换版本号时清空物资选择
row.name = undefined;
row.specification = undefined;
row.unit = undefined;
row.qs = undefined;
row.remark = undefined;
row.arrivalTime = undefined;
row.demandQuantity = undefined;
row.quantityError = '';
row.duplicateError = '';
row.mrpBaseId = '';
// 1. 获取对应版本的物资列表
getNameList(val);
// 2. 触发重复校验(清空物资后可能消除重复)
checkDuplicate();
};
/** 页面挂载时初始化 */
onMounted(() => { onMounted(() => {
getList(); getList();
// getNameList();
getVersion(); getVersion();
}); });
//监听项目id刷新数据 /** 监听项目ID变化刷新数据 */
const listeningProject = watch( const listeningProject = watch(
() => currentProject.value?.id, () => currentProject.value?.id,
(nid, oid) => { (newId, oldId) => {
queryParams.value.mainData.projectId = nid; if (newId !== oldId && newId) {
queryParams.value.batchData.projectId = nid; queryParams.value.mainData.projectId = newId;
form.value.mrpBaseBo.projectId = nid; queryParams.value.batchData.projectId = newId;
form.value.mrpBaseBo.projectId = newId;
getList(); getList();
getVersion(); // 重新获取对应项目的版本号
}
} }
); );
/** 页面卸载时清理监听 */
onUnmounted(() => { onUnmounted(() => {
listeningProject(); listeningProject();
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.custom-tree-node { .custom-tree-node {
flex: 1; flex: 1;
@ -563,4 +830,17 @@ onUnmounted(() => {
font-size: 14px; font-size: 14px;
padding-right: 8px; padding-right: 8px;
} }
/* 错误提示样式补充 */
.text-red-500 {
color: #f56c6c;
}
.text-xs {
font-size: 12px;
}
.dialog-footer {
text-align: right;
}
</style> </style>

View File

@ -56,7 +56,7 @@
<el-table-column label="操作" align="center" min-width="150" fixed="right"> <el-table-column label="操作" align="center" min-width="150" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['materials:materialIssue:query']">查看</el-button> <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['materials:materialIssue:query']">查看</el-button>
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['materials:materialIssue:edit']">修改</el-button> <!-- <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['materials:materialIssue:edit']">修改</el-button> -->
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materialIssue:remove']" <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materialIssue:remove']"
>删除</el-button >删除</el-button
> >
@ -85,9 +85,10 @@
<el-input v-model="form.projectName" placeholder="请输入工程名称" /> <el-input v-model="form.projectName" placeholder="请输入工程名称" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="24">
<!-- 设备材料名称从数量验收列表自动生成禁用手动输入 -->
<el-form-item label="设备材料名称" prop="materialName"> <el-form-item label="设备材料名称" prop="materialName">
<el-input v-model="form.materialName" placeholder="请输入设备材料名称" /> <el-input v-model="form.materialName" placeholder="由数量验收列表中的名称自动生成" disabled style="cursor: not-allowed" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -129,8 +130,8 @@
:prop="`itemList.${index}.name`" :prop="`itemList.${index}.name`"
:rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]" :rules="[{ required: true, message: '名称不能为空', trigger: 'blur' }]"
> >
<el-select v-model="item.name" placeholder="请选择名称" @change="(value) => getNameChange(value, index, item)"> <el-select v-model="item.inventoryId" placeholder="请选择名称" @change="(value) => getNameChange(value, index, item)">
<el-option v-for="item in optionsName" :key="item.id" :label="item.materialsName" :value="item.id" /> <el-option v-for="opt in optionsName" :key="opt.id" :label="opt.materialsName" :value="opt.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -140,7 +141,7 @@
:prop="`itemList.${index}.specification`" :prop="`itemList.${index}.specification`"
:rules="[{ required: true, message: '规格不能为空', trigger: 'blur' }]" :rules="[{ required: true, message: '规格不能为空', trigger: 'blur' }]"
> >
<el-input v-model="item.specification" placeholder="请输入规格" /> <el-input disabled v-model="item.specification" placeholder="请输入规格" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -149,41 +150,51 @@
:prop="`itemList.${index}.unit`" :prop="`itemList.${index}.unit`"
:rules="[{ required: true, message: '单位不能为空', trigger: 'blur' }]" :rules="[{ required: true, message: '单位不能为空', trigger: 'blur' }]"
> >
<el-input v-model="item.unit" placeholder="请输入单位" /> <el-input disabled v-model="item.unit" placeholder="请输入单位" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <!-- <el-col :span="12">
<el-form-item <el-form-item
label="库存" label="库存"
:prop="`itemList.${index}.stockQuantity`" :prop="`itemList.${index}.stockQuantity`"
:rules="[{ required: true, message: '库存不能为空', trigger: 'blur' }]" :rules="[
{ required: true, message: '库存不能为空', trigger: 'blur' },
{ type: 'number', min: 0, message: '库存不能小于0', trigger: ['blur', 'change'] }
]"
> >
<el-input v-model="item.stockQuantity" placeholder="请输入库存" /> <el-input
v-model.number="item.stockQuantity"
placeholder="请输入库存"
@input="handleStockChange(index)"
@blur="handleStockChange(index)"
/>
</el-form-item>
</el-col> -->
<el-col :span="12">
<el-form-item label="领取" :prop="`itemList.${index}.issuedQuantity`">
<el-input
v-model.number="item.issuedQuantity"
disabled
placeholder="请输入领取数量"
@input="handleIssuedChange(index)"
@blur="handleIssuedChange(index)"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <!-- <el-col :span="12">
<el-form-item
label="领取"
:prop="`itemList.${index}.issuedQuantity`"
:rules="[{ required: true, message: '领取数量不能为空', trigger: 'blur' }]"
>
<el-input v-model="item.issuedQuantity" placeholder="请输入领取数量" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item <el-form-item
label="剩余" label="剩余"
:prop="`itemList.${index}.remainingQuantity`" :prop="`itemList.${index}.remainingQuantity`"
:rules="[{ required: true, message: '剩余数量不能为空', trigger: 'blur' }]" :rules="[{ required: true, message: '剩余数量不能为空', trigger: 'blur' }]"
> >
<el-input v-model="item.remainingQuantity" placeholder="请输入剩余数量" /> <el-input v-model.number="item.remainingQuantity" placeholder="请输入剩余数量" readonly />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" :prop="`itemList.${index}.remark`">
<el-input v-model="item.remark" placeholder="请输入内容" /> <el-input v-model="item.remark" placeholder="请输入内容" />
</el-form-item> </el-form-item>
</el-col> </el-col> -->
<el-col :span="12" v-if="form.itemList.length > 1"> <el-col :span="12" v-if="form.itemList.length > 1">
<div class="item-actions"> <div class="item-actions">
<el-button type="danger" link @click="removeItem(index)" icon="Delete">删除</el-button> <el-button type="danger" link @click="removeItem(index)" icon="Delete">删除</el-button>
@ -242,12 +253,16 @@ import {
delMaterialIssue, delMaterialIssue,
addMaterialIssue, addMaterialIssue,
updateMaterialIssue, updateMaterialIssue,
inventoryList,
getMaterialName getMaterialName
} from '@/api/materials/materialIssue'; } from '@/api/materials/materialIssue';
import { MaterialIssueVO, MaterialIssueQuery, MaterialIssueForm } from '@/api/materials/materialIssue/types'; import { MaterialIssueVO, MaterialIssueQuery, MaterialIssueForm } from '@/api/materials/materialIssue/types';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import wordllssue from './word/index.vue'; import wordllssue from './word/index.vue';
import { watch, onMounted, onUnmounted, ref, reactive, computed, getCurrentInstance } from 'vue';
import type { ComponentInternalInstance, ElFormInstance, DialogOption } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store // 获取用户 store
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
@ -265,6 +280,8 @@ const total = ref(0);
const wordllssueRef = ref<InstanceType<typeof wordllssue>>(); const wordllssueRef = ref<InstanceType<typeof wordllssue>>();
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const materialIssueFormRef = ref<ElFormInstance>(); const materialIssueFormRef = ref<ElFormInstance>();
// 存储每个条目的watch停止函数
const itemWatchStopFns = ref<Array<() => void>>([]);
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
@ -279,7 +296,7 @@ const getInitFormData = () => {
materialSource: '1', materialSource: '1',
formCode: undefined, formCode: undefined,
projectName: undefined, projectName: undefined,
materialName: undefined, materialName: '', // 初始化为空字符串
orderingUnit: undefined, orderingUnit: undefined,
supplierUnit: undefined, supplierUnit: undefined,
issueUnit: undefined, issueUnit: undefined,
@ -302,13 +319,15 @@ const getInitFormData = () => {
stockQuantity: undefined, stockQuantity: undefined,
issuedQuantity: undefined, issuedQuantity: undefined,
remainingQuantity: undefined, remainingQuantity: undefined,
name: undefined, name: undefined, // 数量验收的名称
remark: undefined remark: undefined,
materialsId: undefined
} }
] ]
}; };
}; };
const data = reactive<PageData<MaterialIssueForm, MaterialIssueQuery>>({
const data = reactive({
form: getInitFormData(), form: getInitFormData(),
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@ -326,43 +345,127 @@ const data = reactive<PageData<MaterialIssueForm, MaterialIssueQuery>>({
params: {} params: {}
}, },
rules: { rules: {
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }] formCode: [{ required: true, message: '请输入表单编号', trigger: 'blur' }],
projectName: [{ required: true, message: '请输入工程名称', trigger: 'blur' }],
materialName: [{ required: true, message: '请先在数量验收列表中选择名称', trigger: 'blur' }], // 确保有名称生成
orderingUnit: [{ required: true, message: '请输入订货单位', trigger: 'blur' }],
supplierUnit: [{ required: true, message: '请输入供货单位', trigger: 'blur' }],
issueUnit: [{ required: true, message: '请输入领料单位', trigger: 'blur' }],
storageUnit: [{ required: true, message: '请输入保管单位', trigger: 'blur' }]
} }
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
const optionsName: any = ref([]);
/**
* 核心函数:从数量验收列表提取名称,用逗号拼接生成设备材料名称
*/
const computeMaterialName = () => {
if (!form.value.itemList || form.value.itemList.length === 0) {
form.value.materialName = '';
return;
}
// 过滤空名称,去重(可选,根据业务需求决定是否去重)
const validNames = form.value.itemList
.filter((item) => item.name && item.name.trim() !== '')
.map((item) => item.name.trim())
.filter((name, index, self) => self.indexOf(name) === index); // 去重(如需保留重复则删除这行)
form.value.materialName = validNames.join(',');
};
/** 查询物料领料单列表 */ /** 查询物料领料单列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
try {
const res = await listMaterialIssue(queryParams.value); const res = await listMaterialIssue(queryParams.value);
materialIssueList.value = res.rows; materialIssueList.value = res.rows;
total.value = res.total; total.value = res.total;
} finally {
loading.value = false; loading.value = false;
};
const optionsName: any = ref([]);
//获取一起名称
const getName = async () => {
const res = await getMaterialName(currentProject.value.id);
console.log(res);
if (res.code == 200) {
optionsName.value = res.data;
} }
}; };
// 获取材料名称列表
const getName = async () => {
try {
const res = await inventoryList(currentProject.value.id);
console.log(res);
if (res.code == 200) {
optionsName.value = res.data;
}
} catch (error) {
proxy?.$modal.msgError('获取材料名称失败');
}
};
// 材料名称选择变化处理修改select的value为名称而非ID
const getNameChange = (value, index, item) => { const getNameChange = (value, index, item) => {
// 这里可以添加处理逻辑 const selected = optionsName.value.find((opt) => opt.id === value);
console.log(value); if (selected) {
item.name = selected.materialsName; // 直接赋值名称
item.materialsId = selected.id; // 保留ID用于后端
item.specification = selected.typeSpecificationName;
item.unit = selected.weightId;
item.issuedQuantity = selected.number;
item.stockQuantity = Number(selected.inventoryNumber) || 0;
// calculateRemaining(index); // 计算剩余数量
}
};
const data = optionsName.value.find((item) => item.id == value); /** 验证领取数量不能超过库存 */
console.log(data); const validateIssuedQuantity = (rule, value, callback, index) => {
const item = form.value.itemList[index];
const stock = Number(item.stockQuantity) || 0;
const issued = Number(value) || 0;
form.value.itemList[index].name = data.materialsName; if (stock === 0) {
form.value.itemList[index].materialsId = data.id; callback();
form.value.itemList[index].specification = data.typeSpecificationName; return;
form.value.itemList[index].unit = data.weightId; }
form.value.itemList[index].stockQuantity = data.inventoryNumber;
if (issued > stock) {
callback(new Error(`领取数量不能超过库存(${stock})`));
} else {
callback();
}
};
/** 计算剩余数量(库存 - 领取数量) */
const calculateRemaining = (index: number) => {
const item = form.value.itemList[index];
const stock = Number(item.stockQuantity) || 0;
const issued = Number(item.issuedQuantity) || 0;
// 确保领取数量不超过库存
if (issued > stock) {
item.issuedQuantity = stock;
proxy?.$modal.msgWarning(`领取数量不能超过库存(${stock}),已自动调整`);
}
// 计算剩余数量
item.remainingQuantity = stock - (item.issuedQuantity || 0);
};
/** 库存变化时重新计算剩余数量 */
const handleStockChange = (index: number) => {
calculateRemaining(index);
// 触发验证
if (materialIssueFormRef.value) {
materialIssueFormRef.value.validateField(`itemList.${index}.issuedQuantity`);
materialIssueFormRef.value.validateField(`itemList.${index}.remainingQuantity`);
}
};
/** 领取数量变化时重新计算剩余数量 */
const handleIssuedChange = (index: number) => {
calculateRemaining(index);
// 触发验证
if (materialIssueFormRef.value) {
materialIssueFormRef.value.validateField(`itemList.${index}.remainingQuantity`);
}
}; };
/** 取消按钮 */ /** 取消按钮 */
@ -373,8 +476,38 @@ const cancel = () => {
/** 表单重置 */ /** 表单重置 */
const reset = () => { const reset = () => {
// 停止所有监听
itemWatchStopFns.value.forEach((stop) => stop());
itemWatchStopFns.value = [];
form.value = getInitFormData(); form.value = getInitFormData();
materialIssueFormRef.value?.resetFields(); materialIssueFormRef.value?.resetFields();
// 重新监听初始条目
if (form.value.itemList.length > 0) {
watchItemChanges(0);
}
// 初始计算一次材料名称
computeMaterialName();
};
/** 监听条目变化,自动计算剩余数量 */
const watchItemChanges = (index: number) => {
// 停止已有监听
if (itemWatchStopFns.value[index]) {
itemWatchStopFns.value[index]();
}
// // 监听库存和领取数量变化
// const stop = watch(
// () => [form.value.itemList[index].stockQuantity, form.value.itemList[index].issuedQuantity],
// () => {
// calculateRemaining(index);
// },
// { immediate: true }
// );
itemWatchStopFns.value[index] = stop;
}; };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
@ -401,16 +534,58 @@ const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = '添加物料领料单'; dialog.title = '添加物料领料单';
// 新增时初始计算材料名称
computeMaterialName();
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: MaterialIssueVO) => { const handleUpdate = async (row?: MaterialIssueVO) => {
reset(); reset();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
try {
const res = await getMaterialIssue(_id); const res = await getMaterialIssue(_id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
// 确保itemList存在且格式正确
if (!form.value.itemList) {
form.value.itemList = [];
}
// 转换数据类型并计算剩余数量
form.value.itemList = form.value.itemList.map((item) => ({
...item,
stockQuantity: Number(item.stockQuantity) || 0,
issuedQuantity: Number(item.issuedQuantity) || 0,
remainingQuantity: Number(item.remainingQuantity) || 0,
name: item.name || '' // 确保名称不为undefined
}));
// 为每个条目添加监听并强制计算剩余数量
form.value.itemList.forEach((_, index) => {
watchItemChanges(index);
calculateRemaining(index);
});
// 关键编辑时从itemList重新计算设备材料名称覆盖后端返回的旧值
computeMaterialName();
// 手动触发一次验证
setTimeout(() => {
if (materialIssueFormRef.value) {
form.value.itemList.forEach((_, index) => {
materialIssueFormRef.value.validateField(`itemList.${index}.issuedQuantity`);
materialIssueFormRef.value.validateField(`itemList.${index}.remainingQuantity`);
});
// 验证设备材料名称
materialIssueFormRef.value.validateField('materialName');
}
}, 0);
dialog.visible = true; dialog.visible = true;
dialog.title = '修改物料领料单'; dialog.title = '修改物料领料单';
} catch (error) {
proxy?.$modal.msgError('获取详情失败');
}
}; };
/** 提交按钮 */ /** 提交按钮 */
@ -418,21 +593,38 @@ const submitForm = () => {
materialIssueFormRef.value?.validate(async (valid: boolean) => { materialIssueFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
try {
// 处理提交数据,确保数量为数字类型
const submitData = {
...form.value,
itemList: form.value.itemList.map((item) => ({
...item,
stockQuantity: Number(item.stockQuantity),
issuedQuantity: Number(item.issuedQuantity),
remainingQuantity: Number(item.remainingQuantity)
}))
};
if (form.value.id) { if (form.value.id) {
await updateMaterialIssue(form.value).finally(() => (buttonLoading.value = false)); await updateMaterialIssue(submitData);
} else { } else {
await addMaterialIssue(form.value).finally(() => (buttonLoading.value = false)); await addMaterialIssue(submitData);
} }
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} catch (error) {
proxy?.$modal.msgError('操作失败');
} finally {
buttonLoading.value = false;
}
} }
}); });
}; };
// 添加数量验收条目 // 添加数量验收条目
const addItem = () => { const addItem = () => {
form.value.itemList.push({ const newItem = {
id: undefined, id: undefined,
specification: undefined, specification: undefined,
unit: undefined, unit: undefined,
@ -440,46 +632,81 @@ const addItem = () => {
issuedQuantity: undefined, issuedQuantity: undefined,
remainingQuantity: undefined, remainingQuantity: undefined,
name: undefined, name: undefined,
remark: undefined remark: undefined,
}); materialsId: undefined
};
form.value.itemList.push(newItem);
// 监听新条目
watchItemChanges(form.value.itemList.length - 1);
// 添加后重新计算材料名称
computeMaterialName();
}; };
// 删除数量验收条目 // 删除数量验收条目
const removeItem = (index: number) => { const removeItem = (index: number) => {
if (form.value.itemList.length > 1) { if (form.value.itemList.length > 1) {
// 停止该条目的监听
if (itemWatchStopFns.value[index]) {
itemWatchStopFns.value[index]();
}
form.value.itemList.splice(index, 1); form.value.itemList.splice(index, 1);
itemWatchStopFns.value.splice(index, 1);
// 删除后重新计算材料名称
computeMaterialName();
} else { } else {
proxy?.$modal.msgWarning('至少需要保留一条数量验收记录'); proxy?.$modal.msgWarning('至少需要保留一条数量验收记录');
} }
}; };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: MaterialIssueVO) => { const handleDelete = async (row?: MaterialIssueVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除物料领料单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); try {
await proxy?.$modal.confirm(`是否确认删除物料领料单编号为"${_ids}"的数据项?`);
await delMaterialIssue(_ids); await delMaterialIssue(_ids);
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
await getList(); await getList();
} catch (error) {
// 取消删除不提示
} finally {
loading.value = false;
}
}; };
const handleView = (row) => { const handleView = (row) => {
// 查看详情 // 查看详情
wordllssueRef.value?.openDialog(row); wordllssueRef.value?.openDialog(row);
}; };
// 关键:监听数量验收列表变化,实时更新设备材料名称
watch(
() => form.value.itemList,
() => {
computeMaterialName();
},
{ deep: true, immediate: true } // deep监听数组内部变化immediate初始执行
);
onMounted(() => { onMounted(() => {
getList(); getList();
getName(); getName();
}); });
//监听项目id刷新数据
// 监听项目id刷新数据
const listeningProject = watch( const listeningProject = watch(
() => currentProject.value?.id, () => currentProject.value?.id,
(nid, oid) => { (nid) => {
queryParams.value.projectId = nid; queryParams.value.projectId = nid;
form.value.projectId = nid; form.value.projectId = nid;
getList(); getList();
getName();
} }
); );
onUnmounted(() => { onUnmounted(() => {
listeningProject(); listeningProject();
// 清理所有监听
itemWatchStopFns.value.forEach((stop) => stop());
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -57,16 +57,15 @@
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" min-width="120" fixed="right"> <el-table-column label="操作" align="center" min-width="120" fixed="right">
<template #default="scope"> <template #default="scope">
<el-tooltip content="查看" placement="top"> <!-- <el-button link type="primary" icon="edit" @click="handleUpdate(scope.row)" v-hasPermi="['materials:materialReceive:edit']"
>修改</el-button
> -->
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['materials:materialReceive:query']" <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['materials:materialReceive:query']"
>查看</el-button >查看</el-button
> >
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materialReceive:remove']" <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['materials:materialReceive:remove']"
>删除</el-button >删除</el-button
> >
</el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -74,7 +73,15 @@
</el-card> </el-card>
<!-- 添加或修改物料接收单对话框 --> <!-- 添加或修改物料接收单对话框 -->
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body> <el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
draggable
:title="dialog.title"
v-model="dialog.visible"
width="800px"
append-to-body
>
<el-form ref="materialReceiveFormRef" :model="form" :rules="rules" label-width="110px"> <el-form ref="materialReceiveFormRef" :model="form" :rules="rules" label-width="110px">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@ -122,7 +129,6 @@
:value="item.contractCode" :value="item.contractCode"
></el-option> ></el-option>
</el-select> </el-select>
<!-- <el-input v-model="form.contractName" placeholder="请输入合同名称" /> -->
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
@ -131,14 +137,13 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 数量验收区域修复v-for key问题 --> <!-- 数量验收区域 -->
<el-col :span="24"> <el-col :span="24">
<div class="detail"> <div class="detail">
<div class="detail-header"> <div class="detail-header">
<span>数量验收</span> <span>数量验收</span>
<el-button type="primary" v-if="form.materialSource == '1'" link @click="addItem" icon="Plus">添加数量验收</el-button> <el-button type="primary" v-if="form.materialSource == '1'" link @click="addItem" icon="Plus">添加数量验收</el-button>
</div> </div>
<!-- 关键修复v-for key改为item.id唯一标识而非index -->
<div v-for="(item, index) in form.itemList" :key="item.id" class="detail-item"> <div v-for="(item, index) in form.itemList" :key="item.id" class="detail-item">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@ -161,21 +166,27 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item label="数量" :prop="`itemList.${index}.quantity`" :rules="rules.quantityRule" ref="quantityFormItemRefs[index]">
label="数量" <el-input
:prop="`itemList.${index}.quantity`" :disabled="form.materialSource == '2'"
:rules="{ required: true, message: '数量不能为空', trigger: 'blur' }" type="number"
> v-model.number="item.quantity"
<el-input :disabled="form.materialSource == '2'" type="number" v-model="item.quantity" placeholder="请输入数量" /> placeholder="请输入数量"
min="0"
@input="handleQuantityInput(index)"
@blur="handleQuantityBlur(index)"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item <el-form-item label="验收" :prop="`itemList.${index}.acceptedQuantity`" :rules="rules.acceptedQuantityRule">
label="验收" <el-input
:prop="`itemList.${index}.acceptedQuantity`" type="number"
:rules="{ required: true, message: '验收数量不能为空', trigger: 'blur' }" v-model.number="item.acceptedQuantity"
> placeholder="请输入验收"
<el-input type="number" v-model="item.acceptedQuantity" placeholder="请输入验收" /> min="0"
@input="handleAcceptedInput(index)"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -184,13 +195,13 @@
:prop="`itemList.${index}.shortageQuantity`" :prop="`itemList.${index}.shortageQuantity`"
:rules="{ required: true, message: '缺件数量不能为空', trigger: 'blur' }" :rules="{ required: true, message: '缺件数量不能为空', trigger: 'blur' }"
> >
<el-input type="number" v-model="item.shortageQuantity" placeholder="自动计算(数量-验收数量)" readonly /> <el-input type="number" min="0" v-model="item.shortageQuantity" placeholder="自动计算" readonly />
<span class="tips">*自动计算数量-验收数量</span> <span class="tips">*自动计算数量-验收数量</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="24">
<el-form-item label="备注" :prop="`itemList.${index}.remark`"> <el-form-item label="备注" :prop="`itemList.${index}.remark`">
<el-input v-model="item.remark" placeholder="请输入备注" /> <el-input v-model="item.remark" type="textarea" placeholder="请输入备注" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.itemList.length > 1 && form.materialSource == '1'"> <el-col :span="12" v-if="form.itemList.length > 1 && form.materialSource == '1'">
@ -205,26 +216,26 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="合格证文件" prop="certCountFileId"> <el-form-item label="合格证文件" prop="certCountFileId">
<file-upload :isShowTip="false" v-model="form.certCountFileId" /> <file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.certCountFileId" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="出厂报告文件" prop="reportCountFileId"> <el-form-item label="出厂报告文件" prop="reportCountFileId">
<file-upload :isShowTip="false" v-model="form.reportCountFileId" /> <file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.reportCountFileId" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="技术资料文件" prop="techDocCountFileId"> <el-form-item label="技术资料文件" prop="techDocCountFileId">
<file-upload :isShowTip="false" v-model="form.techDocCountFileId" /> <file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.techDocCountFileId" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="厂家资质文件" prop="licenseCountFileId"> <el-form-item label="厂家资质文件" prop="licenseCountFileId">
<file-upload :isShowTip="false" v-model="form.licenseCountFileId" /> <file-upload :isShowTip="false" :fileType="['pdf', 'png', 'jpg', 'jpeg']" v-model="form.licenseCountFileId" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<span style="color: #ff0000ab; margin-bottom: 10px; display: block">注意请上传doc/xls/ppt/txt/pdf/png/jpg/jpeg/zip格式文件</span> <span style="color: #ff0000ab; margin-bottom: 10px; display: block">注意pdf/png/jpg/jpeg格式文件</span>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
@ -258,7 +269,7 @@ import { useUserStoreHook } from '@/store/modules/user';
import wordllReceive from './word/index.vue'; import wordllReceive from './word/index.vue';
import { listPurchaseDoc, purchaseDocPlanList } from '@/api/materials/purchaseDoc'; import { listPurchaseDoc, purchaseDocPlanList } from '@/api/materials/purchaseDoc';
import { watch, onMounted, onUnmounted, ref, reactive, computed, toRefs, getCurrentInstance } from 'vue'; import { watch, onMounted, onUnmounted, ref, reactive, computed, toRefs, getCurrentInstance } from 'vue';
import type { ComponentInternalInstance, ElFormInstance, DialogOption } from 'element-plus'; import type { ComponentInternalInstance, ElFormInstance, DialogOption, ElFormItem } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { storage_type } = toRefs<any>(proxy?.useDict('storage_type')); const { storage_type } = toRefs<any>(proxy?.useDict('storage_type'));
@ -266,8 +277,10 @@ const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject); const currentProject = computed(() => userStore.selectedProject);
const wordllReceiveRef = ref<InstanceType<typeof wordllReceive>>(); const wordllReceiveRef = ref<InstanceType<typeof wordllReceive>>();
// 核心修复1存储每个验收条目的watch停止函数,避免内存泄漏 // 存储每个验收条目的watch停止函数
const itemWatchStopFns = ref<Array<() => void>>([]); const itemWatchStopFns = ref<Array<() => void>>([]);
// 存储数量表单项的引用,用于手动触发验证
const quantityFormItemRefs = ref<(ElFormItem | null)[]>([]);
// 列表数据 // 列表数据
const materialReceiveList = ref<MaterialReceiveVO[]>([]); const materialReceiveList = ref<MaterialReceiveVO[]>([]);
@ -285,18 +298,19 @@ const materialReceiveFormRef = ref<ElFormInstance>();
const purchaseDocList = ref([]); // 物资采购单列表 const purchaseDocList = ref([]); // 物资采购单列表
const purchaseMap = new Map(); // 采购单映射id -> 采购单对象) const purchaseMap = new Map(); // 采购单映射id -> 采购单对象)
const contractNameList = ref([]); //合同列表 const contractNameList = ref([]); //合同列表
// 对话框配置 // 对话框配置
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
}); });
// 生成验收条目唯一ID用于v-for key // 生成验收条目唯一ID
const generateItemId = () => { const generateItemId = () => {
return Date.now() + Math.random().toString(36).substr(2, 9); return Date.now() + Math.random().toString(36).substr(2, 9);
}; };
// 初始化表单数据修复给验收条目添加唯一ID // 初始化表单数据
const getInitFormData = (): MaterialReceiveForm => { const getInitFormData = (): MaterialReceiveForm => {
return { return {
id: undefined, id: undefined,
@ -323,7 +337,7 @@ const getInitFormData = (): MaterialReceiveForm => {
docCode: undefined, docCode: undefined,
itemList: [ itemList: [
{ {
id: generateItemId(), // 新增唯一ID解决v-for渲染问题 id: generateItemId(),
name: undefined, name: undefined,
specification: undefined, specification: undefined,
unit: undefined, unit: undefined,
@ -357,7 +371,38 @@ const data = reactive({
formCode: [{ required: true, message: '请输入表单编号', trigger: 'blur' }], formCode: [{ required: true, message: '请输入表单编号', trigger: 'blur' }],
docId: [{ required: true, message: '请选择物资采购单', trigger: 'change' }], docId: [{ required: true, message: '请选择物资采购单', trigger: 'change' }],
supplierUnit: [{ required: true, message: '请输入供货单位', trigger: 'blur' }], supplierUnit: [{ required: true, message: '请输入供货单位', trigger: 'blur' }],
orderingUnit: [{ required: true, message: '请输入订货单位', trigger: 'blur' }] orderingUnit: [{ required: true, message: '请输入订货单位', trigger: 'blur' }],
// 数量校验规则确保触发时机包含change且类型为number
quantityRule: [
{ required: true, message: '数量不能为空', trigger: ['blur', 'change'] },
{ type: 'number', min: 0, message: '数量不能小于0', trigger: ['blur', 'change'] }
],
// 验收数量规则(允许≤数量)
acceptedQuantityRule: [
{ required: true, message: '验收数量不能为空', trigger: ['blur', 'change'] },
{ type: 'number', min: 0, message: '验收数量不能小于0', trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
const prop = rule.field;
const index = Number(prop.split('.')[1]);
const quantity = Number(form.value.itemList[index].quantity) || 0;
// 数量未填写时不验证大小关系数量有值但验收数量未填时也不阻断由required规则处理
if (form.value.itemList[index].quantity === undefined || form.value.itemList[index].quantity === null) {
callback();
return;
}
// 处理value为undefined/null的情况避免Number(undefined)转为NaN
const acceptedVal = Number(value) || 0;
if (acceptedVal > quantity) {
callback(new Error('验收数量必须小于等于数量'));
} else {
callback();
}
},
trigger: ['blur', 'change']
}
]
} }
}); });
@ -374,25 +419,29 @@ const getList = async () => {
loading.value = false; loading.value = false;
} }
}; };
// 获取合同列表数据 // 获取合同列表数据
const getContractList = async () => { const getContractList = async () => {
let res = await getContractNameList(currentProject.value?.id); let res = await getContractNameList(currentProject.value?.id);
contractNameList.value = res.rows; contractNameList.value = res.rows;
}; };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
}; };
/** 表单重置(修复:清理验收条目监听) */ /** 表单重置 */
const reset = () => { const reset = () => {
// 停止所有验收条目的watch监听 // 停止所有验收条目的watch监听
itemWatchStopFns.value.forEach((stopFn) => stopFn()); itemWatchStopFns.value.forEach((stopFn) => stopFn());
itemWatchStopFns.value = []; itemWatchStopFns.value = [];
form.value = getInitFormData(); form.value = getInitFormData();
materialReceiveFormRef.value?.resetFields(); if (materialReceiveFormRef.value) {
materialReceiveFormRef.value.resetFields();
}
// 重新监听初始条目 // 重新监听初始条目
if (form.value.itemList.length > 0) { if (form.value.itemList.length > 0) {
@ -408,7 +457,9 @@ const handleQuery = () => {
/** 重置按钮操作 */ /** 重置按钮操作 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); if (queryFormRef.value) {
queryFormRef.value.resetFields();
}
handleQuery(); handleQuery();
}; };
@ -427,24 +478,35 @@ const handleAdd = () => {
form.value.projectName = currentProject.value?.name; form.value.projectName = currentProject.value?.name;
}; };
/** 修改按钮操作(修复:清理旧监听+添加唯一ID */ /** 修改按钮操作(核心修复:赋值后主动触发验证+数据类型转换 */
const handleUpdate = async (row?: MaterialReceiveVO) => { const handleUpdate = async (row?: MaterialReceiveVO) => {
reset(); reset();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
try { try {
const res = await getMaterialReceive(_id); const res = await getMaterialReceive(_id);
// 给验收条目补充唯一ID避免后端返回无ID
const formData = res.data; const formData = res.data;
// 修复1处理itemList数据类型确保数量/验收数量为数字避免字符串与number规则冲突
formData.itemList = formData.itemList.map((item) => ({ formData.itemList = formData.itemList.map((item) => ({
...item, ...item,
id: item.id || generateItemId() id: item.id || generateItemId(),
quantity: item.quantity !== undefined ? Number(item.quantity) : undefined, // 转为数字
acceptedQuantity: item.acceptedQuantity !== undefined ? Number(item.acceptedQuantity) : undefined, // 转为数字
shortageQuantity: item.shortageQuantity !== undefined ? Number(item.shortageQuantity) : undefined // 转为数字
})); }));
Object.assign(form.value, formData); Object.assign(form.value, formData);
// 重新监听所有条目 // 修复2重新监听所有条目,并主动触发每个条目的验证(更新验证状态)
form.value.itemList.forEach((_, index) => { form.value.itemList.forEach((_, index) => {
watchItemChanges(index); watchItemChanges(index);
// 手动触发当前条目的数量、验收数量、缺件数量验证
if (materialReceiveFormRef.value) {
materialReceiveFormRef.value.validateField(`itemList.${index}.quantity`, () => {});
materialReceiveFormRef.value.validateField(`itemList.${index}.acceptedQuantity`, () => {});
materialReceiveFormRef.value.validateField(`itemList.${index}.shortageQuantity`, () => {});
}
}); });
dialog.visible = true; dialog.visible = true;
@ -460,13 +522,24 @@ const submitForm = () => {
if (valid) { if (valid) {
buttonLoading.value = true; buttonLoading.value = true;
try { try {
// 提交前确保数据类型正确(数字)
const submitForm = {
...form.value,
itemList: form.value.itemList.map((item) => ({
...item,
quantity: Number(item.quantity),
acceptedQuantity: Number(item.acceptedQuantity),
shortageQuantity: Number(item.shortageQuantity)
}))
};
if (form.value.id) { if (form.value.id) {
await updateMaterialReceive({ ...form.value }); await updateMaterialReceive(submitForm);
} else { } else {
form.value.itemList.forEach((item) => { submitForm.itemList.forEach((item) => {
delete item.id; delete item.id;
}); });
await addMaterialReceive({ ...form.value }); await addMaterialReceive(submitForm);
} }
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
@ -495,10 +568,10 @@ const handleDelete = async (row?: MaterialReceiveVO) => {
} }
}; };
/** 添加数量验收条目修复添加唯一ID+监听) */ /** 添加数量验收条目 */
const addItem = () => { const addItem = () => {
const newItem = { const newItem = {
id: generateItemId(), // 唯一ID id: generateItemId(),
name: undefined, name: undefined,
specification: undefined, specification: undefined,
unit: undefined, unit: undefined,
@ -508,33 +581,79 @@ const addItem = () => {
remark: undefined remark: undefined
}; };
form.value.itemList.push(newItem); form.value.itemList.push(newItem);
// 监听新条目变化 // 监听新条目变化并触发初始验证
watchItemChanges(form.value.itemList.length - 1); watchItemChanges(form.value.itemList.length - 1);
validateQuantityField(form.value.itemList.length - 1);
}; };
/** 监听验收条目变化,自动计算缺件数量(修复:存储停止函数) */ // 数量输入事件处理
const handleQuantityInput = (index: number) => {
// 确保值为数字类型
if (form.value.itemList[index].quantity !== undefined) {
form.value.itemList[index].quantity = Number(form.value.itemList[index].quantity);
}
// 手动触发验证
validateQuantityField(index);
};
// 数量失焦事件
const handleQuantityBlur = (index: number) => {
validateQuantityField(index);
};
// 验收数量输入事件
const handleAcceptedInput = (index: number) => {
// 确保值为数字类型
if (form.value.itemList[index].acceptedQuantity !== undefined) {
form.value.itemList[index].acceptedQuantity = Number(form.value.itemList[index].acceptedQuantity);
}
// 手动触发相关字段验证
validateQuantityField(index);
};
// 手动验证数量和验收数量字段
const validateQuantityField = (index: number) => {
if (materialReceiveFormRef.value) {
materialReceiveFormRef.value.validateField(`itemList.${index}.quantity`, () => {});
materialReceiveFormRef.value.validateField(`itemList.${index}.acceptedQuantity`, () => {});
materialReceiveFormRef.value.validateField(`itemList.${index}.shortageQuantity`, () => {});
}
};
// 监听条目变化,自动计算缺件数量(修复:计算后触发验证)
const watchItemChanges = (index: number) => { const watchItemChanges = (index: number) => {
// 停止该索引已有的监听(避免重复监听)
if (itemWatchStopFns.value[index]) { if (itemWatchStopFns.value[index]) {
itemWatchStopFns.value[index](); itemWatchStopFns.value[index]();
} }
// 监听数量和验收数量变化
const stopFn = watch( const stopFn = watch(
() => [form.value.itemList[index].quantity, form.value.itemList[index].acceptedQuantity], () => [form.value.itemList[index].quantity, form.value.itemList[index].acceptedQuantity],
([quantity, acceptedQuantity]) => { ([quantity, acceptedQuantity]) => {
const qty = Number(quantity) || 0; const qty = Number(quantity) || 0;
const acceptedQty = Number(acceptedQuantity) || 0; let acceptedQty = Number(acceptedQuantity) || 0;
// 仅当验收数量>数量时才修正(允许等于)
if (acceptedQty > qty && qty > 0) {
acceptedQty = qty; // 修正为数量值(最大合法值)
form.value.itemList[index].acceptedQuantity = acceptedQty;
proxy?.$modal.msgWarning(`验收数量不能大于数量,已自动修正为${acceptedQty}`);
}
// 计算缺件数量允许为0
form.value.itemList[index].shortageQuantity = qty - acceptedQty; form.value.itemList[index].shortageQuantity = qty - acceptedQty;
// 修复3计算后触发当前条目的验证确保缺件数量状态更新
validateQuantityField(index);
}, },
{ immediate: true } // 初始时立即计算 { immediate: true }
); );
// 存储停止函数,用于后续删除时清理
itemWatchStopFns.value[index] = stopFn; itemWatchStopFns.value[index] = stopFn;
}; };
/** 删除数量验收条目(修复:清理监听+删除条目) */ /** 删除数量验收条目 */
const removeItem = (index: number) => { const removeItem = (index: number) => {
if (form.value.itemList.length <= 1) { if (form.value.itemList.length <= 1) {
proxy?.$modal.msgWarning('至少需要保留一条数量验收记录'); proxy?.$modal.msgWarning('至少需要保留一条数量验收记录');
@ -549,6 +668,7 @@ const removeItem = (index: number) => {
// 删除条目和对应的停止函数 // 删除条目和对应的停止函数
form.value.itemList.splice(index, 1); form.value.itemList.splice(index, 1);
itemWatchStopFns.value.splice(index, 1); itemWatchStopFns.value.splice(index, 1);
quantityFormItemRefs.value.splice(index, 1);
}; };
/** 查看详情 */ /** 查看详情 */
@ -564,7 +684,6 @@ const getlistPurchase = async () => {
status: 'finish' status: 'finish'
}); });
purchaseDocList.value = res.rows; purchaseDocList.value = res.rows;
// 构建采购单映射
purchaseDocList.value.forEach((item) => { purchaseDocList.value.forEach((item) => {
purchaseMap.set(item.id, item); purchaseMap.set(item.id, item);
}); });
@ -573,7 +692,7 @@ const getlistPurchase = async () => {
} }
}; };
/** 通过采购单获取需求信息(修复:清理旧监听+添加新监听 */ /** 通过采购单获取需求信息(修复:数据类型转换 */
const getdemandInfo = async (docId: string) => { const getdemandInfo = async (docId: string) => {
if (!docId) return; if (!docId) return;
@ -583,25 +702,25 @@ const getdemandInfo = async (docId: string) => {
// 清空旧监听和条目 // 清空旧监听和条目
itemWatchStopFns.value.forEach((stopFn) => stopFn()); itemWatchStopFns.value.forEach((stopFn) => stopFn());
itemWatchStopFns.value = []; itemWatchStopFns.value = [];
quantityFormItemRefs.value = [];
form.value.itemList = []; form.value.itemList = [];
// 赋值需求数据并添加监听 // 赋值需求数据并添加监听(确保数量为数字)
res.data.forEach((item, index) => { res.data.forEach((item, index) => {
const qty = Number(item.demandQuantity) || 0; const qty = Number(item.demandQuantity) || 0;
const newItem = { const newItem = {
id: generateItemId(), // 唯一ID id: generateItemId(),
name: item.name, name: item.name,
specification: item.specification, specification: item.specification,
unit: item.unit, unit: item.unit,
quantity: qty, quantity: qty, // 确保数字类型
acceptedQuantity: 0, acceptedQuantity: 0, // 初始值为0数字
shortageQuantity: qty, // 初始缺件=数量 shortageQuantity: qty, // 初始缺件=数量(数字)
remark: item.remark, remark: item.remark,
planId: item.id, planId: item.id,
id: null // 保留后端需要的空id字段 id: null
}; };
form.value.itemList.push(newItem); form.value.itemList.push(newItem);
// 监听当前条目
watchItemChanges(index); watchItemChanges(index);
}); });
} }
@ -621,21 +740,25 @@ const handleSelect = (val: string) => {
form.value.materialName = obj.name || ''; form.value.materialName = obj.name || '';
} }
// 获取采购单对应的需求信息
getdemandInfo(val); getdemandInfo(val);
}; };
/** 核心修复2监听材料来源变化,重置数量验收列表 */ /** 监听材料来源变化,重置数量验收列表 */
watch( watch(
() => form.value.materialSource, () => form.value.materialSource,
(newSource, oldSource) => { (newSource, oldSource) => {
if (newSource === oldSource) return; if (newSource === oldSource) return;
// 1. 停止所有验收条目的监听 // 停止所有验收条目的监听
itemWatchStopFns.value.forEach((stopFn) => stopFn()); itemWatchStopFns.value.forEach((stopFn) => stopFn());
itemWatchStopFns.value = []; itemWatchStopFns.value = [];
quantityFormItemRefs.value = [];
// 2. 重置数量验收列表为初始状态1条空记录 // 清空所有文件上传字段的值
form.value.certCountFileId = undefined; // 合格证文件
form.value.reportCountFileId = undefined; // 出厂报告文件
form.value.techDocCountFileId = undefined; // 技术资料文件
form.value.licenseCountFileId = undefined; // 厂家资质文件
// 重置数量验收列表为初始状态
form.value.itemList = [ form.value.itemList = [
{ {
id: generateItemId(), id: generateItemId(),
@ -649,10 +772,11 @@ watch(
} }
]; ];
// 3. 重新监听初始条目 // 重新监听初始条目并触发验证
watchItemChanges(0); watchItemChanges(0);
validateQuantityField(0);
// 4. 切换到乙供时,清空采购单相关数据 // 切换到乙供时,清空采购单相关数据
if (newSource === '2') { if (newSource === '2') {
form.value.docId = undefined; form.value.docId = undefined;
form.value.supplierUnit = undefined; form.value.supplierUnit = undefined;
@ -668,9 +792,10 @@ onMounted(() => {
getContractList(); getContractList();
getList(); getList();
getlistPurchase(); getlistPurchase();
// 监听初始验收条目 // 监听初始验收条目并触发验证
if (form.value.itemList.length > 0) { if (form.value.itemList.length > 0) {
watchItemChanges(0); watchItemChanges(0);
validateQuantityField(0);
} }
}); });
@ -689,7 +814,6 @@ const listeningProject = watch(
/** 页面卸载时清理监听 */ /** 页面卸载时清理监听 */
onUnmounted(() => { onUnmounted(() => {
listeningProject(); listeningProject();
// 清理验收条目监听
itemWatchStopFns.value.forEach((stopFn) => stopFn()); itemWatchStopFns.value.forEach((stopFn) => stopFn());
}); });
</script> </script>

View File

@ -116,6 +116,9 @@
<el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['cailiaoshebei:purchaseDoc:remove']" <el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['cailiaoshebei:purchaseDoc:remove']"
>详情</el-button >详情</el-button
> >
<el-button link type="primary" icon="Download" @click="handleDownload(scope.row)" v-hasPermi="['cailiaoshebei:purchaseDoc:downloadWord']"
>下载</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -130,8 +133,8 @@
></el-col> ></el-col>
<el-col :span="12" :offset="0" <el-col :span="12" :offset="0"
><el-form-item label="供应商" prop="supplier"> ><el-form-item label="供应商" prop="supplier">
<el-select v-model="form.supplier" value-key="id" placeholder="请选择供应商" clearable filterable @change=""> <el-select v-model="form.supplierId" value-key="id" placeholder="请选择供应商" clearable filterable @change="">
<el-option v-for="item in supplierOptions" :key="item.id" :label="item.name" :value="item.name"> </el-option> <el-option v-for="item in supplierOptions" :key="item.id" :label="item.supplierName" :value="item.id"> </el-option>
</el-select> </el-form-item </el-select> </el-form-item
></el-col> ></el-col>
<el-col :span="12" :offset="0" <el-col :span="12" :offset="0"
@ -265,6 +268,7 @@ import { useUserStoreHook } from '@/store/modules/user';
import { getToken } from '@/utils/auth'; import { getToken } from '@/utils/auth';
import logisticsDetail from './comm/logisticsDetail.vue'; import logisticsDetail from './comm/logisticsDetail.vue';
import { FormRules } from 'element-plus'; import { FormRules } from 'element-plus';
import { listSupplierInput } from '@/api/supplierInput/supplierInput';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@ -304,6 +308,8 @@ const initFormData: any = {
docCode: undefined, docCode: undefined,
supplier: undefined, supplier: undefined,
reason: undefined, reason: undefined,
supplierId: undefined,
name: undefined, name: undefined,
arrivalDate: undefined, arrivalDate: undefined,
designDirectorTel: undefined, designDirectorTel: undefined,
@ -379,6 +385,15 @@ const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
}; };
const handleDownload = async (row) => {
proxy?.download(
'/cailiaoshebei/purchaseDoc/export/word',
{
id: row.id
},
`${row.docCode}.doc`
);
};
const handleDetail = async (row?: PurchaseDocVO) => { const handleDetail = async (row?: PurchaseDocVO) => {
proxy?.$modal.loading('加载中'); proxy?.$modal.loading('加载中');
@ -467,6 +482,7 @@ const submitForm = () => {
form.value.associationList = form.value.planId?.map((item: any) => ({ form.value.associationList = form.value.planId?.map((item: any) => ({
planId: item planId: item
})); }));
form.value.supplier = supplierOptions.value.find((item) => item.id == form.value.supplierId)?.supplierName;
if (form.value.id) { if (form.value.id) {
await updatePurchaseDoc(form.value).finally(() => (buttonLoading.value = false)); await updatePurchaseDoc(form.value).finally(() => (buttonLoading.value = false));
@ -502,10 +518,10 @@ const getBatchList = async () => {
}; };
const getSupplierList = async () => { const getSupplierList = async () => {
const res = await listContractor({ const res = await listSupplierInput({
projectId: currentProject.value?.id, projectId: currentProject.value?.id,
pageNum: 1, pageNum: 1,
contractorType: 4, state: 'finish',
pageSize: 10000 pageSize: 10000
}); });
supplierOptions.value = res.rows; supplierOptions.value = res.rows;

View File

@ -35,7 +35,7 @@
</el-table> </el-table>
<el-table v-loading="loading" :data="tableData" v-if="activeTab == '3'"> <el-table v-loading="loading" :data="tableData" v-if="activeTab == '3'">
<el-table-column label="项目" align="center" prop="projectName" /> <el-table-column label="项目" align="center" prop="projectName" />
<el-table-column label="累计完工产值" align="center" prop="totalCompletionOutputValue" /> <!-- <el-table-column label="累计完工产值" align="center" prop="totalCompletionOutputValue" /> -->
<el-table-column label="分包累计结算产值" align="center" prop="subTotalSettlementOutputValue" /> <el-table-column label="分包累计结算产值" align="center" prop="subTotalSettlementOutputValue" />
<el-table-column label="业主累计结算产值" align="center" prop="ownerTotalSettlementOutputValue" /> <el-table-column label="业主累计结算产值" align="center" prop="ownerTotalSettlementOutputValue" />
<el-table-column label="差额" align="center" prop="differenceValue" /> <el-table-column label="差额" align="center" prop="differenceValue" />

View File

@ -23,9 +23,9 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['patch:master:add']">新增</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['patch:master:add']">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <!-- <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['patch:master:export']">导出</el-button> <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['patch:master:export']">导出</el-button>
</el-col> </el-col> -->
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>

View File

@ -64,32 +64,32 @@
</el-table-column> </el-table-column>
<el-table-column label="计量单位" align="center" prop="unit"> <el-table-column label="计量单位" align="center" prop="unit">
<template #default="{ row }"> <template #default="{ row }">
{{ row.parentId == 0 ? '' : row.unit }} {{ row.unitType == 0 ? '' : row.unit }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="综合单价(业主)" align="center" prop="ownerPrice"> <el-table-column label="综合单价(业主)" align="center" prop="ownerPrice">
<template #default="{ row }"> <template #default="{ row }">
{{ row.parentId == 0 ? '' : row.ownerPrice }} {{ row.unitType == 0 ? '' : row.ownerPrice }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="综合单价(分包)" align="center" prop="constructionPrice"> <el-table-column label="综合单价(分包)" align="center" prop="constructionPrice">
<template #default="{ row }"> <template #default="{ row }">
{{ row.parentId == 0 ? '' : row.constructionPrice }} {{ row.unitType == 0 ? '' : row.constructionPrice }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="产值金额(业主)" align="center" prop="ownerOutputValue"> <el-table-column label="产值金额(业主)" align="center" prop="ownerOutputValue">
<template #default="{ row }"> <template #default="{ row }">
{{ row.parentId == 0 ? '' : row.ownerOutputValue }} {{ row.unitType == 0 ? '' : row.ownerOutputValue }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="产值金额(分包)" align="center" prop="constructionOutputValue"> <el-table-column label="产值金额(分包)" align="center" prop="constructionOutputValue">
<template #default="{ row }"> <template #default="{ row }">
{{ row.parentId == 0 ? '' : row.constructionOutputValue }} {{ row.unitType == 0 ? '' : row.constructionOutputValue }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="总数量" align="center" prop="total"> <el-table-column label="总数量" align="center" prop="total">
<template #default="{ row }"> <template #default="{ row }">
{{ row.parentId == 0 ? '' : row.total }} {{ row.unitType == 0 ? '' : row.total }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="关联结构" align="center" prop="relevancyStructure" width="100"> <el-table-column label="关联结构" align="center" prop="relevancyStructure" width="100">
@ -128,7 +128,7 @@
check-strictly check-strictly
/> />
</el-form-item> </el-form-item>
<el-form-item label="计量方式" prop="unitType" v-if="!form.workType"> <el-form-item label="计量方式" prop="unitType" v-if="!form.workType && form.unitType != '0'">
<el-select v-model="form.unitType" placeholder="请选择关联数据"> <el-select v-model="form.unitType" placeholder="请选择关联数据">
<el-option v-for="dict in progress_unit_type" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in progress_unit_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
@ -153,12 +153,12 @@
<el-option v-for="dict in progress_work_type" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in progress_work_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="关联结构" prop="relevancyStructure"> <!-- <el-form-item label="关联结构" prop="relevancyStructure">
<el-select v-model="form.relevancyStructure" value-key="" placeholder="请选择关联结构" clearable filterable @change=""> <el-select v-model="form.relevancyStructure" value-key="" placeholder="请选择关联结构" clearable filterable @change="">
<el-option label="子项目" value="1"></el-option> <el-option label="子项目" value="1"></el-option>
<el-option label="方阵" value="2"></el-option> <el-option label="方阵" value="2"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item> -->
<el-form-item label="备注" prop="remark" v-if="!form.id"> <el-form-item label="备注" prop="remark" v-if="!form.id">
<el-input v-model="form.remark" placeholder="请输入备注" /> <el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item> </el-form-item>
@ -433,12 +433,12 @@ const handleUpdate = async (row: ProgressCategoryVO) => {
}; };
const handleExport = async () => { const handleExport = async () => {
const ids = treeRef.value.getCheckedNodes()[0].pathNodes[0].childrenData.map((item) => item.matrixId); const ids = queryParams.value.projectId;
const fileName = treeRef.value.getCheckedNodes()[0].pathNodes[0].label; const fileName = matrixOptions.value.find((item) => item.projectId == ids)?.name || currentProject.value?.name || '工程项目';
proxy?.download( proxy?.download(
'/progress/progressCategory/export', '/progress/progressCategory/export',
{ {
ids: ids projectId: ids
}, },
`${fileName}分项工程单价导入.xlsx`, `${fileName}分项工程单价导入.xlsx`,
true true

View File

@ -81,6 +81,11 @@
<el-form-item label="公司名称" prop="name"> <el-form-item label="公司名称" prop="name">
<el-input v-model="form.name" placeholder="请输入公司名称" /> <el-input v-model="form.name" placeholder="请输入公司名称" />
</el-form-item> </el-form-item>
<el-form-item label="供应商" prop="supplier">
<el-select v-model="form.supplierId" value-key="id" placeholder="请选择供应商" clearable filterable @change="">
<el-option v-for="item in supplierOptions" :key="item.id" :label="item.supplierName" :value="item.id"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="负责人" prop="principal"> <el-form-item label="负责人" prop="principal">
<el-input v-model="form.principal" placeholder="请输入负责人" /> <el-input v-model="form.principal" placeholder="请输入负责人" />
</el-form-item> </el-form-item>
@ -121,6 +126,7 @@ import { ContractorForm, ContractorQuery, ContractorVO } from '@/api/project/con
import ContractorFileDialog from '@/views/project/contractor/component/ContractorFileDialog.vue'; import ContractorFileDialog from '@/views/project/contractor/component/ContractorFileDialog.vue';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { getDicts, listData } from '@/api/system/dict/data'; import { getDicts, listData } from '@/api/system/dict/data';
import { listSupplierInput } from '@/api/supplierInput/supplierInput';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -152,6 +158,8 @@ const initFormData: ContractorForm = {
principalPhone: undefined, principalPhone: undefined,
custodian: undefined, custodian: undefined,
custodianPhone: undefined, custodianPhone: undefined,
supplierId: undefined,
supplier: undefined,
contractorType: undefined, contractorType: undefined,
fileMap: undefined, fileMap: undefined,
remark: undefined, remark: undefined,
@ -257,6 +265,8 @@ const submitForm = () => {
if (valid) { if (valid) {
form.value.projectId = currentProject.value?.id; form.value.projectId = currentProject.value?.id;
buttonLoading.value = true; buttonLoading.value = true;
form.value.supplier = supplierOptions.value.find((item) => item.id == form.value.supplierId)?.supplierName;
if (form.value.id) { if (form.value.id) {
await updateContractor(form.value).finally(() => (buttonLoading.value = false)); await updateContractor(form.value).finally(() => (buttonLoading.value = false));
} else { } else {
@ -278,15 +288,16 @@ const handleDelete = async (row?: ContractorVO) => {
await getList(); await getList();
}; };
/** 导出按钮操作 */ /** 获取供应商 */
const handleExport = () => { const supplierOptions = ref([]);
proxy?.download( const getSupplierList = async () => {
'project/contractor/export', const res = await listSupplierInput({
{ projectId: currentProject.value?.id,
...queryParams.value pageNum: 1,
}, state: 'finish',
`contractor_${new Date().getTime()}.xlsx` pageSize: 10000
); });
supplierOptions.value = res.rows;
}; };
/** 文件操作 **/ /** 文件操作 **/
@ -304,6 +315,7 @@ const listeningProject = watch(
queryParams.value.projectId = nid; queryParams.value.projectId = nid;
form.value.projectId = nid; form.value.projectId = nid;
console.log('监听项目id', queryParams.value.projectId, form.value.projectId); console.log('监听项目id', queryParams.value.projectId, form.value.projectId);
getSupplierList();
getList(); getList();
} }
); );
@ -314,5 +326,6 @@ onUnmounted(() => {
onMounted(() => { onMounted(() => {
getDictList(); getDictList();
getList(); getList();
getSupplierList();
}); });
</script> </script>

View File

@ -46,16 +46,11 @@
</template> </template>
</el-upload> </el-upload>
</el-col> </el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['land:landBlock:remove']"
>删除</el-button
>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
<el-table draggable v-loading="loading" :data="landBlockList" @selection-change="handleSelectionChange"> <el-table draggable v-loading="loading" :data="landBlockList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="index" label="序号" width="55" align="center" />
<el-table-column label="地块编号" align="center" prop="landCode" /> <el-table-column label="地块编号" align="center" prop="landCode" />
<el-table-column label="地块名称" align="center" prop="landName" /> <el-table-column label="地块名称" align="center" prop="landName" />
<el-table-column label="方阵" align="center" prop="unit" /> <el-table-column label="方阵" align="center" prop="unit" />
@ -80,11 +75,9 @@
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card> </el-card>
<!-- 地块表单弹窗 -->
<el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> <el-dialog draggable :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="landBlockFormRef" :model="form" :rules="rules" label-width="100px"> <el-form ref="landBlockFormRef" :model="form" :rules="rules" label-width="100px">
<!-- <el-form-item label="地块编号" prop="landCode">
<el-input v-model="form.landCode" placeholder="请输入地块编号" />
</el-form-item> -->
<el-form-item label="地块名称" prop="landName"> <el-form-item label="地块名称" prop="landName">
<el-input v-model="form.landName" placeholder="请输入地块名称" /> <el-input v-model="form.landName" placeholder="请输入地块名称" />
</el-form-item> </el-form-item>
@ -111,15 +104,25 @@
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<el-dialog draggable :title="dialogMatrix.title" v-model="dialogMatrix.visible" width="800px" append-to-body> <!-- 关联方阵弹窗核心修改区域 -->
<el-dialog draggable :title="dialogMatrix.title" v-model="dialogMatrix.visible" width="900px" append-to-body>
<el-button type="primary" plain icon="Plus" @click="addUnitBoItem" style="margin-bottom: 15px">添加</el-button> <el-button type="primary" plain icon="Plus" @click="addUnitBoItem" style="margin-bottom: 15px">添加</el-button>
<el-form ref="landBlockFormMatrixRef" :model="formM" :rules="rules" label-width="100px"> <!-- 方阵表单绑定unitBoList的索引实现动态校验 -->
<el-row v-for="(item, i) of unitBoList" :key="i"> <el-form ref="landBlockFormMatrixRef" :model="formM" label-width="100px">
<el-col :span="9"> <el-row v-for="(item, i) of unitBoList" :key="i" class="mb-4">
<el-form-item label="方阵" prop="matrixId"> <!-- 方阵选择必填校验 -->
<el-col :span="8">
<el-form-item
label="方阵"
:prop="`unitBoList[${i}].unitProjectId`"
:rules="[
{ required: true, message: '请选择方阵', trigger: 'change' },
{ type: 'array', min: 2, message: '请选择完整的方阵层级', trigger: 'change' }
]"
>
<el-cascader <el-cascader
:options="fangzhenList" :options="fangzhenList"
placeholder="请选择" placeholder="请选择方阵"
filterable filterable
:props="{ value: 'matrixId', label: 'name' }" :props="{ value: 'matrixId', label: 'name' }"
v-model="item.unitProjectId" v-model="item.unitProjectId"
@ -127,18 +130,28 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="7"> <!-- 所属工区必填校验 -->
<el-form-item label="所属工区" prop="unitProjectArea"> <el-col :span="8">
<el-form-item
label="所属工区"
:prop="`unitBoList[${i}].unitProjectArea`"
:rules="{ required: true, message: '请输入所属工区', trigger: 'blur' }"
>
<el-input v-model="item.unitProjectArea" placeholder="请输入所属工区" /> <el-input v-model="item.unitProjectArea" placeholder="请输入所属工区" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 方阵状态必填校验 -->
<el-col :span="6"> <el-col :span="6">
<el-form-item label="方阵状态" prop="unitProjectStatus"> <el-form-item
label="方阵状态"
:prop="`unitBoList[${i}].unitProjectStatus`"
:rules="{ required: true, message: '请输入方阵状态', trigger: 'blur' }"
>
<el-input v-model="item.unitProjectStatus" placeholder="请输入方阵状态" /> <el-input v-model="item.unitProjectStatus" placeholder="请输入方阵状态" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="1" style="margin-left: 15px"> <el-col :span="1" style="margin-left: 15px; display: flex; align-items: flex-end">
<el-button type="danger" icon="Delete" @click="removeUnitBoItem(i)"></el-button> <el-button style="margin-bottom: 18px" type="danger" icon="Delete" @click="removeUnitBoItem(i)"></el-button>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
@ -153,44 +166,65 @@
</template> </template>
<script setup name="LandBlock" lang="ts"> <script setup name="LandBlock" lang="ts">
import { listLandBlock, getLandBlock, delLandBlock, LandUnit, addLandBlock, updateLandBlock, subMatrix,importLandBlock } from '@/api/system/landTransfer/landBlock'; import {
listLandBlock,
getLandBlock,
delLandBlock,
LandUnit,
addLandBlock,
updateLandBlock,
subMatrix,
importLandBlock
} from '@/api/system/landTransfer/landBlock';
import { LandBlockVO, LandBlockQuery, LandBlockForm } from '@/api/system/landTransfer/landBlock/types'; import { LandBlockVO, LandBlockQuery, LandBlockForm } from '@/api/system/landTransfer/landBlock/types';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { getCurrentInstance, ComponentInternalInstance, onMounted, onUnmounted, watch } from 'vue';
import { ElFormInstance, ElMessage } from 'element-plus';
// 类型定义补充避免any
interface DialogOption {
visible: boolean;
title: string;
}
interface UnitBoItem {
unitProjectArea: string;
unitProjectStatus: string;
unitProjectId: (string | number)[]; // 级联选择值(数组)
}
// 基础实例与Store
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject); const currentProject = computed(() => userStore.selectedProject);
// 响应式数据
const landBlockList = ref<LandBlockVO[]>([]); const landBlockList = ref<LandBlockVO[]>([]);
const fangzhenList = ref([]); //方阵列表数据 const fangzhenList = ref<any[]>([]); // 方阵列表(实际项目建议定义具体类型)
const buttonLoading = ref(false); const buttonLoading = ref(false);
const loading = ref(true); const loading = ref(true);
const showSearch = ref(true); const showSearch = ref(true);
const unitBoList = ref([ const uploadRef = ref<any>(null); // upload组件ref
{
unitProjectArea: '1', // 方阵表单数据(核心修改:初始值为空,避免默认填充无效数据)
unitProjectStatus: '1', const unitBoList = ref<UnitBoItem[]>([{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }]);
unitProjectId: []
} // 表格选择相关
]);
const ids = ref<Array<string | number>>([]); const ids = ref<Array<string | number>>([]);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
const total = ref(0); const total = ref(0);
// 表单Ref
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const landBlockFormRef = ref<ElFormInstance>(); const landBlockFormRef = ref<ElFormInstance>();
const landBlockFormMatrixRef = ref<ElFormInstance>(); const landBlockFormMatrixRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({ // 弹窗配置
visible: false, const dialog = reactive<DialogOption>({ visible: false, title: '' });
title: '' const dialogMatrix = reactive<DialogOption>({ visible: false, title: '选择方阵' });
});
const dialogMatrix = reactive<DialogOption>({
visible: false,
title: '选择方阵'
});
// 初始表单数据
const initFormData: LandBlockForm = { const initFormData: LandBlockForm = {
id: undefined, id: undefined,
projectId: currentProject.value?.id, projectId: currentProject.value?.id,
@ -202,11 +236,11 @@ const initFormData: LandBlockForm = {
farmerCount: undefined, farmerCount: undefined,
remark: undefined remark: undefined
}; };
// 核心数据(含表单规则)
const data = reactive({ const data = reactive({
form: { ...initFormData }, form: { ...initFormData },
formM: { formM: { landId: undefined }, // 方阵关联表单仅存地块ID
landId: undefined
},
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@ -219,6 +253,7 @@ const data = reactive({
farmerCount: undefined, farmerCount: undefined,
params: {} params: {}
}, },
// 地块表单规则(原有规则保留)
rules: { rules: {
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }], id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }], projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
@ -229,216 +264,285 @@ const data = reactive({
const { queryParams, form, rules, formM } = toRefs(data); const { queryParams, form, rules, formM } = toRefs(data);
/** 查询地块信息列表 */ /** 查询地块列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
try {
const res = await listLandBlock(queryParams.value); const res = await listLandBlock(queryParams.value);
landBlockList.value = res.rows; landBlockList.value = res.rows;
total.value = res.total; total.value = res.total;
} catch (err) {
proxy?.$modal.msgError('获取地块列表失败');
} finally {
loading.value = false; loading.value = false;
}
}; };
/** 取消按钮 */ /** 地块表单取消 */
const cancel = () => { const cancel = () => {
reset(); reset();
dialog.visible = false; dialog.visible = false;
}; };
/** 表单重置 */ /** 地块表单重置 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
landBlockFormRef.value?.resetFields(); landBlockFormRef.value?.resetFields();
}; };
/** 搜索按钮操作 */ /** 搜索提交 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
getList(); getList();
}; };
/** 重置按钮操作 */ /** 搜索重置 */
const resetQuery = () => { const resetQuery = () => {
queryFormRef.value?.resetFields(); queryFormRef.value?.resetFields();
handleQuery(); handleQuery();
}; };
/** 多选框选中数据 */ /** 表格选择变化 */
const handleSelectionChange = (selection: LandBlockVO[]) => { const handleSelectionChange = (selection: LandBlockVO[]) => {
ids.value = selection.map((item) => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length != 1; single.value = selection.length !== 1;
multiple.value = !selection.length; multiple.value = selection.length === 0;
}; };
/** 新增按钮操作 */ /** 新增地块 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
dialog.visible = true; dialog.visible = true;
dialog.title = '添加地块信息'; dialog.title = '添加地块信息';
}; };
/** 修改按钮操作 */ /** 编辑地块 */
const handleUpdate = async (row?: LandBlockVO) => { const handleUpdate = async (row?: LandBlockVO) => {
reset(); reset();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
if (!_id) return proxy?.$modal.msgWarning('请选择要编辑的地块');
try {
const res = await getLandBlock(_id); const res = await getLandBlock(_id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
dialog.visible = true; dialog.visible = true;
dialog.title = '修改地块信息'; dialog.title = '修改地块信息';
} catch (err) {
proxy?.$modal.msgError('获取地块详情失败');
}
}; };
/** 提交按钮 */ /** 提交地块表单 */
const submitForm = () => { const submitForm = () => {
landBlockFormRef.value?.validate(async (valid: boolean) => { landBlockFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (!valid) return;
buttonLoading.value = true; buttonLoading.value = true;
try {
if (form.value.id) { if (form.value.id) {
await updateLandBlock(form.value).finally(() => (buttonLoading.value = false)); await updateLandBlock(form.value);
} else { } else {
await addLandBlock(form.value).finally(() => (buttonLoading.value = false)); await addLandBlock(form.value);
} }
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
} catch (err) {
proxy?.$modal.msgError(form.value.id ? '修改失败' : '新增失败');
} finally {
buttonLoading.value = false;
} }
}); });
}; };
/** 删除按钮操作 */ /** 删除地块 */
const handleDelete = async (row?: LandBlockVO) => { const handleDelete = async (row?: LandBlockVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除地块信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); if (!_ids.length) return proxy?.$modal.msgWarning('请选择要删除地块');
try {
await proxy?.$modal.confirm(`是否确认删除地块信息编号为"${_ids}"的数据项?`);
await delLandBlock(_ids); await delLandBlock(_ids);
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
await getList(); await getList();
} catch (err) {
// 取消确认时不提示错误
if (err !== 'cancel') proxy?.$modal.msgError('删除失败');
}
}; };
// 获取方阵列表 /** 获取方阵列表 */
const getfangzhenList = async () => { const getfangzhenList = async () => {
if (!currentProject.value?.id) return;
loading.value = true; loading.value = true;
const res = await subMatrix(currentProject.value?.id); try {
res.data.forEach((item) => { const res = await subMatrix(currentProject.value.id);
item.children.forEach((item2) => { // 处理方阵数据(级联选择需父子结构,此处保留原有逻辑)
item2.matrixId = item2.name + '_' + item2.matrixId; res.data.forEach((item: any) => {
item.children?.forEach((item2: any) => {
item2.matrixId = `${item2.name}_${item2.matrixId}`; // 拼接名称+ID便于后续拆分
}); });
}); });
fangzhenList.value = res.data; fangzhenList.value = res.data;
} catch (err) {
proxy?.$modal.msgError('获取方阵列表失败');
} finally {
loading.value = false;
}
}; };
const handleView = async (row) => { /** 关联方阵(核心修改:打开弹窗前强制重置表单) */
// 关联方阵 const handleView = async (row: LandBlockVO) => {
if (!row?.id) return proxy?.$modal.msgWarning('请选择有效的地块');
// 1. 重置方阵表单(清空历史数据)
resetMatrix();
// 2. 绑定当前地块ID
formM.value.landId = row.id;
// 3. 打开弹窗
dialogMatrix.visible = true; dialogMatrix.visible = true;
dialogMatrix.title = '关联方阵'; dialogMatrix.title = `关联方阵(地块:${row.landName || row.landCode}`;
data.formM.landId = row.id;
}; };
// 动态添加unitBoList项
/** 新增方阵表单项 */
const addUnitBoItem = () => { const addUnitBoItem = () => {
unitBoList.value.push({ unitBoList.value.push({
unitProjectArea: '', unitProjectArea: '',
unitProjectStatus: '', unitProjectStatus: '',
unitProjectId: [] unitProjectId: [] // 级联选择初始为空数组
}); });
}; };
// 移除unitBoList项 /** 删除方阵表单项 */
const removeUnitBoItem = (index: number) => { const removeUnitBoItem = (index: number) => {
if (unitBoList.value.length > 1) { if (unitBoList.value.length <= 1) {
unitBoList.value.splice(index, 1); return proxy?.$modal.msgWarning('至少保留一项方阵配置');
} else {
proxy?.$modal.msgWarning('至少保留一项');
} }
unitBoList.value.splice(index, 1);
// 重置表单校验状态(避免删除后残留校验提示)
landBlockFormMatrixRef.value?.clearValidate();
}; };
/** 提交方阵关联表单 */
const submitFormMatrix = () => { const submitFormMatrix = () => {
landBlockFormMatrixRef.value?.validate(async (valid: boolean) => { landBlockFormMatrixRef.value?.validate(async (valid: boolean) => {
if (valid) { if (!valid) return;
var arr = []; if (!formM.value.landId) return proxy?.$modal.msgWarning('地块ID异常请重新选择地块');
unitBoList.value.forEach((item) => {
let str = item.unitProjectId[1].split('_'); try {
arr.push({ // 处理方阵数据(拆分名称+ID
unitProjectArea: item.unitProjectArea, const unitBoListParams = unitBoList.value.map((item) => {
unitProjectStatus: item.unitProjectStatus, const [unitProjectName, unitProjectId] = item.unitProjectId[1]?.split('_') || [];
unitProjectId: str[1], if (!unitProjectId) throw new Error('方阵ID解析失败请重新选择方阵');
unitProjectName: str[0]
return {
unitProjectArea: item.unitProjectArea.trim(),
unitProjectStatus: item.unitProjectStatus.trim(),
unitProjectId: unitProjectId, // 纯ID后端需要
unitProjectName: unitProjectName // 名称(可选,用于显示)
};
}); });
// 调用关联接口
const res = await LandUnit({
landId: formM.value.landId,
unitBoList: unitBoListParams
}); });
var res = await LandUnit({ ...formM.value, unitBoList: arr });
if (res.code == 200) { if (res.code === 200) {
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('关联方阵成功');
dialogMatrix.visible = false; dialogMatrix.visible = false;
await getList(); await getList(); // 刷新地块列表
} else { } else {
proxy?.$modal.msgError(res.msg); proxy?.$modal.msgError(res.msg || '关联失败');
} }
} catch (err: any) {
proxy?.$modal.msgError(err.msg || '关联过程异常');
} }
}); });
}; };
/** 取消按钮 */
/** 方阵表单取消 */
const cancelMatrix = () => { const cancelMatrix = () => {
resetMatrix(); resetMatrix();
dialogMatrix.visible = false; dialogMatrix.visible = false;
}; };
/** 表单重置 */
/** 方阵表单重置(核心修改:清空所有数据+重置校验) */
const resetMatrix = () => { const resetMatrix = () => {
data.formM.landId = ''; // 1. 清空地块ID
unitBoList.value = [ formM.value.landId = undefined;
{ // 2. 重置方阵列表(仅保留一个空项)
unitProjectArea: '1', unitBoList.value = [{ unitProjectArea: '', unitProjectStatus: '', unitProjectId: [] }];
unitProjectStatus: '1', // 3. 重置表单校验状态
unitProjectId: [] if (landBlockFormMatrixRef.value) {
landBlockFormMatrixRef.value.resetFields();
landBlockFormMatrixRef.value.clearValidate();
} }
];
landBlockFormMatrixRef.value?.resetFields();
}; };
//监听项目id刷新数据
/** 监听项目变化,刷新数据 */
const listeningProject = watch( const listeningProject = watch(
() => currentProject.value?.id, () => currentProject.value?.id,
(nid, oid) => { (newId) => {
queryParams.value.projectId = nid; if (newId) {
getfangzhenList(); queryParams.value.projectId = newId;
getList(); getfangzhenList(); // 刷新方阵列表
getList(); // 刷新地块列表
} }
},
{ immediate: true } // 初始加载时执行一次
); );
// 导入文件 /** 导入Excel */
const handleImport = (options:any) => { const handleImport = (options: { file: File }) => {
if (!currentProject.value?.id) return proxy?.$modal.msgWarning('请先选择项目');
if (!options.file) return proxy?.$modal.msgWarning('请选择Excel文件');
loading.value = true; loading.value = true;
let formData = new FormData(); const formData = new FormData();
formData.append('file', options.file); formData.append('file', options.file);
importLandBlock(currentProject.value?.id,formData).then((res) => {
if (res.code == 200) { importLandBlock(currentProject.value.id, formData)
proxy.$modal.msgSuccess(res.msg || '导入成功'); .then((res) => {
if (res.code === 200) {
proxy?.$modal.msgSuccess(res.msg || '导入成功');
getList(); getList();
getfangzhenList(); getfangzhenList();
} else {
proxy?.$modal.msgError(res.msg || '导入失败');
} }
}).catch((err) => { })
proxy.$modal.msgError(err.msg || '导入失败'); .catch((err) => {
}).finally(() => { proxy?.$modal.msgError(err.msg || '导入接口异常');
})
.finally(() => {
loading.value = false; loading.value = false;
}); });
}; };
// 下载模板 /** 下载导入模板 */
const downloadTemplate = () => { const downloadTemplate = () => {
// 导出模版文件
try { try {
// 创建a标签
const link = document.createElement('a'); const link = document.createElement('a');
// 设置PDF文件路径 - 相对于public目录 link.href = '/landBlock.xlsx'; // 模板路径(需确保public目录下存在该文件)
link.href = '/landBlock.xlsx'; link.download = '地块信息导入模板.xlsx'; // 下载后文件名
// 设置下载后的文件名
link.download = '地块信息导入模板.xlsx';
// 触发点击
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click(); // 触发下载
// 清理 } catch (err) {
document.body.removeChild(link); proxy?.$modal.msgError('模板下载失败,请重试');
} catch (error) { } finally {
alert('下载失败,请重试'); document.body.removeChild(link); // 清理DOM
} }
}; };
/** 生命周期:组件卸载时清理监听 */
onUnmounted(() => { onUnmounted(() => {
listeningProject(); listeningProject();
}); });
/** 生命周期:组件挂载时初始化数据 */
onMounted(() => { onMounted(() => {
getList(); getList();
getfangzhenList(); getfangzhenList();

View File

@ -1,8 +1,37 @@
<template> <template>
<div class="p-2"> <div class="p-2 detailbox">
<div class="box1">
<div>
<div>
<span>设计面积</span>
<span>{{ detailInfo.designArea }} </span>
</div>
<el-icon :size="50" color="#3176ff">
<Postcard />
</el-icon>
</div>
<div>
<div>
<span>已流转面积</span>
<span>{{ detailInfo.transferAea }} </span>
</div>
<el-icon :size="50" color="#3176ff">
<Postcard />
</el-icon>
</div>
<div>
<div>
<span>租金</span>
<span>{{ detailInfo.landRent / 1000 }} 万元</span>
</div>
<el-icon :size="50" color="#3176ff">
<Postcard />
</el-icon>
</div>
</div>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="never">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="110px"> <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="110px">
<el-form-item label="对应地块" prop="landBlockId"> <el-form-item label="对应地块" prop="landBlockId">
<el-select v-model="queryParams.landBlockId" clearable placeholder="请选择对应地块"> <el-select v-model="queryParams.landBlockId" clearable placeholder="请选择对应地块">
@ -12,8 +41,9 @@
<el-form-item label="流转台账状态" prop="transferStatus"> <el-form-item label="流转台账状态" prop="transferStatus">
<el-select v-model="queryParams.transferStatus" placeholder="请选择流转台账状态" clearable> <el-select v-model="queryParams.transferStatus" placeholder="请选择流转台账状态" clearable>
<el-option label="待流转" :value="0"></el-option> <el-option label="待流转" :value="0"></el-option>
<el-option label="已流转" :value="1"></el-option> </el-select <el-option label="已流转" :value="1"></el-option>
></el-form-item> </el-select>
</el-form-item>
<el-form-item label="责任人" prop="responsiblePerson"> <el-form-item label="责任人" prop="responsiblePerson">
<el-input v-model="queryParams.responsiblePerson" placeholder="请输入责任人" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.responsiblePerson" placeholder="请输入责任人" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
@ -31,15 +61,7 @@
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['land:landTransferLedger:add']">新增</el-button> <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['land:landTransferLedger:add']">新增</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="6"></el-col>
<el-tag size="large" type="primary">设计面积{{ detailInfo.designArea }}</el-tag>
</el-col>
<el-col :span="1.5">
<el-tag size="large" type="success">已流转面积{{ detailInfo.transferAea }}</el-tag>
</el-col>
<el-col :span="1.5">
<el-tag size="large" type="warning">租金{{ detailInfo.landRent / 1000 }}万元</el-tag>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
</template> </template>
@ -81,90 +103,107 @@
<el-form-item label="对应地块" prop="landBlockId"> <el-form-item label="对应地块" prop="landBlockId">
<el-select v-model="form.landBlockId" clearable placeholder="请选择对应地块" @change="handleLandBlockChange"> <el-select v-model="form.landBlockId" clearable placeholder="请选择对应地块" @change="handleLandBlockChange">
<el-option v-for="item in landBlockList" :key="item.id" :label="item.landName" :value="item.id" /> <el-option v-for="item in landBlockList" :key="item.id" :label="item.landName" :value="item.id" />
</el-select> </el-form-item </el-select>
></el-col> </el-form-item>
</el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="进场道路" prop="enterRoadId"> <el-form-item label="进场道路" prop="enterRoadId">
<el-select v-model="form.enterRoadId" clearable placeholder="请选择对应地块"> <el-select v-model="form.enterRoadId" clearable placeholder="请选择对应地块">
<el-option v-for="item in enterRoadList" :key="item.id" :label="item.roadName" :value="item.id" /> <el-option v-for="item in enterRoadList" :key="item.id" :label="item.roadName" :value="item.id" />
</el-select> </el-form-item </el-select>
></el-col> </el-form-item>
</el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="土地类型" prop="landType"> <el-form-item label="土地类型" prop="landType">
<el-select v-model="form.landType" placeholder="请选择土地类型" clearable> <el-select v-model="form.landType" placeholder="请选择土地类型" clearable>
<el-option v-for="dict in land_type" :key="dict.value" :label="dict.label" :value="dict.value" /> </el-select></el-form-item <el-option v-for="dict in land_type" :key="dict.value" :label="dict.label" :value="dict.value" />
></el-col> </el-select>
</el-form-item>
</el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="流转台账状态" prop="transferStatus"> <el-form-item label="流转台账状态" prop="transferStatus">
<el-select v-model="form.transferStatus" :disabled="!form.id" placeholder="请选择流转台账状态" clearable> <el-select v-model="form.transferStatus" :disabled="!form.id" placeholder="请选择流转台账状态" clearable @change="calcTransferRatio">
<el-option <el-option v-for="dict in land_transfer_status" :key="dict.value" :label="dict.label" :value="dict.value" />
v-for="dict in land_transfer_status" </el-select>
:key="dict.value" </el-form-item>
:label="dict.label" </el-col>
:value="dict.value"
/> </el-select></el-form-item
></el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="设计面积(亩)" prop="designArea"> <el-form-item label="设计面积(亩)" prop="designArea">
<el-input type="number" v-model="form.designArea" placeholder="请输入设计面积" /> </el-form-item <el-input type="number" v-model="form.designArea" placeholder="请输入设计面积" @input="calcTransferRatio" />
></el-col> </el-form-item>
<el-col :span="12" </el-col>
><el-form-item label="责任人" prop="responsiblePerson"> <el-col :span="12">
<el-input v-model="form.responsiblePerson" placeholder="请输入责任人" /> </el-form-item <el-form-item label="责任人" prop="responsiblePerson">
></el-col> <el-input v-model="form.responsiblePerson" placeholder="请输入责任人" />
<el-col v-if="form.transferStatus != '2'" :span="12" </el-form-item>
><el-form-item label="预计完成日期" prop="expectedFinishDate"> </el-col>
<el-col v-if="form.transferStatus != '2'" :span="12">
<el-form-item label="预计完成日期" prop="expectedFinishDate">
<el-date-picker clearable v-model="form.expectedFinishDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择预计完成时间"> <el-date-picker clearable v-model="form.expectedFinishDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择预计完成时间">
</el-date-picker> </el-form-item </el-date-picker>
></el-col> </el-form-item>
<el-col v-if="form.transferStatus == '1'" :span="12" </el-col>
><el-form-item label="已流转面积(亩)" prop="transferAea"> <el-col v-if="form.transferStatus == '1'" :span="12">
<el-input v-model="form.transferAea" type="number" placeholder="请输入已流转面积" /> </el-form-item <el-form-item label="已流转面积(亩)" prop="transferAea">
></el-col> <el-input v-model="form.transferAea" type="number" placeholder="请输入已流转面积" @input="calcTransferRatio" />
<el-col v-if="form.transferStatus == '1'" :span="12" </el-form-item>
><el-form-item label="流转比例(%)" prop="transferRatio"> </el-col>
<el-input v-model="form.transferRatio" type="number" placeholder="请输入流转比例" /> </el-form-item <!-- 流转比例改为禁用输入自动计算展示 -->
></el-col> <el-col v-if="form.transferStatus == '1'" :span="12">
<el-form-item label="流转比例(%)" prop="transferRatio">
<el-input v-model="form.transferRatio" type="text" :disabled="true" placeholder="自动计算中..." />
</el-form-item>
</el-col>
<el-col v-if="form.transferStatus == '1'" :span="12"> <el-col v-if="form.transferStatus == '1'" :span="12">
<el-form-item label="土地租金(元)" prop="landRent"> <el-form-item label="土地租金(元)" prop="landRent">
<el-input type="number" v-model="form.landRent" placeholder="请输入土地租金" /> </el-form-item <el-input type="number" v-model="form.landRent" placeholder="请输入土地租金" />
></el-col> </el-form-item>
</el-col>
<el-col v-if="form.transferStatus == '1'" :span="12"> <el-col v-if="form.transferStatus == '1'" :span="12">
<el-form-item label="青苗赔偿(元)" prop="seedlingCompensation"> <el-form-item label="青苗赔偿(元)" prop="seedlingCompensation">
<el-input v-model="form.seedlingCompensation" type="number" placeholder="请输入青苗赔偿" /> </el-form-item <el-input v-model="form.seedlingCompensation" type="number" placeholder="请输入青苗赔偿" />
></el-col> </el-form-item>
</el-col>
<el-col v-if="form.transferStatus == '1'" :span="12"> <el-col v-if="form.transferStatus == '1'" :span="12">
<el-form-item label="总金额(元)" prop="totalAmount"> <el-form-item label="总金额(元)" prop="totalAmount">
<el-input type="number" v-model="form.totalAmount" placeholder="请输入总金额" /> </el-form-item <el-input type="number" v-model="form.totalAmount" placeholder="请输入总金额" />
></el-col> </el-form-item>
<el-col v-if="form.transferStatus == '2'" :span="12" </el-col>
><el-form-item label="不签合同面积(亩)" prop="noContractArea"> <el-col v-if="form.transferStatus == '2'" :span="12">
<el-input v-model="form.noContractArea" type="number" placeholder="请输入不签合同面积" /> </el-form-item <el-form-item label="不签合同面积(亩)" prop="noContractArea">
></el-col> <el-input v-model="form.noContractArea" type="number" placeholder="请输入不签合同面积" />
</el-form-item>
</el-col>
<el-col v-if="form.transferStatus == '2'" :span="12"> <el-col v-if="form.transferStatus == '2'" :span="12">
<el-form-item label="不测量面积(亩)" prop="noSurveyArea"> <el-form-item label="不测量面积(亩)" prop="noSurveyArea">
<el-input v-model="form.noSurveyArea" type="number" placeholder="请输入不测量面积" /> </el-form-item <el-input v-model="form.noSurveyArea" type="number" placeholder="请输入不测量面积" />
></el-col> </el-form-item>
<el-col v-if="form.transferStatus == '2'" :span="24" </el-col>
><el-form-item label="不签合同原因" prop="noContractReason"> <el-col v-if="form.transferStatus == '2'" :span="24">
<el-input v-model="form.noContractReason" type="textarea" placeholder="请输入内容" /> </el-form-item <el-form-item label="不签合同原因" prop="noContractReason">
></el-col> <el-input v-model="form.noContractReason" type="textarea" placeholder="请输入内容" />
<el-col v-if="form.transferStatus == '2'" :span="24" </el-form-item>
><el-form-item label="不流转原因" prop="nonTransferReason"> </el-col>
<el-input v-model="form.nonTransferReason" type="textarea" placeholder="请输入内容" /> </el-form-item <el-col v-if="form.transferStatus == '2'" :span="24">
></el-col> <el-form-item label="不流转原因" prop="nonTransferReason">
<el-col v-if="form.transferStatus == '1'" :span="24" <el-input v-model="form.nonTransferReason" type="textarea" placeholder="请输入内容" />
><el-form-item label="状态说明" prop="statusDescription"> </el-form-item>
<el-input v-model="form.statusDescription" type="textarea" placeholder="请输入内容" /> </el-form-item </el-col>
></el-col> <el-col v-if="form.transferStatus == '1'" :span="24">
<el-col v-if="form.transferStatus == '1'" :span="24" <el-form-item label="状态说明" prop="statusDescription">
><el-form-item label="问题总结" prop="issueSummary"> <el-input v-model="form.statusDescription" type="textarea" placeholder="请输入内容" />
<el-input v-model="form.issueSummary" type="textarea" placeholder="请输入内容" /> </el-form-item </el-form-item>
></el-col> </el-col>
<el-col v-if="form.transferStatus == '1'" :span="24">
<el-form-item label="问题总结" prop="issueSummary">
<el-input v-model="form.issueSummary" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-col>
<el-col v-if="form.transferStatus == '1'" :span="24"> <el-col v-if="form.transferStatus == '1'" :span="24">
<el-form-item label="下一步策略" prop="nextStrategy"> <el-form-item label="下一步策略" prop="nextStrategy">
<el-input v-model="form.nextStrategy" type="textarea" placeholder="请输入内容" /> </el-form-item <el-input v-model="form.nextStrategy" type="textarea" placeholder="请输入内容" />
></el-col> </el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
<template #footer> <template #footer>
@ -190,6 +229,20 @@ import { listEnterRoad } from '@/api/system/landTransfer/enterRoad';
import { LandTransferLedgerVO, LandTransferLedgerQuery, LandTransferLedgerForm } from '@/api/system/landTransfer/landTransferLedger/types'; import { LandTransferLedgerVO, LandTransferLedgerQuery, LandTransferLedgerForm } from '@/api/system/landTransfer/landTransferLedger/types';
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { listLandBlock } from '@/api/system/landTransfer/landBlock'; import { listLandBlock } from '@/api/system/landTransfer/landBlock';
import { getCurrentInstance, ComponentInternalInstance, watch, onUnmounted, onMounted } from 'vue';
import { ElFormInstance } from 'element-plus';
// 类型定义补充
interface DialogOption {
visible: boolean;
title: string;
}
interface PageData<T, Q> {
form: T;
queryParams: Q;
rules: Record<string, any[]>;
}
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store // 获取用户 store
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
@ -207,6 +260,7 @@ const landBlockList = ref([]);
const queryFormRef = ref<ElFormInstance>(); const queryFormRef = ref<ElFormInstance>();
const landTransferLedgerFormRef = ref<ElFormInstance>(); const landTransferLedgerFormRef = ref<ElFormInstance>();
const enterRoadList = ref([]); const enterRoadList = ref([]);
// 字典数据
const { land_type, land_transfer_status } = toRefs<any>(proxy?.useDict('land_type', 'land_transfer_status')); const { land_type, land_transfer_status } = toRefs<any>(proxy?.useDict('land_type', 'land_transfer_status'));
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
@ -214,7 +268,8 @@ const dialog = reactive<DialogOption>({
title: '' title: ''
}); });
const initFormData = { // 表单初始数据
const initFormData: LandTransferLedgerForm = {
id: undefined, id: undefined,
projectId: currentProject.value?.id, projectId: currentProject.value?.id,
landType: undefined, landType: undefined,
@ -237,6 +292,8 @@ const initFormData = {
noSurveyArea: undefined, noSurveyArea: undefined,
nonTransferReason: undefined nonTransferReason: undefined
}; };
// 核心数据响应式对象
const data = reactive<PageData<LandTransferLedgerForm, LandTransferLedgerQuery>>({ const data = reactive<PageData<LandTransferLedgerForm, LandTransferLedgerQuery>>({
form: { ...initFormData }, form: { ...initFormData },
queryParams: { queryParams: {
@ -264,9 +321,40 @@ const data = reactive<PageData<LandTransferLedgerForm, LandTransferLedgerQuery>>
rules: { rules: {
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }], id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }], projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }],
landType: [{ required: true, message: '土地类型不能为空', trigger: 'change' }] landType: [{ required: true, message: '土地类型不能为空', trigger: 'change' }],
transferRatio: [
// 动态校验:仅已流转状态下必填
{
required: true,
message: '流转比例不能为空',
trigger: ['blur', 'change'],
validator: (rule, value, callback) => {
if (data.form.transferStatus !== '1') {
callback(); // 非已流转状态跳过校验
return;
}
if (value === undefined || value === null || value === '') {
callback(new Error('流转比例不能为空'));
} else {
callback();
}
}
},
// 比例范围校验0-100
{
validator: (rule, value, callback) => {
if (value < 0 || value > 100) {
callback(new Error('流转比例必须在 0-100 之间'));
} else {
callback();
}
},
trigger: 'blur'
}
]
} }
}); });
const detailInfo = ref({ const detailInfo = ref({
transferAea: 0, transferAea: 0,
transferRatio: 0, transferRatio: 0,
@ -274,18 +362,53 @@ const detailInfo = ref({
}); });
const { queryParams, form, rules } = toRefs(data); const { queryParams, form, rules } = toRefs(data);
/**
* 自动计算流转比例:(已流转面积 / 设计面积) × 100保留2位小数最大100%
*/
const calcTransferRatio = () => {
// 仅已流转状态下计算
if (form.value.transferStatus !== '1') {
form.value.transferRatio = undefined;
return;
}
// 转换为数字(避免字符串计算)
const designArea = Number(form.value.designArea) || 0;
const transferAea = Number(form.value.transferAea) || 0;
// 边界处理设计面积为0时避免报错
if (designArea === 0) {
form.value.transferRatio = 0;
return;
}
// 核心计算限制最大100%保留2位小数
const ratio = Math.min((transferAea / designArea) * 100, 100);
form.value.transferRatio = Number(ratio.toFixed(2));
};
/** 查询项目土地流转台账列表 */ /** 查询项目土地流转台账列表 */
const getList = async () => { const getList = async () => {
loading.value = true; loading.value = true;
try {
const res = await listLandTransferLedger(queryParams.value); const res = await listLandTransferLedger(queryParams.value);
landTransferLedgerList.value = res.rows; landTransferLedgerList.value = res.rows;
total.value = res.total; total.value = res.total;
} finally {
loading.value = false; loading.value = false;
}
}; };
/** 获取地块统计信息 */
const getLandBlockList = async () => { const getLandBlockList = async () => {
let res = await landTransferLedgerCount(currentProject.value?.id); try {
const res = await landTransferLedgerCount(currentProject.value?.id);
detailInfo.value = res.data; detailInfo.value = res.data;
} catch (error) {
console.error('获取地块统计信息失败:', error);
}
}; };
/** 取消按钮 */ /** 取消按钮 */
const cancel = () => { const cancel = () => {
dialog.visible = false; dialog.visible = false;
@ -313,92 +436,176 @@ const resetQuery = () => {
/** 多选框选中数据 */ /** 多选框选中数据 */
const handleSelectionChange = (selection: LandTransferLedgerVO[]) => { const handleSelectionChange = (selection: LandTransferLedgerVO[]) => {
ids.value = selection.map((item) => item.id); ids.value = selection.map((item) => item.id);
single.value = selection.length != 1; single.value = selection.length !== 1;
multiple.value = !selection.length; multiple.value = selection.length === 0;
}; };
/** 新增按钮操作 */ /** 新增按钮操作 */
const handleAdd = () => { const handleAdd = () => {
reset(); reset();
form.value.transferStatus = '0'; form.value.transferStatus = '0'; // 默认待流转
dialog.visible = true;
enterRoadList.value = []; enterRoadList.value = [];
dialog.title = '添加项目土地流转台账'; dialog.title = '添加项目土地流转台账';
dialog.visible = true;
}; };
/** 修改按钮操作 */ /** 修改按钮操作 */
const handleUpdate = async (row?: LandTransferLedgerVO) => { const handleUpdate = async (row?: LandTransferLedgerVO) => {
reset(); reset();
form.value.landBlockId = row?.landBlockId;
getListRoad();
const _id = row?.id || ids.value[0]; const _id = row?.id || ids.value[0];
if (!_id) return;
try {
// 获取编辑数据
const res = await getLandTransferLedger(_id); const res = await getLandTransferLedger(_id);
Object.assign(form.value, res.data); Object.assign(form.value, res.data);
console.log(form.value); // 回显地块对应的道路列表
form.value.landBlockId = row?.landBlockId;
dialog.visible = true; await getListRoad();
// 初始化计算流转比例
calcTransferRatio();
// 打开弹窗
dialog.title = '修改项目土地流转台账'; dialog.title = '修改项目土地流转台账';
dialog.visible = true;
} catch (error) {
console.error('获取编辑数据失败:', error);
proxy?.$modal.msgError('加载数据失败,请重试');
}
}; };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
landTransferLedgerFormRef.value?.validate(async (valid: boolean) => { landTransferLedgerFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (!valid) return;
buttonLoading.value = true; buttonLoading.value = true;
try {
if (form.value.id) { if (form.value.id) {
await updateLandTransferLedger(form.value).finally(() => (buttonLoading.value = false)); await updateLandTransferLedger(form.value);
} else { } else {
await addLandTransferLedger(form.value).finally(() => (buttonLoading.value = false)); await addLandTransferLedger(form.value);
} }
proxy?.$modal.msgSuccess('操作成功'); proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false; dialog.visible = false;
await getList(); await getList();
await getLandBlockList(); // 刷新统计信息
} catch (error) {
proxy?.$modal.msgError('操作失败,请重试');
console.error('提交表单失败:', error);
} finally {
buttonLoading.value = false;
} }
}); });
}; };
/** 删除按钮操作 */ /** 删除按钮操作 */
const handleDelete = async (row?: LandTransferLedgerVO) => { const handleDelete = async (row?: LandTransferLedgerVO) => {
const _ids = row?.id || ids.value; const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除项目土地流转台账编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); if (!_ids.length) return;
try {
await proxy?.$modal.confirm(`是否确认删除项目土地流转台账编号为"${_ids}"的数据项?`);
await delLandTransferLedger(_ids); await delLandTransferLedger(_ids);
proxy?.$modal.msgSuccess('删除成功'); proxy?.$modal.msgSuccess('删除成功');
await getList(); await getList();
await getLandBlockList(); // 刷新统计信息
} catch (error) {
console.error('删除数据失败:', error);
if (error !== 'cancel') {
// 排除用户取消确认的情况
proxy?.$modal.msgError('删除失败,请重试');
}
} finally {
loading.value = false;
}
}; };
// 选择地块
const handleLandBlockChange = (val) => { /** 选择地块后加载对应道路 */
getListRoad(); const handleLandBlockChange = async () => {
await getListRoad();
}; };
/** 查询地块信息列表 */
/** 查询地块列表 */
const getListLand = async () => { const getListLand = async () => {
try {
const res = await listLandBlock({ const res = await listLandBlock({
pageNum: 1, pageNum: 1,
pageSize: 10000, pageSize: 10000,
projectId: currentProject.value?.id projectId: currentProject.value?.id
}); });
landBlockList.value = res.rows; landBlockList.value = res.rows;
} catch (error) {
console.error('获取地块列表失败:', error);
}
}; };
/** 查询进场道路信息列表 */
/** 查询进场道路列表(按地块筛选) */
const getListRoad = async () => { const getListRoad = async () => {
const res = await listEnterRoad({ pageNum: 1, pageSize: 10000, projectId: currentProject.value?.id, landBlockId: form.value.landBlockId }); try {
const res = await listEnterRoad({
pageNum: 1,
pageSize: 10000,
projectId: currentProject.value?.id,
landBlockId: form.value.landBlockId
});
enterRoadList.value = res.rows; enterRoadList.value = res.rows;
} catch (error) {
console.error('获取进场道路列表失败:', error);
}
}; };
//监听项目id刷新数据
/** 监听项目切换,刷新数据 */
const listeningProject = watch( const listeningProject = watch(
() => currentProject.value?.id, () => currentProject.value?.id,
(nid, oid) => { async (newId) => {
queryParams.value.projectId = nid; if (newId) {
getLandBlockList(); queryParams.value.projectId = newId;
getListLand(); await Promise.all([getLandBlockList(), getListLand(), getList()]);
getList();
} }
},
{ immediate: true } // 初始加载时触发
); );
/** 组件卸载时清理监听 */
onUnmounted(() => { onUnmounted(() => {
listeningProject(); listeningProject();
}); });
/** 组件挂载时初始化数据 */
onMounted(() => { onMounted(() => {
getLandBlockList(); Promise.all([getLandBlockList(), getListLand(), getList()]);
getList();
getListLand();
}); });
</script> </script>
<style lang="scss">
.detailbox {
width: 100%;
.box1 {
width: 100%;
display: flex;
justify-content: center;
margin: 20px 0;
> div {
width: 300px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 15px;
border: 1px solid #e7e7e7;
margin: 0 20px;
border-radius: 6px;
> div {
display: flex;
flex-direction: column;
> span:first-child {
font-size: 16px;
margin-bottom: 10px;
}
> span:last-child {
font-size: 24px;
/* font-weight: bold; */
}
}
}
}
}
</style>

View File

@ -1,7 +1,6 @@
<template> <template>
<div class="p-2"> <div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
:leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
@ -9,13 +8,11 @@
<el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" /> <el-input v-model="queryParams.deptName" placeholder="请输入部门名称" clearable @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="类别编码" prop="deptCategory"> <el-form-item label="类别编码" prop="deptCategory">
<el-input v-model="queryParams.deptCategory" placeholder="请输入类别编码" clearable style="width: 240px" <el-input v-model="queryParams.deptCategory" placeholder="请输入类别编码" clearable style="width: 240px" @keyup.enter="handleQuery" />
@keyup.enter="handleQuery" />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="部门状态" clearable> <el-select v-model="queryParams.status" placeholder="部门状态" clearable>
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
:value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -31,8 +28,7 @@
<template #header> <template #header>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="1.5"> <el-col :span="1.5">
<el-button v-hasPermi="['system:dept:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 <el-button v-hasPermi="['system:dept:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 </el-button>
</el-button>
</el-col> </el-col>
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button> <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
@ -41,8 +37,14 @@
</el-row> </el-row>
</template> </template>
<el-table ref="deptTableRef" v-loading="loading" :data="deptList" row-key="deptId" <el-table
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :default-expand-all="isExpandAll"> ref="deptTableRef"
v-loading="loading"
:data="deptList"
row-key="deptId"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:default-expand-all="isExpandAll"
>
<el-table-column prop="deptName" label="部门名称" width="260"></el-table-column> <el-table-column prop="deptName" label="部门名称" width="260"></el-table-column>
<el-table-column prop="deptCategory" align="center" label="类别编码" width="200"></el-table-column> <el-table-column prop="deptCategory" align="center" label="类别编码" width="200"></el-table-column>
<el-table-column prop="deptType" align="center" label="部门类型" width="200"> <el-table-column prop="deptType" align="center" label="部门类型" width="200">
@ -64,16 +66,13 @@
<el-table-column fixed="right" align="center" label="操作"> <el-table-column fixed="right" align="center" label="操作">
<template #default="scope"> <template #default="scope">
<el-tooltip content="修改" placement="top"> <el-tooltip content="修改" placement="top">
<el-button v-hasPermi="['system:dept:edit']" link type="primary" icon="Edit" <el-button v-hasPermi="['system:dept:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
@click="handleUpdate(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="新增" placement="top" v-if="scope.row.deptType != '2' && scope.row.deptType != '5'"> <el-tooltip content="新增" placement="top">
<el-button v-hasPermi="['system:dept:add']" link type="primary" icon="Plus" <el-button v-hasPermi="['system:dept:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
@click="handleAdd(scope.row)" />
</el-tooltip> </el-tooltip>
<el-tooltip content="删除" placement="top"> <el-tooltip content="删除" placement="top">
<el-button v-hasPermi="['system:dept:remove']" link type="primary" icon="Delete" <el-button v-hasPermi="['system:dept:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
@click="handleDelete(scope.row)" />
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
@ -85,9 +84,14 @@
<el-row> <el-row>
<el-col v-if="form.parentId !== 0" :span="24"> <el-col v-if="form.parentId !== 0" :span="24">
<el-form-item label="上级部门" prop="parentId"> <el-form-item label="上级部门" prop="parentId">
<el-tree-select v-model="form.parentId" :data="deptOptions" <el-tree-select
:props="{ value: 'deptId', label: 'deptName', children: 'children' }" value-key="deptId" v-model="form.parentId"
placeholder="选择上级部门" check-strictly /> :data="deptOptions"
:props="{ value: 'deptId', label: 'deptName', children: 'children' }"
value-key="deptId"
placeholder="选择上级部门"
check-strictly
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -108,8 +112,7 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="负责人" prop="leader"> <el-form-item label="负责人" prop="leader">
<el-select v-model="form.leader" placeholder="请选择负责人"> <el-select v-model="form.leader" placeholder="请选择负责人">
<el-option v-for="item in deptUserList" :key="item.userId" :label="item.userName" <el-option v-for="item in deptUserList" :key="item.userId" :label="item.userName" :value="item.userId" />
:value="item.userId" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -126,32 +129,17 @@
<el-col :span="12"> <el-col :span="12">
<el-form-item label="部门状态"> <el-form-item label="部门状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="部门类型"> <el-form-item label="部门类型">
<el-select v-model="form.deptType" placeholder="请选择部门类型" @change="changeProject"> <el-select v-model="form.deptType" placeholder="请选择部门类型">
<el-option v-for="dict in sys_dept_type" :key="dict.value" :label="dict.label" :value="dict.value" /> <el-option v-for="dict in sys_dept_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" v-if="form.deptType == '4'">
<el-form-item label="所属项目" prop="projectId">
<el-select v-model="form.projectId" placeholder="请选择所属项目">
<el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.deptType == '5'">
<el-form-item label="分包单位" prop="contractorId">
<el-select v-model="form.contractorId" placeholder="请选择分包单位">
<el-option v-for="item in contractorList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
<template #footer> <template #footer>
@ -336,16 +324,6 @@ const handleUpdate = async (row: DeptVO) => {
dialog.title = '修改部门'; dialog.title = '修改部门';
}; };
const changeProject = async (val: any) => {
if (val == '4' && (!projectList.value || !projectList.value.length)) {
const res = await getDeptList();
projectList.value = res.data;
} else if (val == '5' && (!contractorList.value || !contractorList.value.length)) {
const res = await optionProjectSelect(form.value.rowProjectId);
contractorList.value = res;
}
};
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
deptFormRef.value?.validate(async (valid: boolean) => { deptFormRef.value?.validate(async (valid: boolean) => {

View File

@ -174,8 +174,6 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="手机号码" prop="phonenumber"> <el-form-item label="手机号码" prop="phonenumber">
<el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" /> <el-input v-model="form.phonenumber" placeholder="请输入手机号码" maxlength="11" />
@ -186,8 +184,6 @@
<el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" /> <el-input v-model="form.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName"> <el-form-item v-if="form.userId == undefined" label="用户名称" prop="userName">
<el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" /> <el-input v-model="form.userName" placeholder="请输入用户名称" maxlength="30" />
@ -198,8 +194,6 @@
<el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password /> <el-input v-model="form.password" placeholder="请输入用户密码" type="password" maxlength="20" show-password />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row>
<el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="用户性别"> <el-form-item label="用户性别">
<el-select v-model="form.sex" placeholder="请选择"> <el-select v-model="form.sex" placeholder="请选择">
@ -207,15 +201,6 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="岗位"> <el-form-item label="岗位">
<el-select v-model="form.postIds" multiple placeholder="请选择"> <el-select v-model="form.postIds" multiple placeholder="请选择">
@ -229,9 +214,18 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="24">
<el-form-item label="角色" prop="roleIds"> <el-row :gutter="20" v-for="(item, index) in form.projectRoles">
<el-select v-model="form.roleIds" filterable multiple placeholder="请选择"> <el-col :span="11" :offset="0">
<el-form-item label="项目列表">
<el-select v-model="item.projectId" placeholder="请选择">
<el-option v-for="dict in projectOptions" :key="dict.id" :label="dict.shortName" :value="dict.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="11" :offset="0">
<el-form-item label="角色">
<el-select v-model="item.roleIds" filterable multiple placeholder="请选择">
<el-option <el-option
v-for="item in roleOptions" v-for="item in roleOptions"
:key="item.roleId" :key="item.roleId"
@ -242,8 +236,19 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="1" :offset="0">
<el-button type="primary" circle icon="Plus" @click="handleAddProject" v-if="index == 0"></el-button>
<el-button type="danger" circle icon="Delete" @click="delProject(index)" v-else></el-button>
</el-col>
</el-row> </el-row>
<el-row> </el-col>
<el-col :span="12">
<el-form-item label="状态">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="备注"> <el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input> <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
@ -317,8 +322,9 @@ import { RoleVO } from '@/api/system/role/types';
import { PostVO } from '@/api/system/post/types'; import { PostVO } from '@/api/system/post/types';
import { globalHeaders } from '@/utils/request'; import { globalHeaders } from '@/utils/request';
import { to } from 'await-to-js'; import { to } from 'await-to-js';
import { getRoleList, optionselect } from '@/api/system/post'; import { getProjectByDeptId, getRoleList, optionselect } from '@/api/system/post';
import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue'; import ShuttleFrame from '../../project/projectRelevancy/component/ShuttleFrame.vue';
import { listProject } from '@/api/project/project';
const router = useRouter(); const router = useRouter();
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -337,6 +343,8 @@ const enabledDeptOptions = ref<DeptTreeVO[]>([]);
const initPassword = ref<string>(''); const initPassword = ref<string>('');
const postOptions = ref<PostVO[]>([]); const postOptions = ref<PostVO[]>([]);
const roleOptions = ref<RoleVO[]>([]); const roleOptions = ref<RoleVO[]>([]);
const projectOptions = ref<any[]>([]);
/*** 用户导入参数 */ /*** 用户导入参数 */
const upload = reactive<ImportOption>({ const upload = reactive<ImportOption>({
// 是否显示弹出层(用户导入) // 是否显示弹出层(用户导入)
@ -383,10 +391,15 @@ const initFormData: UserForm = {
phonenumber: undefined, phonenumber: undefined,
email: undefined, email: undefined,
sex: undefined, sex: undefined,
projectRoles: [
{
projectId: '',
roleIds: []
}
],
status: '0', status: '0',
remark: '', remark: '',
postIds: [], postIds: [],
roleIds: [],
filePath: undefined filePath: undefined
}; };
@ -436,8 +449,7 @@ const initData: PageData<UserForm, UserQuery> = {
message: '请输入正确的手机号码', message: '请输入正确的手机号码',
trigger: 'blur' trigger: 'blur'
} }
], ]
roleIds: [{ required: true, message: '用户角色不能为空', trigger: 'blur' }]
} }
}; };
const data = reactive<PageData<UserForm, UserQuery>>(initData); const data = reactive<PageData<UserForm, UserQuery>>(initData);
@ -473,6 +485,8 @@ const getDeptTree = async () => {
const res = await api.deptTreeSelect({ isShow: '1' }); const res = await api.deptTreeSelect({ isShow: '1' });
deptOptions.value = res.data; deptOptions.value = res.data;
enabledDeptOptions.value = filterDisabledDept(res.data); enabledDeptOptions.value = filterDisabledDept(res.data);
const projectList = await listProject();
projectOptions.value = projectList.rows;
}; };
/** 过滤禁用的部门 */ /** 过滤禁用的部门 */
@ -494,6 +508,19 @@ const handleNodeClick = (data: DeptVO) => {
handleQuery(); handleQuery();
}; };
/** 部门选择变化 */
const handleAddProject = () => {
form.value.projectRoles.push({
projectId: '',
roleIds: []
});
};
/** 删除项目 */
const delProject = (index: number) => {
form.value.projectRoles.splice(index, 1);
};
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
@ -609,6 +636,13 @@ function submitFileForm() {
/** 重置操作表单 */ /** 重置操作表单 */
const reset = () => { const reset = () => {
form.value = { ...initFormData }; form.value = { ...initFormData };
form.value.projectRoles = [
{
projectId: '',
roleIds: []
}
];
userFormRef.value?.resetFields(); userFormRef.value?.resetFields();
}; };
/** 取消按钮 */ /** 取消按钮 */
@ -638,12 +672,32 @@ const handleUpdate = async (row?: UserForm) => {
postOptions.value = data.posts; postOptions.value = data.posts;
roleOptions.value = data.roles; roleOptions.value = data.roles;
form.value.postIds = data.postIds; form.value.postIds = data.postIds;
form.value.roleIds = data.user.roleIds; form.value.projectRoles = data.projectRoles;
form.value.password = ''; form.value.password = '';
const roleList = await getRoleList(form.value.deptId);
roleOptions.value = roleList.data;
};
const validate = () => {
for (let i = 0; i < form.value.projectRoles.length; i++) {
const item = form.value.projectRoles[i];
if (!item.projectId || item.projectId.length === 0) {
proxy?.$modal.msgError(`${i + 1} 行“项目列表”未填写`);
return false; // 阻止提交
}
if (!item.roleIds || item.roleIds.length === 0) {
proxy?.$modal.msgError(`${i + 1} 行“角色”未填写`);
return false; // 阻止提交
}
}
return true;
}; };
/** 提交按钮 */ /** 提交按钮 */
const submitForm = () => { const submitForm = () => {
const isValid = validate();
if (!isValid) return;
userFormRef.value?.validate(async (valid: boolean) => { userFormRef.value?.validate(async (valid: boolean) => {
if (valid) { if (valid) {
form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value); form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
@ -683,10 +737,16 @@ onMounted(() => {
async function handleDeptChange(value: number | string) { async function handleDeptChange(value: number | string) {
const response = await optionselect(value); const response = await optionselect(value);
const roleList = await getRoleList(value); const roleList = await getRoleList(value);
roleOptions.value = roleList.data; roleOptions.value = roleList.data;
postOptions.value = response.data; postOptions.value = response.data;
form.value.postIds = []; form.value.postIds = [];
form.value.roleIds = []; form.value.projectRoles = [
{
projectId: [],
roleIds: []
}
];
} }
const shuttleVisible = ref(false); const shuttleVisible = ref(false);

View File

@ -15,12 +15,14 @@
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" /> <el-option v-for="item in sheets" :key="item" :label="item" :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="toggleExpandAll(true)">一键展开</el-button> <el-button type="primary" @click="toggleExpandAll(true)">一键展开</el-button>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="toggleExpandAll(false)">一键收起</el-button> <el-button type="primary" @click="toggleExpandAll(false)">一键收起</el-button>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
@ -42,11 +44,16 @@
type="warning" type="warning"
icon="view" icon="view"
@click="handleAudit()" @click="handleAudit()"
v-if="versionsData.status == 'draft'" v-if="versionsData.status && versionsData.status == 'draft'"
v-hasPermi="['tender:tenderPlanLimitList:getVersionDetail']" v-hasPermi="['tender:tenderPlanLimitList:getVersionDetail']"
>审核</el-button >审核</el-button
> >
<el-button type="warning" icon="view" @click="handleAudit()" v-else v-hasPermi="['tender:tenderPlanLimitList:getVersionDetail']" <el-button
type="warning"
icon="view"
@click="handleAudit()"
v-if="versionsData.status && versionsData.status != 'draft'"
v-hasPermi="['tender:tenderPlanLimitList:getVersionDetail']"
>查看流程</el-button >查看流程</el-button
> >
</el-form-item> </el-form-item>
@ -58,12 +65,21 @@
<el-table-column prop="num" label="编号" /> <el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" /> <el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" /> <el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" /> <el-table-column prop="quantity" label="数量">
<template #default="scope">
{{ scope.row.children.length > 0 ? '' : scope.row.quantity }}
</template>
</el-table-column>
<el-table-column prop="remark" label="单价" align="center"> <el-table-column prop="remark" label="单价" align="center">
<template #default="scope"> <template #default="scope">
<el-input-number <el-input-number
:model-value="scope.row.unitPrice" :model-value="scope.row.unitPrice"
@change="(val) => (scope.row.unitPrice = val)" @change="
(val) => {
scope.row.unitPrice = val;
changePrice(scope.row);
}
"
:precision="2" :precision="2"
:step="0.1" :step="0.1"
:controls="false" :controls="false"
@ -82,7 +98,7 @@
<el-button <el-button
type="primary" type="primary"
size="small" size="small"
@click="handleSave(scope.row)" @click="handleSave(scope.row, 'all')"
v-if="scope.row.quantity && scope.row.quantity != 0" v-if="scope.row.quantity && scope.row.quantity != 0"
v-hasPermi="['tender:tenderPlanLimitList:edit']" v-hasPermi="['tender:tenderPlanLimitList:edit']"
:disabled="versionsData.status != 'draft'" :disabled="versionsData.status != 'draft'"
@ -133,6 +149,8 @@ const versionMap = new Map();
// 切换tab // 切换tab
const handleTabChange = (tab: string) => { const handleTabChange = (tab: string) => {
activeTab.value = tab; activeTab.value = tab;
tableData.value = [];
versionsData.value = {};
getVersionNums(); getVersionNums();
}; };
//切换版本 //切换版本
@ -205,6 +223,8 @@ const getSheetName = async () => {
//获取表格数据 //获取表格数据
const getTableData = async () => { const getTableData = async () => {
try { try {
loading.value = true;
const params = { const params = {
projectId: currentProject.value?.id, projectId: currentProject.value?.id,
versions: queryForm.value.versions, versions: queryForm.value.versions,
@ -217,6 +237,8 @@ const getTableData = async () => {
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} finally {
loading.value = false;
} }
}; };
//导入 //导入
@ -257,18 +279,21 @@ const handleExport = () => {
`招标一览表${queryForm.value.sheet}.xlsx` `招标一览表${queryForm.value.sheet}.xlsx`
); );
}; };
//确认修改 const modifyPrice = new Map();
const handleSave = (row: any) => {
const changePrice = (row: any) => {
modifyPrice.set(row.id, row);
// if (!row.unitPrice) {
// modifyPrice.delete(row.id);
// }
};
//修改单价
const handleSave = (row?: any, type?: any) => {
try { try {
if (!row.unitPrice) { if (type == 'single') {
ElMessage({
message: '请输入单价',
type: 'warning'
});
return;
}
loading.value = true; loading.value = true;
updatePrice(row).then((res) => { const list = [{ ...row, type: activeTab.value }];
updatePrice(list).then((res) => {
if (res.code == 200) { if (res.code == 200) {
ElMessage({ ElMessage({
message: '修改成功', message: '修改成功',
@ -277,9 +302,28 @@ const handleSave = (row: any) => {
getTableData(); getTableData();
} }
}); });
}
if (type == 'all') {
loading.value = true;
const list = [];
modifyPrice.forEach((item) => {
list.push({ ...item, type: activeTab.value });
});
updatePrice(list).then((res) => {
if (res.code == 200) {
ElMessage({
message: '修改成功',
type: 'success'
});
getTableData();
}
});
}
} catch (error) { } catch (error) {
console.log(error); ElMessage({
loading.value = false; message: '修改失败',
type: 'error'
});
} finally { } finally {
loading.value = false; loading.value = false;
} }

View File

@ -1,245 +1,78 @@
<template> <template>
<div class="p-2"> <el-dialog v-model="dialogVisible" title="招标文件" width="500" draggable>
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> <el-form ref="ruleFormRef" style="max-width: 600px" :model="ruleForm" :rules="rules" label-width="auto">
<el-card shadow="always"> <el-form-item label="招标文件" prop="name">
<el-form :model="queryForm" :inline="true"> <file-upload
<el-form-item label="表名" prop="sheet"> v-model="form.costEstimationFile"
<el-select v-model="queryForm.sheet" placeholder="选择表名" @change="changeSheet"> :fileSize="100"
<el-option v-for="item in sheets" :key="item" :label="item" :value="item" /> :auto-upload="false"
</el-select> uploadUrl="/tender/biddingPlan/uploadBiddingDocuments"
</el-form-item> method="put"
<el-form-item> ref="fileUploadRef"
<el-button type="primary" @click="toggleExpandAll">{{ isExpandAll ? '一键收起' : '一键展开' }}</el-button> :data="{
</el-form-item> projectId: currentProject?.id,
<el-form-item> type: planType,
<el-upload fileType: '1',
ref="uploadRef" bidStatus: '0',
class="upload-demo" id: row.id
:http-request="importExcel" }"
:show-file-list="false" showFileList
v-hasPermi="['bidding:biddingLimitList:importExcelFile']" />
>
<template #trigger>
<el-button type="primary">导入excel</el-button>
</template>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleExport()" v-hasPermi="['bidding:biddingLimitList:export']">导出excel</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> <template #footer>
</transition> <div class="dialog-footer">
<el-card shadow="never" class="mb8"> <el-button @click="closeDialog()"> 取消 </el-button>
<el-table ref="tableRef" v-loading="loading" :data="tableData" row-key="id" border lazy default-expand-all> <el-button type="primary" @click="submitForm()">确定</el-button>
<el-table-column prop="num" label="编号" />
<el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" />
<el-table-column prop="quantity" label="数量" />
<el-table-column prop="remark" label="单价" align="center">
<template #default="scope">
<el-input-number
:model-value="scope.row.unitPrice"
@change="(val) => (scope.row.unitPrice = val)"
:precision="2"
:step="0.1"
:controls="false"
v-if="scope.row.quantity && scope.row.quantity != 0"
/>
</template>
</el-table-column>
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{ scope.row.price }}
</template>
</el-table-column>
<el-table-column prop="price" label="操作" align="center">
<template #default="scope">
<el-button
type="primary"
size="small"
@click="handleSave(scope.row)"
v-if="scope.row.quantity && scope.row.quantity != 0"
v-hasPermi="['bidding:biddingLimitList:edit']"
>修改</el-button
>
</template>
</el-table-column>
</el-table>
</el-card>
</div> </div>
</template>
</el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useUserStoreHook } from '@/store/modules/user'; import { useUserStoreHook } from '@/store/modules/user';
import { obtainAllVersionNumbers } from '@/api/contract/index';
import { BiddingImportExcelFile, getTreeLimit, biddingLimitListUpdate, sheetList } from '@/api/bidding/biddingLimit';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject); const currentProject = computed(() => userStore.selectedProject);
const queryForm = ref({ const dialogVisible = ref(false);
versions: '', const row = ref<any>();
sheet: '' const planType = ref<any>('');
const fileUploadRef = ref<any>();
const ruleForm = ref<any>();
const rules = ref({
costEstimationFile: [{ required: true, message: '请上传招标文件', trigger: ['blur'] }]
}); });
const loading = ref(false); const emit = defineEmits(['success']);
const options = ref<any[]>([]); const form = ref({
const sheets = ref<any[]>([]); costEstimationFile: ''
const tableData = ref<any[]>([]);
const isExpandAll = ref(true);
// 接受父组件传递的参数
const props = defineProps({
type: {
type: String,
default: ''
}
}); });
//获取版本号 const open = (rows: any, type: string) => {
const getVersionNums = async () => { dialogVisible.value = true;
try { console.log(rows, type);
const params = { row.value = rows;
projectId: currentProject.value?.id, planType.value = type;
pageSize: 1000,
pageNum: 1
};
const res = await obtainAllVersionNumbers(params);
if (res.code == 200) {
options.value = res.data;
if (res.data.length > 0) {
queryForm.value.versions = res.data[0];
getSheetName();
} else {
queryForm.value.versions = '';
}
}
} catch (error) {
console.log(error);
}
}; };
//选择版本号 const closeDialog = () => {
const changeVersions = () => { dialogVisible.value = false;
getSheetName(); form.value.costEstimationFile = '';
emit('success');
}; };
const submitForm = () => {
//选择表名 fileUploadRef.value.submitUpload().then((res) => {
const changeSheet = () => { if (res == 'noFile') {
getTableData();
};
//获取表名
const getSheetName = async () => {
try {
const params = {
projectId: currentProject.value?.id
// versions: queryForm.value.versions
};
const res = await sheetList(params);
if (res.code == 200) {
sheets.value = res.data;
if (res.data.length > 0) {
queryForm.value.sheet = res.data[0];
} else {
queryForm.value.sheet = '';
}
getTableData();
}
} catch (error) {}
};
//获取表格
const getTableData = async () => {
loading.value = true;
const params = {
projectId: currentProject.value?.id,
sheet: queryForm.value.sheet,
type: props.type
};
const res = await getTreeLimit(params);
loading.value = false;
if (res.code == 200) {
tableData.value = [res.data[0]];
}
console.log(loading.value);
};
//修改单价
const handleSave = (row: any) => {
try {
if (!row.unitPrice) {
ElMessage({ ElMessage({
message: '请输入单价', message: '请上传招标文件',
type: 'warning' type: 'warning'
}); });
return; return;
} }
loading.value = true; dialogVisible.value = false;
biddingLimitListUpdate(row).then((res) => { emit('success');
if (res.code == 200) {
ElMessage({
message: '修改成功',
type: 'success'
});
getTableData();
}
});
} catch (error) {
ElMessage({
message: '修改失败',
type: 'error'
});
}
};
const tableRef = ref<any>();
const toggleExpandAll = () => {
isExpandAll.value = !isExpandAll.value;
console.log(isExpandAll.value);
tableData.value.forEach((row) => {
tableRef.value.toggleRowExpansion(row, isExpandAll.value);
}); });
}; };
//导入 defineExpose({
const importExcel = (options: any): any => { open
let formData = new FormData();
formData.append('file', options.file);
loading.value = true;
BiddingImportExcelFile({ projectId: currentProject.value?.id }, formData)
.then((res) => {
const { code } = res;
if (code == 200) {
proxy.$modal.msgSuccess(res.msg || '导入成功');
getTableData();
}
})
.catch((err) => {})
.finally(() => {
loading.value = false;
});
};
//监听项目id刷新数据
const listeningProject = watch(
() => currentProject.value?.id,
(nid, oid) => {
getVersionNums();
}
);
const handleExport = () => {
proxy?.download(
'/bidding/biddingLimitList/export',
{
projectId: currentProject.value?.id,
sheet: queryForm.value.sheet
},
`限价一览表${queryForm.value.sheet}.xlsx`
);
};
onUnmounted(() => {
listeningProject();
});
onMounted(() => {
getSheetName();
}); });
</script> </script>
<style scoped></style> <style scoped lang="scss"></style>

View File

@ -0,0 +1,99 @@
<template>
<el-dialog v-model="dialogVisible" title="中标文件" width="500" draggable>
<el-form ref="ruleFormRef" style="max-width: 600px" :model="form" :rules="rules" label-width="auto">
<el-form-item label="中标单位" prop="winningBidder">
<el-select v-model="form.winningBidder" filterable placeholder="请选择单位" style="width: 240px">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="招标文件" prop="name">
<file-upload
v-model="form.costEstimationFile"
:fileSize="100"
:auto-upload="false"
uploadUrl="/tender/biddingPlan/uploadBiddingDocuments"
method="put"
ref="fileUploadRef"
:data="{
projectId: currentProject?.id,
type: planType,
fileType: '0',
bidStatus: '0',
id: row.id,
winningBidderId: form.winningBidder
}"
showFileList
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog()"> 取消 </el-button>
<el-button type="primary" @click="submitForm()">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { getUnitList } from '@/api/tender/index';
import { useUserStoreHook } from '@/store/modules/user';
const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject);
const dialogVisible = ref(false);
const row = ref<any>();
const planType = ref<any>('');
const fileUploadRef = ref<any>();
const ruleForm = ref<any>();
const options = ref<any>([]);
const rules = ref({
costEstimationFile: [{ required: true, message: '请上传招标文件', trigger: ['blur'] }]
});
const emit = defineEmits(['success']);
const form = ref({
costEstimationFile: '',
winningBidder: ''
});
const open = (rows: any, type: string) => {
dialogVisible.value = true;
console.log(rows, type);
row.value = rows;
planType.value = type;
getUnitListData();
};
const getUnitListData = async () => {
let res = await getUnitList({
projectId: currentProject.value?.id
});
if (res.code == 200) {
options.value = res.data.map((item: any) => {
return {
label: item.supplierName,
value: item.id
};
});
}
console.log(res);
};
const closeDialog = () => {
dialogVisible.value = false;
form.value.winningBidder = '';
emit('success');
};
const submitForm = () => {
fileUploadRef.value.submitUpload().then((res) => {
if (res == 'noFile') {
ElMessage({
message: '请上传招标文件',
type: 'warning'
});
return;
}
closeDialog();
emit('success');
});
};
defineExpose({
open
});
</script>
<style scoped lang="scss"></style>

View File

@ -189,7 +189,8 @@
<el-table-column prop="name" label="工程或费用名称" /> <el-table-column prop="name" label="工程或费用名称" />
<el-table-column prop="unit" label="单位" /> <el-table-column prop="unit" label="单位" />
<!-- <el-table-column prop="quantity" label="数量" /> --> <!-- <el-table-column prop="quantity" label="数量" /> -->
<el-table-column prop="selectNum" label="选择数量" align="center"> <el-table-column prop="quantity" label="计划量" align="center" />
<el-table-column prop="selectNum" label="设计量" align="center">
<template #default="scope"> <template #default="scope">
<el-input-number <el-input-number
:model-value="scope.row.selectNum" :model-value="scope.row.selectNum"
@ -203,16 +204,36 @@
:step="1" :step="1"
:controls="false" :controls="false"
:max="Math.floor(scope.row.quantity)" :max="Math.floor(scope.row.quantity)"
v-if="scope.row.quantity && scope.row.quantity != 0" v-if="scope.row.quantity && scope.row.quantity != 0 && scope.row.unitPrice"
/> />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="unitPrice" label="单价" align="center" />
<!-- <el-table-column prop="price" label="总价" align="center"> <el-table-column prop="useQuantity" label="剩余量" align="center">
<template #default="scope"> <template #default="scope">
{{ scope.row.price }} {{
(scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0) == 0
? ''
: (scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)
}}
</template> </template>
</el-table-column> --> </el-table-column>
<el-table-column prop="unitPrice" label="单价" align="center" />
<el-table-column prop="price" label="总价" align="center">
<template #default="scope">
{{
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
Number(scope.row.unitPrice) ==
0
? ''
: (
((scope.row.quantity ? Number(scope.row.quantity) : 0) - (scope.row.useQuantity ? Number(scope.row.useQuantity) : 0)) *
Number(scope.row.unitPrice)
).toFixed(2)
}}
</template>
</el-table-column>
</el-table> </el-table>
</el-col> </el-col>
</el-row> </el-row>
@ -266,6 +287,9 @@ import { useUserStoreHook } from '@/store/modules/user';
import { getDicts } from '@/api/system/dict/data'; import { getDicts } from '@/api/system/dict/data';
import { Plus } from '@element-plus/icons-vue'; import { Plus } from '@element-plus/icons-vue';
import { FormInstance } from 'element-plus'; import { FormInstance } from 'element-plus';
import winTheBid from './comm/winTheBid.vue';
import information from './comm/planPage.vue';
import { import {
sheetList, sheetList,
tenderPlanList, tenderPlanList,
@ -279,7 +303,6 @@ import {
delBiddView, delBiddView,
editStatus editStatus
} from '@/api/tender/index'; } from '@/api/tender/index';
import { it } from 'element-plus/es/locale/index.mjs';
const userStore = useUserStoreHook(); const userStore = useUserStoreHook();
const currentProject = computed(() => userStore.selectedProject); const currentProject = computed(() => userStore.selectedProject);

View File

@ -275,6 +275,7 @@
<el-row class="mb-4"> <el-row class="mb-4">
<el-col :span="24"> <el-col :span="24">
<el-form-item label="入库资料" prop="inputFile"> <el-form-item label="入库资料" prop="inputFile">
<template #label> <span class="text-red">*</span> 入库资料 </template>
<file-upload <file-upload
v-model="form.inputFile" v-model="form.inputFile"
:fileType="['doc', 'docx', 'pdf']" :fileType="['doc', 'docx', 'pdf']"

View File

@ -4,11 +4,11 @@
<div v-show="showSearch" class="mb-[10px]"> <div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover"> <el-card shadow="hover">
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true"> <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <!-- <el-form-item>
<el-badge :value="userSelectCount" :max="10" class="item"> <el-badge :value="userSelectCount" :max="10" class="item">
<el-button type="primary" @click="openUserSelect">选择申请人</el-button> <el-button type="primary" @click="openUserSelect">选择申请人</el-button>
</el-badge> </el-badge>
</el-form-item> </el-form-item> -->
<el-form-item label="任务名称" prop="nodeName"> <el-form-item label="任务名称" prop="nodeName">
<el-input v-model="queryParams.nodeName" placeholder="请输入任务名称" @keyup.enter="handleQuery" /> <el-input v-model="queryParams.nodeName" placeholder="请输入任务名称" @keyup.enter="handleQuery" />
</el-form-item> </el-form-item>

File diff suppressed because one or more lines are too long