修复文件问题 修改bug
This commit is contained in:
BIN
public/excel/dictlist.xlsx
Normal file
BIN
public/excel/dictlist.xlsx
Normal file
Binary file not shown.
30
src/api/basic/basicasynctask.ts
Normal file
30
src/api/basic/basicasynctask.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, cyweb All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
*/
|
||||
|
||||
import request from '/@/utils/request';
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
* @param query
|
||||
*/
|
||||
export const fetchList = (query?: any) => {
|
||||
return request({
|
||||
url: '/basic/basicAsyncTask/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
});
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import request from '/@/utils/request';
|
||||
|
||||
|
||||
export const exportTeacherInfoBySelf = (data?: any) => {
|
||||
export const makeExportTeacherInfoBySelfTask = (data?: any) => {
|
||||
return request({
|
||||
url: '/professional/file/exportTeacherInfoBySelf',
|
||||
url: '/professional/file/makeExportTeacherInfoBySelfTask',
|
||||
method: 'post',
|
||||
data: data,
|
||||
});
|
||||
|
||||
106
src/api/purchase/acceptanceItemConfig.ts
Normal file
106
src/api/purchase/acceptanceItemConfig.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
// ========== 基础CRUD接口 ==========
|
||||
|
||||
/**
|
||||
* 分页查询列表数据
|
||||
* @param query - 查询参数对象
|
||||
* @returns Promise<分页数据>
|
||||
*/
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/purchase/acceptanceItemConfig/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
* @param obj - 要新增的数据对象
|
||||
* @returns Promise<boolean> - 操作结果
|
||||
*/
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/acceptanceItemConfig',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情数据
|
||||
* @param obj - 查询参数对象(包含ID等)
|
||||
* @returns Promise<数据详情>
|
||||
*/
|
||||
export function getObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/acceptanceItemConfig/details',
|
||||
method: 'get',
|
||||
params: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除数据
|
||||
* @param ids - 要删除的ID数组
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: '/purchase/acceptanceItemConfig',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param obj - 要更新的数据对象
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/acceptanceItemConfig',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 工具函数 ==========
|
||||
|
||||
/**
|
||||
* 验证字段值唯一性
|
||||
* @param rule - 验证规则对象
|
||||
* @param value - 要验证的值
|
||||
* @param callback - 验证回调函数
|
||||
* @param isEdit - 是否为编辑模式
|
||||
*
|
||||
* @example
|
||||
* // 在表单验证规则中使用
|
||||
* fieldName: [
|
||||
* {
|
||||
* validator: (rule, value, callback) => {
|
||||
* validateExist(rule, value, callback, form.id !== '');
|
||||
* },
|
||||
* trigger: 'blur',
|
||||
* },
|
||||
* ]
|
||||
*/
|
||||
export function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {
|
||||
// 编辑模式下跳过验证
|
||||
if (isEdit) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// 查询是否存在相同值
|
||||
getObj({ [rule.field]: value }).then((response) => {
|
||||
const result = response.data;
|
||||
if (result !== null && result.length > 0) {
|
||||
callback(new Error('数据已经存在'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
106
src/api/purchase/puchasingAcceptContent.ts
Normal file
106
src/api/purchase/puchasingAcceptContent.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
// ========== 基础CRUD接口 ==========
|
||||
|
||||
/**
|
||||
* 分页查询列表数据
|
||||
* @param query - 查询参数对象
|
||||
* @returns Promise<分页数据>
|
||||
*/
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptContent/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
* @param obj - 要新增的数据对象
|
||||
* @returns Promise<boolean> - 操作结果
|
||||
*/
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptContent',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情数据
|
||||
* @param obj - 查询参数对象(包含ID等)
|
||||
* @returns Promise<数据详情>
|
||||
*/
|
||||
export function getObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptContent/details',
|
||||
method: 'get',
|
||||
params: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除数据
|
||||
* @param ids - 要删除的ID数组
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptContent',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param obj - 要更新的数据对象
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptContent',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 工具函数 ==========
|
||||
|
||||
/**
|
||||
* 验证字段值唯一性
|
||||
* @param rule - 验证规则对象
|
||||
* @param value - 要验证的值
|
||||
* @param callback - 验证回调函数
|
||||
* @param isEdit - 是否为编辑模式
|
||||
*
|
||||
* @example
|
||||
* // 在表单验证规则中使用
|
||||
* fieldName: [
|
||||
* {
|
||||
* validator: (rule, value, callback) => {
|
||||
* validateExist(rule, value, callback, form.id !== '');
|
||||
* },
|
||||
* trigger: 'blur',
|
||||
* },
|
||||
* ]
|
||||
*/
|
||||
export function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {
|
||||
// 编辑模式下跳过验证
|
||||
if (isEdit) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// 查询是否存在相同值
|
||||
getObj({ [rule.field]: value }).then((response) => {
|
||||
const result = response.data;
|
||||
if (result !== null && result.length > 0) {
|
||||
callback(new Error('数据已经存在'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
106
src/api/purchase/puchasingAcceptTeam.ts
Normal file
106
src/api/purchase/puchasingAcceptTeam.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
// ========== 基础CRUD接口 ==========
|
||||
|
||||
/**
|
||||
* 分页查询列表数据
|
||||
* @param query - 查询参数对象
|
||||
* @returns Promise<分页数据>
|
||||
*/
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptTeam/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
* @param obj - 要新增的数据对象
|
||||
* @returns Promise<boolean> - 操作结果
|
||||
*/
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptTeam',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情数据
|
||||
* @param obj - 查询参数对象(包含ID等)
|
||||
* @returns Promise<数据详情>
|
||||
*/
|
||||
export function getObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptTeam/details',
|
||||
method: 'get',
|
||||
params: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除数据
|
||||
* @param ids - 要删除的ID数组
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptTeam',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param obj - 要更新的数据对象
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/puchasingAcceptTeam',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 工具函数 ==========
|
||||
|
||||
/**
|
||||
* 验证字段值唯一性
|
||||
* @param rule - 验证规则对象
|
||||
* @param value - 要验证的值
|
||||
* @param callback - 验证回调函数
|
||||
* @param isEdit - 是否为编辑模式
|
||||
*
|
||||
* @example
|
||||
* // 在表单验证规则中使用
|
||||
* fieldName: [
|
||||
* {
|
||||
* validator: (rule, value, callback) => {
|
||||
* validateExist(rule, value, callback, form.id !== '');
|
||||
* },
|
||||
* trigger: 'blur',
|
||||
* },
|
||||
* ]
|
||||
*/
|
||||
export function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {
|
||||
// 编辑模式下跳过验证
|
||||
if (isEdit) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// 查询是否存在相同值
|
||||
getObj({ [rule.field]: value }).then((response) => {
|
||||
const result = response.data;
|
||||
if (result !== null && result.length > 0) {
|
||||
callback(new Error('数据已经存在'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
173
src/api/purchase/purchasingAccept.ts
Normal file
173
src/api/purchase/purchasingAccept.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
// ========== 基础CRUD接口 ==========
|
||||
|
||||
/**
|
||||
* 分页查询列表数据
|
||||
* @param query - 查询参数对象
|
||||
* @returns Promise<分页数据>
|
||||
*/
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
* @param obj - 要新增的数据对象
|
||||
* @returns Promise<boolean> - 操作结果
|
||||
*/
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详情数据
|
||||
* @param obj - 查询参数对象(包含ID等)
|
||||
* @returns Promise<数据详情>
|
||||
*/
|
||||
export function getObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/details',
|
||||
method: 'get',
|
||||
params: obj
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除数据
|
||||
* @param ids - 要删除的ID数组
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据
|
||||
* @param obj - 要更新的数据对象
|
||||
* @returns Promise<操作结果>
|
||||
*/
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 履约验收流程接口 ==========
|
||||
|
||||
/**
|
||||
* 第一步:保存履约验收公共配置,按分期次数自动生成批次
|
||||
*/
|
||||
export function saveCommonConfig(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/saveCommonConfig',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取履约验收公共配置及批次列表
|
||||
*/
|
||||
export function getCommonConfigWithBatches(purchaseId: string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/commonConfigWithBatches',
|
||||
method: 'get',
|
||||
params: { purchaseId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 第二步:更新单个批次
|
||||
*/
|
||||
export function updateBatch(data: any) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/updateBatch',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验收详情(含验收内容、验收小组)
|
||||
*/
|
||||
export function getDetail(purchaseId: string, batch?: number) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/detail',
|
||||
method: 'get',
|
||||
params: { purchaseId, batch }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许填报方式(金额<30万)
|
||||
*/
|
||||
export function canFillForm(purchaseId: string) {
|
||||
return request({
|
||||
url: '/purchase/purchasingAccept/canFillForm',
|
||||
method: 'get',
|
||||
params: { purchaseId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据品目类型获取验收项配置
|
||||
*/
|
||||
export function getAcceptanceItems(acceptanceType: string) {
|
||||
return request({
|
||||
url: `/purchase/acceptanceItemConfig/listByType/${acceptanceType}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// ========== 工具函数 ==========
|
||||
|
||||
/**
|
||||
* 验证字段值唯一性
|
||||
* @param rule - 验证规则对象
|
||||
* @param value - 要验证的值
|
||||
* @param callback - 验证回调函数
|
||||
* @param isEdit - 是否为编辑模式
|
||||
*
|
||||
* @example
|
||||
* // 在表单验证规则中使用
|
||||
* fieldName: [
|
||||
* {
|
||||
* validator: (rule, value, callback) => {
|
||||
* validateExist(rule, value, callback, form.id !== '');
|
||||
* },
|
||||
* trigger: 'blur',
|
||||
* },
|
||||
* ]
|
||||
*/
|
||||
export function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {
|
||||
// 编辑模式下跳过验证
|
||||
if (isEdit) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// 查询是否存在相同值
|
||||
getObj({ [rule.field]: value }).then((response) => {
|
||||
const result = response.data;
|
||||
if (result !== null && result.length > 0) {
|
||||
callback(new Error('数据已经存在'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
188
src/assets/styles/page-cards.scss
Normal file
188
src/assets/styles/page-cards.scss
Normal file
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* 列表页卡片布局公共样式(筛选卡片 + 内容卡片 + 表头)
|
||||
* 各业务模块通用,不限于专业模块
|
||||
*
|
||||
* 使用:@import '/@/assets/styles/page-cards.scss';
|
||||
* 模板类名:.page-cards > .page-wrapper > .search-card / .content-card
|
||||
*/
|
||||
|
||||
// 页面整体
|
||||
.page-cards {
|
||||
padding: 12px;
|
||||
min-height: 100%;
|
||||
background: var(--el-bg-color-page, #f5f6f8);
|
||||
}
|
||||
|
||||
// 页面包装器
|
||||
.page-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
// 筛选卡片
|
||||
.search-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
background: var(--el-bg-color);
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 18px 20px 5px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// 列表内容卡片
|
||||
.content-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
background: var(--el-bg-color);
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 20px 20px 15px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 15px 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-primary);
|
||||
|
||||
.title-icon {
|
||||
font-size: 16px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.action-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
// 小屏幕适配(≤768px)
|
||||
@media screen and (max-width: 768px) {
|
||||
.page-cards {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.search-card :deep(.el-card__body) {
|
||||
padding: 12px 14px 4px 14px;
|
||||
}
|
||||
|
||||
.content-card :deep(.el-card__header) {
|
||||
padding: 14px 14px 12px;
|
||||
}
|
||||
|
||||
.content-card :deep(.el-card__body) {
|
||||
padding: 12px 14px 14px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 14px;
|
||||
.title-icon {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-group {
|
||||
gap: 8px;
|
||||
// 按钮间距
|
||||
.el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// 超小屏幕适配(≤576px)
|
||||
@media screen and (max-width: 576px) {
|
||||
.page-cards {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.search-card :deep(.el-card__body) {
|
||||
padding: 10px 12px 14px 12px;
|
||||
}
|
||||
|
||||
.content-card :deep(.el-card__header) {
|
||||
padding: 12px 12px 10px;
|
||||
}
|
||||
|
||||
.content-card :deep(.el-card__body) {
|
||||
padding: 10px 12px 12px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
width: 100%;
|
||||
}
|
||||
.action-group {
|
||||
gap: 6px;
|
||||
// 按钮间距
|
||||
.el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
padding-top: 8px;
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
}
|
||||
@@ -2,25 +2,21 @@
|
||||
<el-tag
|
||||
:type="type"
|
||||
:size="size"
|
||||
:effect="effect"
|
||||
:class="['clickable-tag', { 'has-action': actualRightIcon !== null}]"
|
||||
:style="{ width: width ? `${width}px` : 'auto' }"
|
||||
@click="handleClick">
|
||||
<!-- 左侧图标 -->
|
||||
<el-icon
|
||||
v-if="leftIcon"
|
||||
:size="size"
|
||||
class="left-icon">
|
||||
<!-- 左侧图标:支持 Vue 组件(Element 图标,线条)或字符串(如 FontAwesome class,可实心) -->
|
||||
<i v-if="leftIcon && isLeftIconString" :class="leftIcon" class="left-icon left-icon--fa"></i>
|
||||
<el-icon v-else-if="leftIcon" :size="size" class="left-icon">
|
||||
<component :is="leftIcon" />
|
||||
</el-icon>
|
||||
|
||||
<!-- 主要内容 -->
|
||||
<slot></slot>
|
||||
|
||||
<!-- 中间图标(如警告图标) -->
|
||||
<el-icon
|
||||
v-if="middleIcon"
|
||||
:size="size"
|
||||
class="middle-icon">
|
||||
<!-- 中间图标:支持 Vue 组件或字符串(如 FontAwesome class) -->
|
||||
<i v-if="middleIcon && isMiddleIconString" :class="middleIcon" class="middle-icon middle-icon--fa"></i>
|
||||
<el-icon v-else-if="middleIcon" :size="size" class="middle-icon">
|
||||
<component :is="middleIcon" />
|
||||
</el-icon>
|
||||
|
||||
@@ -36,26 +32,32 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { Right } from '@element-plus/icons-vue'
|
||||
import { InfoFilled, Right } from '@element-plus/icons-vue'
|
||||
|
||||
interface Props {
|
||||
type?: 'success' | 'info' | 'warning' | 'danger' | 'primary'
|
||||
size?: 'large' | 'default' | 'small'
|
||||
leftIcon?: any // 左侧图标组件
|
||||
middleIcon?: any // 中间图标组件(如警告图标)
|
||||
rightIcon?: any // 右侧图标组件(默认为 Right null 则不显示)
|
||||
effect?: 'dark' | 'light' | 'plain' // 主题,与 el-tag 一致
|
||||
leftIcon?: any // 左侧图标:Vue 组件(Element 图标)或字符串(如 FontAwesome class 'fa-solid fa-circle-xmark')
|
||||
middleIcon?: any // 中间图标:Vue 组件或字符串(如 FontAwesome class)
|
||||
rightIcon?: any // 右侧图标:默认 InfoFilled(表示「可查看详情」);传 null 不显示;也可用 Right(跳转)、View(查看)等
|
||||
width?: string | number // 自定义宽度
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'primary',
|
||||
size: 'default',
|
||||
effect: 'light',
|
||||
leftIcon: undefined,
|
||||
middleIcon: undefined,
|
||||
rightIcon: undefined,
|
||||
width: undefined
|
||||
})
|
||||
|
||||
// 左侧/中间图标为字符串时(如 FontAwesome class)用 <i> 渲染,与 AuditState 实心一致
|
||||
const isLeftIconString = computed(() => typeof props.leftIcon === 'string')
|
||||
const isMiddleIconString = computed(() => typeof props.middleIcon === 'string')
|
||||
|
||||
// 获取实际的右侧图标:未传值时使用默认图标,传 null 则不显示
|
||||
const actualRightIcon = computed(() => {
|
||||
if (props.rightIcon === null) return null
|
||||
@@ -80,6 +82,11 @@ export default {
|
||||
<style scoped lang="scss">
|
||||
.clickable-tag {
|
||||
transition: all 0.2s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
.left-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
// 覆盖 el-tag 的内部结构
|
||||
:deep(.el-tag__content) {
|
||||
@@ -91,10 +98,8 @@ export default {
|
||||
// 有交互功能时才显示手型光标和悬停效果
|
||||
&.has-action {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.right-icon {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
@@ -103,7 +108,6 @@ export default {
|
||||
.middle-icon {
|
||||
animation: pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
opacity: 0.7;
|
||||
transition: all 0.2s;
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
<template>
|
||||
<div class="search-form__wrap" :class="{ 'search-form__wrap--with-title': filterTitle }">
|
||||
<div class="search-form__wrap" :class="{ 'search-form__wrap--with-title': filterTitle && props.showFilterTitle }">
|
||||
<div class="search-form__bar">
|
||||
<div class="search-form-container">
|
||||
<el-form :model="formModel" ref="formRef" :inline="true" @keyup.enter="handleKeyupEnter" :label-width="labelWidth">
|
||||
<!-- 筛选 + 展开更多 放在同一表单项内,保证垂直对齐 -->
|
||||
<el-form-item v-if="filterTitle || hasCollapsibleItems" class="search-form__left-group">
|
||||
<el-form-item v-if="(filterTitle && props.showFilterTitle) || hasCollapsibleItems" class="search-form__left-group">
|
||||
<div class="search-form__left-inner">
|
||||
<span v-if="filterTitle" class="search-form__title">
|
||||
<span v-if="filterTitle && props.showFilterTitle" class="search-form__title" :style="{ marginRight: hasCollapsibleItems ? '12px' : '0' }">
|
||||
<el-icon class="search-form__title-icon"><Search /></el-icon>
|
||||
{{ filterTitle }}
|
||||
</span>
|
||||
<el-button
|
||||
v-if="hasCollapsibleItems"
|
||||
link
|
||||
type="primary"
|
||||
class="toggle-btn"
|
||||
@click="toggleExpand"
|
||||
round
|
||||
>
|
||||
<el-icon style="margin-right: 4px">
|
||||
<ArrowUp v-if="isExpanded" />
|
||||
<ArrowDown v-else />
|
||||
<el-icon
|
||||
class="toggle-btn__icon"
|
||||
:class="{ 'toggle-btn__icon--expanded': isExpanded }"
|
||||
>
|
||||
<CaretBottom />
|
||||
</el-icon>
|
||||
{{ isExpanded ? '收起' : '展开更多' }}
|
||||
<span class="toggle-btn__text">
|
||||
{{ isExpanded ? '收起筛选' : '更多筛选' }}
|
||||
</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
@@ -44,7 +48,7 @@
|
||||
|
||||
<script setup lang="ts" name="search-form">
|
||||
import { ref, watch, computed, onMounted, nextTick } from 'vue';
|
||||
import { ArrowUp, ArrowDown, Search } from '@element-plus/icons-vue';
|
||||
import { CaretBottom, Search } from '@element-plus/icons-vue';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
@@ -52,7 +56,14 @@ const props = defineProps({
|
||||
*/
|
||||
filterTitle: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: '筛选',
|
||||
},
|
||||
/**
|
||||
* 是否显示筛选标题文案(仅控制左侧「筛选」文字和图标)
|
||||
*/
|
||||
showFilterTitle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/**
|
||||
* 表单数据模型
|
||||
@@ -227,7 +238,6 @@ defineExpose({
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
padding-right: 12px;
|
||||
margin-right: 12px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
@@ -261,13 +271,29 @@ defineExpose({
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-form__left-inner :deep(.toggle-btn__icon) {
|
||||
transition: transform 0.25s ease;
|
||||
}
|
||||
.search-form__left-inner :deep(.toggle-btn__icon--expanded) {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.search-form__left-inner :deep(.toggle-btn) {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
height: 32px;
|
||||
padding: 0 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
// 默认态直接使用 hover 的视觉样式
|
||||
color: var(--el-color-primary);
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
border: 1px solid var(--el-color-primary-light-7);
|
||||
transition: color 0.2s ease, background-color 0.2s ease, border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-fill-color-light);
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form-item:not(:has(.el-button))) {
|
||||
|
||||
150
src/layout/navBars/breadcrumb/asyncTaskDrawer.vue
Normal file
150
src/layout/navBars/breadcrumb/asyncTaskDrawer.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
v-model="visible"
|
||||
title="上传和下载任务"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
destroy-on-close
|
||||
@open="onOpen"
|
||||
>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="上传" name="upload" />
|
||||
<el-tab-pane label="下载" name="download" />
|
||||
<el-tab-pane label="其他" name="other" />
|
||||
</el-tabs>
|
||||
|
||||
<div class="task-table-wrap">
|
||||
<el-table
|
||||
v-loading="loading[activeTab]"
|
||||
:data="list[activeTab]"
|
||||
height="calc(100vh - 240px)"
|
||||
row-key="id"
|
||||
:empty-text="emptyText"
|
||||
size="small"
|
||||
stripe
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
<el-table-column label="所属模块" prop="moduleName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="任务类型" prop="typeLabel" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="任务名称" prop="detailType" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column label="任务状态" align="center" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<el-tag>{{ row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="时间" width="150" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="task-pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="pagination[activeTab].current"
|
||||
v-model:page-size="pagination[activeTab].size"
|
||||
:page-sizes="[10, 20, 50]"
|
||||
:total="pagination[activeTab].total"
|
||||
layout="prev, pager, next, sizes"
|
||||
small
|
||||
@current-change="onPageChange"
|
||||
@size-change="onSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, computed } from 'vue'
|
||||
import { fetchList } from '/@/api/basic/basicasynctask'
|
||||
|
||||
type TaskTab = 'upload' | 'download' | 'other'
|
||||
|
||||
const visible = ref(false)
|
||||
const activeTab = ref<TaskTab>('upload')
|
||||
const loading = reactive<Record<TaskTab, boolean>>({ upload: false, download: false, other: false })
|
||||
const list = reactive<Record<TaskTab, any[]>>({ upload: [], download: [], other: [] })
|
||||
const pagination = reactive<Record<TaskTab, { current: number; size: number; total: number }>>({
|
||||
upload: { current: 1, size: 10, total: 0 },
|
||||
download: { current: 1, size: 10, total: 0 },
|
||||
other: { current: 1, size: 10, total: 0 },
|
||||
})
|
||||
|
||||
/** Tab 对应接口 type:1 上传 2 下载 3 其他 */
|
||||
const TAB_TYPE_MAP: Record<TaskTab, number> = { upload: 1, download: 2, other: 3 }
|
||||
const EMPTY_TEXT_MAP: Record<TaskTab, string> = { upload: '暂无上传任务', download: '暂无下载任务', other: '暂无其他任务' }
|
||||
|
||||
// 表格样式,参考主列表页通用样式
|
||||
const tableStyle = {
|
||||
cellStyle: { textAlign: 'center' },
|
||||
headerCellStyle: {
|
||||
textAlign: 'center',
|
||||
background: 'var(--el-table-row-hover-bg-color)',
|
||||
color: 'var(--el-text-color-primary)',
|
||||
},
|
||||
}
|
||||
|
||||
const emptyText = computed(() => EMPTY_TEXT_MAP[activeTab.value])
|
||||
|
||||
const loadList = async () => {
|
||||
const type = activeTab.value
|
||||
const p = pagination[type]
|
||||
loading[type] = true
|
||||
try {
|
||||
const res = await fetchList({ type: TAB_TYPE_MAP[type], current: p.current, size: p.size })
|
||||
const data = res?.data ?? res
|
||||
const records = data?.records ?? []
|
||||
const total = data?.total ?? 0
|
||||
list[type] = Array.isArray(records) ? records : []
|
||||
p.total = Number(total) || 0
|
||||
} catch {
|
||||
list[type] = []
|
||||
} finally {
|
||||
loading[type] = false
|
||||
}
|
||||
}
|
||||
|
||||
const onPageChange = (page: number) => {
|
||||
pagination[activeTab.value].current = page
|
||||
loadList()
|
||||
}
|
||||
|
||||
const onSizeChange = (size: number) => {
|
||||
pagination[activeTab.value].size = size
|
||||
pagination[activeTab.value].current = 1
|
||||
loadList()
|
||||
}
|
||||
|
||||
const onOpen = () => {
|
||||
loadList()
|
||||
}
|
||||
|
||||
// 切换 Tab 时加载对应列表
|
||||
watch(activeTab, () => {
|
||||
if (visible.value) loadList()
|
||||
})
|
||||
|
||||
const formatTime = (item: any) => {
|
||||
const t = item?.createTime ?? item?.create_time ?? item?.updateTime ?? item?.update_time ?? ''
|
||||
return t ? (t.slice(0, 16).replace('T', ' ')) : '-'
|
||||
}
|
||||
|
||||
const open = () => {
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.task-table-wrap {
|
||||
padding: 0 4px;
|
||||
}
|
||||
.task-pagination {
|
||||
padding: 12px 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user pr15" :style="{ flex: layoutUserFlexNum }">
|
||||
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
|
||||
<!-- <el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onLanguageChange">
|
||||
<div class="layout-navbars-breadcrumb-user-icon">
|
||||
<i class="iconfont" :class="state.disabledI18n === 'en' ? 'icon-fuhao-yingwen' : 'icon-fuhao-zhongwen'" :title="$t('user.title1')"></i>
|
||||
</div>
|
||||
@@ -10,7 +10,12 @@
|
||||
<el-dropdown-item command="en" :disabled="state.disabledI18n === 'en'">English</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-dropdown> -->
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onAsyncTaskClick">
|
||||
<el-icon title="上传和下载任务">
|
||||
<ele-FolderOpened />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="layout-navbars-breadcrumb-user-icon" @click="onLockClick">
|
||||
<el-icon :title="$t('layout.threeLockScreenTime')">
|
||||
<ele-Lock />
|
||||
@@ -76,6 +81,7 @@
|
||||
<personal-drawer ref="personalDrawerRef"></personal-drawer>
|
||||
|
||||
<change-role ref="ChangeRoleRef" />
|
||||
<AsyncTaskDrawer ref="asyncTaskDrawerRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -95,8 +101,10 @@ import { fetchUserMessageList } from '/@/api/admin/message';
|
||||
import {useFlowJob} from "/@/flow/stores/flowJob";
|
||||
|
||||
|
||||
const ChangeRoleRef=ref()
|
||||
const ChangeRoleRef = ref()
|
||||
const ChangeRole = defineAsyncComponent(() => import('/@/views/admin/system/role/change-role.vue'))
|
||||
const asyncTaskDrawerRef = ref()
|
||||
const AsyncTaskDrawer = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/asyncTaskDrawer.vue'))
|
||||
// 引入组件
|
||||
const GlobalWebsocket = defineAsyncComponent(() => import('/@/components/Websocket/index.vue'));
|
||||
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
|
||||
@@ -207,6 +215,10 @@ const onHandleCommandClick = (path: string) => {
|
||||
const onSearchClick = () => {
|
||||
searchRef.value.openSearch();
|
||||
};
|
||||
// 上传/下载任务点击
|
||||
const onAsyncTaskClick = () => {
|
||||
asyncTaskDrawerRef.value?.open();
|
||||
};
|
||||
// 语言切换
|
||||
const onLanguageChange = (lang: string) => {
|
||||
Local.remove('themeConfig');
|
||||
@@ -241,6 +253,16 @@ const getIsDot = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 登录后若存储中无角色信息则弹出角色切换框
|
||||
const openChangeRoleIfMissing = () => {
|
||||
const hasRole = Local.get('roleCode') && Local.get('roleName') && Local.get('roleId')
|
||||
if (!hasRole) {
|
||||
nextTick(() => {
|
||||
setTimeout(() => ChangeRoleRef.value?.open(), 100)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
if (Local.get('themeConfig')) {
|
||||
@@ -248,8 +270,8 @@ onMounted(() => {
|
||||
initI18nOrSize('globalI18n', 'disabledI18n');
|
||||
}
|
||||
useFlowJob().topJobList()
|
||||
|
||||
getIsDot();
|
||||
getIsDot()
|
||||
openChangeRoleIfMissing()
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ body,
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.layout-pd {
|
||||
padding: 10px !important;
|
||||
padding: 12px !important;
|
||||
background: linear-gradient(135deg,#f5f7fa,#e9ecef);
|
||||
}
|
||||
.layout-flex {
|
||||
|
||||
@@ -439,6 +439,12 @@
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
|
||||
/* 表格单元格内边距(全局)- 左右用默认,上下 9px */
|
||||
.el-table .el-table__body td,
|
||||
.el-table .el-table__header th {
|
||||
padding: 9px 0;
|
||||
}
|
||||
|
||||
/* scrollbar
|
||||
------------------------------- */
|
||||
.el-scrollbar__bar {
|
||||
|
||||
@@ -1,60 +1,86 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" title="角色切换" width="50%">
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="角色切换"
|
||||
width="50%"
|
||||
:show-close="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:before-close="handleBeforeClose"
|
||||
>
|
||||
<el-form>
|
||||
<!-- <el-form-item label="学校">-->
|
||||
<!-- <el-tag>{{schoolName}}</el-tag>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="角色">
|
||||
<el-radio-group v-model="radio">
|
||||
<el-radio-button v-for="(item,index) in allRole" :key="index" :label="item.roleCode" @click.native="handleChangeRole(item.roleCode)">{{item.roleName}}</el-radio-button>
|
||||
<el-form-item label="角色" class="role-form-item">
|
||||
<el-radio-group v-model="radio" class="role-radio-group" @change="handleChangeRole">
|
||||
<el-radio-button
|
||||
v-for="item in allRole"
|
||||
:key="item.roleCode"
|
||||
:label="item.roleCode"
|
||||
>
|
||||
{{ item.roleName }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
<template #footer>
|
||||
<!-- <el-button type="primary" @click="handleChangeRole">切换</el-button>-->
|
||||
<el-button @click="visible=false">关 闭</el-button>
|
||||
<!-- <el-button type="primary" @click="handleChangeRole">切换</el-button>-->
|
||||
<el-button @click="handleFooterClose">关 闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {listAllRole} from '/@/api/admin/role'
|
||||
import {Local, Session} from '/@/utils/storage';
|
||||
import {useMessage} from "/@/hooks/message";
|
||||
// import {querySchoolName} from "/@/api/admin/tenant"
|
||||
import { listAllRole } from '/@/api/admin/role'
|
||||
import { Local } from '/@/utils/storage'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
|
||||
const visible=ref(false)
|
||||
const radio=ref('')
|
||||
const allRole=reactive([])
|
||||
// const schoolName=ref('')
|
||||
const visible = ref(false)
|
||||
const radio = ref('')
|
||||
const allRole = reactive<any[]>([])
|
||||
|
||||
const open=()=>{
|
||||
visible.value=true
|
||||
// handleQuerySchoolName()
|
||||
listAllRole().then(res=>{
|
||||
Object.assign(allRole,res.data)
|
||||
radio.value=Local.get("roleCode")
|
||||
visible.value=true
|
||||
const open = () => {
|
||||
visible.value = true
|
||||
listAllRole().then((res) => {
|
||||
Object.assign(allRole, res.data)
|
||||
radio.value = Local.get('roleCode')
|
||||
visible.value = true
|
||||
})
|
||||
}
|
||||
const handleChangeRole=(label:any)=>{
|
||||
let obj:any=allRole.find((v:any) => v.roleCode == label)
|
||||
Local.set("roleCode",obj.roleCode)
|
||||
Local.set("roleName",obj.roleName)
|
||||
Local.set("roleId",obj.roleId)
|
||||
useMessage().success("操作成功")
|
||||
setTimeout(()=>{
|
||||
window.location.reload()
|
||||
},500)
|
||||
|
||||
const canClose = () => {
|
||||
if (!radio.value) {
|
||||
useMessage().warning('请选择一个角色')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const handleQuerySchoolName=()=>{
|
||||
// querySchoolName({id:Session.get("tenantId")}).then((res:any)=>{
|
||||
// schoolName.value=res.data
|
||||
// })
|
||||
const handleBeforeClose = (done: () => void) => {
|
||||
if (!canClose()) return
|
||||
done()
|
||||
}
|
||||
|
||||
const handleFooterClose = () => {
|
||||
if (!canClose()) return
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
const handleChangeRole = (label: string) => {
|
||||
const obj = allRole.find((v: any) => v.roleCode === label)
|
||||
if (!obj) return
|
||||
Local.set('roleCode', obj.roleCode)
|
||||
Local.set('roleName', obj.roleName)
|
||||
Local.set('roleId', obj.roleId)
|
||||
useMessage().success('操作成功')
|
||||
setTimeout(() => {
|
||||
// 切换角色后统一回到首页,避免停留在诸如 jsonflow/run-job/do-job 等内部路由
|
||||
window.location.hash = '#/'
|
||||
window.location.reload()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
@@ -62,6 +88,28 @@ defineExpose({
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
.role-form-item {
|
||||
:deep(.el-form-item__content) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
.role-radio-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
/* 每个按钮独立边框,换行后左侧也有边线 */
|
||||
:deep(.el-radio-button) {
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.el-radio-button__inner) {
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid var(--el-border-color) !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
:deep(.el-radio-button.is-active .el-radio-button__inner) {
|
||||
border-color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="160px" class="accept-batch-form">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="验收方式" prop="acceptType">
|
||||
<el-radio-group v-model="form.acceptType" :disabled="readonly">
|
||||
<el-radio label="1">填写履约验收评价表</el-radio>
|
||||
<el-radio label="2" :disabled="!canFill">上传履约验收评价表</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="!canFill" class="el-form-item__tip">金额≥30万,仅支持上传模版</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="验收日期" prop="acceptDate">
|
||||
<el-date-picker
|
||||
v-model="form.acceptDate"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<!-- 填报方式:验收内容表格 -->
|
||||
<template v-if="form.acceptType === '1' && canFill">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="验收内容" prop="acceptContents">
|
||||
<el-table :data="form.acceptContents" border size="small" max-height="260" class="accept-content-table">
|
||||
<el-table-column prop="itemName" label="验收项" >
|
||||
<template #default="{ row }">
|
||||
{{row.itemName}}
|
||||
<el-input
|
||||
v-if="row.type === 'input'"
|
||||
v-model="row.remark"
|
||||
placeholder="请输入"
|
||||
size="small"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isQualified" label="合格/不合格" width="140" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-radio-group v-model="row.isQualified" size="small" :disabled="readonly">
|
||||
<el-radio label="1">合格</el-radio>
|
||||
<el-radio label="0">不合格</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<!-- 上传方式 -->
|
||||
<template v-if="form.acceptType === '2'">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="履约验收模版" prop="templateFileIds">
|
||||
<UploadFile
|
||||
v-model="templateFileIdsStr"
|
||||
:limit="1"
|
||||
:data="{ purchaseId: purchaseId || '', fileType: '110' }"
|
||||
upload-file-url="/purchase/purchasingfiles/upload"
|
||||
:disabled="readonly"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<!-- 验收小组 -->
|
||||
<el-col :span="24">
|
||||
<el-form-item label="验收小组" prop="acceptTeam">
|
||||
<div class="team-list">
|
||||
<div v-for="(m, idx) in form.acceptTeam" :key="idx" class="team-row">
|
||||
<el-input v-model="m.name" placeholder="姓名" size="small" style="width:120px" :disabled="readonly" />
|
||||
<el-input v-model="m.deptName" placeholder="部门" size="small" style="width:160px" :disabled="readonly" />
|
||||
<el-button v-if="!readonly && form.acceptTeam.length > 3" type="danger" link size="small" @click="removeTeam(idx)">删除</el-button>
|
||||
</div>
|
||||
<el-button v-if="!readonly" type="primary" link size="small" @click="addTeam">+ 增加成员</el-button>
|
||||
</div>
|
||||
<div class="el-form-item__tip">至少3人,且为单数</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入" :disabled="readonly" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: Record<string, any>
|
||||
canFill: boolean
|
||||
readonly?: boolean
|
||||
purchaseId?: string
|
||||
acceptanceItems?: any[]
|
||||
}>(),
|
||||
{ readonly: false, canFill: true, purchaseId: '' }
|
||||
)
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const templateFileIdsStr = ref('')
|
||||
|
||||
const form = reactive({
|
||||
acceptType: '1',
|
||||
acceptDate: '',
|
||||
acceptContents: [] as any[],
|
||||
acceptTeam: [
|
||||
{ name: '', deptCode: '', deptName: '' },
|
||||
{ name: '', deptCode: '', deptName: '' },
|
||||
{ name: '', deptCode: '', deptName: '' },
|
||||
] as any[],
|
||||
templateFileIds: [] as string[],
|
||||
remark: '',
|
||||
...props.modelValue,
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (val) => Object.assign(form, val || {}), { deep: true })
|
||||
watch(form, () => emit('update:modelValue', { ...form }), { deep: true })
|
||||
|
||||
watch(() => props.acceptanceItems, (items) => {
|
||||
if (items?.length && form.acceptContents.length === 0) {
|
||||
form.acceptContents = items.map((it: any) => ({
|
||||
configId: it.id,
|
||||
itemName: it.itemName,
|
||||
type: it.type,
|
||||
isQualified: '1',
|
||||
remark: '',
|
||||
}))
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
watch(templateFileIdsStr, (s) => {
|
||||
const arr = s ? s.split(',').map((x: string) => x.trim()).filter(Boolean) : []
|
||||
if (JSON.stringify(form.templateFileIds) !== JSON.stringify(arr)) {
|
||||
form.templateFileIds = arr
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => form.templateFileIds, (arr) => {
|
||||
if (Array.isArray(arr) && arr.length) templateFileIdsStr.value = arr.join(',')
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
const addTeam = () => {
|
||||
form.acceptTeam.push({ name: '', deptCode: '', deptName: '' })
|
||||
}
|
||||
|
||||
const removeTeam = (idx: number) => {
|
||||
form.acceptTeam.splice(idx, 1)
|
||||
}
|
||||
|
||||
const rules: FormRules = {
|
||||
acceptType: [{ required: true, message: '请选择验收方式', trigger: 'change' }],
|
||||
acceptDate: [{ required: true, message: '请选择验收日期', trigger: 'change' }],
|
||||
}
|
||||
|
||||
const validate = () => formRef.value?.validate()
|
||||
|
||||
defineExpose({ validate, form })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.accept-batch-form :deep(.el-form-item) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.accept-content-table :deep(.el-table__body td) {
|
||||
padding: 10px 0;
|
||||
width: 200px;
|
||||
}
|
||||
.team-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.team-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.el-form-item__tip {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-top: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px" class="accept-common-form compact-form">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="项目名称">
|
||||
<el-input :model-value="projectName || form.projectName" readonly placeholder="-" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="需求部门">
|
||||
<el-input :model-value="deptName || form.deptName" readonly placeholder="-" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否签订合同" prop="hasContract">
|
||||
<el-radio-group v-model="form.hasContract">
|
||||
<el-radio label="0">否</el-radio>
|
||||
<el-radio label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.hasContract === '1'">
|
||||
<el-form-item label="合同" prop="contractId">
|
||||
<el-input v-model="form.contractId" placeholder="请选择合同(待对接合同接口)" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="是否分期验收" prop="isInstallment">
|
||||
<el-radio-group v-model="form.isInstallment">
|
||||
<el-radio label="0">否</el-radio>
|
||||
<el-radio label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" v-if="form.isInstallment === '1'">
|
||||
<el-form-item label="分期次数" prop="totalPhases">
|
||||
<el-input-number v-model="form.totalPhases" :min="1" :max="99" placeholder="请输入" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="供应商名称" prop="supplierName">
|
||||
<el-input v-model="form.supplierName" placeholder="选择合同后自动带出" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="供应商联系人及电话" prop="supplierContact">
|
||||
<el-input v-model="form.supplierContact" placeholder="请输入" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="采购人员" prop="purchaserId">
|
||||
<org-selector v-model:orgList="purchaserList" type="user" :multiple="false" @update:orgList="onPurchaserChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="资产管理员" prop="assetAdminId">
|
||||
<org-selector v-model:orgList="assetAdminList" type="user" :multiple="false" @update:orgList="onAssetAdminChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: Record<string, any>
|
||||
projectName?: string
|
||||
deptName?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
const purchaserList = ref<any[]>([])
|
||||
const assetAdminList = ref<any[]>([])
|
||||
|
||||
const form = reactive({
|
||||
hasContract: '0',
|
||||
contractId: '',
|
||||
isInstallment: '0',
|
||||
totalPhases: 1,
|
||||
supplierName: '',
|
||||
supplierContact: '',
|
||||
purchaserId: '',
|
||||
purchaserName: '',
|
||||
assetAdminId: '',
|
||||
assetAdminName: '',
|
||||
...props.modelValue,
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (val) => {
|
||||
Object.assign(form, val || {})
|
||||
// 人员选择回显
|
||||
if (form.purchaserId && form.purchaserName) {
|
||||
purchaserList.value = [{ id: form.purchaserId, name: form.purchaserName, type: 'user' }]
|
||||
} else {
|
||||
purchaserList.value = []
|
||||
}
|
||||
if (form.assetAdminId && form.assetAdminName) {
|
||||
assetAdminList.value = [{ id: form.assetAdminId, name: form.assetAdminName, type: 'user' }]
|
||||
} else {
|
||||
assetAdminList.value = []
|
||||
}
|
||||
}, { deep: true, immediate: true })
|
||||
watch(form, () => emit('update:modelValue', { ...form }), { deep: true })
|
||||
|
||||
const onPurchaserChange = (list: any[]) => {
|
||||
if (list?.length) {
|
||||
const u = list[0]
|
||||
form.purchaserId = u.userId || u.id || ''
|
||||
form.purchaserName = u.name || u.realName || ''
|
||||
} else {
|
||||
form.purchaserId = ''
|
||||
form.purchaserName = ''
|
||||
}
|
||||
}
|
||||
|
||||
const onAssetAdminChange = (list: any[]) => {
|
||||
if (list?.length) {
|
||||
const u = list[0]
|
||||
form.assetAdminId = u.userId || u.id || ''
|
||||
form.assetAdminName = u.name || u.realName || ''
|
||||
} else {
|
||||
form.assetAdminId = ''
|
||||
form.assetAdminName = ''
|
||||
}
|
||||
}
|
||||
|
||||
const rules: FormRules = {
|
||||
isInstallment: [{ required: true, message: '请选择是否分期验收', trigger: 'change' }],
|
||||
totalPhases: [{ required: true, message: '请输入分期次数', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
const validate = () => formRef.value?.validate()
|
||||
|
||||
defineExpose({ validate, form })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.accept-common-form {
|
||||
padding: 0 4px;
|
||||
}
|
||||
.accept-common-form :deep(.el-form-item) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
/* 紧凑表单样式 */
|
||||
.compact-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
padding-right: 12px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.el-input__inner),
|
||||
:deep(.el-textarea__inner) {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,405 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="履约验收"
|
||||
width="85%"
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
class="purchasing-accept-modal"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div v-loading="loading" class="modal-body">
|
||||
<div class="main-tabs">
|
||||
<div class="main-tab-nav">
|
||||
<div
|
||||
class="main-tab-item"
|
||||
:class="{ active: mainTab === 'common' }"
|
||||
@click="mainTab = 'common'"
|
||||
>
|
||||
公共信息
|
||||
</div>
|
||||
<div
|
||||
class="main-tab-item"
|
||||
:class="{ active: mainTab === 'batch' }"
|
||||
@click="mainTab = 'batch'"
|
||||
>
|
||||
分期验收{{ batches.length > 0 ? ` (${batches.length})` : '' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-tab-content">
|
||||
<div v-show="mainTab === 'common'" class="tab-content">
|
||||
<AcceptCommonForm
|
||||
ref="commonFormRef"
|
||||
v-model="commonForm"
|
||||
:project-name="applyInfo?.projectName"
|
||||
:dept-name="applyInfo?.deptName"
|
||||
/>
|
||||
</div>
|
||||
<div v-show="mainTab === 'batch'" class="tab-content">
|
||||
<div v-if="batches.length > 0">
|
||||
<div class="batch-tabs">
|
||||
<div
|
||||
v-for="b in batches"
|
||||
:key="b.id"
|
||||
class="batch-tab-item"
|
||||
:class="{ active: String(b.batch) === activeTab, disabled: !canEditBatch(b.batch) }"
|
||||
@click="canEditBatch(b.batch) && (activeTab = String(b.batch))"
|
||||
>
|
||||
<span>第{{ b.batch }}期</span>
|
||||
<el-tag v-if="isBatchCompleted(b)" type="success" size="small">已填</el-tag>
|
||||
<el-tag v-else-if="!canEditBatch(b.batch)" type="info" size="small">需先完成上一期</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="batch-panel">
|
||||
<AcceptBatchForm
|
||||
v-for="b in batches"
|
||||
v-show="String(b.batch) === activeTab"
|
||||
:key="b.id"
|
||||
v-model="batchForms[b.batch]"
|
||||
:can-fill="canFillForm"
|
||||
:readonly="false"
|
||||
:purchase-id="String(purchaseId)"
|
||||
:acceptance-items="acceptanceItems"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="tip-box">
|
||||
<el-alert type="info" :closable="false" show-icon>
|
||||
请先在「公共信息」中填写并点击「保存公共配置」,系统将按分期次数自动生成验收批次
|
||||
</el-alert>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="handleClose">关 闭</el-button>
|
||||
<el-button
|
||||
v-if="mainTab === 'common' || batches.length === 0"
|
||||
type="primary"
|
||||
@click="saveCommonConfig"
|
||||
:loading="saving"
|
||||
>
|
||||
保存公共配置
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else-if="mainTab === 'batch' && activeBatchId"
|
||||
type="primary"
|
||||
@click="saveCurrentBatch"
|
||||
:loading="saving"
|
||||
>
|
||||
保存第{{ activeTab }}期
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
import {
|
||||
saveCommonConfig as apiSaveCommonConfig,
|
||||
getCommonConfigWithBatches,
|
||||
updateBatch,
|
||||
canFillForm as apiCanFillForm,
|
||||
getAcceptanceItems,
|
||||
getDetail,
|
||||
} from '/@/api/purchase/purchasingAccept'
|
||||
import AcceptCommonForm from './AcceptCommonForm.vue'
|
||||
import AcceptBatchForm from './AcceptBatchForm.vue'
|
||||
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const purchaseId = ref<string | number>('')
|
||||
const applyInfo = ref<any>(null)
|
||||
const rowProjectType = ref<string>('A')
|
||||
const canFillForm = ref(true)
|
||||
const acceptanceItems = ref<any[]>([])
|
||||
const batches = ref<any[]>([])
|
||||
const mainTab = ref('common')
|
||||
const activeTab = ref('1')
|
||||
const commonFormRef = ref()
|
||||
const commonForm = reactive<Record<string, any>>({})
|
||||
const batchForms = reactive<Record<number, any>>({})
|
||||
|
||||
const activeBatchId = computed(() => {
|
||||
const b = batches.value.find((x: any) => String(x.batch) === activeTab.value)
|
||||
return b?.id || ''
|
||||
})
|
||||
|
||||
const canEditBatch = (batch: number) => {
|
||||
if (batch === 1) return true
|
||||
for (let i = 1; i < batch; i++) {
|
||||
if (!isBatchCompletedByIdx(i)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const isBatchCompleted = (b: any) => {
|
||||
return !!(b.acceptType && b.acceptDate)
|
||||
}
|
||||
|
||||
const isBatchCompletedByIdx = (batch: number) => {
|
||||
const b = batches.value.find((x: any) => x.batch === batch)
|
||||
return b ? isBatchCompleted(b) : false
|
||||
}
|
||||
|
||||
const loadData = async () => {
|
||||
if (!purchaseId.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const [configRes, canFillRes] = await Promise.all([
|
||||
getCommonConfigWithBatches(String(purchaseId.value)),
|
||||
apiCanFillForm(String(purchaseId.value)),
|
||||
])
|
||||
const config = configRes?.data
|
||||
canFillForm.value = !!canFillRes?.data
|
||||
|
||||
if (config?.common) {
|
||||
Object.assign(commonForm, {
|
||||
hasContract: config.common.hasContract || '0',
|
||||
contractId: config.common.contractId || '',
|
||||
isInstallment: config.common.isInstallment || '0',
|
||||
totalPhases: config.common.totalPhases || 1,
|
||||
supplierName: config.common.supplierName || '',
|
||||
supplierContact: config.common.supplierContact || '',
|
||||
purchaserId: config.common.purchaserId || '',
|
||||
purchaserName: config.common.purchaserName || '',
|
||||
assetAdminId: config.common.assetAdminId || '',
|
||||
assetAdminName: config.common.assetAdminName || '',
|
||||
})
|
||||
applyInfo.value = config.common
|
||||
}
|
||||
|
||||
const projectType = applyInfo.value?.projectType || rowProjectType.value || 'A'
|
||||
const typeMap: Record<string, string> = { A: 'A', B: 'B', C: 'C' }
|
||||
const at = typeMap[projectType] || 'A'
|
||||
const itemsRes = await getAcceptanceItems(at)
|
||||
acceptanceItems.value = itemsRes?.data || []
|
||||
|
||||
if (config?.batches?.length) {
|
||||
batches.value = config.batches.sort((a: any, b: any) => (a.batch || 0) - (b.batch || 0))
|
||||
activeTab.value = String(batches.value[0]?.batch || '1')
|
||||
mainTab.value = 'batch'
|
||||
for (const b of batches.value) {
|
||||
if (!batchForms[b.batch]) batchForms[b.batch] = {}
|
||||
}
|
||||
await loadBatchDetails()
|
||||
} else {
|
||||
batches.value = []
|
||||
}
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '加载失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadBatchDetails = async () => {
|
||||
for (const b of batches.value) {
|
||||
try {
|
||||
const res = await getDetail(String(purchaseId.value), b.batch)
|
||||
const d = res?.data
|
||||
if (d?.accept) {
|
||||
const itemMap = (acceptanceItems.value || []).reduce((acc: any, it: any) => {
|
||||
acc[it.id] = it
|
||||
return acc
|
||||
}, {})
|
||||
batchForms[b.batch] = {
|
||||
acceptType: d.accept.acceptType || '1',
|
||||
acceptDate: d.accept.acceptDate || '',
|
||||
remark: d.accept.remark || '',
|
||||
templateFileIds: d.accept.templateFileIds || [],
|
||||
acceptContents: (d.contents || []).map((c: any) => {
|
||||
const cfg = itemMap[c.configId]
|
||||
return {
|
||||
configId: c.configId,
|
||||
itemName: cfg?.itemName || '',
|
||||
type: cfg?.type,
|
||||
isQualified: c.isQualified || '1',
|
||||
remark: c.remark || '',
|
||||
}
|
||||
}),
|
||||
acceptTeam: (d.team || []).map((t: any) => ({
|
||||
name: t.name,
|
||||
deptCode: t.deptCode,
|
||||
deptName: t.deptName,
|
||||
})),
|
||||
}
|
||||
if (batchForms[b.batch].acceptTeam.length < 3) {
|
||||
while (batchForms[b.batch].acceptTeam.length < 3) {
|
||||
batchForms[b.batch].acceptTeam.push({ name: '', deptCode: '', deptName: '' })
|
||||
}
|
||||
}
|
||||
if (acceptanceItems.value.length && (!batchForms[b.batch].acceptContents || batchForms[b.batch].acceptContents.length === 0)) {
|
||||
batchForms[b.batch].acceptContents = acceptanceItems.value.map((it: any) => ({
|
||||
configId: it.id,
|
||||
itemName: it.itemName,
|
||||
type: it.type,
|
||||
isQualified: '1',
|
||||
remark: '',
|
||||
}))
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
const saveCommonConfig = async () => {
|
||||
const valid = await commonFormRef.value?.validate?.().catch(() => false)
|
||||
if (!valid) return
|
||||
if (commonForm.isInstallment === '1' && (!commonForm.totalPhases || commonForm.totalPhases < 1)) {
|
||||
useMessage().error('请填写分期次数')
|
||||
return
|
||||
}
|
||||
saving.value = true
|
||||
try {
|
||||
await apiSaveCommonConfig({
|
||||
purchaseId: String(purchaseId.value),
|
||||
hasContract: commonForm.hasContract,
|
||||
contractId: commonForm.contractId,
|
||||
isInstallment: commonForm.isInstallment,
|
||||
totalPhases: commonForm.isInstallment === '1' ? commonForm.totalPhases : 1,
|
||||
supplierName: commonForm.supplierName,
|
||||
supplierContact: commonForm.supplierContact,
|
||||
purchaserId: commonForm.purchaserId,
|
||||
purchaserName: commonForm.purchaserName,
|
||||
assetAdminId: commonForm.assetAdminId,
|
||||
assetAdminName: commonForm.assetAdminName,
|
||||
})
|
||||
useMessage().success('保存成功')
|
||||
await loadData()
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const saveCurrentBatch = async () => {
|
||||
const b = batches.value.find((x: any) => String(x.batch) === activeTab.value)
|
||||
if (!b?.id) return
|
||||
const form = batchForms[Number(activeTab.value)]
|
||||
if (!form) return
|
||||
|
||||
const team = (form.acceptTeam || []).filter((m: any) => m?.name)
|
||||
if (team.length < 3 || team.length % 2 === 0) {
|
||||
useMessage().error('验收小组至少3人且为单数')
|
||||
return
|
||||
}
|
||||
|
||||
saving.value = true
|
||||
try {
|
||||
await updateBatch({
|
||||
id: b.id,
|
||||
purchaseId: String(purchaseId.value),
|
||||
acceptType: form.acceptType,
|
||||
acceptDate: form.acceptDate,
|
||||
remark: form.remark,
|
||||
templateFileIds: form.templateFileIds || [],
|
||||
acceptContents: form.acceptContents || [],
|
||||
acceptTeam: team,
|
||||
})
|
||||
useMessage().success('保存成功')
|
||||
await loadData()
|
||||
} catch (e: any) {
|
||||
useMessage().error(e?.msg || '保存失败')
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
emit('refresh')
|
||||
}
|
||||
|
||||
const open = (row: any) => {
|
||||
purchaseId.value = row?.id ?? ''
|
||||
rowProjectType.value = row?.projectType || 'A'
|
||||
visible.value = true
|
||||
batches.value = []
|
||||
Object.keys(batchForms).forEach((k) => delete batchForms[Number(k)])
|
||||
nextTick(() => loadData())
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-body {
|
||||
padding: 0;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.main-tab-nav {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
.main-tab-item {
|
||||
padding: 12px 20px;
|
||||
cursor: pointer;
|
||||
color: var(--el-text-color-regular);
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.main-tab-item:hover {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.main-tab-item.active {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 600;
|
||||
border-bottom-color: var(--el-color-primary);
|
||||
}
|
||||
.main-tab-content {
|
||||
padding-top: 4px;
|
||||
}
|
||||
.tab-content {
|
||||
min-height: 200px;
|
||||
display: block;
|
||||
}
|
||||
.tip-box {
|
||||
padding: 20px 0;
|
||||
}
|
||||
.batch-tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.batch-tab-item {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.batch-tab-item:hover:not(.disabled) {
|
||||
border-color: var(--el-color-primary);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.batch-tab-item.active {
|
||||
background: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
.batch-tab-item.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.batch-panel {
|
||||
min-height: 200px;
|
||||
}
|
||||
</style>
|
||||
@@ -191,7 +191,7 @@
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="300">
|
||||
<el-table-column label="操作" align="center" fixed="right" width="360">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="View"
|
||||
@@ -216,6 +216,13 @@
|
||||
@click="handleDelete(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="DocumentChecked"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleAccept(scope.row)">
|
||||
履约验收
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -258,6 +265,9 @@
|
||||
ref="formDialogRef"
|
||||
:dict-data="dictData"
|
||||
@refresh="getDataList" />
|
||||
|
||||
<!-- 履约验收弹窗 -->
|
||||
<PurchasingAcceptModal ref="acceptModalRef" @refresh="getDataList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -269,10 +279,11 @@ import { getPage, delObj } from "/@/api/finance/purchasingrequisition";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { getDicts } from '/@/api/admin/dict';
|
||||
import { getTree } from '/@/api/finance/purchasingcategory';
|
||||
import { List, Document, DocumentCopy, Search, Collection, Money, CircleCheck, InfoFilled, Calendar, OfficeBuilding, Warning } from '@element-plus/icons-vue'
|
||||
import { List, Document, DocumentCopy, Search, Collection, Money, CircleCheck, InfoFilled, Calendar, OfficeBuilding, Warning, DocumentChecked } from '@element-plus/icons-vue'
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const PurchasingAcceptModal = defineAsyncComponent(() => import('./accept/PurchasingAcceptModal.vue'));
|
||||
|
||||
// 字典数据和品目树数据
|
||||
const dictData = ref({
|
||||
@@ -289,6 +300,7 @@ const dictData = ref({
|
||||
const router = useRouter()
|
||||
const tableRef = ref()
|
||||
const formDialogRef = ref()
|
||||
const acceptModalRef = ref()
|
||||
const searchFormRef = ref()
|
||||
const showSearch = ref(true)
|
||||
const showAddIframe = ref(false)
|
||||
@@ -363,6 +375,7 @@ const handleIframeMessage = (event: MessageEvent) => {
|
||||
};
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* 打开查看对话框
|
||||
* @param row - 当前行数据
|
||||
*/
|
||||
@@ -376,6 +389,12 @@ const handleView = (row: any) => {
|
||||
*/
|
||||
const handleEdit = (row: any) => {
|
||||
formDialogRef.value?.openDialog('edit', row);
|
||||
=======
|
||||
* 履约验收
|
||||
*/
|
||||
const handleAccept = (row: any) => {
|
||||
acceptModalRef.value?.open(row);
|
||||
>>>>>>> 61380fa2bca0fe9fd37af1c494a51ba523501f7d
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
116
src/views/professional/common/import-teacher-other-info.vue
Normal file
116
src/views/professional/common/import-teacher-other-info.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<el-dialog v-model="visible" :title="title" width="600" append-to-body>
|
||||
<!-- <el-alert-->
|
||||
<!-- type="warning"-->
|
||||
<!-- :closable="false"-->
|
||||
<!-- show-icon-->
|
||||
<!-- style="margin-bottom: 20px;">-->
|
||||
<!-- <template #title>-->
|
||||
<!-- <span>下载模板</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-alert>-->
|
||||
|
||||
<div style="text-align: center; margin-bottom: 20px">
|
||||
<el-button type="success" :icon="Download" @click="handleDownloadTemplate">下载模板</el-button>
|
||||
</div>
|
||||
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
:action="uploadUrl"
|
||||
:headers="headers"
|
||||
:accept="'.xls,.xlsx'"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleAvatarError"
|
||||
:limit="1"
|
||||
drag
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传 .xls 或 .xlsx 格式的 Excel 文件</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import { Download, UploadFilled } from '@element-plus/icons-vue';
|
||||
import { Session } from '/@/utils/storage';
|
||||
import { downBlobFile } from '/@/utils/other';
|
||||
|
||||
const title = ref('');
|
||||
// 响应式数据
|
||||
const visible = ref(false);
|
||||
|
||||
// 计算属性
|
||||
const headers = computed(() => {
|
||||
return {
|
||||
Authorization: 'Bearer ' + Session.getToken(),
|
||||
TENANT_ID: Session.getTenant()
|
||||
};
|
||||
});
|
||||
|
||||
const uploadUrl = ref('')
|
||||
const currentType = ref('')
|
||||
const titleMap: Record<string, string> = {
|
||||
titleRelation: '职称信息导入',
|
||||
quaRelation: '职业资格信息导入',
|
||||
cerRelation: '教师资格证信息导入',
|
||||
eduDegree: '学历学位信息导入',
|
||||
partyChange: '党组织变动信息导入',
|
||||
honor: '综合表彰信息导入'
|
||||
}
|
||||
// 方法
|
||||
const init = (type: any) => {
|
||||
currentType.value = type
|
||||
uploadUrl.value = '/professional/file/importTeacherOtherInfo?type=' + type
|
||||
title.value = titleMap[type] || '信息导入'
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits<{
|
||||
(e: 'refreshData'): void
|
||||
}>()
|
||||
|
||||
const handleUploadSuccess = () => {
|
||||
visible.value = false;
|
||||
ElNotification({
|
||||
title: '成功',
|
||||
message: '导入成功',
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
emit('refreshData')
|
||||
};
|
||||
|
||||
const handleAvatarError = (err: any) => {
|
||||
const result = JSON.parse(err.message);
|
||||
if (result.code == '1') {
|
||||
ElNotification.error({
|
||||
title: '错误',
|
||||
message: result.msg,
|
||||
duration: 30000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDownloadTemplate = () => {
|
||||
downBlobFile('/professional/file/exportTeacherInfoTemplate', { type: currentType.value || 'titleRelation' }, '职工信息导入模板.xlsx')
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
init,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.upload-demo {
|
||||
:deep(.el-upload-dragger) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -37,6 +37,20 @@
|
||||
</template>
|
||||
</search-form>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-row>
|
||||
<div class="mb15" style="width: 100%;">
|
||||
<el-button
|
||||
v-auth="'professional_teacherinfo_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog"
|
||||
>导入信息</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
@@ -77,6 +91,9 @@
|
||||
@current-change="currentChangeHandle"
|
||||
@size-change="sizeChangeHandle"
|
||||
/>
|
||||
|
||||
<!-- 子组件 -->
|
||||
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -88,6 +105,7 @@ import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||
import { fetchList } from '/@/api/professional/professionaluser/professionalpartychange'
|
||||
|
||||
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
|
||||
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
|
||||
|
||||
// 表格引用
|
||||
const tableRef = ref()
|
||||
@@ -100,6 +118,10 @@ const search = reactive({
|
||||
realName: ''
|
||||
})
|
||||
|
||||
// 导入加载状态
|
||||
const exportLoading = ref(false)
|
||||
const importTeacherOtherInfoRef = ref()
|
||||
|
||||
// 配置 useTable
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
pageList: async (params: any) => {
|
||||
@@ -130,6 +152,11 @@ const resetQuery = () => {
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 打开导入弹窗
|
||||
const handleImportDialog = () => {
|
||||
importTeacherOtherInfoRef.value?.init('partyChange')
|
||||
}
|
||||
|
||||
// 表格数据由 useTable(createdIsNeed 默认 true)在挂载时自动请求
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<!-- 搜索表单 -->
|
||||
<div class="page-cards">
|
||||
<div class="page-wrapper">
|
||||
<!-- 筛选卡片 -->
|
||||
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||
<search-form
|
||||
v-show="showSearch"
|
||||
:model="search"
|
||||
ref="searchFormRef"
|
||||
@keyup-enter="handleFilter"
|
||||
@@ -24,7 +24,6 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="工号" prop="teacherNo">
|
||||
<el-input
|
||||
v-model="search.teacherNo"
|
||||
@@ -32,7 +31,6 @@
|
||||
placeholder="请输入工号"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="姓名" prop="realName">
|
||||
<el-input
|
||||
v-model="search.realName"
|
||||
@@ -42,8 +40,6 @@
|
||||
</el-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<template #actions>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleFilter" icon="Search">查询</el-button>
|
||||
@@ -51,26 +47,50 @@
|
||||
</el-form-item>
|
||||
</template>
|
||||
</search-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<el-row>
|
||||
<div class="mb15" style="width: 100%;">
|
||||
<!-- 列表内容卡片 -->
|
||||
<el-card class="content-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Medal /></el-icon>
|
||||
职业资格信息
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<div class="action-group">
|
||||
<el-button
|
||||
v-if="hasAuth('professional_professionalqualificationrelation_add')"
|
||||
type="primary"
|
||||
icon="FolderAdd"
|
||||
@click="handleAdd">新 增
|
||||
</el-button>
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-if="hasAuth('professional_teacherbase_export')"
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleDownLoadWord"
|
||||
:loading="exportLoading">导出信息
|
||||
</el-button>
|
||||
:loading="exportLoading"
|
||||
>导出信息</el-button>
|
||||
<el-button
|
||||
v-auth="'professional_teacherinfo_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog"
|
||||
>导入信息</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
<div class="header-right">
|
||||
<RightToolbar
|
||||
v-model:showSearch="showSearch"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 表格 -->
|
||||
<el-table
|
||||
@@ -79,6 +99,7 @@
|
||||
row-key="id"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
stripe
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
>
|
||||
@@ -180,6 +201,8 @@
|
||||
@current-change="currentChangeHandle"
|
||||
@size-change="sizeChangeHandle"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 材料预览:图片直接显示,PDF 在组件内部 dialog 中显示 -->
|
||||
<preview-file
|
||||
@@ -192,7 +215,7 @@
|
||||
<!-- 子组件 -->
|
||||
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
|
||||
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
|
||||
</div>
|
||||
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -213,11 +236,14 @@ import { getLevelList } from '/@/api/professional/rsbase/professionalqualificati
|
||||
import { getWorkTypeList } from '/@/api/professional/rsbase/professionalworktype'
|
||||
import { PROFESSIONAL_AUDIT_STATE_OPTIONS } from '/@/config/global'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { Medal } from '@element-plus/icons-vue'
|
||||
const RightToolbar = defineAsyncComponent(() => import('/@/components/RightToolbar/index.vue'))
|
||||
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
|
||||
const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/index.vue'))
|
||||
const DataForm = defineAsyncComponent(() => import('./form.vue'))
|
||||
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
|
||||
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
|
||||
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
|
||||
|
||||
// 审核状态选项
|
||||
const auditStateOptions = PROFESSIONAL_AUDIT_STATE_OPTIONS
|
||||
@@ -249,8 +275,9 @@ const search = reactive({
|
||||
// 材料预览
|
||||
const imgUrl = ref<Array<{ title: string; url: string }>>([])
|
||||
|
||||
// 导出加载状态
|
||||
// 导出/导入加载状态
|
||||
const exportLoading = ref(false)
|
||||
const importTeacherOtherInfoRef = ref()
|
||||
|
||||
// 资格等级和工种列表
|
||||
const qualificationLevelList = ref<any[]>([])
|
||||
@@ -398,6 +425,11 @@ const getWorkTypeName = (id: string | number) => {
|
||||
return item ? item.workName : '-'
|
||||
}
|
||||
|
||||
// 打开导入弹窗
|
||||
const handleImportDialog = () => {
|
||||
importTeacherOtherInfoRef.value?.init('quaRelation')
|
||||
}
|
||||
|
||||
// 加载字典数据
|
||||
const loadDictData = async () => {
|
||||
try {
|
||||
@@ -424,4 +456,5 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '/@/assets/styles/page-cards.scss';
|
||||
</style>
|
||||
|
||||
@@ -69,6 +69,14 @@
|
||||
@click="handleDownLoadWord"
|
||||
:loading="exportLoading">导出信息
|
||||
</el-button>
|
||||
<el-button
|
||||
v-auth="'professional_teacherinfo_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog">导入信息
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
@@ -218,6 +226,7 @@
|
||||
<!-- 子组件 -->
|
||||
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
|
||||
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
|
||||
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -245,6 +254,7 @@ const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/i
|
||||
const DataForm = defineAsyncComponent(() => import('./form.vue'))
|
||||
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
|
||||
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
|
||||
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
|
||||
|
||||
// 审核状态选项
|
||||
const auditStateOptions = PROFESSIONAL_AUDIT_STATE_OPTIONS
|
||||
@@ -279,6 +289,7 @@ const imgUrl = ref<Array<{ title: string; url: string }>>([])
|
||||
|
||||
// 导出加载状态
|
||||
const exportLoading = ref(false)
|
||||
const importTeacherOtherInfoRef = ref()
|
||||
|
||||
// 学位、学历和教育类型列表
|
||||
const degreeList = ref<any[]>([])
|
||||
@@ -442,6 +453,11 @@ const getEducationTypeName = (id: string | number | undefined) => {
|
||||
return item ? item.name : '-'
|
||||
}
|
||||
|
||||
// 打开导入弹窗
|
||||
const handleImportDialog = () => {
|
||||
importTeacherOtherInfoRef.value?.init('eduDegree')
|
||||
}
|
||||
|
||||
// 加载字典数据
|
||||
const loadDictData = async () => {
|
||||
try {
|
||||
|
||||
@@ -69,6 +69,14 @@
|
||||
@click="handleDownLoadWord"
|
||||
:loading="exportLoading">导出信息
|
||||
</el-button>
|
||||
<el-button
|
||||
v-auth="'professional_teacherinfo_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog">导入信息
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
@@ -188,6 +196,7 @@
|
||||
<!-- 子组件 -->
|
||||
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
|
||||
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
|
||||
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -213,6 +222,7 @@ const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/i
|
||||
const DataForm = defineAsyncComponent(() => import('./form.vue'))
|
||||
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
|
||||
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
|
||||
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
|
||||
|
||||
// 审核状态选项(独立定义,防止其他页面修改时被波及)
|
||||
import type { StateOption } from '/@/components/AuditState/index.vue'
|
||||
@@ -247,6 +257,7 @@ const imgUrl = ref<Array<{ title: string; url: string }>>([])
|
||||
|
||||
// 导出加载状态
|
||||
const exportLoading = ref(false)
|
||||
const importTeacherOtherInfoRef = ref()
|
||||
|
||||
// 证书列表
|
||||
const certificateList = ref<any[]>([])
|
||||
@@ -385,6 +396,11 @@ const getCertificateName = (id: string | number) => {
|
||||
return item ? item.cretificateName : '-'
|
||||
}
|
||||
|
||||
// 打开导入弹窗
|
||||
const handleImportDialog = () => {
|
||||
importTeacherOtherInfoRef.value?.init('cerRelation')
|
||||
}
|
||||
|
||||
// 加载字典数据
|
||||
const loadDictData = async () => {
|
||||
try {
|
||||
|
||||
@@ -68,6 +68,14 @@
|
||||
@click="handleDownLoadWord"
|
||||
:loading="exportLoading">导出信息
|
||||
</el-button>
|
||||
<el-button
|
||||
v-auth="'professional_teacherinfo_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog">导入信息
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
@@ -83,9 +91,45 @@
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
<el-table-column prop="state" label="审核状态" width="120" align="center">
|
||||
<el-table-column prop="state" label="审核状态" width="130" align="center">
|
||||
<template #default="scope">
|
||||
<AuditState :state="scope.row.state" :options="auditStateOptions" />
|
||||
<DetailPopover
|
||||
v-if="scope.row.state === '-2' && scope.row.backReason"
|
||||
title="审核详情"
|
||||
placement="top"
|
||||
:width="300"
|
||||
:items="[
|
||||
{ label: '审核状态', layout: 'horizontal', content: getAuditStateTagConfig(scope.row.state)?.label },
|
||||
{ label: '驳回理由', content: scope.row.backReason, contentClass: 'reason-content' }
|
||||
]"
|
||||
>
|
||||
<template #reference>
|
||||
<ClickableTag
|
||||
:type="getAuditStateTagConfig(scope.row.state)?.type || 'danger'"
|
||||
:left-icon="getAuditStateTagConfig(scope.row.state)?.leftIcon"
|
||||
:effect="getAuditStateTagConfig(scope.row.state)?.effect || 'dark'"
|
||||
>
|
||||
{{ getAuditStateTagConfig(scope.row.state)?.label || '已驳回' }}
|
||||
</ClickableTag>
|
||||
</template>
|
||||
<template #content-0>
|
||||
<ClickableTag
|
||||
:type="getAuditStateTagConfig(scope.row.state)?.type || 'danger'"
|
||||
:left-icon="getAuditStateTagConfig(scope.row.state)?.leftIcon"
|
||||
:effect="getAuditStateTagConfig(scope.row.state)?.effect || 'dark'"
|
||||
:right-icon="null"
|
||||
>
|
||||
{{ getAuditStateTagConfig(scope.row.state)?.label || '已驳回' }}
|
||||
</ClickableTag>
|
||||
</template>
|
||||
<template #content-1>
|
||||
<div class="reason-content">
|
||||
<el-icon class="reason-icon"><Warning /></el-icon>
|
||||
<span>{{ scope.row.backReason }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</DetailPopover>
|
||||
<AuditState v-else :state="scope.row.state" :options="auditStateOptions" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -114,8 +158,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="backReason" label="驳回理由" min-width="150" align="center" show-overflow-tooltip />
|
||||
|
||||
<el-table-column label="操作" width="280" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
@@ -158,7 +200,6 @@
|
||||
type="danger"
|
||||
link
|
||||
icon="delete"
|
||||
style="margin-left: 12px"
|
||||
@click="handleDel(scope.row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -183,6 +224,7 @@
|
||||
<!-- 子组件 -->
|
||||
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
|
||||
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
|
||||
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -199,13 +241,16 @@ import {
|
||||
examObj,
|
||||
delObj
|
||||
} from '/@/api/professional/professionaluser/professionalteacherhonor'
|
||||
import { PROFESSIONAL_AUDIT_STATE_OPTIONS } from '/@/config/global'
|
||||
import { PROFESSIONAL_AUDIT_STATE_OPTIONS, getStatusConfig } from '/@/config/global'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const TeacherNameNo = defineAsyncComponent(() => import('/@/components/TeacherNameNo/index.vue'))
|
||||
const AuditState = defineAsyncComponent(() => import('/@/components/AuditState/index.vue'))
|
||||
const ClickableTag = defineAsyncComponent(() => import('/@/components/ClickableTag/index.vue'))
|
||||
const DetailPopover = defineAsyncComponent(() => import('/@/components/DetailPopover/index.vue'))
|
||||
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
|
||||
const DataForm = defineAsyncComponent(() => import('./form.vue'))
|
||||
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
|
||||
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
|
||||
|
||||
// 消息提示 hooks
|
||||
const message = useMessage()
|
||||
@@ -217,6 +262,13 @@ const { professional_state: professionalState } = useDict('professional_state')
|
||||
// 审核状态选项
|
||||
const auditStateOptions = PROFESSIONAL_AUDIT_STATE_OPTIONS
|
||||
|
||||
// 审核状态转 ClickableTag 配置(用 options 的 icon 字符串,与 AuditState 一致为实心)
|
||||
const getAuditStateTagConfig = (state: string | number) => {
|
||||
const opt = getStatusConfig(auditStateOptions, state)
|
||||
if (!opt) return null
|
||||
return { type: opt.type, label: opt.label, leftIcon: opt.icon, effect: opt.effect || 'dark' }
|
||||
}
|
||||
|
||||
// 无权限即无节点
|
||||
const { hasAuth } = useAuth()
|
||||
|
||||
@@ -239,6 +291,7 @@ const imgUrl = ref<Array<{ title: string; url: string }>>([])
|
||||
|
||||
// 导出加载状态
|
||||
const exportLoading = ref(false)
|
||||
const importTeacherOtherInfoRef = ref()
|
||||
|
||||
// 配置 useTable
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
@@ -365,8 +418,38 @@ const handleDownLoadWord = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 打开导入弹窗
|
||||
const handleImportDialog = () => {
|
||||
importTeacherOtherInfoRef.value?.init('honor')
|
||||
}
|
||||
|
||||
// 表格数据由 useTable(createdIsNeed 默认 true)在挂载时自动请求
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 驳回理由展示(与 backSchoolCheckin 一致) */
|
||||
.reason-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background-color: #fef0f0;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #f56c6c;
|
||||
|
||||
.reason-icon {
|
||||
color: #f56c6c;
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #f56c6c;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,41 +1,9 @@
|
||||
<template>
|
||||
<div class="titlerelation-page">
|
||||
<div class="page-wrapper">
|
||||
<!-- 方案 F:最上标题+右侧按钮,下方搜索,再下方表格 -->
|
||||
<!-- 内容区:最上搜索,其次标题+按钮,再下方表格 -->
|
||||
<div class="content-block">
|
||||
<!-- 最上:左侧图标+标题,右侧所有按钮 -->
|
||||
<div class="content-block__header">
|
||||
<span class="card-title">
|
||||
<el-icon class="title-icon"><Document /></el-icon>
|
||||
职称关系
|
||||
</span>
|
||||
<div class="header-actions">
|
||||
<div class="action-group">
|
||||
<el-button
|
||||
v-if="hasAuth('professional_professionaltitlerelation_add')"
|
||||
type="primary"
|
||||
icon="FolderAdd"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-if="hasAuth('professional_teacherbase_export')"
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleDownLoadWord"
|
||||
:loading="exportLoading"
|
||||
>导出信息</el-button>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<RightToolbar
|
||||
v-model:showSearch="showSearch"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下方:搜索区,方案 F 默认收起 -->
|
||||
<!-- 最上:搜索区 -->
|
||||
<div v-show="showSearch" class="content-block__filter">
|
||||
<search-form
|
||||
:model="search"
|
||||
@@ -118,6 +86,42 @@
|
||||
</search-form>
|
||||
</div>
|
||||
|
||||
<!-- 其次:左侧按钮,右侧 RightToolbar -->
|
||||
<div class="content-block__header">
|
||||
<div class="header-actions">
|
||||
<div class="action-group">
|
||||
<el-button
|
||||
v-if="hasAuth('professional_professionaltitlerelation_add')"
|
||||
type="primary"
|
||||
icon="FolderAdd"
|
||||
@click="handleAdd"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
v-if="hasAuth('professional_teacherbase_export')"
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
@click="handleDownLoadWord"
|
||||
:loading="exportLoading"
|
||||
>导出信息</el-button>
|
||||
<el-button
|
||||
v-auth="'professional_teacherinfo_import'"
|
||||
type="primary"
|
||||
plain
|
||||
icon="UploadFilled"
|
||||
:loading="exportLoading"
|
||||
@click="handleImportDialog">导入信息
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<RightToolbar
|
||||
v-model:showSearch="showSearch"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 再下方:表格 -->
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
@@ -242,12 +246,13 @@
|
||||
<MultiDialog ref="multiDialogRef" @getList="getDataList" :page="state.pagination" :nowRow="null" />
|
||||
<DataForm ref="dataFormRef" @refreshData="handleFilter" />
|
||||
<ProfessionalBackResaon ref="backReasonRef" @refreshData="handleFilter" />
|
||||
|
||||
<import-teacher-other-info ref="importTeacherOtherInfoRef" @refreshData="handleFilter"></import-teacher-other-info>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { Document } from '@element-plus/icons-vue'
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||
import { useAuth } from '/@/hooks/auth'
|
||||
import { useMessage } from '/@/hooks/message'
|
||||
@@ -270,6 +275,7 @@ const MultiDialog = defineAsyncComponent(() => import('/@/views/professional/tea
|
||||
const DataForm = defineAsyncComponent(() => import('./form.vue'))
|
||||
const ProfessionalBackResaon = defineAsyncComponent(() => import('/@/views/professional/common/professional-back-resaon.vue'))
|
||||
const previewFile = defineAsyncComponent(() => import('/@/components/tools/preview-file.vue'))
|
||||
const ImportTeacherOtherInfo = defineAsyncComponent(() => import('/@/views/professional/common/import-teacher-other-info.vue'))
|
||||
|
||||
// 无权限即无节点:使用 useAuth + v-if
|
||||
const { hasAuth } = useAuth()
|
||||
@@ -467,6 +473,11 @@ const loadDictData = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const importTeacherOtherInfoRef=ref()
|
||||
const handleImportDialog = () => {
|
||||
importTeacherOtherInfoRef.value?.init('titleRelation')
|
||||
}
|
||||
|
||||
// 初始化:仅加载下拉字典,表格数据由 useTable 在 createdIsNeed: true 时自动请求
|
||||
onMounted(async () => {
|
||||
await loadDictData()
|
||||
@@ -486,10 +497,10 @@ onMounted(async () => {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* 筛选:方案 F,内容区内置筛选区,默认收起 */
|
||||
/* 筛选:内容区最上方,无上外边距;与下方标题栏间距用 margin-bottom */
|
||||
.content-block__filter {
|
||||
padding: 16px 20px 5px 20px;
|
||||
margin-top: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 8px;
|
||||
@@ -535,21 +546,25 @@ onMounted(async () => {
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 按钮间距按规范 10px;与 RightToolbar 区隔 */
|
||||
.action-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-left: 12px;
|
||||
// border-left: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
/* 表格 */
|
||||
|
||||
@@ -68,7 +68,7 @@ import { ElNotification } from 'element-plus';
|
||||
import { Download } from '@element-plus/icons-vue';
|
||||
import global from '/@/components/tools/commondict.vue';
|
||||
import request from '/@/utils/request';
|
||||
import { exportTeacherInfoBySelf } from '/@/api/professional/professionalfile';
|
||||
import { makeExportTeacherInfoBySelfTask } from '/@/api/professional/professionalfile';
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
@@ -224,7 +224,7 @@ const exportTeacherInfo = async () => {
|
||||
teacherBaseDTO: searchData,
|
||||
};
|
||||
|
||||
exportTeacherInfoBySelf(params).then((res:any)=>{
|
||||
makeExportTeacherInfoBySelfTask(params).then((res:any)=>{
|
||||
ElNotification.success({
|
||||
title: '下载后台进行中',
|
||||
message: '操作成功'
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
action="/professional/file/importTeacherInfoSimple"
|
||||
action="/professional/file/makeImportTeacherInfoSimpleTask"
|
||||
:headers="headers"
|
||||
:accept="'.xls,.xlsx'"
|
||||
:on-success="handleUploadSuccess"
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
|
||||
<template>
|
||||
<div class="teacherbase-page">
|
||||
<div class="page-cards">
|
||||
<div class="page-wrapper">
|
||||
<!-- 筛选条件卡片:标题由 SearchForm 组件内置 -->
|
||||
<el-card v-show="showSearch" class="search-card" shadow="never">
|
||||
<search-form
|
||||
:model="search"
|
||||
ref="searchFormRef"
|
||||
filter-title="筛选"
|
||||
@keyup-enter="handleFilter(search)"
|
||||
>
|
||||
<template #default="{ visible }">
|
||||
@@ -215,14 +214,14 @@
|
||||
icon="Lock"
|
||||
@click="dialogVisible.statusDialogFormVisible=true">状态锁定
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="permissions.professional_teacherbase_export"
|
||||
type="warning"
|
||||
plain
|
||||
icon="Download"
|
||||
:loading="exportLoading"
|
||||
@click="handleDownLoadWord('')">导出WORD
|
||||
</el-button>
|
||||
<!-- <el-button-->
|
||||
<!-- v-if="permissions.professional_teacherbase_export"-->
|
||||
<!-- type="warning"-->
|
||||
<!-- plain-->
|
||||
<!-- icon="Download"-->
|
||||
<!-- :loading="exportLoading"-->
|
||||
<!-- @click="handleDownLoadWord('')">导出WORD-->
|
||||
<!-- </el-button>-->
|
||||
<el-button
|
||||
v-if="permissions.professional_teacherbase_export"
|
||||
type="warning"
|
||||
@@ -2698,96 +2697,7 @@
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// 页面整体布局
|
||||
.teacherbase-page {
|
||||
padding: 12px;
|
||||
min-height: 100%;
|
||||
background: var(--el-bg-color-page, #f5f6f8);
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
// 筛选卡片(标题栏与表单样式在 SearchForm 组件内)
|
||||
.search-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
background: var(--el-bg-color);
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 18px 20px 5px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// 列表内容卡片
|
||||
.content-card {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
background: var(--el-bg-color);
|
||||
|
||||
:deep(.el-card__header) {
|
||||
padding: 20px 20px 15px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 15px 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: var(--el-text-color-primary);
|
||||
|
||||
.title-icon {
|
||||
font-size: 16px;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
// 主表格行高稍增加
|
||||
.teacherbase-table {
|
||||
:deep(.el-table__body td),
|
||||
:deep(.el-table__header th) {
|
||||
padding: 9px 0;
|
||||
}
|
||||
}
|
||||
@import '/@/assets/styles/page-cards.scss';
|
||||
|
||||
// 列表内链接与占位(主表格与弹窗内通用)
|
||||
:deep(.certificate-link) {
|
||||
|
||||
136
src/views/purchase/acceptanceItemConfig/form.vue
Normal file
136
src/views/purchase/acceptanceItemConfig/form.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="remark" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入remark"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="关联验收类型:A:货物 B:工程 C:服务" prop="acceptanceType">
|
||||
<el-input v-model="form.acceptanceType" placeholder="请输入关联验收类型:A:货物 B:工程 C:服务"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="验收项名称" prop="itemName">
|
||||
<el-input v-model="form.itemName" placeholder="请输入验收项名称"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="是否启用 0:不启用 1:启用" prop="isEnabled">
|
||||
<el-input v-model="form.isEnabled" placeholder="请输入是否启用 0:不启用 1:启用"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="排序" prop="sortOrder">
|
||||
<el-input v-model="form.sortOrder" placeholder="请输入排序"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AcceptanceItemConfigDialog">
|
||||
// ========== 1. 导入语句 ==========
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { rule } from '/@/utils/validate';
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
import { getObj, addObj, putObj, validateExist } from '/@/api/purchase/acceptanceItemConfig';
|
||||
|
||||
// ========== 2. 组件定义 ==========
|
||||
// 定义组件事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// ========== 3. 响应式数据定义 ==========
|
||||
// 基础响应式变量
|
||||
const dataFormRef = ref(); // 表单引用
|
||||
const visible = ref(false); // 弹窗显示状态
|
||||
const loading = ref(false); // 加载状态
|
||||
|
||||
// 表单数据对象
|
||||
const form = reactive({
|
||||
id: '', // 主键
|
||||
remark: '', // ${field.fieldComment}
|
||||
acceptanceType: '', // 关联验收类型:A:货物 B:工程 C:服务
|
||||
itemName: '', // 验收项名称
|
||||
isEnabled: '', // 是否启用 0:不启用 1:启用
|
||||
sortOrder: '', // 排序
|
||||
});
|
||||
|
||||
// ========== 4. 字典数据处理 ==========
|
||||
|
||||
// ========== 5. 表单校验规则 ==========
|
||||
const dataRules = ref({
|
||||
});
|
||||
|
||||
// ========== 6. 方法定义 ==========
|
||||
// 获取详情数据
|
||||
const getAcceptanceItemConfigData = async (id: string) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await getObj({ id: id });
|
||||
// 直接将第一条数据赋值给表单
|
||||
Object.assign(form, data[0]);
|
||||
} catch (error) {
|
||||
useMessage().error('获取数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 打开弹窗方法
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取AcceptanceItemConfig信息
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getAcceptanceItemConfigData(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单方法
|
||||
const onSubmit = async () => {
|
||||
loading.value = true; // 防止重复提交
|
||||
|
||||
// 表单校验
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 根据是否有ID判断是新增还是修改
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh'); // 通知父组件刷新列表
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 7. 对外暴露 ==========
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
223
src/views/purchase/acceptanceItemConfig/index.vue
Normal file
223
src/views/purchase/acceptanceItemConfig/index.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button
|
||||
icon="folder-add"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="formDialogRef.openDialog()"
|
||||
v-auth="'purchase_acceptanceItemConfig_add'"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
icon="upload-filled"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="excelUploadRef.show()"
|
||||
v-auth="'purchase_acceptanceItemConfig_add'"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
:disabled="multiple"
|
||||
icon="Delete"
|
||||
type="primary"
|
||||
v-auth="'purchase_acceptanceItemConfig_del'"
|
||||
@click="handleDelete(selectObjs)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'purchase_acceptanceItemConfig_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right;"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 数据表格区域 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="40" />
|
||||
<el-table-column
|
||||
prop="remark"
|
||||
label="remark"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="acceptanceType"
|
||||
label="关联验收类型:A:货物 B:工程 C:服务"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="itemName"
|
||||
label="验收项名称"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="isEnabled"
|
||||
label="是否启用 0:不启用 1:启用"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="sortOrder"
|
||||
label="排序"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="edit-pen"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_acceptanceItemConfig_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="delete"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_acceptanceItemConfig_del'"
|
||||
@click="handleDelete([scope.row.id])"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页组件 -->
|
||||
<pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
v-bind="state.pagination"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增弹窗 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<!-- 导入excel弹窗 (需要在 upms-biz/resources/file 下维护模板) -->
|
||||
<upload-excel
|
||||
ref="excelUploadRef"
|
||||
title="导入"
|
||||
url="/purchase/acceptanceItemConfig/import"
|
||||
temp-url="/admin/sys-file/local/file/acceptanceItemConfig.xlsx"
|
||||
@refreshDataList="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemAcceptanceItemConfig">
|
||||
// ========== 导入声明 ==========
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObjs } from "/@/api/purchase/acceptanceItemConfig";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// ========== 组件声明 ==========
|
||||
// 异步加载表单弹窗组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
|
||||
// ========== 字典数据 ==========
|
||||
|
||||
// ========== 组件引用 ==========
|
||||
const formDialogRef = ref(); // 表单弹窗引用
|
||||
const excelUploadRef = ref(); // Excel上传弹窗引用
|
||||
const queryRef = ref(); // 查询表单引用
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
const showSearch = ref(true); // 是否显示搜索区域
|
||||
const selectObjs = ref([]) as any; // 表格多选数据
|
||||
const multiple = ref(true); // 是否多选
|
||||
|
||||
// ========== 表格状态 ==========
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {}, // 查询参数
|
||||
pageList: fetchList // 分页查询方法
|
||||
});
|
||||
|
||||
// ========== Hook引用 ==========
|
||||
// 表格相关Hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state);
|
||||
|
||||
// ========== 方法定义 ==========
|
||||
/**
|
||||
* 重置查询条件
|
||||
*/
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
// 重新查询
|
||||
getDataList();
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出Excel文件
|
||||
*/
|
||||
const exportExcel = () => {
|
||||
downBlobFile(
|
||||
'/purchase/acceptanceItemConfig/export',
|
||||
Object.assign(state.queryForm, { ids: selectObjs }),
|
||||
'acceptanceItemConfig.xlsx'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格多选事件处理
|
||||
* @param objs 选中的数据行
|
||||
*/
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除数据处理
|
||||
* @param ids 要删除的数据ID数组
|
||||
*/
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
130
src/views/purchase/puchasingAcceptContent/form.vue
Normal file
130
src/views/purchase/puchasingAcceptContent/form.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="remark" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入remark"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="配置项ID" prop="configId">
|
||||
<el-input v-model="form.configId" placeholder="请输入配置项ID"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="是否合格 0:不合格 1:合格" prop="isQualified">
|
||||
<el-input v-model="form.isQualified" placeholder="请输入是否合格 0:不合格 1:合格"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="验收批次ID" prop="acceptBatchId">
|
||||
<el-input v-model="form.acceptBatchId" placeholder="请输入验收批次ID"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PuchasingAcceptContentDialog">
|
||||
// ========== 1. 导入语句 ==========
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { rule } from '/@/utils/validate';
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
import { getObj, addObj, putObj, validateExist } from '/@/api/purchase/puchasingAcceptContent';
|
||||
|
||||
// ========== 2. 组件定义 ==========
|
||||
// 定义组件事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// ========== 3. 响应式数据定义 ==========
|
||||
// 基础响应式变量
|
||||
const dataFormRef = ref(); // 表单引用
|
||||
const visible = ref(false); // 弹窗显示状态
|
||||
const loading = ref(false); // 加载状态
|
||||
|
||||
// 表单数据对象
|
||||
const form = reactive({
|
||||
id: '', // 主键
|
||||
remark: '', // ${field.fieldComment}
|
||||
configId: '', // 配置项ID
|
||||
isQualified: '', // 是否合格 0:不合格 1:合格
|
||||
acceptBatchId: '', // 验收批次ID
|
||||
});
|
||||
|
||||
// ========== 4. 字典数据处理 ==========
|
||||
|
||||
// ========== 5. 表单校验规则 ==========
|
||||
const dataRules = ref({
|
||||
});
|
||||
|
||||
// ========== 6. 方法定义 ==========
|
||||
// 获取详情数据
|
||||
const getPuchasingAcceptContentData = async (id: string) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await getObj({ id: id });
|
||||
// 直接将第一条数据赋值给表单
|
||||
Object.assign(form, data[0]);
|
||||
} catch (error) {
|
||||
useMessage().error('获取数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 打开弹窗方法
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取PuchasingAcceptContent信息
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getPuchasingAcceptContentData(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单方法
|
||||
const onSubmit = async () => {
|
||||
loading.value = true; // 防止重复提交
|
||||
|
||||
// 表单校验
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 根据是否有ID判断是新增还是修改
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh'); // 通知父组件刷新列表
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 7. 对外暴露 ==========
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
218
src/views/purchase/puchasingAcceptContent/index.vue
Normal file
218
src/views/purchase/puchasingAcceptContent/index.vue
Normal file
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button
|
||||
icon="folder-add"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="formDialogRef.openDialog()"
|
||||
v-auth="'purchase_puchasingAcceptContent_add'"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
icon="upload-filled"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="excelUploadRef.show()"
|
||||
v-auth="'purchase_puchasingAcceptContent_add'"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
:disabled="multiple"
|
||||
icon="Delete"
|
||||
type="primary"
|
||||
v-auth="'purchase_puchasingAcceptContent_del'"
|
||||
@click="handleDelete(selectObjs)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'purchase_puchasingAcceptContent_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right;"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 数据表格区域 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="40" />
|
||||
<el-table-column
|
||||
prop="remark"
|
||||
label="remark"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="configId"
|
||||
label="配置项ID"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="isQualified"
|
||||
label="是否合格 0:不合格 1:合格"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="acceptBatchId"
|
||||
label="验收批次ID"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="edit-pen"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_puchasingAcceptContent_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="delete"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_puchasingAcceptContent_del'"
|
||||
@click="handleDelete([scope.row.id])"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页组件 -->
|
||||
<pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
v-bind="state.pagination"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增弹窗 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<!-- 导入excel弹窗 (需要在 upms-biz/resources/file 下维护模板) -->
|
||||
<upload-excel
|
||||
ref="excelUploadRef"
|
||||
title="导入"
|
||||
url="/purchase/puchasingAcceptContent/import"
|
||||
temp-url="/admin/sys-file/local/file/puchasingAcceptContent.xlsx"
|
||||
@refreshDataList="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemPuchasingAcceptContent">
|
||||
// ========== 导入声明 ==========
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObjs } from "/@/api/purchase/puchasingAcceptContent";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// ========== 组件声明 ==========
|
||||
// 异步加载表单弹窗组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
|
||||
// ========== 字典数据 ==========
|
||||
|
||||
// ========== 组件引用 ==========
|
||||
const formDialogRef = ref(); // 表单弹窗引用
|
||||
const excelUploadRef = ref(); // Excel上传弹窗引用
|
||||
const queryRef = ref(); // 查询表单引用
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
const showSearch = ref(true); // 是否显示搜索区域
|
||||
const selectObjs = ref([]) as any; // 表格多选数据
|
||||
const multiple = ref(true); // 是否多选
|
||||
|
||||
// ========== 表格状态 ==========
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {}, // 查询参数
|
||||
pageList: fetchList // 分页查询方法
|
||||
});
|
||||
|
||||
// ========== Hook引用 ==========
|
||||
// 表格相关Hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state);
|
||||
|
||||
// ========== 方法定义 ==========
|
||||
/**
|
||||
* 重置查询条件
|
||||
*/
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
// 重新查询
|
||||
getDataList();
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出Excel文件
|
||||
*/
|
||||
const exportExcel = () => {
|
||||
downBlobFile(
|
||||
'/purchase/puchasingAcceptContent/export',
|
||||
Object.assign(state.queryForm, { ids: selectObjs }),
|
||||
'puchasingAcceptContent.xlsx'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格多选事件处理
|
||||
* @param objs 选中的数据行
|
||||
*/
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除数据处理
|
||||
* @param ids 要删除的数据ID数组
|
||||
*/
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
136
src/views/purchase/puchasingAcceptTeam/form.vue
Normal file
136
src/views/purchase/puchasingAcceptTeam/form.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="remark" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入remark"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="所在部门编码" prop="deptCode">
|
||||
<el-input v-model="form.deptCode" placeholder="请输入所在部门编码"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="所在部门" prop="deptName">
|
||||
<el-input v-model="form.deptName" placeholder="请输入所在部门"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入姓名"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="验收批次ID" prop="acceptBatchId">
|
||||
<el-input v-model="form.acceptBatchId" placeholder="请输入验收批次ID"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PuchasingAcceptTeamDialog">
|
||||
// ========== 1. 导入语句 ==========
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { rule } from '/@/utils/validate';
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
import { getObj, addObj, putObj, validateExist } from '/@/api/purchase/puchasingAcceptTeam';
|
||||
|
||||
// ========== 2. 组件定义 ==========
|
||||
// 定义组件事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// ========== 3. 响应式数据定义 ==========
|
||||
// 基础响应式变量
|
||||
const dataFormRef = ref(); // 表单引用
|
||||
const visible = ref(false); // 弹窗显示状态
|
||||
const loading = ref(false); // 加载状态
|
||||
|
||||
// 表单数据对象
|
||||
const form = reactive({
|
||||
id: '', // 主键
|
||||
remark: '', // ${field.fieldComment}
|
||||
deptCode: '', // 所在部门编码
|
||||
deptName: '', // 所在部门
|
||||
name: '', // 姓名
|
||||
acceptBatchId: '', // 验收批次ID
|
||||
});
|
||||
|
||||
// ========== 4. 字典数据处理 ==========
|
||||
|
||||
// ========== 5. 表单校验规则 ==========
|
||||
const dataRules = ref({
|
||||
});
|
||||
|
||||
// ========== 6. 方法定义 ==========
|
||||
// 获取详情数据
|
||||
const getPuchasingAcceptTeamData = async (id: string) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await getObj({ id: id });
|
||||
// 直接将第一条数据赋值给表单
|
||||
Object.assign(form, data[0]);
|
||||
} catch (error) {
|
||||
useMessage().error('获取数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 打开弹窗方法
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取PuchasingAcceptTeam信息
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getPuchasingAcceptTeamData(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单方法
|
||||
const onSubmit = async () => {
|
||||
loading.value = true; // 防止重复提交
|
||||
|
||||
// 表单校验
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 根据是否有ID判断是新增还是修改
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh'); // 通知父组件刷新列表
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 7. 对外暴露 ==========
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
223
src/views/purchase/puchasingAcceptTeam/index.vue
Normal file
223
src/views/purchase/puchasingAcceptTeam/index.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button
|
||||
icon="folder-add"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="formDialogRef.openDialog()"
|
||||
v-auth="'purchase_puchasingAcceptTeam_add'"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
icon="upload-filled"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="excelUploadRef.show()"
|
||||
v-auth="'purchase_puchasingAcceptTeam_add'"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
:disabled="multiple"
|
||||
icon="Delete"
|
||||
type="primary"
|
||||
v-auth="'purchase_puchasingAcceptTeam_del'"
|
||||
@click="handleDelete(selectObjs)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'purchase_puchasingAcceptTeam_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right;"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 数据表格区域 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="40" />
|
||||
<el-table-column
|
||||
prop="remark"
|
||||
label="remark"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="deptCode"
|
||||
label="所在部门编码"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="deptName"
|
||||
label="所在部门"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="姓名"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="acceptBatchId"
|
||||
label="验收批次ID"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="edit-pen"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_puchasingAcceptTeam_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="delete"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_puchasingAcceptTeam_del'"
|
||||
@click="handleDelete([scope.row.id])"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页组件 -->
|
||||
<pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
v-bind="state.pagination"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增弹窗 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<!-- 导入excel弹窗 (需要在 upms-biz/resources/file 下维护模板) -->
|
||||
<upload-excel
|
||||
ref="excelUploadRef"
|
||||
title="导入"
|
||||
url="/purchase/puchasingAcceptTeam/import"
|
||||
temp-url="/admin/sys-file/local/file/puchasingAcceptTeam.xlsx"
|
||||
@refreshDataList="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemPuchasingAcceptTeam">
|
||||
// ========== 导入声明 ==========
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObjs } from "/@/api/purchase/puchasingAcceptTeam";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// ========== 组件声明 ==========
|
||||
// 异步加载表单弹窗组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
|
||||
// ========== 字典数据 ==========
|
||||
|
||||
// ========== 组件引用 ==========
|
||||
const formDialogRef = ref(); // 表单弹窗引用
|
||||
const excelUploadRef = ref(); // Excel上传弹窗引用
|
||||
const queryRef = ref(); // 查询表单引用
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
const showSearch = ref(true); // 是否显示搜索区域
|
||||
const selectObjs = ref([]) as any; // 表格多选数据
|
||||
const multiple = ref(true); // 是否多选
|
||||
|
||||
// ========== 表格状态 ==========
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {}, // 查询参数
|
||||
pageList: fetchList // 分页查询方法
|
||||
});
|
||||
|
||||
// ========== Hook引用 ==========
|
||||
// 表格相关Hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state);
|
||||
|
||||
// ========== 方法定义 ==========
|
||||
/**
|
||||
* 重置查询条件
|
||||
*/
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
// 重新查询
|
||||
getDataList();
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出Excel文件
|
||||
*/
|
||||
const exportExcel = () => {
|
||||
downBlobFile(
|
||||
'/purchase/puchasingAcceptTeam/export',
|
||||
Object.assign(state.queryForm, { ids: selectObjs }),
|
||||
'puchasingAcceptTeam.xlsx'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格多选事件处理
|
||||
* @param objs 选中的数据行
|
||||
*/
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除数据处理
|
||||
* @param ids 要删除的数据ID数组
|
||||
*/
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
142
src/views/purchase/purchasingAccept/form.vue
Normal file
142
src/views/purchase/purchasingAccept/form.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible"
|
||||
:close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="remark" prop="remark">
|
||||
<el-input v-model="form.remark" placeholder="请输入remark"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="采购申请ID" prop="purchaseId">
|
||||
<el-input v-model="form.purchaseId" placeholder="请输入采购申请ID"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="验收日期" prop="acceptDate">
|
||||
<el-input v-model="form.acceptDate" placeholder="请输入验收日期"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="验收地点" prop="acceptAddress">
|
||||
<el-input v-model="form.acceptAddress" placeholder="请输入验收地点"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="验收批次" prop="batch">
|
||||
<el-input v-model="form.batch" placeholder="请输入验收批次"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="问题意见" prop="question">
|
||||
<el-input v-model="form.question" placeholder="请输入问题意见"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确 认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="PurchasingAcceptDialog">
|
||||
// ========== 1. 导入语句 ==========
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { rule } from '/@/utils/validate';
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
import { getObj, addObj, putObj, validateExist } from '/@/api/purchase/purchasingAccept';
|
||||
|
||||
// ========== 2. 组件定义 ==========
|
||||
// 定义组件事件
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// ========== 3. 响应式数据定义 ==========
|
||||
// 基础响应式变量
|
||||
const dataFormRef = ref(); // 表单引用
|
||||
const visible = ref(false); // 弹窗显示状态
|
||||
const loading = ref(false); // 加载状态
|
||||
|
||||
// 表单数据对象
|
||||
const form = reactive({
|
||||
id: '', // 主键
|
||||
remark: '', // ${field.fieldComment}
|
||||
purchaseId: '', // 采购申请ID
|
||||
acceptDate: '', // 验收日期
|
||||
acceptAddress: '', // 验收地点
|
||||
batch: '', // 验收批次
|
||||
question: '', // 问题意见
|
||||
});
|
||||
|
||||
// ========== 4. 字典数据处理 ==========
|
||||
|
||||
// ========== 5. 表单校验规则 ==========
|
||||
const dataRules = ref({
|
||||
});
|
||||
|
||||
// ========== 6. 方法定义 ==========
|
||||
// 获取详情数据
|
||||
const getPurchasingAcceptData = async (id: string) => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await getObj({ id: id });
|
||||
// 直接将第一条数据赋值给表单
|
||||
Object.assign(form, data[0]);
|
||||
} catch (error) {
|
||||
useMessage().error('获取数据失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 打开弹窗方法
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// 获取PurchasingAccept信息
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getPurchasingAcceptData(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单方法
|
||||
const onSubmit = async () => {
|
||||
loading.value = true; // 防止重复提交
|
||||
|
||||
// 表单校验
|
||||
const valid = await dataFormRef.value.validate().catch(() => {});
|
||||
if (!valid) {
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 根据是否有ID判断是新增还是修改
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh'); // 通知父组件刷新列表
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 7. 对外暴露 ==========
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
228
src/views/purchase/purchasingAccept/index.vue
Normal file
228
src/views/purchase/purchasingAccept/index.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button
|
||||
icon="folder-add"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="formDialogRef.openDialog()"
|
||||
v-auth="'purchase_purchasingAccept_add'"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
icon="upload-filled"
|
||||
type="primary"
|
||||
class="ml10"
|
||||
@click="excelUploadRef.show()"
|
||||
v-auth="'purchase_purchasingAccept_add'"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
:disabled="multiple"
|
||||
icon="Delete"
|
||||
type="primary"
|
||||
v-auth="'purchase_purchasingAccept_del'"
|
||||
@click="handleDelete(selectObjs)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'purchase_purchasingAccept_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right;"
|
||||
@queryTable="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<!-- 数据表格区域 -->
|
||||
<el-table
|
||||
:data="state.dataList"
|
||||
v-loading="state.loading"
|
||||
border
|
||||
:cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle"
|
||||
@selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle"
|
||||
>
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="40" />
|
||||
<el-table-column
|
||||
prop="remark"
|
||||
label="remark"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="purchaseId"
|
||||
label="采购申请ID"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="acceptDate"
|
||||
label="验收日期"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="acceptAddress"
|
||||
label="验收地点"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="batch"
|
||||
label="验收批次"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="question"
|
||||
label="问题意见"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
icon="edit-pen"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_purchasingAccept_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="delete"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'purchase_purchasingAccept_del'"
|
||||
@click="handleDelete([scope.row.id])"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页组件 -->
|
||||
<pagination
|
||||
@size-change="sizeChangeHandle"
|
||||
@current-change="currentChangeHandle"
|
||||
v-bind="state.pagination"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增弹窗 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
<!-- 导入excel弹窗 (需要在 upms-biz/resources/file 下维护模板) -->
|
||||
<upload-excel
|
||||
ref="excelUploadRef"
|
||||
title="导入"
|
||||
url="/purchase/purchasingAccept/import"
|
||||
temp-url="/admin/sys-file/local/file/purchasingAccept.xlsx"
|
||||
@refreshDataList="getDataList"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemPurchasingAccept">
|
||||
// ========== 导入声明 ==========
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObjs } from "/@/api/purchase/purchasingAccept";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// ========== 组件声明 ==========
|
||||
// 异步加载表单弹窗组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
|
||||
// ========== 字典数据 ==========
|
||||
|
||||
// ========== 组件引用 ==========
|
||||
const formDialogRef = ref(); // 表单弹窗引用
|
||||
const excelUploadRef = ref(); // Excel上传弹窗引用
|
||||
const queryRef = ref(); // 查询表单引用
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
const showSearch = ref(true); // 是否显示搜索区域
|
||||
const selectObjs = ref([]) as any; // 表格多选数据
|
||||
const multiple = ref(true); // 是否多选
|
||||
|
||||
// ========== 表格状态 ==========
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {}, // 查询参数
|
||||
pageList: fetchList // 分页查询方法
|
||||
});
|
||||
|
||||
// ========== Hook引用 ==========
|
||||
// 表格相关Hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state);
|
||||
|
||||
// ========== 方法定义 ==========
|
||||
/**
|
||||
* 重置查询条件
|
||||
*/
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
// 重新查询
|
||||
getDataList();
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出Excel文件
|
||||
*/
|
||||
const exportExcel = () => {
|
||||
downBlobFile(
|
||||
'/purchase/purchasingAccept/export',
|
||||
Object.assign(state.queryForm, { ids: selectObjs }),
|
||||
'purchasingAccept.xlsx'
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格多选事件处理
|
||||
* @param objs 选中的数据行
|
||||
*/
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除数据处理
|
||||
* @param ids 要删除的数据ID数组
|
||||
*/
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user