Compare commits
12 Commits
e15601c13c
...
f047d9a461
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f047d9a461 | ||
|
|
bff8907f88 | ||
|
|
cc9e0a1022 | ||
|
|
1e6d6ae981 | ||
|
|
c3daa41f10 | ||
|
|
48b31285c8 | ||
|
|
7f61c9bdce | ||
|
|
69439fdfab | ||
|
|
e3437921e5 | ||
|
|
9ecb26732f | ||
|
|
4a1e096925 | ||
|
|
39bab0f1c2 |
14450
docs/默认模块.openapi.json
14450
docs/默认模块.openapi.json
File diff suppressed because it is too large
Load Diff
45
src/App.vue
45
src/App.vue
@@ -17,6 +17,7 @@ import { Local, Session } from '/@/utils/storage';
|
|||||||
import mittBus from '/@/utils/mitt';
|
import mittBus from '/@/utils/mitt';
|
||||||
import { needRoleSelection, isRoleDialogTriggered, setRoleDialogTriggered } from '/@/utils/roleSelect';
|
import { needRoleSelection, isRoleDialogTriggered, setRoleDialogTriggered } from '/@/utils/roleSelect';
|
||||||
import setIntroduction from '/@/utils/setIconfont';
|
import setIntroduction from '/@/utils/setIconfont';
|
||||||
|
import { listAllRole } from '/@/api/admin/role';
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
|
const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
|
||||||
@@ -58,6 +59,34 @@ onBeforeMount(() => {
|
|||||||
});
|
});
|
||||||
// 角色选择弹框是否已在本轮打开过(防止事件被触发两次)
|
// 角色选择弹框是否已在本轮打开过(防止事件被触发两次)
|
||||||
let roleDialogOpenedThisSession = false
|
let roleDialogOpenedThisSession = false
|
||||||
|
|
||||||
|
/** 校验缓存中的 roleId 是否仍在 listAllRole 结果中;若不存在则清除 roleId/roleCode/roleName 并返回 true(需要弹框) */
|
||||||
|
async function validateCachedRoleId(): Promise<boolean> {
|
||||||
|
const cachedRoleId = Local.get('roleId');
|
||||||
|
if (cachedRoleId == null || cachedRoleId === '') return false;
|
||||||
|
try {
|
||||||
|
const res = await listAllRole();
|
||||||
|
const data = res?.data;
|
||||||
|
const allRoles: any[] = Array.isArray(data)
|
||||||
|
? data
|
||||||
|
: data && typeof data === 'object'
|
||||||
|
? (Object.values(data) as any[]).flat()
|
||||||
|
: [];
|
||||||
|
const exists = allRoles.some(
|
||||||
|
(r: any) => r && String(r.roleId) === String(cachedRoleId)
|
||||||
|
);
|
||||||
|
if (!exists) {
|
||||||
|
Local.remove('roleId');
|
||||||
|
Local.remove('roleCode');
|
||||||
|
Local.remove('roleName');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 唯一入口:只通过事件打开,且只打开一次;延迟打开以等待异步组件挂载
|
// 唯一入口:只通过事件打开,且只打开一次;延迟打开以等待异步组件挂载
|
||||||
mittBus.on('openRoleSelectDialog', () => {
|
mittBus.on('openRoleSelectDialog', () => {
|
||||||
@@ -67,7 +96,7 @@ onMounted(() => {
|
|||||||
changeRoleFirRef.value?.open()
|
changeRoleFirRef.value?.open()
|
||||||
}, 300)
|
}, 300)
|
||||||
})
|
})
|
||||||
nextTick(() => {
|
nextTick(async () => {
|
||||||
// 监听布局配置弹窗点击打开
|
// 监听布局配置弹窗点击打开
|
||||||
mittBus.on('openSettingsDrawer', () => {
|
mittBus.on('openSettingsDrawer', () => {
|
||||||
settingsRef.value.openDrawer();
|
settingsRef.value.openDrawer();
|
||||||
@@ -81,10 +110,16 @@ onMounted(() => {
|
|||||||
if (Session.get('isTagsViewCurrenFull')) {
|
if (Session.get('isTagsViewCurrenFull')) {
|
||||||
stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
|
stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
|
||||||
}
|
}
|
||||||
// 与请求拦截器共用同一逻辑:先设标志再 emit,由监听器统一打开(监听器内会延迟 300ms 以等待异步组件挂载)
|
// 有 token 时:先校验缓存 roleId 是否仍有效,无效则清缓存并弹框选角色
|
||||||
if (Session.getToken() && needRoleSelection() && !isRoleDialogTriggered()) {
|
if (Session.getToken()) {
|
||||||
setRoleDialogTriggered(true)
|
const needOpenByInvalidRole = await validateCachedRoleId();
|
||||||
mittBus.emit('openRoleSelectDialog')
|
if (needOpenByInvalidRole && !isRoleDialogTriggered()) {
|
||||||
|
setRoleDialogTriggered(true);
|
||||||
|
mittBus.emit('openRoleSelectDialog');
|
||||||
|
} else if (!needOpenByInvalidRole && needRoleSelection() && !isRoleDialogTriggered()) {
|
||||||
|
setRoleDialogTriggered(true);
|
||||||
|
mittBus.emit('openRoleSelectDialog');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -124,6 +124,20 @@ export function canFillForm(purchaseId: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载履约验收模板(根据金额判断使用down/up目录)
|
||||||
|
* @param purchaseId 采购申请ID
|
||||||
|
* @returns Promise<Blob> - 模板文件 blob
|
||||||
|
*/
|
||||||
|
export function downloadTemplate(purchaseId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingAccept/download-template',
|
||||||
|
method: 'get',
|
||||||
|
params: { purchaseId },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据品目类型获取验收项配置
|
* 根据品目类型获取验收项配置
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -252,12 +252,13 @@ export function searchTeachers(keyword: string) {
|
|||||||
* @param id 采购申请ID
|
* @param id 采购申请ID
|
||||||
* @param representorTeacherNo 指定采购代表人(单人,用户ID或工号)
|
* @param representorTeacherNo 指定采购代表人(单人,用户ID或工号)
|
||||||
* @param representors 部门多人由系统抽取(多人,用户ID或工号逗号分隔)
|
* @param representors 部门多人由系统抽取(多人,用户ID或工号逗号分隔)
|
||||||
|
* @param identity 身份:purchase_rep-采购代表,judge-评委
|
||||||
*/
|
*/
|
||||||
export function saveRepresentor(id: number, representorTeacherNo?: string, representors?: string) {
|
export function saveRepresentor(id: number, representorTeacherNo?: string, representors?: string, identity?: string) {
|
||||||
return request({
|
return request({
|
||||||
url: '/purchase/purchasingapply/saveRepresentor',
|
url: '/purchase/purchasingapply/save-representor',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: { id, representorTeacherNo, representors }
|
data: { id, representorTeacherNo, representors, representorType: identity }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +370,15 @@ export function downloadFileById(fileId: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function previewFileById(fileId: string) {
|
||||||
|
return request({
|
||||||
|
url: '/purchase/purchasingfiles/previewById',
|
||||||
|
method: 'get',
|
||||||
|
params: { fileId },
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量获取文件下载地址列表
|
* 批量获取文件下载地址列表
|
||||||
* @param purchaseId 采购申请ID
|
* @param purchaseId 采购申请ID
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import request from "/@/utils/request"
|
import request from '/@/utils/request'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据分页查询参数获取列表数据。
|
* 根据分页查询参数获取列表数据。
|
||||||
@@ -9,7 +9,7 @@ export function fetchList(query?: Object) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/stuwork/weekPlan/page',
|
url: '/stuwork/weekPlan/page',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ export function addObj(obj?: Object) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/stuwork/weekPlan',
|
url: '/stuwork/weekPlan',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj
|
data: obj,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export function getObj(obj?: Object) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/stuwork/weekPlan/detail',
|
url: '/stuwork/weekPlan/detail',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: obj
|
params: obj,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export function delObjs(ids?: Object) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/stuwork/weekPlan/delete',
|
url: '/stuwork/weekPlan/delete',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: ids
|
data: ids,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,20 @@ export function putObj(obj?: Object) {
|
|||||||
return request({
|
return request({
|
||||||
url: '/stuwork/weekPlan/edit',
|
url: '/stuwork/weekPlan/edit',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj
|
data: obj,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计班主任查看情况
|
||||||
|
* GET /api/stuwork/weekPlan/readStatistics
|
||||||
|
* @param params 包含 deptCode, weekPlanId
|
||||||
|
*/
|
||||||
|
export function readStatistics(params: { deptCode: string; weekPlanId: string }) {
|
||||||
|
return request({
|
||||||
|
url: '/stuwork/weekPlan/readStatistics',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,17 +97,16 @@ export function putObj(obj?: Object) {
|
|||||||
*/
|
*/
|
||||||
export function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {
|
export function validateExist(rule: any, value: any, callback: any, isEdit: boolean) {
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
return callback();
|
return callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
getObj({ [rule.field]: value }).then((response) => {
|
getObj({ [rule.field]: value }).then(response => {
|
||||||
const result = response.data;
|
const result = response.data
|
||||||
if (result !== null && result.length > 0) {
|
if (result !== null && result.length > 0) {
|
||||||
callback(new Error('数据已经存在'));
|
callback(new Error('数据已经存在'))
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -441,6 +441,7 @@
|
|||||||
else {
|
else {
|
||||||
setPropsDataValue(row, $route.query, 'id', 'flowInstId', 'isHiJob', 'isView', 'isRead', 'isApp', 'isForm')
|
setPropsDataValue(row, $route.query, 'id', 'flowInstId', 'isHiJob', 'isView', 'isRead', 'isApp', 'isForm')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row.isHiJob !== '0') {
|
if (row.isHiJob !== '0') {
|
||||||
await handleToDoneDetail(row, data, row.isApp, row.isForm);
|
await handleToDoneDetail(row, data, row.isApp, row.isForm);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -114,6 +114,34 @@ const isActive = (v: RouteItem) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/** 标题为「轮播图」的 tag 不展示(切换角色后常误出现,直接过滤) */
|
||||||
|
const isCarouselTag = (v: RouteItem) => other.setTagsViewNameI18n(v) === '轮播图';
|
||||||
|
/** 仅首页作为固定 tag,其它 isAffix(如后端菜单的轮播图)不再默认占一条 tag */
|
||||||
|
const isAffixTagAllowed = (v: RouteItem) => v.path === '/home' || v.path === '/home/index';
|
||||||
|
/** 后端菜单 sortOrder/sort_order/weight 为 1 的项常被误当“默认”加载为 tag,排除(首页除外) */
|
||||||
|
const isSortOrderOneExcluded = (v: RouteItem) => {
|
||||||
|
const meta = v.meta as any;
|
||||||
|
const order =
|
||||||
|
meta?.sortOrder ?? meta?.sort_order ?? meta?.weight ?? (v as any).sort_order ?? (v as any).weight;
|
||||||
|
return Number(order) === 1 && !isAffixTagAllowed(v);
|
||||||
|
};
|
||||||
|
/** 递归扁平化路由(含 children),用于校验 path 是否在当前角色菜单中 */
|
||||||
|
const flattenRoutes = (routes: RouteItem[]): RouteItem[] => {
|
||||||
|
let list: RouteItem[] = [];
|
||||||
|
(routes || []).forEach((r: RouteItem) => {
|
||||||
|
list.push(r);
|
||||||
|
if ((r as any).children?.length) list.push(...flattenRoutes((r as any).children));
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
/** 当前 tag 的 path 是否存在于当前角色路由列表中(避免恢复出“不存在的 tag”) */
|
||||||
|
const pathInCurrentRoutes = (tag: RouteItem): boolean => {
|
||||||
|
const flat = flattenRoutes(state.tagsViewRoutesList);
|
||||||
|
return flat.some(
|
||||||
|
(r: RouteItem) =>
|
||||||
|
r.path === tag.path || (r.meta?.isDynamic && (r.meta as any).isDynamicPath === tag.path)
|
||||||
|
);
|
||||||
|
};
|
||||||
// 存储 tagsViewList 到浏览器临时缓存中,页面刷新时,保留记录
|
// 存储 tagsViewList 到浏览器临时缓存中,页面刷新时,保留记录
|
||||||
const addBrowserSetSession = (tagsViewList: Array<object>) => {
|
const addBrowserSetSession = (tagsViewList: Array<object>) => {
|
||||||
Session.set('tagsViewList', tagsViewList);
|
Session.set('tagsViewList', tagsViewList);
|
||||||
@@ -128,11 +156,20 @@ const getTagsViewRoutes = async () => {
|
|||||||
};
|
};
|
||||||
// pinia 中获取路由信息:如果是设置了固定的(isAffix),进行初始化显示
|
// pinia 中获取路由信息:如果是设置了固定的(isAffix),进行初始化显示
|
||||||
const initTagsView = async () => {
|
const initTagsView = async () => {
|
||||||
if (Session.get('tagsViewList') && getThemeConfig.value.isCacheTagsView) {
|
const cached = Session.get('tagsViewList');
|
||||||
state.tagsViewList = await Session.get('tagsViewList');
|
const hasValidCache = cached && getThemeConfig.value.isCacheTagsView && Array.isArray(cached);
|
||||||
|
// 仅当当前角色已有路由列表时,才从缓存恢复;恢复时只保留当前路由中存在的 path,避免切换角色后出现“不存在的 tag”
|
||||||
|
if (hasValidCache && state.tagsViewRoutesList.length > 0) {
|
||||||
|
state.tagsViewList = cached.filter(
|
||||||
|
(v: RouteItem) =>
|
||||||
|
!isCarouselTag(v) &&
|
||||||
|
!isSortOrderOneExcluded(v) &&
|
||||||
|
pathInCurrentRoutes(v) &&
|
||||||
|
(v.meta?.isAffix ? isAffixTagAllowed(v) : true)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await state.tagsViewRoutesList.map((v: RouteItem) => {
|
await state.tagsViewRoutesList.map((v: RouteItem) => {
|
||||||
if (v.meta?.isAffix && !v.meta.isHide) {
|
if (v.meta?.isAffix && !v.meta.isHide && !isCarouselTag(v) && isAffixTagAllowed(v) && !isSortOrderOneExcluded(v)) {
|
||||||
v.url = setTagsViewHighlight(v);
|
v.url = setTagsViewHighlight(v);
|
||||||
state.tagsViewList.push({ ...v });
|
state.tagsViewList.push({ ...v });
|
||||||
storesKeepALiveNames.addCachedView(v);
|
storesKeepALiveNames.addCachedView(v);
|
||||||
@@ -158,6 +195,8 @@ const solveAddTagsView = async (path: string, to?: RouteToFrom) => {
|
|||||||
// 防止:Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
|
// 防止:Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.
|
||||||
let findItem = state.tagsViewRoutesList.find((v: RouteItem) => v.path === isDynamicPath);
|
let findItem = state.tagsViewRoutesList.find((v: RouteItem) => v.path === isDynamicPath);
|
||||||
if (!findItem) return false;
|
if (!findItem) return false;
|
||||||
|
if (isCarouselTag(findItem)) return false;
|
||||||
|
if (isSortOrderOneExcluded(findItem)) return false;
|
||||||
if (findItem.meta.isAffix) return false;
|
if (findItem.meta.isAffix) return false;
|
||||||
if (findItem.meta.isLink && !findItem.meta.isIframe) return false;
|
if (findItem.meta.isLink && !findItem.meta.isIframe) return false;
|
||||||
to?.meta?.isDynamic ? (findItem.params = to.params) : (findItem.query = to?.query);
|
to?.meta?.isDynamic ? (findItem.params = to.params) : (findItem.query = to?.query);
|
||||||
@@ -211,6 +250,8 @@ const addTagsView = (path: string, to?: RouteToFrom) => {
|
|||||||
item = state.tagsViewRoutesList.find((v: RouteItem) => v.path === path);
|
item = state.tagsViewRoutesList.find((v: RouteItem) => v.path === path);
|
||||||
}
|
}
|
||||||
if (!item) return false;
|
if (!item) return false;
|
||||||
|
if (isCarouselTag(item)) return false;
|
||||||
|
if (isSortOrderOneExcluded(item)) return false;
|
||||||
if (item?.meta?.isLink && !item.meta.isIframe) return false;
|
if (item?.meta?.isLink && !item.meta.isIframe) return false;
|
||||||
if (to?.meta?.isDynamic) item.params = to?.params ? to?.params : route.params;
|
if (to?.meta?.isDynamic) item.params = to?.params ? to?.params : route.params;
|
||||||
else item.query = to?.query ? to?.query : route.query;
|
else item.query = to?.query ? to?.query : route.query;
|
||||||
@@ -279,7 +320,7 @@ const closeOtherTagsView = (path: string) => {
|
|||||||
if (Session.get('tagsViewList')) {
|
if (Session.get('tagsViewList')) {
|
||||||
state.tagsViewList = [];
|
state.tagsViewList = [];
|
||||||
Session.get('tagsViewList').map((v: RouteItem) => {
|
Session.get('tagsViewList').map((v: RouteItem) => {
|
||||||
if (v.meta?.isAffix && !v.meta.isHide) {
|
if (v.meta?.isAffix && !v.meta?.isHide && !isCarouselTag(v) && pathInCurrentRoutes(v) && isAffixTagAllowed(v) && !isSortOrderOneExcluded(v)) {
|
||||||
v.url = setTagsViewHighlight(v);
|
v.url = setTagsViewHighlight(v);
|
||||||
storesKeepALiveNames.delOthersCachedViews(v);
|
storesKeepALiveNames.delOthersCachedViews(v);
|
||||||
state.tagsViewList.push({ ...v });
|
state.tagsViewList.push({ ...v });
|
||||||
@@ -295,7 +336,7 @@ const closeAllTagsView = () => {
|
|||||||
storesKeepALiveNames.delAllCachedViews();
|
storesKeepALiveNames.delAllCachedViews();
|
||||||
state.tagsViewList = [];
|
state.tagsViewList = [];
|
||||||
Session.get('tagsViewList').map((v: RouteItem) => {
|
Session.get('tagsViewList').map((v: RouteItem) => {
|
||||||
if (v.meta?.isAffix && !v.meta.isHide) {
|
if (v.meta?.isAffix && !v.meta?.isHide && !isCarouselTag(v) && pathInCurrentRoutes(v) && isAffixTagAllowed(v) && !isSortOrderOneExcluded(v)) {
|
||||||
v.url = setTagsViewHighlight(v);
|
v.url = setTagsViewHighlight(v);
|
||||||
state.tagsViewList.push({ ...v });
|
state.tagsViewList.push({ ...v });
|
||||||
router.push({ path: state.tagsViewList[state.tagsViewList.length - 1].path });
|
router.push({ path: state.tagsViewList[state.tagsViewList.length - 1].path });
|
||||||
@@ -547,7 +588,7 @@ onBeforeMount(() => {
|
|||||||
router.push('/home/index');
|
router.push('/home/index');
|
||||||
state.tagsViewList = [];
|
state.tagsViewList = [];
|
||||||
state.tagsViewRoutesList.map((v: RouteItem) => {
|
state.tagsViewRoutesList.map((v: RouteItem) => {
|
||||||
if (v.meta?.isAffix && !v.meta.isHide) {
|
if (v.meta?.isAffix && !v.meta?.isHide && !isCarouselTag(v) && isAffixTagAllowed(v) && !isSortOrderOneExcluded(v)) {
|
||||||
v.url = setTagsViewHighlight(v);
|
v.url = setTagsViewHighlight(v);
|
||||||
state.tagsViewList.push({ ...v });
|
state.tagsViewList.push({ ...v });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export const useUserInfo = defineStore('userInfo', {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
login(data)
|
login(data)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
debugger
|
|
||||||
this.setTokenCache(res.access_token, res.refresh_token);
|
this.setTokenCache(res.access_token, res.refresh_token);
|
||||||
Local.remove('roleCode');
|
Local.remove('roleCode');
|
||||||
Local.remove('roleName');
|
Local.remove('roleName');
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, toRef } from 'vue'
|
import { ref, computed, toRef } from 'vue'
|
||||||
import { listAllRole } from '/@/api/admin/role'
|
import { listAllRole } from '/@/api/admin/role'
|
||||||
import { Local } from '/@/utils/storage'
|
import { Local, Session } from '/@/utils/storage'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
|
||||||
/** 弹框标题,如「角色切换」「登录角色选择」 */
|
/** 弹框标题,如「角色切换」「登录角色选择」 */
|
||||||
@@ -108,9 +108,14 @@ const handleChangeRole = (label: string) => {
|
|||||||
Local.set('roleName', obj.roleName)
|
Local.set('roleName', obj.roleName)
|
||||||
Local.set('roleId', obj.roleId)
|
Local.set('roleId', obj.roleId)
|
||||||
useMessage().success('操作成功')
|
useMessage().success('操作成功')
|
||||||
|
// 清掉 tags 缓存,重载后只保留首页 tag
|
||||||
|
Session.remove('tagsViewList')
|
||||||
|
// 清除 pinia 持久化的 tagsView 路由,避免重载后先恢复旧角色菜单再被新路由覆盖前就初始化出“不存在的 tag”
|
||||||
|
try {
|
||||||
|
window.localStorage.removeItem('tagsViewRoutes')
|
||||||
|
} catch (_) {}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 切换角色后统一回到首页,避免停留在诸如 jsonflow/run-job/do-job 等内部路由
|
window.location.hash = '#/home'
|
||||||
window.location.hash = '#/'
|
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="home">
|
<script setup lang="ts" name="home">
|
||||||
import { ref, onMounted, onUnmounted } from 'vue'
|
import { ref, onMounted, onActivated, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { fetchTodoPage } from '/@/api/jsonflow/do-job'
|
import { fetchTodoPage } from '/@/api/jsonflow/do-job'
|
||||||
import { fetchList as fetchNoticeList } from '/@/api/jsonflow/ws-notice'
|
import { fetchList as fetchNoticeList } from '/@/api/jsonflow/ws-notice'
|
||||||
@@ -278,6 +278,11 @@ onMounted(() => {
|
|||||||
loadMessageList()
|
loadMessageList()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 页面激活时刷新待办列表(处理从待办返回的情况)
|
||||||
|
onActivated(() => {
|
||||||
|
loadTodoList()
|
||||||
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (timeTimer) {
|
if (timeTimer) {
|
||||||
clearInterval(timeTimer)
|
clearInterval(timeTimer)
|
||||||
|
|||||||
@@ -3,6 +3,16 @@
|
|||||||
:close-on-click-modal="false" draggable>
|
:close-on-click-modal="false" draggable>
|
||||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="采购人员" prop="purchaserName">
|
||||||
|
<el-input v-model="form.purchaserName" placeholder="请输入采购人员" disabled/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12" class="mb20">
|
||||||
|
<el-form-item label="资产管理员" prop="assetAdminName">
|
||||||
|
<el-input v-model="form.assetAdminName" placeholder="请输入资产管理员" disabled/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="12" class="mb20">
|
<el-col :span="12" class="mb20">
|
||||||
<el-form-item label="remark" prop="remark">
|
<el-form-item label="remark" prop="remark">
|
||||||
<el-input v-model="form.remark" placeholder="请输入remark"/>
|
<el-input v-model="form.remark" placeholder="请输入remark"/>
|
||||||
@@ -49,7 +59,7 @@
|
|||||||
import { useDict } from '/@/hooks/dict';
|
import { useDict } from '/@/hooks/dict';
|
||||||
import { rule } from '/@/utils/validate';
|
import { rule } from '/@/utils/validate';
|
||||||
import { useMessage } from "/@/hooks/message";
|
import { useMessage } from "/@/hooks/message";
|
||||||
import { getObj, addObj, putObj, validateExist } from '/@/api/purchase/purchasingAccept';
|
import { getObj, addObj, putObj, validateExist, getCommonConfigWithBatches } from '/@/api/purchase/purchasingAccept';
|
||||||
|
|
||||||
// ========== 2. 组件定义 ==========
|
// ========== 2. 组件定义 ==========
|
||||||
// 定义组件事件
|
// 定义组件事件
|
||||||
@@ -70,6 +80,10 @@ const form = reactive({
|
|||||||
acceptAddress: '', // 验收地点
|
acceptAddress: '', // 验收地点
|
||||||
batch: '', // 验收批次
|
batch: '', // 验收批次
|
||||||
question: '', // 问题意见
|
question: '', // 问题意见
|
||||||
|
purchaserId: '', // 采购人员ID
|
||||||
|
purchaserName: '', // 采购人员姓名
|
||||||
|
assetAdminId: '', // 资产管理员ID
|
||||||
|
assetAdminName: '', // 资产管理员姓名
|
||||||
});
|
});
|
||||||
|
|
||||||
// ========== 4. 字典数据处理 ==========
|
// ========== 4. 字典数据处理 ==========
|
||||||
@@ -86,6 +100,17 @@ const getPurchasingAcceptData = async (id: string) => {
|
|||||||
const { data } = await getObj({ id: id });
|
const { data } = await getObj({ id: id });
|
||||||
// 直接将第一条数据赋值给表单
|
// 直接将第一条数据赋值给表单
|
||||||
Object.assign(form, data[0]);
|
Object.assign(form, data[0]);
|
||||||
|
|
||||||
|
// 获取采购人员和资产管理员信息
|
||||||
|
if (form.purchaseId) {
|
||||||
|
const { data: commonData } = await getCommonConfigWithBatches(form.purchaseId);
|
||||||
|
if (commonData && commonData.common) {
|
||||||
|
form.purchaserId = commonData.common.purchaserId || '';
|
||||||
|
form.purchaserName = commonData.common.purchaserName || '';
|
||||||
|
form.assetAdminId = commonData.common.assetAdminId || '';
|
||||||
|
form.assetAdminName = commonData.common.assetAdminName || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
useMessage().error('获取数据失败');
|
useMessage().error('获取数据失败');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -98,10 +98,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed, onUnmounted } from 'vue'
|
||||||
import { FolderOpened, Download, Document } from '@element-plus/icons-vue'
|
import { FolderOpened, Download, Document } from '@element-plus/icons-vue'
|
||||||
import { useMessage } from '/@/hooks/message'
|
import { useMessage } from '/@/hooks/message'
|
||||||
import { listDownloadUrls, getArchiveDownloadUrl } from '/@/api/purchase/purchasingrequisition'
|
import { listDownloadUrls, getArchiveDownloadUrl, downloadFileById, previewFileById } from '/@/api/purchase/purchasingrequisition'
|
||||||
import other from '/@/utils/other'
|
import other from '/@/utils/other'
|
||||||
|
|
||||||
interface FileItem {
|
interface FileItem {
|
||||||
@@ -117,6 +117,7 @@ const loading = ref(false)
|
|||||||
const downloading = ref(false)
|
const downloading = ref(false)
|
||||||
const previewVisible = ref(false)
|
const previewVisible = ref(false)
|
||||||
const previewUrl = ref('')
|
const previewUrl = ref('')
|
||||||
|
const previewLoading = ref(false)
|
||||||
const purchaseId = ref('')
|
const purchaseId = ref('')
|
||||||
const purchaseNo = ref('')
|
const purchaseNo = ref('')
|
||||||
const fileList = ref<FileItem[]>([])
|
const fileList = ref<FileItem[]>([])
|
||||||
@@ -155,9 +156,9 @@ const isPdfFile = (fileName: string): boolean => {
|
|||||||
return ext === 'pdf'
|
return ext === 'pdf'
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePreview = (row: FileItem) => {
|
const handlePreview = async (row: FileItem) => {
|
||||||
if (!row.downloadUrl) {
|
if (!row.id) {
|
||||||
useMessage().warning('文件预览地址不存在')
|
useMessage().warning('文件ID不存在')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,16 +168,41 @@ const handlePreview = (row: FileItem) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用iframe预览PDF(通过后端下载接口获取文件流)
|
previewLoading.value = true
|
||||||
previewUrl.value = row.downloadUrl
|
|
||||||
previewVisible.value = true
|
previewVisible.value = true
|
||||||
|
previewUrl.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await previewFileById(row.id)
|
||||||
|
const blob = res as unknown as Blob
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
previewUrl.value = url
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err?.msg || '预览失败')
|
||||||
|
previewVisible.value = false
|
||||||
|
} finally {
|
||||||
|
previewLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleDownloadFile = (row: FileItem) => {
|
const handleDownloadFile = async (row: FileItem) => {
|
||||||
if (row.downloadUrl) {
|
if (!row.id) {
|
||||||
window.open(row.downloadUrl, '_blank')
|
useMessage().warning('文件ID不存在')
|
||||||
} else {
|
return
|
||||||
useMessage().warning('文件下载地址不存在')
|
}
|
||||||
|
try {
|
||||||
|
const res = await downloadFileById(row.id)
|
||||||
|
const blob = res as unknown as Blob
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = row.fileTitle || 'download'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
} catch (err: any) {
|
||||||
|
useMessage().error(err?.msg || '下载失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +223,12 @@ const handleDownloadAll = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (previewUrl.value) {
|
||||||
|
window.URL.revokeObjectURL(previewUrl.value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
open
|
open
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
<!-- 上传履约验收模版 -->
|
<!-- 上传履约验收模版 -->
|
||||||
<el-col :span="12" class="mb20">
|
<el-col :span="12" class="mb20">
|
||||||
<el-form-item label="履约验收文件" prop="templateFileIds">
|
<el-form-item label="履约验收文件" prop="templateFileIds">
|
||||||
<upload-file v-model="templateFiles" :limit="1" :file-type="['doc', 'docx', 'pdf']" :data="{ purchaseId: purchaseId || '', fileType: '110' }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="readonly" />
|
<upload-file v-model="templateFiles" :limit="1" :file-type="['pdf']" :data="{ purchaseId: purchaseId || '', fileType: '110' }" upload-file-url="/purchase/purchasingfiles/upload" :disabled="readonly" />
|
||||||
<el-link v-if="!readonly" type="primary" :href="lyysTemplateUrl" :download="lyysTemplateDownloadName" target="_blank" style="margin-top: 8px; display: inline-flex; align-items: center;">
|
<el-link v-if="!readonly" type="primary" @click="handleDownloadTemplate" style="margin-top: 8px; display: inline-flex; align-items: center;">
|
||||||
<el-icon><Download /></el-icon>
|
<el-icon><Download /></el-icon>
|
||||||
<span style="margin-left: 4px;">下载{{ lyysTemplateLabel }}</span>
|
<span style="margin-left: 4px;">下载{{ lyysTemplateLabel }}</span>
|
||||||
</el-link>
|
</el-link>
|
||||||
@@ -39,7 +39,9 @@
|
|||||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { Download } from '@element-plus/icons-vue'
|
import { Download } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
import UploadFile from "/@/components/Upload/index.vue";
|
import UploadFile from "/@/components/Upload/index.vue";
|
||||||
|
import { downloadTemplate } from "/@/api/purchase/purchasingAccept";
|
||||||
|
|
||||||
/** 项目类型 A:货物 B:工程 C:服务 */
|
/** 项目类型 A:货物 B:工程 C:服务 */
|
||||||
const LYYS_TEMPLATE_MAP: Record<string, { label: string }> = {
|
const LYYS_TEMPLATE_MAP: Record<string, { label: string }> = {
|
||||||
@@ -54,6 +56,8 @@ const props = defineProps<{
|
|||||||
purchaseId?: string
|
purchaseId?: string
|
||||||
/** 项目类型 A:货物 B:工程 C:服务,用于模版下载 */
|
/** 项目类型 A:货物 B:工程 C:服务,用于模版下载 */
|
||||||
projectType?: string
|
projectType?: string
|
||||||
|
/** 预算金额,用于判断模板目录 */
|
||||||
|
budget?: number
|
||||||
batchNum?: number
|
batchNum?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
@@ -64,9 +68,33 @@ const formRef = ref<FormInstance>()
|
|||||||
const templateFiles = ref<any[]>([])
|
const templateFiles = ref<any[]>([])
|
||||||
|
|
||||||
const projectTypeKey = computed(() => (props.projectType === 'A' || props.projectType === 'B' || props.projectType === 'C' ? props.projectType : 'A'))
|
const projectTypeKey = computed(() => (props.projectType === 'A' || props.projectType === 'B' || props.projectType === 'C' ? props.projectType : 'A'))
|
||||||
const lyysTemplateLabel = computed(() => LYYS_TEMPLATE_MAP[projectTypeKey.value]?.label || LYYS_TEMPLATE_MAP.A.label)
|
const lyysTemplateLabel = computed(() => {
|
||||||
|
const amountLabel = (props.budget && props.budget >= 50000) ? '(≥5万)' : '(<5万)'
|
||||||
|
return LYYS_TEMPLATE_MAP[projectTypeKey.value]?.label + amountLabel || LYYS_TEMPLATE_MAP.A.label + amountLabel
|
||||||
|
})
|
||||||
const lyysTemplateDownloadName = computed(() => `${lyysTemplateLabel.value}.docx`)
|
const lyysTemplateDownloadName = computed(() => `${lyysTemplateLabel.value}.docx`)
|
||||||
const lyysTemplateUrl = computed(() => `/templates/lyys-template-${projectTypeKey.value}.docx`)
|
|
||||||
|
/** 下载模板 - 调用后台接口 */
|
||||||
|
const handleDownloadTemplate = async () => {
|
||||||
|
if (!props.purchaseId) {
|
||||||
|
ElMessage.warning('采购ID不存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await downloadTemplate(props.purchaseId)
|
||||||
|
const blob = res as unknown as Blob
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = lyysTemplateDownloadName.value
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e?.message || '下载模板失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
acceptType: '2', // 固定为上传模式
|
acceptType: '2', // 固定为上传模式
|
||||||
|
|||||||
@@ -208,6 +208,12 @@ const loadContractOptions = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onContractSelectVisibleChange = (visible: boolean) => {
|
||||||
|
if (visible && form.hasContract === '1' && contractOptions.value.length === 0) {
|
||||||
|
loadContractOptions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const searchPurchaser = async (query: string) => {
|
const searchPurchaser = async (query: string) => {
|
||||||
if (!query) {
|
if (!query) {
|
||||||
purchaserOptions.value = []
|
purchaserOptions.value = []
|
||||||
@@ -240,9 +246,29 @@ const searchAssetAdmin = async (query: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onContractSelectVisibleChange = (visible: boolean) => {
|
const onPurchaserChange = (teacherNo: string) => {
|
||||||
if (visible && form.hasContract === '1' && contractOptions.value.length === 0) {
|
if (!teacherNo) {
|
||||||
loadContractOptions()
|
form.purchaserId = ''
|
||||||
|
form.purchaserName = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selected = purchaserOptions.value.find((item: any) => item.teacherNo === teacherNo)
|
||||||
|
if (selected) {
|
||||||
|
form.purchaserId = selected.teacherNo
|
||||||
|
form.purchaserName = selected.realName || selected.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onAssetAdminChange = (teacherNo: string) => {
|
||||||
|
if (!teacherNo) {
|
||||||
|
form.assetAdminId = ''
|
||||||
|
form.assetAdminName = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const selected = assetAdminOptions.value.find((item: any) => item.teacherNo === teacherNo)
|
||||||
|
if (selected) {
|
||||||
|
form.assetAdminId = selected.teacherNo
|
||||||
|
form.assetAdminName = selected.realName || selected.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,32 +322,6 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onPurchaserChange = (teacherNo: string) => {
|
|
||||||
if (!teacherNo) {
|
|
||||||
form.purchaserId = ''
|
|
||||||
form.purchaserName = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const selected = purchaserOptions.value.find((item: any) => item.teacherNo === teacherNo)
|
|
||||||
if (selected) {
|
|
||||||
form.purchaserId = selected.teacherNo
|
|
||||||
form.purchaserName = selected.realName || selected.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const onAssetAdminChange = (teacherNo: string) => {
|
|
||||||
if (!teacherNo) {
|
|
||||||
form.assetAdminId = ''
|
|
||||||
form.assetAdminName = ''
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const selected = assetAdminOptions.value.find((item: any) => item.teacherNo === teacherNo)
|
|
||||||
if (selected) {
|
|
||||||
form.assetAdminId = selected.teacherNo
|
|
||||||
form.assetAdminName = selected.realName || selected.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules: FormRules = {
|
const rules: FormRules = {
|
||||||
isInstallment: [{ required: true, message: '请选择是否分期验收', trigger: 'change' }],
|
isInstallment: [{ required: true, message: '请选择是否分期验收', trigger: 'change' }],
|
||||||
totalPhases: [{ required: true, message: '请输入分期次数', trigger: 'blur' }],
|
totalPhases: [{ required: true, message: '请输入分期次数', trigger: 'blur' }],
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
:readonly="false"
|
:readonly="false"
|
||||||
:purchase-id="String(purchaseId)"
|
:purchase-id="String(purchaseId)"
|
||||||
:project-type="acceptProjectType"
|
:project-type="acceptProjectType"
|
||||||
|
:budget="applyInfo?.budget"
|
||||||
:batch-num="b.batch"
|
:batch-num="b.batch"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -177,7 +178,13 @@ const loadData = async () => {
|
|||||||
|
|
||||||
if (config?.common) {
|
if (config?.common) {
|
||||||
applyInfo.value = config.common
|
applyInfo.value = config.common
|
||||||
// 仅当存在已保存批次时,才用接口数据回填公共信息;否则保持 open() 中的默认清空值
|
// 采购人员和资产管理员始终回填
|
||||||
|
commonForm.value.purchaserId = config.common.purchaserId || ''
|
||||||
|
commonForm.value.purchaserName = config.common.purchaserName || ''
|
||||||
|
commonForm.value.assetAdminId = config.common.assetAdminId || ''
|
||||||
|
commonForm.value.assetAdminName = config.common.assetAdminName || ''
|
||||||
|
|
||||||
|
// 其他字段仅当存在已保存批次时回填
|
||||||
if (config?.batches?.length) {
|
if (config?.batches?.length) {
|
||||||
Object.assign(commonForm.value, {
|
Object.assign(commonForm.value, {
|
||||||
hasContract: config.common.hasContract || '0',
|
hasContract: config.common.hasContract || '0',
|
||||||
|
|||||||
@@ -967,6 +967,7 @@ import { addObj, tempStore, getObj, editObj, getApplyFiles } from '/@/api/purcha
|
|||||||
import { getTree } from '/@/api/purchase/purchasingcategory';
|
import { getTree } from '/@/api/purchase/purchasingcategory';
|
||||||
import { getDicts } from '/@/api/admin/dict';
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
import { useMessage } from '/@/hooks/message';
|
import { useMessage } from '/@/hooks/message';
|
||||||
|
import { useUserInfo } from '/@/stores/userInfo';
|
||||||
import { usePurchaseRulesSingleton } from '/@/hooks/usePurchaseRules';
|
import { usePurchaseRulesSingleton } from '/@/hooks/usePurchaseRules';
|
||||||
import UploadFile from '/@/components/Upload/index.vue';
|
import UploadFile from '/@/components/Upload/index.vue';
|
||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
@@ -980,6 +981,7 @@ import * as orderVue from '/@/api/order/order-key-vue';
|
|||||||
|
|
||||||
/** 采购中心角色编码:审批时仅该角色可编辑采购方式/采购形式 */
|
/** 采购中心角色编码:审批时仅该角色可编辑采购方式/采购形式 */
|
||||||
const PURCHASE_CENTER_ROLE_CODE = 'PURCHASE_CENTER';
|
const PURCHASE_CENTER_ROLE_CODE = 'PURCHASE_CENTER';
|
||||||
|
const PURCHASE_DEPT_APPLY_ROLE_CODE = 'ROLE_PURCHASE_DEPT_APPLY';
|
||||||
|
|
||||||
// 兼容流程 dynamic-link 引用:接收 currJob / currElTab,并支持 handleJob 事件
|
// 兼容流程 dynamic-link 引用:接收 currJob / currElTab,并支持 handleJob 事件
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -1035,9 +1037,23 @@ const isPurchaseCenter = ref(false);
|
|||||||
/** 流程嵌入时:采购中心审核节点放开所有字段编辑;非采购中心节点只读 */
|
/** 流程嵌入时:采购中心审核节点放开所有字段编辑;非采购中心节点只读 */
|
||||||
function flowFieldDisabled(_key: string) {
|
function flowFieldDisabled(_key: string) {
|
||||||
if (isFlowEmbed.value && isPurchaseCenter.value) return false;
|
if (isFlowEmbed.value && isPurchaseCenter.value) return false;
|
||||||
|
if (isFlowEmbed.value && isApplicant.value) return false;
|
||||||
return !!isFlowEmbed.value;
|
return !!isFlowEmbed.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 当前用户是否为申请人(在流程中可编辑) */
|
||||||
|
const isApplicant = computed(() => {
|
||||||
|
|
||||||
|
const stores = useUserInfo();
|
||||||
|
const currentUser = stores.userInfos?.user || {};
|
||||||
|
console.log(currentUser)
|
||||||
|
if (!dataForm.createBy) return false;
|
||||||
|
|
||||||
|
// const stores = useUserInfo();
|
||||||
|
// const currentUser = stores.userInfos?.user || {};
|
||||||
|
return dataForm.createBy === currentUser.username;
|
||||||
|
});
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const dataForm = reactive({
|
const dataForm = reactive({
|
||||||
@@ -1058,6 +1074,7 @@ const dataForm = reactive({
|
|||||||
categoryCode: '',
|
categoryCode: '',
|
||||||
remark: '',
|
remark: '',
|
||||||
status: '',
|
status: '',
|
||||||
|
createBy: '', // 创建人用户名,用于判断是否为申请人
|
||||||
// 部门自行采购字段
|
// 部门自行采购字段
|
||||||
businessNegotiationTable: '',
|
businessNegotiationTable: '',
|
||||||
marketPurchaseMinutes: '',
|
marketPurchaseMinutes: '',
|
||||||
@@ -1754,6 +1771,7 @@ async function loadDetail(applyId: string | number) {
|
|||||||
categoryCode: detail.categoryCode ?? '',
|
categoryCode: detail.categoryCode ?? '',
|
||||||
remark: detail.remark ?? '',
|
remark: detail.remark ?? '',
|
||||||
status: detail.status ?? '',
|
status: detail.status ?? '',
|
||||||
|
createBy: detail.createBy ?? '',
|
||||||
businessNegotiationTable: detail.businessNegotiationTable ?? '',
|
businessNegotiationTable: detail.businessNegotiationTable ?? '',
|
||||||
marketPurchaseMinutes: detail.marketPurchaseMinutes ?? '',
|
marketPurchaseMinutes: detail.marketPurchaseMinutes ?? '',
|
||||||
onlineMallMaterials: detail.onlineMallMaterials ?? '',
|
onlineMallMaterials: detail.onlineMallMaterials ?? '',
|
||||||
@@ -1888,12 +1906,16 @@ const flowMethods = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 流程嵌入时采购申请权限:根据前端缓存的角色(cloud-ui:roleCode)判断,非采购中心整表只读,采购中心仅采购方式/采购形式可编辑 */
|
/** 流程嵌入时采购申请权限:根据前端缓存的角色(cloud-ui:roleCode)判断,非采购中心整表只读,采购中心仅采购方式/采购形式可编辑;申请人在流程中可编辑 */
|
||||||
function applyPurchaseApplyFormPerm() {
|
function applyPurchaseApplyFormPerm() {
|
||||||
if (!isFlowEmbed.value) return;
|
if (!isFlowEmbed.value) return;
|
||||||
const roleCode = Session.getRoleCode() || '';
|
const roleCode = Session.getRoleCode() || '';
|
||||||
isPurchaseCenter.value = roleCode === PURCHASE_CENTER_ROLE_CODE;
|
isPurchaseCenter.value = roleCode === PURCHASE_CENTER_ROLE_CODE;
|
||||||
flowFormDisabled.value = !isPurchaseCenter.value;
|
if (isApplicant.value) {
|
||||||
|
flowFormDisabled.value = false;
|
||||||
|
} else {
|
||||||
|
flowFormDisabled.value = !isPurchaseCenter.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 流程嵌入时的“保存”回调:校验后调用 editObj,并通知流程已保存 */
|
/** 流程嵌入时的“保存”回调:校验后调用 editObj,并通知流程已保存 */
|
||||||
@@ -1917,6 +1939,7 @@ async function flowSubmitForm() {
|
|||||||
'purchaseRequirementTemplate',
|
'purchaseRequirementTemplate',
|
||||||
'serviceInviteSelect',
|
'serviceInviteSelect',
|
||||||
'servicePublicSelectAuto',
|
'servicePublicSelectAuto',
|
||||||
|
'deptSelfMeetingMinutes', // 部门自行采购会议纪要
|
||||||
'purchaseRequirement',
|
'purchaseRequirement',
|
||||||
'meetingMinutes',
|
'meetingMinutes',
|
||||||
'feasibilityReport',
|
'feasibilityReport',
|
||||||
@@ -2353,6 +2376,7 @@ const handleSubmit = async () => {
|
|||||||
'purchaseRequirementTemplate',
|
'purchaseRequirementTemplate',
|
||||||
'serviceInviteSelect',
|
'serviceInviteSelect',
|
||||||
'servicePublicSelectAuto',
|
'servicePublicSelectAuto',
|
||||||
|
'deptSelfMeetingMinutes', // 部门自行采购会议纪要
|
||||||
'purchaseRequirement',
|
'purchaseRequirement',
|
||||||
'meetingMinutes',
|
'meetingMinutes',
|
||||||
'feasibilityReport',
|
'feasibilityReport',
|
||||||
@@ -2452,6 +2476,7 @@ const handleTempStore = async () => {
|
|||||||
'purchaseRequirementTemplate',
|
'purchaseRequirementTemplate',
|
||||||
'serviceInviteSelect',
|
'serviceInviteSelect',
|
||||||
'servicePublicSelectAuto',
|
'servicePublicSelectAuto',
|
||||||
|
'deptSelfMeetingMinutes', // 部门自行采购会议纪要
|
||||||
'purchaseRequirement',
|
'purchaseRequirement',
|
||||||
'meetingMinutes',
|
'meetingMinutes',
|
||||||
'feasibilityReport',
|
'feasibilityReport',
|
||||||
|
|||||||
@@ -261,7 +261,7 @@
|
|||||||
<!-- 需求部门/内审部门操作按钮 -->
|
<!-- 需求部门/内审部门操作按钮 -->
|
||||||
<el-button v-if="canSubmitToAsset" type="primary" @click="handleSubmitToAsset">提交至资产管理处</el-button>
|
<el-button v-if="canSubmitToAsset" type="primary" @click="handleSubmitToAsset">提交至资产管理处</el-button>
|
||||||
<!-- 通用操作按钮 -->
|
<!-- 通用操作按钮 -->
|
||||||
<el-button v-if="canReturn" type="warning" @click="handleReturn">退回修改</el-button>
|
<el-button v-if="canReturn" type="primary" @click="handleReturn">提交招标代理</el-button>
|
||||||
<el-button v-if="canComplete" type="success" @click="handleComplete">确认流程结束</el-button>
|
<el-button v-if="canComplete" type="success" @click="handleComplete">确认流程结束</el-button>
|
||||||
<el-button @click="handleClose">关闭</el-button>
|
<el-button @click="handleClose">关闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -109,11 +109,21 @@
|
|||||||
<el-icon><List /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="purchaseNo" label="申请单编号" min-width="140" show-overflow-tooltip>
|
<el-table-column prop="purchaseNo" label="申请单编号" min-width="180">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><DocumentCopy /></el-icon>
|
<el-icon><DocumentCopy /></el-icon>
|
||||||
<span style="margin-left: 4px">申请单编号</span>
|
<span style="margin-left: 4px">申请单编号</span>
|
||||||
</template>
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="purchase-no-cell">
|
||||||
|
<el-tooltip :content="scope.row.purchaseNo" placement="top" :show-after="0" v-if="scope.row.purchaseNo">
|
||||||
|
<span class="purchase-no-text">{{ scope.row.purchaseNo }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-button v-if="scope.row.purchaseNo" type="primary" link size="small" class="copy-btn" @click.stop="copyToClipboard(scope.row.purchaseNo)">
|
||||||
|
<el-icon><CopyDocument /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="projectName" label="采购项目名称" min-width="200" show-overflow-tooltip>
|
<el-table-column prop="projectName" label="采购项目名称" min-width="200" show-overflow-tooltip>
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -185,6 +195,30 @@
|
|||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column prop="purchaseMode" label="采购形式" width="140" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Shop /></el-icon>
|
||||||
|
<span style="margin-left: 4px">采购形式</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<div class="purchase-mode-cell">
|
||||||
|
<el-tag v-if="scope.row.purchaseMode === '1'" type="success">部门自行采购</el-tag>
|
||||||
|
<el-tag v-else-if="scope.row.purchaseMode === '2'" type="primary">学校统一采购</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
<el-tag v-if="scope.row.purchaseMode === '1' && scope.row.purchaseChannel === '2'" type="warning" size="small" class="entrust-tag">委</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="purchaseType" label="采购方式" width="120" align="center">
|
||||||
|
<template #header>
|
||||||
|
<el-icon><Shop /></el-icon>
|
||||||
|
<span style="margin-left: 4px">采购方式</span>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag v-if="scope.row.purchaseTypeLabel" type="primary">{{ scope.row.purchaseTypeLabel }}</el-tag>
|
||||||
|
<span v-else>-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="status" label="审核状态" width="100" align="center">
|
<el-table-column prop="status" label="审核状态" width="100" align="center">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><InfoFilled /></el-icon>
|
<el-icon><InfoFilled /></el-icon>
|
||||||
@@ -274,7 +308,7 @@
|
|||||||
@refresh="getDataList" />
|
@refresh="getDataList" />
|
||||||
|
|
||||||
<!-- 履约验收弹窗 -->
|
<!-- 履约验收弹窗 -->
|
||||||
<PurchasingAcceptModal ref="acceptModalRef" @refresh="getDataList" />
|
<PurchasingAcceptModal ref="acceptModalRef" />
|
||||||
|
|
||||||
<!-- 查看审批过程(申请单审批 / 文件审批) -->
|
<!-- 查看审批过程(申请单审批 / 文件审批) -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
@@ -290,7 +324,7 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 实施采购:iframe 嵌入 implement.vue,供列表与流程页面使用 -->
|
<!-- 实施采购:iframe 嵌入 implement.vue,供列表与流程页面使用 -->
|
||||||
<ImplementForm ref="implementFormRef" @refresh="getDataList" />
|
<ImplementForm ref="implementFormRef" />
|
||||||
|
|
||||||
<!-- 文件归档弹窗 -->
|
<!-- 文件归档弹窗 -->
|
||||||
<FileArchiveDialog ref="fileArchiveDialogRef" />
|
<FileArchiveDialog ref="fileArchiveDialogRef" />
|
||||||
@@ -309,6 +343,12 @@
|
|||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<el-form label-width="100px">
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="身份">
|
||||||
|
<el-radio-group v-model="representorForm.identity">
|
||||||
|
<el-radio label="purchase_rep">采购代表</el-radio>
|
||||||
|
<el-radio label="judge">评委</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="选择方式">
|
<el-form-item label="选择方式">
|
||||||
<el-radio-group v-model="representorForm.mode">
|
<el-radio-group v-model="representorForm.mode">
|
||||||
<el-radio label="single">指定采购代表人</el-radio>
|
<el-radio label="single">指定采购代表人</el-radio>
|
||||||
@@ -356,7 +396,7 @@ import { useMessage, useMessageBox } from "/@/hooks/message";
|
|||||||
import { useAuth } from '/@/hooks/auth';
|
import { useAuth } from '/@/hooks/auth';
|
||||||
import { getDicts } from '/@/api/admin/dict';
|
import { getDicts } from '/@/api/admin/dict';
|
||||||
import { getTree } from '/@/api/purchase/purchasingcategory';
|
import { getTree } from '/@/api/purchase/purchasingcategory';
|
||||||
import { List, Document, DocumentCopy, Search, Money, CircleCheck, InfoFilled, Calendar, OfficeBuilding, Warning, DocumentChecked, Edit, Delete, Upload, FolderOpened, Download, User, RefreshRight } from '@element-plus/icons-vue'
|
import { List, Document, DocumentCopy, Search, Money, CircleCheck, InfoFilled, Calendar, OfficeBuilding, Warning, DocumentChecked, Edit, Delete, Upload, FolderOpened, Download, User, RefreshRight, Shop, CopyDocument } from '@element-plus/icons-vue'
|
||||||
import other from '/@/utils/other'
|
import other from '/@/utils/other'
|
||||||
import { Session } from '/@/utils/storage'
|
import { Session } from '/@/utils/storage'
|
||||||
|
|
||||||
@@ -407,7 +447,7 @@ const updateFilesDialogRef = ref()
|
|||||||
/** 采购代表弹窗 */
|
/** 采购代表弹窗 */
|
||||||
const representorDialogVisible = ref(false)
|
const representorDialogVisible = ref(false)
|
||||||
const representorCurrentRow = ref<any>(null)
|
const representorCurrentRow = ref<any>(null)
|
||||||
const representorForm = reactive({ mode: 'single' as 'single' | 'multi', teacherNo: '', multiIds: [] as string[] })
|
const representorForm = reactive({ mode: 'single' as 'single' | 'multi', teacherNo: '', multiIds: [] as string[], identity: 'purchase_rep' as 'purchase_rep' | 'judge' })
|
||||||
const representorDeptMembers = ref<any[]>([])
|
const representorDeptMembers = ref<any[]>([])
|
||||||
const representorSubmitting = ref(false)
|
const representorSubmitting = ref(false)
|
||||||
|
|
||||||
@@ -444,7 +484,7 @@ const handleSaveRepresentor = async () => {
|
|||||||
try {
|
try {
|
||||||
const teacherNo = representorForm.mode === 'single' ? representorForm.teacherNo : undefined
|
const teacherNo = representorForm.mode === 'single' ? representorForm.teacherNo : undefined
|
||||||
const multiIds = representorForm.mode === 'multi' ? representorForm.multiIds.join(',') : undefined
|
const multiIds = representorForm.mode === 'multi' ? representorForm.multiIds.join(',') : undefined
|
||||||
await saveRepresentor(Number(id), teacherNo, multiIds)
|
await saveRepresentor(Number(id), teacherNo, multiIds, representorForm.identity)
|
||||||
useMessage().success('保存采购代表成功')
|
useMessage().success('保存采购代表成功')
|
||||||
representorDialogVisible.value = false
|
representorDialogVisible.value = false
|
||||||
getDataList()
|
getDataList()
|
||||||
@@ -547,6 +587,26 @@ const handleImplement = (row: any) => {
|
|||||||
implementFormRef.value?.openDialog(row);
|
implementFormRef.value?.openDialog(row);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 复制到剪贴板 */
|
||||||
|
const copyToClipboard = async (text: string) => {
|
||||||
|
if (!text) return;
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
useMessage().success('复制成功');
|
||||||
|
} catch (err) {
|
||||||
|
// 降级方案
|
||||||
|
const textarea = document.createElement('textarea');
|
||||||
|
textarea.value = text;
|
||||||
|
textarea.style.position = 'fixed';
|
||||||
|
textarea.style.opacity = '0';
|
||||||
|
document.body.appendChild(textarea);
|
||||||
|
textarea.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(textarea);
|
||||||
|
useMessage().success('复制成功');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** 打开招标文件审核 */
|
/** 打开招标文件审核 */
|
||||||
const handleDocAudit = (row: any) => {
|
const handleDocAudit = (row: any) => {
|
||||||
docAuditDialogRef.value?.open(row);
|
docAuditDialogRef.value?.open(row);
|
||||||
@@ -591,6 +651,7 @@ const handleSubmit = async (row: any) => {
|
|||||||
/** 操作栏「更多」菜单项配置 */
|
/** 操作栏「更多」菜单项配置 */
|
||||||
const getActionMenuItems = (row: any) => {
|
const getActionMenuItems = (row: any) => {
|
||||||
const isTemp = row?.status === '-1';
|
const isTemp = row?.status === '-1';
|
||||||
|
const isRunning = row?.status === '0';
|
||||||
const isCompleted = row?.status === '1';
|
const isCompleted = row?.status === '1';
|
||||||
const items = [
|
const items = [
|
||||||
{
|
{
|
||||||
@@ -615,7 +676,7 @@ const getActionMenuItems = (row: any) => {
|
|||||||
command: 'accept',
|
command: 'accept',
|
||||||
label: '履约验收',
|
label: '履约验收',
|
||||||
icon: DocumentChecked,
|
icon: DocumentChecked,
|
||||||
visible: () => hasAuth('purchase_accept'),
|
visible: () => isCompleted && hasAuth('purchase_accept'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: 'implement',
|
command: 'implement',
|
||||||
@@ -639,13 +700,13 @@ const getActionMenuItems = (row: any) => {
|
|||||||
command: 'representor',
|
command: 'representor',
|
||||||
label: '采购代表',
|
label: '采购代表',
|
||||||
icon: User,
|
icon: User,
|
||||||
visible: () => isDeptAuditRole.value,
|
visible: () => isCompleted && isDeptAuditRole.value,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
command: 'updateFiles',
|
command: 'updateFiles',
|
||||||
label: '更新材料',
|
label: '更新材料',
|
||||||
icon: RefreshRight,
|
icon: RefreshRight,
|
||||||
visible: () => isCompleted && hasAuth('purchase_purchasingapply_edit'),
|
visible: () => (isCompleted || isRunning) && hasAuth('purchase_purchasingapply_edit'),
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// command: 'downloadFileApply',
|
// command: 'downloadFileApply',
|
||||||
@@ -911,5 +972,70 @@ onMounted(() => {
|
|||||||
.status-tag-clickable {
|
.status-tag-clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag) {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag--success) {
|
||||||
|
--el-tag-bg-color: #e8f5e9;
|
||||||
|
--el-tag-border-color: #c8e6c9;
|
||||||
|
--el-tag-text-color: #2e7d32;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag--warning) {
|
||||||
|
--el-tag-bg-color: #fff8e1;
|
||||||
|
--el-tag-border-color: #ffecb3;
|
||||||
|
--el-tag-text-color: #f57c00;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag--danger) {
|
||||||
|
--el-tag-bg-color: #ffebee;
|
||||||
|
--el-tag-border-color: #ffcdd2;
|
||||||
|
--el-tag-text-color: #c62828;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag--primary) {
|
||||||
|
--el-tag-bg-color: #e3f2fd;
|
||||||
|
--el-tag-border-color: #bbdefb;
|
||||||
|
--el-tag-text-color: #1565c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tag--info) {
|
||||||
|
--el-tag-bg-color: #f5f5f5;
|
||||||
|
--el-tag-border-color: #e0e0e0;
|
||||||
|
--el-tag-text-color: #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-no-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.purchase-no-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.purchase-mode-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
.entrust-tag {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 0 4px;
|
||||||
|
height: 18px;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
<el-table-column label="操作" width="360" align="center" fixed="right">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span style="margin-left: 4px">操作</span>
|
<span style="margin-left: 4px">操作</span>
|
||||||
@@ -152,6 +152,13 @@
|
|||||||
@click="formDialogRef.openDialog(scope.row.id)">
|
@click="formDialogRef.openDialog(scope.row.id)">
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
icon="Histogram"
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleReadStatistics(scope.row)">
|
||||||
|
查看班主任查看情况
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
icon="Delete"
|
icon="Delete"
|
||||||
link
|
link
|
||||||
@@ -183,28 +190,32 @@
|
|||||||
|
|
||||||
<!-- 详情对话框 -->
|
<!-- 详情对话框 -->
|
||||||
<DetailDialog ref="detailDialogRef" @refresh="getDataList(false)" />
|
<DetailDialog ref="detailDialogRef" @refresh="getDataList(false)" />
|
||||||
|
<!-- 班主任查看情况统计 -->
|
||||||
|
<ReadStatisticsDialog ref="readStatisticsDialogRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="WeekPlan">
|
<script setup lang="ts" name="WeekPlan">
|
||||||
import { ref, reactive, defineAsyncComponent, onMounted, nextTick } from 'vue'
|
import { ref, reactive, defineAsyncComponent, onMounted, nextTick } from 'vue'
|
||||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
import { BasicTableProps, useTable } from '/@/hooks/table'
|
||||||
import { fetchList, delObjs } from "/@/api/stuwork/weekplan";
|
import { fetchList, delObjs } from '/@/api/stuwork/weekplan'
|
||||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
import { useMessage, useMessageBox } from '/@/hooks/message'
|
||||||
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
import { queryAllSchoolYear } from '/@/api/basic/basicyear'
|
||||||
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
import TableColumnControl from '/@/components/TableColumnControl/index.vue'
|
||||||
import {
|
import {
|
||||||
List, Calendar, Clock, Document, User, Setting, Menu, Search, EditPen
|
List, Calendar, Clock, Document, User, Setting, Menu, Search, EditPen, Histogram,
|
||||||
} from '@element-plus/icons-vue'
|
} from '@element-plus/icons-vue'
|
||||||
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
import { useTableColumnControl } from '/@/hooks/tableColumn'
|
||||||
|
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
const FormDialog = defineAsyncComponent(() => import('./form.vue'))
|
||||||
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'))
|
||||||
|
const ReadStatisticsDialog = defineAsyncComponent(() => import('./readStatistics.vue'))
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const formDialogRef = ref()
|
const formDialogRef = ref()
|
||||||
const detailDialogRef = ref()
|
const detailDialogRef = ref()
|
||||||
|
const readStatisticsDialogRef = ref()
|
||||||
const searchFormRef = ref()
|
const searchFormRef = ref()
|
||||||
const columnControlRef = ref<any>()
|
const columnControlRef = ref<any>()
|
||||||
// 搜索变量
|
// 搜索变量
|
||||||
@@ -261,7 +272,7 @@ const {
|
|||||||
sizeChangeHandle,
|
sizeChangeHandle,
|
||||||
sortChangeHandle,
|
sortChangeHandle,
|
||||||
downBlobFile,
|
downBlobFile,
|
||||||
tableStyle
|
tableStyle,
|
||||||
} = useTable(state)
|
} = useTable(state)
|
||||||
|
|
||||||
// 查询
|
// 查询
|
||||||
@@ -293,6 +304,16 @@ const handleViewDetail = async (id: string) => {
|
|||||||
detailDialogRef.value?.openDialog(id)
|
detailDialogRef.value?.openDialog(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查看班主任查看情况统计
|
||||||
|
const handleReadStatistics = async (row: any) => {
|
||||||
|
if (!row?.id) {
|
||||||
|
useMessage().warning('未获取到每周工作ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await nextTick()
|
||||||
|
readStatisticsDialogRef.value?.openDialog(row.id)
|
||||||
|
}
|
||||||
|
|
||||||
// 删除操作
|
// 删除操作
|
||||||
const handleDelete = async (ids: string[]) => {
|
const handleDelete = async (ids: string[]) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
103
src/views/stuwork/weekplan/readStatistics.vue
Normal file
103
src/views/stuwork/weekplan/readStatistics.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
title="班主任查看情况统计"
|
||||||
|
v-model="visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="900px"
|
||||||
|
draggable
|
||||||
|
>
|
||||||
|
<el-table
|
||||||
|
:data="dataList"
|
||||||
|
v-loading="loading"
|
||||||
|
stripe
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="70" align="center" />
|
||||||
|
<el-table-column prop="deptName" label="学院" min-width="120" align="center" />
|
||||||
|
<el-table-column prop="classNo" label="班号" min-width="100" align="center" />
|
||||||
|
<el-table-column prop="className" label="班级名称" min-width="160" align="center" />
|
||||||
|
<el-table-column prop="teacherRealName" label="班主任" min-width="120" align="center" />
|
||||||
|
<el-table-column label="是否查看" width="110" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.hasRead ? 'success' : 'info'"
|
||||||
|
effect="plain"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ scope.row.hasRead ? '已查看' : '未查看' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="readTime" label="查看时间" min-width="180" align="center" />
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="WeekPlanReadStatisticsDialog">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useMessage } from '/@/hooks/message'
|
||||||
|
import { readStatistics } from '/@/api/stuwork/weekplan'
|
||||||
|
import { useUserInfo } from '/@/stores/userInfo'
|
||||||
|
|
||||||
|
const visible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const dataList = ref<any[]>([])
|
||||||
|
|
||||||
|
const openDialog = async (weekPlanId: string) => {
|
||||||
|
if (!weekPlanId) {
|
||||||
|
useMessage().warning('未获取到每周工作ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
visible.value = true
|
||||||
|
dataList.value = []
|
||||||
|
await fetchData(weekPlanId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = async (weekPlanId: string) => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const userStore = useUserInfo()
|
||||||
|
const deptCode =
|
||||||
|
(userStore.userInfos as any)?.user?.deptCode ||
|
||||||
|
(userStore.userInfos as any)?.user?.deptCodeList?.[0] ||
|
||||||
|
''
|
||||||
|
|
||||||
|
if (!deptCode) {
|
||||||
|
useMessage().error('未获取到部门编码,无法统计班主任查看情况')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res: any = await readStatistics({
|
||||||
|
deptCode,
|
||||||
|
weekPlanId,
|
||||||
|
})
|
||||||
|
if (Array.isArray(res?.data)) {
|
||||||
|
dataList.value = res.data
|
||||||
|
} else if (Array.isArray(res?.data?.data)) {
|
||||||
|
dataList.value = res.data.data
|
||||||
|
} else {
|
||||||
|
dataList.value = []
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
if (!err?._messageShown) {
|
||||||
|
useMessage().error(err?.msg || '统计班主任查看情况失败')
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
openDialog,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
</style>
|
||||||
|
|
||||||
Reference in New Issue
Block a user