模块移植:场地预约、理论学习()
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
-- 普惠疗休养平台电脑端菜单。
|
||||
-- 使用 permission 做幂等判断,避免同一环境重复执行时插入重复菜单。
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bb1a10001',
|
||||
'',
|
||||
LPAD(IFNULL(MAX(CAST(path AS UNSIGNED)), 0) + 1, 4, '0'),
|
||||
'普惠疗休养',
|
||||
'Tour',
|
||||
'menu',
|
||||
'',
|
||||
'',
|
||||
'ti-map-alt',
|
||||
1,
|
||||
0,
|
||||
'tour',
|
||||
NULL,
|
||||
991,
|
||||
1,
|
||||
'',
|
||||
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||
'',
|
||||
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||
0,
|
||||
'PC',
|
||||
NULL,
|
||||
NULL,
|
||||
'p',
|
||||
0,
|
||||
0
|
||||
FROM sys_menu
|
||||
WHERE (parentId = '' OR parentId IS NULL)
|
||||
AND CHAR_LENGTH(path) = 4
|
||||
HAVING NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10002', p.id, CONCAT(p.path, '0001'), '疗休养设置', 'Tour Setting', 'menu', '/platform/tour/setting', 'data-pjax', '', 1, 0, 'tour.setting', NULL, 1, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'l', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.setting') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10003', p.id, CONCAT(p.path, '0002'), '旅行社管理', 'Travel Agency', 'menu', '/platform/tour/travelAgency', 'data-pjax', '', 1, 0, 'tour.travelAgency', NULL, 2, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'l', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.travelAgency') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10004', p.id, CONCAT(p.path, '0003'), '线路管理', 'Route Manage', 'menu', '/platform/tour/route', 'data-pjax', '', 1, 0, 'tour.route', NULL, 3, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'x', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.route') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10005', p.id, CONCAT(p.path, '0004'), '疗休养事项', 'Tour Matter', 'menu', '/platform/tour/matter', 'data-pjax', '', 1, 0, 'tour.matter', NULL, 4, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'l', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.matter') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10006', p.id, CONCAT(p.path, '0005'), '疗休养报名', 'Tour Signup', 'menu', '/platform/tour/signup', 'data-pjax', '', 1, 0, 'tour.signup', NULL, 5, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'l', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.signup') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10008', p.id, CONCAT(p.path, '0006'), '我的报名', 'My Signup', 'menu', '/platform/tour/mysignup', 'data-pjax', '', 1, 0, 'tour.mysignup', NULL, 6, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'w', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.mysignup') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10009', p.id, CONCAT(p.path, '0007'), '分工会查询', 'Union Ledger', 'menu', '/platform/tour/unionledger', 'data-pjax', '', 1, 0, 'tour.unionledger', NULL, 7, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'f', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.unionledger') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10011', p.id, CONCAT(p.path, '0010'), '分工会审核', 'Union Approval', 'menu', '/platform/tour/unionApproval', 'data-pjax', '', 1, 0, 'tour.unionApproval', NULL, 8, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'f', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.unionApproval') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10012', p.id, CONCAT(p.path, '0011'), '校工会审核', 'School Union Approval', 'menu', '/platform/tour/schoolUnionApproval', 'data-pjax', '', 1, 0, 'tour.schoolUnionApproval', NULL, 9, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'x', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.schoolUnionApproval') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10007', p.id, CONCAT(p.path, '0008'), '疗休养台账', 'Tour Ledger', 'menu', '/platform/tour/ledger', 'data-pjax', '', 1, 0, 'tour.ledger', NULL, 10, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'l', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.ledger') t);
|
||||
|
||||
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
|
||||
SELECT 'b7c63f0c73a44a9a8c4f3d3bb1a10010', p.id, CONCAT(p.path, '0009'), '线路成团', 'Tour Group', 'menu', '/platform/tour/group', 'data-pjax', '', 1, 0, 'tour.group', NULL, 11, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'x', 0, 0
|
||||
FROM sys_menu p
|
||||
WHERE p.permission = 'tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'tour.group') t);
|
||||
|
||||
-- 给系统管理员默认授权,其他角色可在角色管理中按需分配。
|
||||
INSERT INTO sys_role_menu (roleId, menuId)
|
||||
SELECT r.id, m.id
|
||||
FROM sys_role r
|
||||
JOIN sys_menu m ON m.permission IN (
|
||||
'tour',
|
||||
'tour.setting',
|
||||
'tour.travelAgency',
|
||||
'tour.route',
|
||||
'tour.matter',
|
||||
'tour.signup',
|
||||
'tour.group',
|
||||
'tour.mysignup',
|
||||
'tour.unionledger',
|
||||
'tour.unionApproval',
|
||||
'tour.schoolUnionApproval',
|
||||
'tour.ledger'
|
||||
)
|
||||
LEFT JOIN sys_role_menu rm ON rm.roleId = r.id AND rm.menuId = m.id
|
||||
WHERE r.code = 'SYSADMIN'
|
||||
AND rm.roleId IS NULL;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `site_cug_info`
|
||||
ADD COLUMN `segmented_open_hours` JSON NULL COMMENT '分段预约场次信息' AFTER `open_hours`,
|
||||
ADD COLUMN `full_day_open_hour` JSON NULL COMMENT '全天候预约时间段' AFTER `segmented_open_hours`;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `site_cug_info`
|
||||
ADD COLUMN `reserve_time_type` INT NULL DEFAULT 1 COMMENT '预约时间段类型(1分段预约,2全天候预约)' AFTER `not_apply_time_list`,
|
||||
ADD COLUMN `open_hours` JSON NULL COMMENT '场次信息' AFTER `reserve_time_type`;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `site_cug_info`
|
||||
ADD COLUMN `site_photo` VARCHAR(255) NULL COMMENT '场地照片' AFTER `introduce`;
|
||||
@@ -0,0 +1,253 @@
|
||||
-- 普惠疗休养系统字典初始化。
|
||||
-- 执行后可在 /platform/sys/dict 页面看到:疗休养(Tour) -> 组织形式/疗休养类型/线路类型/床型。
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict001', '', '9500', '疗休养', '普惠疗休养平台字典', 'Tour', 0, 9500, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'Tour') t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict002', p.id, CONCAT(p.path, '0001'), '组织形式', '疗休养组织形式', 'organizationType', 0, 1, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'Tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'organizationType' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict003', p.id, CONCAT(p.path, '0001'), '校工会组织', '疗休养组织形式', 'schoolUnion', 0, 1, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'organizationType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'schoolUnion' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict004', p.id, CONCAT(p.path, '0002'), '分工会组织', '疗休养组织形式', 'branchUnion', 0, 2, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'organizationType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'branchUnion' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict005', p.id, CONCAT(p.path, '0003'), '个人组织', '疗休养组织形式', 'personal', 0, 3, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'organizationType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'personal' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict006', p.id, CONCAT(p.path, '0002'), '疗休养类型', '疗休养类型', 'tourType', 0, 2, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'Tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'tourType' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict007', p.id, CONCAT(p.path, '0001'), '普惠性疗休养', '疗休养类型', 'inclusiveTour', 0, 1, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'tourType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'inclusiveTour' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict008', p.id, CONCAT(p.path, '0002'), '优秀职工疗休养', '疗休养类型', 'excellentWorkerTour', 0, 2, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'tourType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'excellentWorkerTour' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict009', p.id, CONCAT(p.path, '0003'), '线路类型', '线路类型', 'lineType', 0, 3, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'Tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'lineType' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict010', p.id, CONCAT(p.path, '0001'), '省内线路', '线路类型', 'inProvinceLine', 0, 1, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'lineType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'inProvinceLine' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict011', p.id, CONCAT(p.path, '0002'), '省外线路', '线路类型', 'outProvinceLine', 0, 2, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'lineType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'outProvinceLine' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict020', p.id, CONCAT(p.path, '0004'), '床型', '床型', 'bedType', 0, 4, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'Tour'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'bedType' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict021', p.id, CONCAT(p.path, '0001'), '双人床', '床型', 'doubleBed', 0, 1, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'bedType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'doubleBed' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict022', p.id, CONCAT(p.path, '0002'), '单人床', '床型', 'singleBed', 0, 2, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'bedType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'singleBed' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict023', p.id, CONCAT(p.path, '0003'), '大床', '床型', 'kingBed', 0, 3, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'bedType'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'kingBed' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict012', '', '9600', '亲属关系', '亲属关系', 'familyRelationship', 0, 9600, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'familyRelationship') t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict013', p.id, CONCAT(p.path, '0001'), '直系亲属', '亲属关系', 'directRelative', 0, 1, 1,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'familyRelationship'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'directRelative' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict014', p.id, CONCAT(p.path, '0001'), '父亲', '直系亲属', 'father', 0, 1, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'directRelative'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'father' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict015', p.id, CONCAT(p.path, '0002'), '母亲', '直系亲属', 'mother', 0, 2, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'directRelative'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'mother' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict016', p.id, CONCAT(p.path, '0003'), '丈夫', '直系亲属', 'husband', 0, 3, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'directRelative'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'husband' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict017', p.id, CONCAT(p.path, '0004'), '妻子', '直系亲属', 'wife', 0, 4, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'directRelative'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'wife' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict018', p.id, CONCAT(p.path, '0005'), '儿子', '直系亲属', 'son', 0, 5, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'directRelative'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'son' AND `parentId` = p.id) t);
|
||||
|
||||
INSERT INTO `sys_dict` (
|
||||
`id`, `parentId`, `path`, `name`, `remark`, `code`, `disabled`, `location`, `hasChildren`,
|
||||
`createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`
|
||||
)
|
||||
SELECT
|
||||
'b7c63f0c73a44a9a8c4f3d3bdict019', p.id, CONCAT(p.path, '0006'), '女儿', '直系亲属', 'daughter', 0, 6, 0,
|
||||
'', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0
|
||||
FROM `sys_dict` p
|
||||
WHERE p.`code` = 'directRelative'
|
||||
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM `sys_dict` WHERE `code` = 'daughter' AND `parentId` = p.id) t);
|
||||
|
||||
UPDATE `sys_dict` SET `hasChildren` = 1 WHERE `code` IN ('Tour', 'organizationType', 'tourType', 'lineType', 'bedType', 'familyRelationship', 'directRelative');
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Add matter reference so signup records are unique per travel period/matter.
|
||||
ALTER TABLE `tour_ledger`
|
||||
ADD COLUMN `matterId` varchar(32) DEFAULT NULL COMMENT '报名事项ID' AFTER `signupTime`;
|
||||
|
||||
ALTER TABLE `tour_ledger`
|
||||
ADD KEY `idx_tour_ledger_matter` (`matterId`),
|
||||
ADD KEY `idx_tour_ledger_matter_job` (`matterId`, `jobNo`);
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `tour_ledger`
|
||||
ADD COLUMN `overCostReimbursed` tinyint(1) DEFAULT 0 COMMENT '报销超出费用' AFTER `reimbursed`;
|
||||
@@ -0,0 +1,9 @@
|
||||
-- 疗休养台账查询加速索引。
|
||||
ALTER TABLE `tour_ledger`
|
||||
ADD KEY `idx_tour_ledger_year_signup` (`year`, `delFlag`, `signupTime`),
|
||||
ADD KEY `idx_tour_ledger_year_line` (`year`, `lineId`, `delFlag`),
|
||||
ADD KEY `idx_tour_ledger_year_union_type` (`year`, `unionId`, `lineType`, `delFlag`),
|
||||
ADD KEY `idx_tour_ledger_year_job` (`year`, `jobNo`, `delFlag`);
|
||||
|
||||
ALTER TABLE `wf_process_instance`
|
||||
ADD KEY `idx_wf_process_instance_business_state` (`businessNo`, `state`);
|
||||
@@ -0,0 +1,21 @@
|
||||
-- 直系亲属线路报名信息表,独立于普通携带亲属信息表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_ledger_direct_relative` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`ledgerId` varchar(32) DEFAULT NULL COMMENT '台账ID',
|
||||
`relativeName` varchar(100) DEFAULT NULL COMMENT '亲属姓名',
|
||||
`unitName` varchar(100) DEFAULT NULL COMMENT '所在单位',
|
||||
`relationshipCode` varchar(50) DEFAULT NULL COMMENT '亲属关系编码',
|
||||
`relationshipName` varchar(50) DEFAULT NULL COMMENT '亲属关系',
|
||||
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
|
||||
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
|
||||
`travelStartTime` varchar(20) DEFAULT NULL COMMENT '出行开始日期',
|
||||
`travelEndTime` varchar(20) DEFAULT NULL COMMENT '出行结束日期',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tour_ledger_direct_relative_ledger` (`ledgerId`),
|
||||
KEY `idx_tour_ledger_direct_relative_line` (`lineId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养直系亲属线路报名信息';
|
||||
@@ -0,0 +1,10 @@
|
||||
-- 线路管理增加创建人和所在单位业务字段。
|
||||
ALTER TABLE `tour_line`
|
||||
ADD COLUMN `creatorUserId` varchar(32) DEFAULT NULL COMMENT '创建人ID' AFTER `travelAgencyId`,
|
||||
ADD COLUMN `creatorName` varchar(100) DEFAULT NULL COMMENT '创建人' AFTER `creatorUserId`,
|
||||
ADD COLUMN `unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID' AFTER `creatorName`,
|
||||
ADD COLUMN `unitName` varchar(100) DEFAULT NULL COMMENT '所在单位' AFTER `unitId`;
|
||||
|
||||
ALTER TABLE `tour_line`
|
||||
ADD KEY `idx_tour_line_creator` (`creatorUserId`),
|
||||
ADD KEY `idx_tour_line_unit` (`unitId`);
|
||||
@@ -0,0 +1,3 @@
|
||||
-- 线路管理增加是否直系亲属线路字段,默认否。
|
||||
ALTER TABLE `tour_line`
|
||||
ADD COLUMN `directFamilyUnitLine` tinyint(1) DEFAULT 0 COMMENT '是否直系亲属线路' AFTER `openFlag`;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- 线路管理增加是否对外开放字段,默认是。
|
||||
ALTER TABLE `tour_line`
|
||||
ADD COLUMN `openFlag` tinyint(1) DEFAULT 1 COMMENT '是否对外开放' AFTER `mobileThumb`;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 疗休养事项增加创建人字段。
|
||||
ALTER TABLE `tour_matter`
|
||||
ADD COLUMN `creatorUserId` varchar(32) DEFAULT NULL COMMENT '创建人ID' AFTER `settingId`,
|
||||
ADD COLUMN `creatorName` varchar(100) DEFAULT NULL COMMENT '创建人' AFTER `creatorUserId`;
|
||||
|
||||
ALTER TABLE `tour_matter`
|
||||
ADD KEY `idx_tour_matter_creator` (`creatorUserId`);
|
||||
@@ -0,0 +1,26 @@
|
||||
-- 疗休养事项批次表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_matter_batch` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`matterId` varchar(32) DEFAULT NULL COMMENT '事项ID',
|
||||
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
|
||||
`batchName` varchar(100) DEFAULT NULL COMMENT '批次名称',
|
||||
`signupStartTime` varchar(20) DEFAULT NULL COMMENT '报名开始时间',
|
||||
`signupEndTime` varchar(20) DEFAULT NULL COMMENT '报名结束时间',
|
||||
`travelStartTime` varchar(20) DEFAULT NULL COMMENT '出行开始时间',
|
||||
`travelEndTime` varchar(20) DEFAULT NULL COMMENT '出行结束时间',
|
||||
`changeDeadline` varchar(20) DEFAULT NULL COMMENT '变更截止时间',
|
||||
`contactName` varchar(30) DEFAULT NULL COMMENT '联系人',
|
||||
`contactPhone` varchar(30) DEFAULT NULL COMMENT '联系方式',
|
||||
`minGroupPeople` int DEFAULT NULL COMMENT '最少成团人数',
|
||||
`maxGroupPeople` int DEFAULT NULL COMMENT '最多成团人数',
|
||||
`estimatedCost` decimal(10,2) DEFAULT NULL COMMENT '预计费用',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '状态',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tour_matter_batch_matter` (`matterId`),
|
||||
KEY `idx_tour_matter_batch_line` (`lineId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养事项批次';
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 疗休养事项表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_matter` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`year` int DEFAULT NULL COMMENT '年度',
|
||||
`matterName` varchar(100) DEFAULT NULL COMMENT '事项名称',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '疗休养配置ID',
|
||||
`creatorUserId` varchar(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
`creatorName` varchar(100) DEFAULT NULL COMMENT '创建人',
|
||||
`unionId` varchar(32) DEFAULT NULL COMMENT '所属工会ID',
|
||||
`organizationType` varchar(100) DEFAULT NULL COMMENT '组织形式',
|
||||
`mobileThumb` varchar(500) DEFAULT NULL COMMENT '移动端缩略图',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '事项状态',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tour_matter_year_name` (`year`, `matterName`),
|
||||
KEY `idx_tour_matter_year` (`year`),
|
||||
KEY `idx_tour_matter_creator` (`creatorUserId`),
|
||||
KEY `idx_tour_matter_setting` (`settingId`),
|
||||
KEY `idx_tour_matter_union` (`unionId`),
|
||||
KEY `idx_tour_matter_org_type` (`organizationType`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养事项';
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Add activity user scope reference for tour settings.
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD COLUMN `activityGroupId` varchar(32) DEFAULT NULL COMMENT '可参加人员范围ID' AFTER `tourType`;
|
||||
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD KEY `idx_tour_setting_activity_group` (`activityGroupId`);
|
||||
@@ -0,0 +1,3 @@
|
||||
-- 疗休养配置增加周期允许次数字段。
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD COLUMN `cycleAllowedTimes` int DEFAULT NULL COMMENT '周期允许次数' AFTER `cycleTotalCost`;
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD COLUMN `cycleTotalCost` int DEFAULT NULL COMMENT '周期内总费用' AFTER `cycleEndYear`;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Add optional cycle year range for tour settings.
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD COLUMN `cycleStartYear` int DEFAULT NULL COMMENT '周期开始年度' AFTER `outProvinceRatio`,
|
||||
ADD COLUMN `cycleEndYear` int DEFAULT NULL COMMENT '周期结束年度' AFTER `cycleStartYear`;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- 疗休养配置增加是否填报床位信息字段,默认开启以兼容历史配置。
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD COLUMN `fillBedInfo` tinyint(1) DEFAULT 1 COMMENT '是否填报床位信息' AFTER `allowFamily`;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE `tour_setting`
|
||||
ADD COLUMN `outProvinceRatioType` varchar(50) DEFAULT '当年报名人数' COMMENT '省外人数占比类型' AFTER `outProvinceRatio`,
|
||||
ADD COLUMN `outProvinceFixedPeople` int DEFAULT 0 COMMENT '省外固定人数' AFTER `outProvinceRatioType`;
|
||||
@@ -0,0 +1,183 @@
|
||||
-- 普惠疗休养基础配置表。
|
||||
-- 若生产环境未开启 Nutz 自动建表,请先执行本脚本再使用“疗休养设置”菜单。
|
||||
CREATE TABLE IF NOT EXISTS `tour_setting` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`year` int DEFAULT NULL COMMENT '年度',
|
||||
`configName` varchar(100) DEFAULT NULL COMMENT '疗休养配置名称',
|
||||
`tourType` varchar(50) DEFAULT NULL COMMENT '疗休养类型',
|
||||
`activityGroupId` varchar(32) DEFAULT NULL COMMENT '可参加人员范围ID',
|
||||
`sortNo` int DEFAULT NULL COMMENT '排序编号',
|
||||
`minGroupPeople` int DEFAULT NULL COMMENT '最少成团人数',
|
||||
`maxGroupPeople` int DEFAULT NULL COMMENT '最多成团人数',
|
||||
`outProvinceYears` int DEFAULT NULL COMMENT '省外几年去一次',
|
||||
`outProvinceRatio` decimal(10,2) DEFAULT NULL COMMENT '省外人数占比',
|
||||
`outProvinceRatioType` varchar(50) DEFAULT '当年报名人数' COMMENT '省外人数占比类型',
|
||||
`outProvinceFixedPeople` int DEFAULT 0 COMMENT '省外固定人数',
|
||||
`cycleStartYear` int DEFAULT NULL COMMENT '周期开始年度',
|
||||
`cycleEndYear` int DEFAULT NULL COMMENT '周期结束年度',
|
||||
`cycleTotalCost` int DEFAULT NULL COMMENT '周期内总费用',
|
||||
`cycleAllowedTimes` int DEFAULT NULL COMMENT '周期允许次数',
|
||||
`allowFamily` tinyint(1) DEFAULT 0 COMMENT '是否允许携带家属',
|
||||
`fillBedInfo` tinyint(1) DEFAULT 1 COMMENT '是否填报床位信息',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
|
||||
`serviceNotice` text COMMENT '服务须知',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tour_setting_year` (`year`),
|
||||
KEY `idx_tour_setting_activity_group` (`activityGroupId`),
|
||||
KEY `idx_tour_setting_sort` (`sortNo`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养配置';
|
||||
|
||||
-- 疗休养配置标段表。
|
||||
-- 标段先挂在配置上维护,后续线路、旅行社、报名等模块可继续通过 lotId 做业务关联。
|
||||
CREATE TABLE IF NOT EXISTS `tour_setting_lot` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '所属配置ID',
|
||||
`lotName` varchar(50) DEFAULT NULL COMMENT '标段名称',
|
||||
`lotValue` varchar(50) DEFAULT NULL COMMENT '标段值',
|
||||
`activityCost` int DEFAULT NULL COMMENT '标段费用',
|
||||
`allowOverReimbursement` tinyint(1) DEFAULT 0 COMMENT '允许超出报销',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tour_setting_lot_setting` (`settingId`),
|
||||
KEY `idx_tour_setting_lot_value` (`lotValue`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养配置标段';
|
||||
|
||||
-- 旅行社管理表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_travel_agency` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`year` int DEFAULT NULL COMMENT '年度',
|
||||
`agencyCode` varchar(50) DEFAULT NULL COMMENT '旅行社编号',
|
||||
`agencyName` varchar(100) DEFAULT NULL COMMENT '旅行社名称',
|
||||
`contactName` varchar(30) DEFAULT NULL COMMENT '联系人',
|
||||
`contactPhone` varchar(30) DEFAULT NULL COMMENT '联系人手机',
|
||||
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
|
||||
`remark` text COMMENT '备注',
|
||||
`mobileThumb` varchar(500) DEFAULT NULL COMMENT '移动端缩略图',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '激活状态',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tour_travel_agency_year_code` (`year`, `agencyCode`),
|
||||
KEY `idx_tour_travel_agency_name` (`agencyName`),
|
||||
KEY `idx_tour_travel_agency_contact` (`contactName`, `contactPhone`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养旅行社';
|
||||
|
||||
-- 线路管理表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_line` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`year` int DEFAULT NULL COMMENT '创建年度',
|
||||
`lineCode` varchar(50) DEFAULT NULL COMMENT '线路编号',
|
||||
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
|
||||
`travelAgencyId` varchar(32) DEFAULT NULL COMMENT '旅行社ID',
|
||||
`creatorUserId` varchar(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
`creatorName` varchar(100) DEFAULT NULL COMMENT '创建人',
|
||||
`unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID',
|
||||
`unitName` varchar(100) DEFAULT NULL COMMENT '所在单位',
|
||||
`lineType` varchar(50) DEFAULT NULL COMMENT '线路类型',
|
||||
`lotId` varchar(32) DEFAULT NULL COMMENT '时间标段ID',
|
||||
`lineContent` text COMMENT '线路内容',
|
||||
`mobileThumb` varchar(500) DEFAULT NULL COMMENT '移动端缩略图',
|
||||
`openFlag` tinyint(1) DEFAULT 1 COMMENT '是否对外开放',
|
||||
`directFamilyUnitLine` tinyint(1) DEFAULT 0 COMMENT '是否直系亲属线路',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '激活状态',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tour_line_year_code` (`year`, `lineCode`),
|
||||
KEY `idx_tour_line_year` (`year`),
|
||||
KEY `idx_tour_line_name` (`lineName`),
|
||||
KEY `idx_tour_line_agency` (`travelAgencyId`),
|
||||
KEY `idx_tour_line_creator` (`creatorUserId`),
|
||||
KEY `idx_tour_line_unit` (`unitId`),
|
||||
KEY `idx_tour_line_lot` (`lotId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养线路';
|
||||
|
||||
-- 疗休养事项表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_matter` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`year` int DEFAULT NULL COMMENT '年度',
|
||||
`matterName` varchar(100) DEFAULT NULL COMMENT '事项名称',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '疗休养配置ID',
|
||||
`creatorUserId` varchar(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
`creatorName` varchar(100) DEFAULT NULL COMMENT '创建人',
|
||||
`unionId` varchar(32) DEFAULT NULL COMMENT '所属工会ID',
|
||||
`organizationType` varchar(100) DEFAULT NULL COMMENT '组织形式',
|
||||
`mobileThumb` varchar(500) DEFAULT NULL COMMENT '移动端缩略图',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '事项状态',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_tour_matter_year_name` (`year`, `matterName`),
|
||||
KEY `idx_tour_matter_year` (`year`),
|
||||
KEY `idx_tour_matter_creator` (`creatorUserId`),
|
||||
KEY `idx_tour_matter_setting` (`settingId`),
|
||||
KEY `idx_tour_matter_union` (`unionId`),
|
||||
KEY `idx_tour_matter_org_type` (`organizationType`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养事项';
|
||||
|
||||
-- 疗休养事项批次表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_matter_batch` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`matterId` varchar(32) DEFAULT NULL COMMENT '事项ID',
|
||||
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
|
||||
`batchName` varchar(100) DEFAULT NULL COMMENT '批次名称',
|
||||
`signupStartTime` varchar(20) DEFAULT NULL COMMENT '报名开始时间',
|
||||
`signupEndTime` varchar(20) DEFAULT NULL COMMENT '报名结束时间',
|
||||
`travelStartTime` varchar(20) DEFAULT NULL COMMENT '出行开始时间',
|
||||
`travelEndTime` varchar(20) DEFAULT NULL COMMENT '出行结束时间',
|
||||
`changeDeadline` varchar(20) DEFAULT NULL COMMENT '变更截止时间',
|
||||
`contactName` varchar(30) DEFAULT NULL COMMENT '联系人',
|
||||
`contactPhone` varchar(30) DEFAULT NULL COMMENT '联系方式',
|
||||
`minGroupPeople` int DEFAULT NULL COMMENT '最少成团人数',
|
||||
`maxGroupPeople` int DEFAULT NULL COMMENT '最多成团人数',
|
||||
`estimatedCost` decimal(10,2) DEFAULT NULL COMMENT '预计费用',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '状态',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tour_matter_batch_matter` (`matterId`),
|
||||
KEY `idx_tour_matter_batch_line` (`lineId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养事项批次';
|
||||
|
||||
-- 直系亲属线路报名信息表。
|
||||
CREATE TABLE IF NOT EXISTS `tour_ledger_direct_relative` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`ledgerId` varchar(32) DEFAULT NULL COMMENT '台账ID',
|
||||
`relativeName` varchar(100) DEFAULT NULL COMMENT '亲属姓名',
|
||||
`unitName` varchar(100) DEFAULT NULL COMMENT '所在单位',
|
||||
`relationshipCode` varchar(50) DEFAULT NULL COMMENT '亲属关系编码',
|
||||
`relationshipName` varchar(50) DEFAULT NULL COMMENT '亲属关系',
|
||||
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
|
||||
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
|
||||
`travelStartTime` varchar(20) DEFAULT NULL COMMENT '出行开始日期',
|
||||
`travelEndTime` varchar(20) DEFAULT NULL COMMENT '出行结束日期',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_tour_ledger_direct_relative_ledger` (`ledgerId`),
|
||||
KEY `idx_tour_ledger_direct_relative_line` (`lineId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='普惠疗休养直系亲属线路报名信息';
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `tour_setting_lot`
|
||||
ADD COLUMN `allowOverReimbursement` tinyint(1) DEFAULT 0 COMMENT '允许超出报销' AFTER `activityCost`;
|
||||
@@ -32,7 +32,7 @@ var ioc={
|
||||
// 单个文件最大尺寸(大约的值,单位为字节,即 209715200 为 200M)
|
||||
maxFileSize : 209715200,
|
||||
// 正则表达式匹配可以支持的文件名
|
||||
nameFilter : '^(.+[.])(gif||svg|jpg|png|jpeg|doc|docx|xls|xlsx|rar|zip|7z|txt|pdf|pptx|ppt|mp4)$'
|
||||
nameFilter : '^(.+[.])(gif||svg|jpg|png|jpeg|doc|docx|xls|xlsx|rar|zip|7z|txt|pdf|pptx|ppt|flv|swf|mkv|avi|rm|rmvb|wmv|mp4|mov|mpg|mpeg|webm|m4v|3gp|3g2|ts|mts|m2ts|ogv)$'
|
||||
}
|
||||
},
|
||||
fileUpload : {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
@@ -322,29 +322,51 @@ module.exports = {
|
||||
return true
|
||||
}
|
||||
|
||||
// 去除前后空格并转换为数组
|
||||
const acceptTypes = this.fileAccept
|
||||
.trim()
|
||||
.split(",")
|
||||
.map((type) => type.trim().toUpperCase())
|
||||
|
||||
let fileType
|
||||
const dotIndex = file.name.lastIndexOf(".")
|
||||
if (dotIndex !== -1) {
|
||||
fileType = file.name.substring(dotIndex).toUpperCase()
|
||||
} else {
|
||||
this.$message.error("文件必须包含有效的扩展名!")
|
||||
return false
|
||||
}
|
||||
|
||||
const valid = acceptTypes.includes(fileType)
|
||||
const valid = this.isAcceptFile(file)
|
||||
if (valid) {
|
||||
return true
|
||||
}
|
||||
this.$message.error(`文件只能是 ${this.fileAccept} 格式!`)
|
||||
this.$message.error("文件只能是 " + this.fileAccept + " 格式!")
|
||||
return false
|
||||
},
|
||||
|
||||
// 兼容 accept="video/*" 这类 MIME 通配写法,同时保留原有扩展名白名单校验。
|
||||
isAcceptFile(file) {
|
||||
if (!this.fileAccept || typeof this.fileAccept !== "string") {
|
||||
return true
|
||||
}
|
||||
const acceptTypes = this.getAcceptTypes()
|
||||
const fileName = file.name || ""
|
||||
const dotIndex = fileName.lastIndexOf(".")
|
||||
const fileType = dotIndex !== -1 ? fileName.substring(dotIndex).toUpperCase() : ""
|
||||
const mimeType = (file.type || "").toUpperCase()
|
||||
|
||||
if (!fileType) {
|
||||
this.$message.error("文件必须包含有效的扩展名!")
|
||||
return false
|
||||
}
|
||||
|
||||
return acceptTypes.some((acceptType) => {
|
||||
if (acceptType.endsWith("/*")) {
|
||||
return mimeType.startsWith(acceptType.substring(0, acceptType.length - 1)) || this.matchVideoWildcard(acceptType, fileType)
|
||||
}
|
||||
return acceptType === fileType
|
||||
})
|
||||
},
|
||||
|
||||
// 部分浏览器或系统无法识别视频 MIME,此处用视频后缀兜底保证 video/* 可覆盖常见视频格式。
|
||||
matchVideoWildcard(acceptType, fileType) {
|
||||
const videoExtTypes = [".FLV", ".SWF", ".MKV", ".AVI", ".RM", ".RMVB", ".WMV", ".MP4", ".MOV", ".MPG", ".MPEG", ".WEBM", ".M4V", ".3GP", ".3G2", ".TS", ".MTS", ".M2TS", ".OGV"]
|
||||
return acceptType === "VIDEO/*" && videoExtTypes.includes(fileType)
|
||||
},
|
||||
|
||||
getAcceptTypes() {
|
||||
return this.fileAccept
|
||||
.trim()
|
||||
.split(",")
|
||||
.map((type) => type.trim().toUpperCase())
|
||||
},
|
||||
|
||||
// 预览图片
|
||||
handlePreview(file) {
|
||||
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase()
|
||||
@@ -571,24 +593,13 @@ module.exports = {
|
||||
}
|
||||
|
||||
if (this.fileAccept) {
|
||||
// 去除前后空格并转换为数组
|
||||
const acceptTypes = this.fileAccept
|
||||
.trim()
|
||||
.split(",")
|
||||
.map((type) => type.trim().toUpperCase())
|
||||
|
||||
const validFiles = data.files.filter((file) => {
|
||||
let fileType
|
||||
const dotIndex = file.name.lastIndexOf(".")
|
||||
if (dotIndex !== -1) {
|
||||
fileType = file.name.substring(dotIndex).toUpperCase()
|
||||
}
|
||||
return acceptTypes.includes(fileType)
|
||||
return this.isAcceptFile(file)
|
||||
})
|
||||
|
||||
if (validFiles.length < data.files.length) {
|
||||
data.files = validFiles
|
||||
this.$message.error(`文件只能是 ${this.fileAccept} 格式,已自动过滤掉不符合要求文件!`)
|
||||
this.$message.error("文件只能是 " + this.fileAccept + " 格式,已自动过滤掉不符合要求文件!")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,353 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="线路名称">
|
||||
<el-input
|
||||
v-model="pageForm.lineName"
|
||||
clearable
|
||||
placeholder="请输入线路名称"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="线路类型">
|
||||
<el-select v-model="pageForm.lineType" clearable filterable placeholder="请选择线路类型" style="width: 100%">
|
||||
<el-option v-for="item in lineTypeOptions" :key="item.lineType" :label="item.lineType" :value="item.lineType"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="线路列表"></table-tool>
|
||||
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
:default-sort="{prop: 'lineName', order: 'ascending'}"
|
||||
:row-class-name="tableRowClassName"
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="线路名称" prop="lineName" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="出行时段" prop="travelPeriod" width="260" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="事项名称" prop="matterName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所属工会" prop="unionName" min-width="180" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路类型" prop="lineType" width="140" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="最少成团人数" prop="minGroupPeople" width="140" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
{{ row.minGroupPeople || 0 }}人
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最多成团人数" prop="maxGroupPeople" width="140" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
{{ row.maxGroupPeople || 0 }}人
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报名人数" prop="signupCount" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="Number(row.signupCount || 0) > 0 ? 'success' : 'info'">{{ row.signupCount || 0 }}人</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="成团状态" prop="groupStatusName" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="groupStatusType(row.groupStatus)">{{ row.groupStatusName || '未成团' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button size="mini" type="primary" @click="exportLine(row)">导出</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-drawer
|
||||
:title="viewTitle"
|
||||
:visible.sync="viewVisible"
|
||||
direction="rtl"
|
||||
size="72%"
|
||||
custom-class="tour-group-view-drawer"
|
||||
:close-on-click-modal="false">
|
||||
<div class="tour-group-view">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="viewForm.keyword"
|
||||
clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="viewSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="viewForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="viewSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="viewReset">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<div class="tour-group-view-title">报名人员列表</div>
|
||||
<el-table
|
||||
v-loading="viewLoading"
|
||||
:data="viewTableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="viewPageOrder">
|
||||
<el-table-column label="序号" type="index" :index="viewIndexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="工号" prop="jobNo" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="姓名" prop="userName" width="120" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="手机号码" prop="mobile" width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="身份证号" prop="idCard" min-width="190" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所属工会" prop="unionName" min-width="180" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container tour-group-view-pagination">
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="viewForm.pageNumber"
|
||||
:page-size="viewForm.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="viewForm.totalCount"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="viewSizeChange"
|
||||
@current-change="viewCurrentChange">
|
||||
</el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
return {
|
||||
unionOptions: [],
|
||||
lineTypeOptions: [],
|
||||
viewVisible: false,
|
||||
viewLoading: false,
|
||||
viewRequestSeq: 0,
|
||||
viewTitle: "",
|
||||
viewRow: {},
|
||||
viewTableData: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "lineName",
|
||||
pageOrderBy: "ascending",
|
||||
year: currentYear,
|
||||
lineName: "",
|
||||
lineType: "",
|
||||
unionId: ""
|
||||
},
|
||||
viewForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
keyword: "",
|
||||
unionId: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.year = moment().format("YYYY")
|
||||
this.pageForm.lineName = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.loadLineTypeOptions()
|
||||
this.doSearch()
|
||||
},
|
||||
groupStatusType(status) {
|
||||
if (status === "formed") return "success"
|
||||
if (status === "over") return "danger"
|
||||
return "info"
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return this.isDirectFamilyLine(row) ? "direct-family-row" : ""
|
||||
},
|
||||
isDirectFamilyLine(row) {
|
||||
if (!row) return false
|
||||
return row.directFamilyUnitLine === true
|
||||
|| row.directFamilyUnitLine === 1
|
||||
|| row.directFamilyUnitLine === "1"
|
||||
},
|
||||
openView(row) {
|
||||
this.viewRow = row || {}
|
||||
this.viewTitle = this.viewRow.lineName || "报名人员"
|
||||
this.viewVisible = true
|
||||
this.viewForm.pageNumber = 1
|
||||
this.viewForm.pageSize = 10
|
||||
this.viewForm.totalCount = 0
|
||||
this.viewForm.pageOrderName = "signupTime"
|
||||
this.viewForm.pageOrderBy = "descending"
|
||||
this.viewForm.keyword = ""
|
||||
this.viewForm.unionId = ""
|
||||
this.viewTableData = []
|
||||
this.loadViewData()
|
||||
},
|
||||
viewSearch() {
|
||||
this.viewForm.pageNumber = 1
|
||||
this.loadViewData()
|
||||
},
|
||||
viewReset() {
|
||||
this.viewForm.keyword = ""
|
||||
this.viewForm.unionId = ""
|
||||
this.viewForm.pageNumber = 1
|
||||
this.loadViewData()
|
||||
},
|
||||
viewIndexMethod(index) {
|
||||
return (this.viewForm.pageNumber - 1) * this.viewForm.pageSize + index + 1
|
||||
},
|
||||
viewSizeChange(size) {
|
||||
this.viewForm.pageSize = size
|
||||
this.viewForm.pageNumber = 1
|
||||
this.loadViewData()
|
||||
},
|
||||
viewCurrentChange(page) {
|
||||
this.viewForm.pageNumber = page
|
||||
this.loadViewData()
|
||||
},
|
||||
viewPageOrder({ prop, order }) {
|
||||
this.viewForm.pageOrderName = prop || "signupTime"
|
||||
this.viewForm.pageOrderBy = order || "descending"
|
||||
this.viewForm.pageNumber = 1
|
||||
this.loadViewData()
|
||||
},
|
||||
loadViewData() {
|
||||
if (!this.viewRow || !this.viewRow.matterId) {
|
||||
this.viewTableData = []
|
||||
this.viewForm.totalCount = 0
|
||||
return
|
||||
}
|
||||
this.viewLoading = true
|
||||
const requestSeq = ++this.viewRequestSeq
|
||||
this.$axios.post(loc() + "/signupPageData", {
|
||||
matterId: this.viewRow.matterId,
|
||||
keyword: this.viewForm.keyword,
|
||||
unionId: this.viewForm.unionId,
|
||||
pageNumber: this.viewForm.pageNumber,
|
||||
pageSize: this.viewForm.pageSize,
|
||||
pageOrderName: this.viewForm.pageOrderName,
|
||||
pageOrderBy: this.viewForm.pageOrderBy
|
||||
}).then((res) => {
|
||||
if (requestSeq !== this.viewRequestSeq) return
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.viewTableData = data.list || []
|
||||
this.viewForm.totalCount = data.totalCount || 0
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
if (requestSeq === this.viewRequestSeq) {
|
||||
this.viewLoading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
exportLine(row) {
|
||||
if (!row || !row.matterId) {
|
||||
this.$message.warning("线路信息不完整")
|
||||
return
|
||||
}
|
||||
window.location.href = loc() + "/exportParticipants?matterId=" + encodeURIComponent(row.matterId)
|
||||
},
|
||||
loadUnionOptions() {
|
||||
this.$axios.post(loc() + "/unionOptions").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.unionOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineTypeOptions() {
|
||||
this.$axios.post(loc() + "/lineTypeOptions", {
|
||||
year: this.pageForm.year
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineTypeOptions = (res.data || []).filter(item => item.lineType)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadUnionOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.pageData()
|
||||
},
|
||||
watch: {
|
||||
"pageForm.year"() {
|
||||
this.pageForm.lineType = ""
|
||||
this.loadLineTypeOptions()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tour-group-view-drawer .el-drawer__body {
|
||||
background: #f5f7fa;
|
||||
padding: 12px;
|
||||
overflow: auto;
|
||||
}
|
||||
.tour-group-view-pagination {
|
||||
margin-top: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
.tour-group-view-title {
|
||||
border-left: 4px solid #0079c2;
|
||||
color: #0079c2;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
margin-bottom: 12px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.el-table .direct-family-row > td,
|
||||
.el-table .direct-family-row > td .cell {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,653 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="开始年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.startYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择开始年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="结束年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.endYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择结束年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="pageForm.keyword"
|
||||
clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路">
|
||||
<el-select v-model="pageForm.lineId" clearable filterable placeholder="请选择线路" style="width: 100%">
|
||||
<el-option v-for="item in lineOptions" :key="item.lineId" :label="item.lineName" :value="item.lineId"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="出行时段">
|
||||
<el-select v-model="pageForm.travelPeriod" clearable filterable placeholder="请选择出行时段" style="width: 100%">
|
||||
<el-option v-for="item in travelPeriodOptions" :key="item.travelPeriod" :label="item.travelPeriod" :value="item.travelPeriod"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路类型">
|
||||
<dict-select
|
||||
v-model="pageForm.lineType"
|
||||
code="lineType"
|
||||
option_value="name"
|
||||
placeholder="请选择线路类型">
|
||||
</dict-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<div class="tour-ledger-toolbar">
|
||||
<div class="tour-ledger-toolbar-title">
|
||||
<table-tool :app="this" label="台账列表"></table-tool>
|
||||
</div>
|
||||
<div class="tour-ledger-actions">
|
||||
<el-button size="medium" type="primary" icon="el-icon-upload2" @click="showImportDialog = true">参加人员导入</el-button>
|
||||
<el-button size="medium" type="primary" icon="el-icon-check" @click="setParticipants">设置参加人员</el-button>
|
||||
<el-button size="medium" type="primary" icon="el-icon-download" @click="exportUnionSignupZip">导出分工会报名压缩包</el-button>
|
||||
</div>
|
||||
<div class="tour-ledger-scope">
|
||||
<el-button
|
||||
class="tour-scope-btn"
|
||||
size="medium"
|
||||
:class="{'is-active': pageForm.directFamilyOnly}"
|
||||
@click="setDirectFamilyOnly">
|
||||
参加直系亲属单位线路人员
|
||||
</el-button>
|
||||
<el-button
|
||||
class="tour-scope-btn"
|
||||
size="medium"
|
||||
:class="{'is-active': pageForm.overCostOnly}"
|
||||
@click="setOverCostOnly">
|
||||
申请超出费用由单位承担人员
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
row-key="id"
|
||||
:row-class-name="tableRowClassName"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column type="selection" width="55" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="工号" prop="jobNo" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="姓名" prop="userName" width="120" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名线路" prop="lineName" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="出行时段" prop="travelPeriod" min-width="260" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路类型" prop="lineType" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名时间" prop="signupTime" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="携带家属" prop="familyCount" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="Number(row.familyCount || 0) > 0 ? 'success' : 'info'">{{ row.familyCount || 0 }}人</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否参加" prop="joined" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.joined ? 'success' : 'info'">{{ row.joined ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报销超出费用" prop="overCostReimbursed" width="140" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.overCostReimbursed ? 'success' : 'info'">{{ row.overCostReimbursed ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
<div class="tour-ledger-tip">温馨提醒:走审核流程的报名,审核通过后才在台账中显示</div>
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<excel-import
|
||||
ref="excelImportRef"
|
||||
url="/platform/tour/ledger/importParticipants"
|
||||
template_url="/platform/tour/ledger/downloadTemplate"
|
||||
:visible.sync="showImportDialog"
|
||||
title="参加人员导入"
|
||||
width="700px"
|
||||
@import-success="afterImport"
|
||||
:extra_params="{}">
|
||||
</excel-import>
|
||||
|
||||
<el-dialog
|
||||
title="台账详情"
|
||||
:visible.sync="detailVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="72%">
|
||||
<div class="tour-ledger-section">
|
||||
<div class="tour-ledger-title">教职工信息</div>
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="工号">{{ detail.jobNo || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ detail.userName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ detail.gender || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="身份证号">{{ detail.idCard || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ detail.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在工会">{{ detail.unionName || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div class="tour-ledger-section mt10">
|
||||
<div class="tour-ledger-title">
|
||||
报名信息
|
||||
<el-link v-if="detailHasWorkflow()" type="primary" @click="openChart">点击查看流程图</el-link>
|
||||
</div>
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="报名时间">{{ detail.signupTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名线路">{{ detail.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路类型">{{ detail.lineType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名酒店">{{ detail.hotelName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名旅行社">{{ detail.travelAgencyName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行时间">{{ detail.travelPeriod || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床型">{{ detail.bedType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床位信息">{{ detail.bedInfo || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="意向拼床人">{{ detail.intendedRoommate || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否携带家属">{{ familyText() }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否参加">{{ detail.joined ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否报销">{{ detail.reimbursed ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报销超出费用">{{ detail.overCostReimbursed ? '是' : '否' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="isDirectFamilyLine()" class="tour-ledger-section mt10">
|
||||
<div class="tour-ledger-title">直系亲属线路</div>
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="亲属姓名">{{ directRelative.relativeName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ directRelative.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="亲属关系">{{ directRelative.relationshipName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路名称">{{ directRelative.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行开始日期">{{ directRelative.travelStartTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行结束日期">{{ directRelative.travelEndTime || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="showFamilySection()" class="tour-ledger-section mt10">
|
||||
<div class="tour-ledger-title">家属信息</div>
|
||||
<el-table :data="familyData" border :size="tableSize" empty-text="暂无家属信息">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="家属姓名" prop="familyName" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="性别" prop="gender" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="身份证号码" prop="idCard" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="年龄" prop="age" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="关系" prop="relationship" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床型" prop="bedType" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床位" prop="bedInfo" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="意向拼床人" prop="intendedRoommate" min-width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template v-if="detailHasWorkflow()" v-for="task in doneTasks">
|
||||
<div class="tour-ledger-section mt10" :key="task.id">
|
||||
<div class="tour-ledger-title">{{ task.displayName }}</div>
|
||||
<el-descriptions border :column="3" v-if="task.ext && task.ext.isFirstTaskNode">
|
||||
<el-descriptions-item label="申请用户">
|
||||
{{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions border :column="3" v-else>
|
||||
<el-descriptions-item label="办理用户">
|
||||
{{ (task.taskFormData && task.taskFormData.userName) || '' }}({{ (task.taskFormData && task.taskFormData.loginName) || '' }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理意见" :span="3">
|
||||
{{ (task.taskFormData && (task.taskFormData.opinion || task.taskFormData.tf_opinion)) || '' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<snaker-chart ref="snakerChartRef"></snaker-chart>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="detailVisible = false">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tour-ledger-section {
|
||||
padding: 0 2px;
|
||||
}
|
||||
.tour-ledger-title {
|
||||
border-left: 4px solid #0079c2;
|
||||
color: #0079c2;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
margin-bottom: 14px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.tour-ledger-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 24px;
|
||||
margin-bottom: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tour-ledger-toolbar-title {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.tour-ledger-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
gap: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tour-ledger-actions .el-button {
|
||||
margin: 0;
|
||||
}
|
||||
.tour-ledger-scope {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tour-ledger-scope .el-button {
|
||||
margin: 0;
|
||||
}
|
||||
.tour-ledger-scope .el-button + .el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn {
|
||||
background: #ecf5ff;
|
||||
border-color: #b3d8ff;
|
||||
border-radius: 4px;
|
||||
color: #0079c2;
|
||||
font-weight: 600;
|
||||
height: 34px;
|
||||
line-height: 1;
|
||||
padding: 8px 18px;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn:hover,
|
||||
.tour-ledger-scope .tour-scope-btn:focus {
|
||||
background: #d9ecff;
|
||||
border-color: #66b1ff;
|
||||
color: #006bb0;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn.is-active {
|
||||
background: #0079c2;
|
||||
border-color: #0079c2;
|
||||
box-shadow: 0 2px 6px rgba(0, 121, 194, 0.24);
|
||||
color: #fff;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn.is-active:hover,
|
||||
.tour-ledger-scope .tour-scope-btn.is-active:focus {
|
||||
background: #006bb0;
|
||||
border-color: #006bb0;
|
||||
color: #fff;
|
||||
}
|
||||
.tour-ledger-tip {
|
||||
color: #f56c6c;
|
||||
font-family: SimSun, "宋体", serif;
|
||||
font-size: 9pt;
|
||||
line-height: 1.6;
|
||||
margin-top: -4px;
|
||||
text-align: center;
|
||||
}
|
||||
.el-table .direct-family-row > td,
|
||||
.el-table .direct-family-row > td .cell {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
return {
|
||||
detailVisible: false,
|
||||
unionOptions: [],
|
||||
lineOptions: [],
|
||||
travelPeriodOptions: [],
|
||||
detail: {},
|
||||
familyData: [],
|
||||
directRelative: {},
|
||||
directFamilyUnitLine: false,
|
||||
fillBedInfo: true,
|
||||
detailRow: {},
|
||||
doneTasks: [],
|
||||
showImportDialog: false,
|
||||
multipleSelection: [],
|
||||
filterOptionsTimer: null,
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
startYear: currentYear,
|
||||
endYear: currentYear,
|
||||
keyword: "",
|
||||
unionId: "",
|
||||
lineId: "",
|
||||
travelPeriod: "",
|
||||
lineType: "",
|
||||
directFamilyOnly: false,
|
||||
overCostOnly: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1" || value === "true" || value === "TRUE"
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return this.isDirectFamilyRow(row) ? "direct-family-row" : ""
|
||||
},
|
||||
isDirectFamilyRow(row) {
|
||||
return this.toBoolean(row && row.directFamilyUnitLine) || !!(row && row.directRelativeId)
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val || []
|
||||
},
|
||||
clearTableSelection() {
|
||||
this.multipleSelection = []
|
||||
if (this.$refs.tableRef) {
|
||||
this.$refs.tableRef.clearSelection()
|
||||
}
|
||||
},
|
||||
setParticipants() {
|
||||
if (!this.multipleSelection.length) {
|
||||
this.$message.warning("请选择要设置的参加人员")
|
||||
return
|
||||
}
|
||||
this.$confirm("确定将选中的" + this.multipleSelection.length + "条台账设置为已参加吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/setParticipants", {
|
||||
ids: JSON.stringify(this.multipleSelection.map(item => item.id))
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("设置成功")
|
||||
this.clearTableSelection()
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "设置失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
exportUnionSignupZip() {
|
||||
const params = new URLSearchParams()
|
||||
;["startYear", "endYear", "keyword", "unionId", "lineId", "travelPeriod", "lineType"].forEach(key => {
|
||||
const value = this.pageForm[key]
|
||||
if (value !== undefined && value !== null && value !== "") {
|
||||
params.append(key, value)
|
||||
}
|
||||
})
|
||||
if (this.pageForm.directFamilyOnly) {
|
||||
params.append("directFamilyOnly", "true")
|
||||
}
|
||||
if (this.pageForm.overCostOnly) {
|
||||
params.append("overCostOnly", "true")
|
||||
}
|
||||
window.location.href = loc() + "/exportUnionSignupZip?" + params.toString()
|
||||
},
|
||||
afterImport() {
|
||||
this.showImportDialog = false
|
||||
this.clearTableSelection()
|
||||
this.pageData()
|
||||
this.loadFilterOptions()
|
||||
},
|
||||
isDirectFamilyLine() {
|
||||
return this.directFamilyUnitLine
|
||||
|| this.toBoolean(this.detail && this.detail.directFamilyUnitLine)
|
||||
|| !!(this.directRelative && this.directRelative.id)
|
||||
},
|
||||
detailHasWorkflow() {
|
||||
return !!(this.detailRow && this.detailRow.instanceId)
|
||||
},
|
||||
hasFamily() {
|
||||
return this.toBoolean(this.detail && this.detail.hasFamily) || this.familyData.length > 0
|
||||
},
|
||||
familyText() {
|
||||
if (this.isDirectFamilyLine()) {
|
||||
return "否"
|
||||
}
|
||||
if (!this.hasFamily()) {
|
||||
return "否"
|
||||
}
|
||||
const count = Number(this.familyData.length || 0)
|
||||
return count > 0 ? count + "人" : "是"
|
||||
},
|
||||
showFamilySection() {
|
||||
return this.hasFamily() && !this.isDirectFamilyLine()
|
||||
},
|
||||
loadDoneTasks() {
|
||||
this.$axios.post("/flow/common/doneTasks", { bizId: this.detailRow.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
openChart() {
|
||||
if (!this.detailRow.instanceProcessDefineId || !this.detailRow.instanceId) {
|
||||
this.$message.warning("暂无流程图信息")
|
||||
return
|
||||
}
|
||||
this.$refs.snakerChartRef.onOpenFull(this.detailRow.instanceProcessDefineId, this.detailRow.instanceId)
|
||||
},
|
||||
resetSearch() {
|
||||
this.pageForm.startYear = moment().format("YYYY")
|
||||
this.pageForm.endYear = moment().format("YYYY")
|
||||
this.pageForm.keyword = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.pageForm.directFamilyOnly = false
|
||||
this.pageForm.overCostOnly = false
|
||||
this.doSearch()
|
||||
},
|
||||
openView(row) {
|
||||
this.detailRow = row || {}
|
||||
this.detail = {}
|
||||
this.familyData = []
|
||||
this.directRelative = {}
|
||||
this.directFamilyUnitLine = false
|
||||
this.fillBedInfo = true
|
||||
this.doneTasks = []
|
||||
this.$axios.post(loc() + "/detail", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.detail = data.ledger || {}
|
||||
this.detail.travelPeriod = data.travelPeriod || ""
|
||||
this.familyData = data.families || []
|
||||
this.directRelative = data.directRelative || {}
|
||||
this.directFamilyUnitLine = this.toBoolean(data.directFamilyUnitLine)
|
||||
this.fillBedInfo = data.fillBedInfo === undefined || data.fillBedInfo === null || this.toBoolean(data.fillBedInfo)
|
||||
this.detailVisible = true
|
||||
if (this.detailHasWorkflow()) {
|
||||
this.loadDoneTasks()
|
||||
}
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定删除【" + row.userName + "】的报名台账吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
this.loadFilterOptions()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "删除失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
loadUnionOptions() {
|
||||
this.$axios.post(loc() + "/unionOptions").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.unionOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
loadFilterOptions() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
},
|
||||
scheduleFilterOptions(delay) {
|
||||
if (this.filterOptionsTimer) {
|
||||
clearTimeout(this.filterOptionsTimer)
|
||||
}
|
||||
this.filterOptionsTimer = setTimeout(() => {
|
||||
this.loadFilterOptions()
|
||||
this.filterOptionsTimer = null
|
||||
}, delay === undefined ? 80 : delay)
|
||||
},
|
||||
loadLineOptions() {
|
||||
this.$axios.post(loc() + "/lineOptions", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
travelPeriod: this.pageForm.travelPeriod,
|
||||
lineType: this.pageForm.lineType,
|
||||
unionId: this.pageForm.unionId,
|
||||
keyword: this.pageForm.keyword,
|
||||
directFamilyOnly: this.pageForm.directFamilyOnly,
|
||||
overCostOnly: this.pageForm.overCostOnly
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineOptions = res.data || []
|
||||
if (this.pageForm.lineId && !this.lineOptions.some(item => item.lineId === this.pageForm.lineId)) {
|
||||
this.pageForm.lineId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadTravelPeriodOptions() {
|
||||
this.$axios.post(loc() + "/travelPeriodOptions", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
lineId: this.pageForm.lineId,
|
||||
lineType: this.pageForm.lineType,
|
||||
unionId: this.pageForm.unionId,
|
||||
keyword: this.pageForm.keyword,
|
||||
directFamilyOnly: this.pageForm.directFamilyOnly,
|
||||
overCostOnly: this.pageForm.overCostOnly
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.travelPeriodOptions = res.data || []
|
||||
if (this.pageForm.travelPeriod && !this.travelPeriodOptions.some(item => item.travelPeriod === this.pageForm.travelPeriod)) {
|
||||
this.pageForm.travelPeriod = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.tableKey = new Date().getTime()
|
||||
this.pageForm.pageNumber = 1
|
||||
this.clearTableSelection()
|
||||
this.pageData()
|
||||
this.scheduleFilterOptions(0)
|
||||
},
|
||||
setDirectFamilyOnly() {
|
||||
this.pageForm.directFamilyOnly = !this.pageForm.directFamilyOnly
|
||||
this.pageForm.pageNumber = 1
|
||||
this.clearTableSelection()
|
||||
this.pageData()
|
||||
this.scheduleFilterOptions(0)
|
||||
},
|
||||
setOverCostOnly() {
|
||||
this.pageForm.overCostOnly = !this.pageForm.overCostOnly
|
||||
this.pageForm.pageNumber = 1
|
||||
this.clearTableSelection()
|
||||
this.pageData()
|
||||
this.scheduleFilterOptions(0)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadUnionOptions()
|
||||
this.pageData()
|
||||
this.scheduleFilterOptions(150)
|
||||
},
|
||||
watch: {
|
||||
"pageForm.startYear"() {
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.scheduleFilterOptions()
|
||||
},
|
||||
"pageForm.endYear"() {
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.scheduleFilterOptions()
|
||||
},
|
||||
"pageForm.lineId"() {
|
||||
this.scheduleFilterOptions()
|
||||
},
|
||||
"pageForm.travelPeriod"() {
|
||||
this.scheduleFilterOptions()
|
||||
},
|
||||
"pageForm.lineType"() {
|
||||
this.scheduleFilterOptions()
|
||||
},
|
||||
"pageForm.unionId"() {
|
||||
this.scheduleFilterOptions()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,712 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="事项名称">
|
||||
<el-input
|
||||
v-model="pageForm.matterName"
|
||||
clearable
|
||||
placeholder="请输入事项名称"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="组织形式">
|
||||
<el-select v-model="pageForm.organizationType" clearable filterable placeholder="请选择组织形式" style="width: 100%">
|
||||
<el-option v-for="item in organizationTypeOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="事项列表">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="el-icon-plus"></i>
|
||||
新增
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
:row-class-name="tableRowClassName"
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="年度" prop="year" width="110" sortable="custom" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="事项名称" prop="matterName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所属工会" prop="unionName" min-width="180" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路名称" prop="lineName" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="组织形式" prop="organizationTypeName" width="150" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="联系人" prop="contactName" width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="联系方式" prop="contactPhone" width="150" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="事项状态" prop="enabled" width="160" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-switch
|
||||
v-model="row.enabled"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
@change="toggleEnabled(row)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="260" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openBatchForm(row)">选择线路</el-button>
|
||||
<el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" :loading="row.deleteLoading" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="54%"
|
||||
@closed="destroyForm">
|
||||
<el-form :model="formData" :rules="formRules" label-width="110px" ref="form" :disabled="viewMode">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年度" prop="year">
|
||||
<el-date-picker
|
||||
v-model="formData.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%"
|
||||
@change="yearChange">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="事项状态" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio :label="true">启用</el-radio>
|
||||
<el-radio :label="false">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="事项名称" prop="matterName">
|
||||
<el-input v-model="formData.matterName" maxlength="30" show-word-limit placeholder="请输入事项名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建人">
|
||||
<el-input v-model="formData.creatorName" readonly placeholder="当前登录人"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="疗休养配置" prop="settingId">
|
||||
<el-select v-model="formData.settingId" clearable filterable placeholder="请选择配置" style="width: 100%">
|
||||
<el-option v-for="item in settingOptions" :key="item.id" :label="item.configName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="组织形式" prop="organizationType">
|
||||
<el-select v-model="formData.organizationType" clearable filterable placeholder="请选择组织形式" style="width: 100%" @change="organizationTypeChange">
|
||||
<el-option v-for="item in organizationTypeOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属工会" prop="unionId">
|
||||
<el-select v-model="formData.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%" :disabled="isUnionDisabled()">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button v-if="!viewMode" type="primary" :loading="subDis" @click="doSubmit">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
:title="batchTitle"
|
||||
:visible.sync="batchFormVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="74%">
|
||||
<el-form :model="batchForm" :rules="batchRules" label-width="110px" ref="batchFormRef">
|
||||
<el-form-item label="线路" prop="lineId">
|
||||
<el-select v-model="batchForm.lineId" clearable filterable placeholder="请选择线路" style="width: 100%" @change="lineChange">
|
||||
<el-option v-for="item in lineOptions" :key="item.id" :label="lineOptionLabel(item)" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="报名开始时间" prop="signupStartTime">
|
||||
<el-date-picker v-model="batchForm.signupStartTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择时间" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="报名结束时间" prop="signupEndTime">
|
||||
<el-date-picker v-model="batchForm.signupEndTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择时间" style="width: 100%"></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出行开始时间" prop="travelStartTime">
|
||||
<el-date-picker v-model="batchForm.travelStartTime" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 100%" @change="travelStartChange"></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出行结束时间" prop="travelEndTime">
|
||||
<el-date-picker v-model="batchForm.travelEndTime" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 100%" @change="travelEndChange"></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系人" prop="contactName">
|
||||
<el-input v-model="batchForm.contactName" maxlength="10" placeholder="请输入联系人"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="联系方式" prop="contactPhone">
|
||||
<el-input v-model="batchForm.contactPhone" maxlength="11" placeholder="请输入联系方式"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="预计费用" prop="estimatedCost">
|
||||
<el-input v-model="batchForm.estimatedCost" maxlength="10" placeholder="请输入预计费用"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="最少成团人数" prop="minGroupPeople">
|
||||
<el-input-number v-model="batchForm.minGroupPeople" :min="1" :precision="0" controls-position="right" style="width: 100%" @change="peopleChange"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="最多成团人数" prop="maxGroupPeople">
|
||||
<el-input-number v-model="batchForm.maxGroupPeople" :min="1" :precision="0" controls-position="right" style="width: 100%" @change="peopleChange"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="batchFormVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="batchSubDis" @click="submitBatch">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.el-table .direct-family-row > td,
|
||||
.el-table .direct-family-row > td .cell {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
const validateUnion = (rule, value, callback) => {
|
||||
if (this.formData.organizationType === "schoolUnion" || value) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error("必填"))
|
||||
}
|
||||
}
|
||||
const validateMobile = (rule, value, callback) => {
|
||||
const mobileReg = /^1[3-9]\d{9}$/
|
||||
if (!value) {
|
||||
callback(new Error("必填"))
|
||||
} else if (!mobileReg.test(value)) {
|
||||
callback(new Error("手机号格式不正确"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateMoney = (rule, value, callback) => {
|
||||
const moneyReg = /^(0|[1-9]\d*)(\.\d{1,2})?$/
|
||||
if (value === "" || value === null || value === undefined) {
|
||||
callback(new Error("必填"))
|
||||
} else if (!moneyReg.test(String(value))) {
|
||||
callback(new Error("请输入非负金额,最多两位小数"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validatePeople = (rule, value, callback) => {
|
||||
if (!value || value <= 0) {
|
||||
callback(new Error("人数必须大于0"))
|
||||
} else if (this.batchForm.minGroupPeople && this.batchForm.maxGroupPeople
|
||||
&& this.batchForm.minGroupPeople > this.batchForm.maxGroupPeople) {
|
||||
callback(new Error("最少人数不能大于最多人数"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateBatchTime = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error("必填"))
|
||||
return
|
||||
}
|
||||
const form = this.batchForm
|
||||
if (form.signupStartTime && form.signupEndTime && form.signupStartTime >= form.signupEndTime) {
|
||||
callback(new Error("报名开始时间必须小于报名结束时间"))
|
||||
return
|
||||
}
|
||||
if (form.travelStartTime && form.travelEndTime && form.travelStartTime > form.travelEndTime) {
|
||||
callback(new Error("出行开始时间不能晚于出行结束时间"))
|
||||
return
|
||||
}
|
||||
if (form.signupEndTime && form.travelStartTime && form.signupEndTime >= form.travelStartTime) {
|
||||
callback(new Error("报名结束时间必须小于出行开始时间"))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
return {
|
||||
title: "",
|
||||
dialogVisible: false,
|
||||
viewMode: false,
|
||||
subDis: false,
|
||||
batchFormVisible: false,
|
||||
batchSubDis: false,
|
||||
batchTitle: "",
|
||||
currentMatter: {},
|
||||
batchForm: {},
|
||||
lineOptions: [],
|
||||
settingOptions: [],
|
||||
unionOptions: [],
|
||||
organizationTypeOptions: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
year: currentYear,
|
||||
matterName: "",
|
||||
unionId: "",
|
||||
organizationType: ""
|
||||
},
|
||||
formData: {},
|
||||
formRules: {
|
||||
year: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
matterName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
settingId: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
unionId: [{ validator: validateUnion, trigger: ["blur", "change"] }],
|
||||
organizationType: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
},
|
||||
batchRules: {
|
||||
lineId: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
signupStartTime: [{ validator: validateBatchTime, trigger: ["blur", "change"] }],
|
||||
signupEndTime: [{ validator: validateBatchTime, trigger: ["blur", "change"] }],
|
||||
travelStartTime: [{ validator: validateBatchTime, trigger: ["blur", "change"] }],
|
||||
travelEndTime: [{ validator: validateBatchTime, trigger: ["blur", "change"] }],
|
||||
contactName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
contactPhone: [{ validator: validateMobile, trigger: ["blur", "change"] }],
|
||||
minGroupPeople: [{ validator: validatePeople, trigger: ["blur", "change"] }],
|
||||
maxGroupPeople: [{ validator: validatePeople, trigger: ["blur", "change"] }],
|
||||
estimatedCost: [{ validator: validateMoney, trigger: ["blur", "change"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"pageForm.year"(year) {
|
||||
this.loadSettings(year)
|
||||
},
|
||||
"batchForm.lineId"() {
|
||||
if (this.batchFormVisible) {
|
||||
this.applySelectedLineDefaults(true)
|
||||
}
|
||||
},
|
||||
"batchForm.travelStartTime"() {
|
||||
if (this.batchFormVisible) {
|
||||
this.fillTravelEndByLot()
|
||||
}
|
||||
},
|
||||
"batchForm.travelEndTime"() {
|
||||
if (this.batchFormVisible) {
|
||||
this.validateTravelFields()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.year = moment().format("YYYY")
|
||||
this.pageForm.matterName = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.organizationType = ""
|
||||
this.loadSettings(this.pageForm.year)
|
||||
this.doSearch()
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return this.isDirectFamilyLine(row) ? "direct-family-row" : ""
|
||||
},
|
||||
isDirectFamilyLine(row) {
|
||||
if (!row) return false
|
||||
return row.directFamilyUnitLine === true
|
||||
|| row.directFamilyUnitLine === 1
|
||||
|| row.directFamilyUnitLine === "1"
|
||||
},
|
||||
emptyForm() {
|
||||
const user = this.currentUser()
|
||||
return {
|
||||
year: moment().format("YYYY"),
|
||||
matterName: "",
|
||||
settingId: "",
|
||||
creatorUserId: user.id || "",
|
||||
creatorName: user.username || "",
|
||||
unionId: "",
|
||||
organizationType: "",
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.title = "新增事项信息"
|
||||
this.viewMode = false
|
||||
this.formData = this.emptyForm()
|
||||
this.loadSettings(this.formData.year, true)
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
},
|
||||
openEdit(row) {
|
||||
this.title = "编辑事项信息"
|
||||
this.viewMode = false
|
||||
this.loadDetail(row.id)
|
||||
},
|
||||
openView(row) {
|
||||
this.title = "查看事项信息"
|
||||
this.viewMode = true
|
||||
this.loadDetail(row.id)
|
||||
},
|
||||
loadDetail(id) {
|
||||
this.$axios.post(loc() + "/detail", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = Object.assign(this.emptyForm(), res.data || {})
|
||||
this.formData.year = this.formData.year ? String(this.formData.year) : ""
|
||||
this.ensureSelectedUnionOption()
|
||||
this.loadSettings(this.formData.year)
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
currentUser() {
|
||||
return (this.$store && this.$store.state && this.$store.state.user) || {}
|
||||
},
|
||||
currentUnion() {
|
||||
return this.currentUser().union || {}
|
||||
},
|
||||
organizationTypeChange() {
|
||||
this.applyOrganizationTypeRule()
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.validateField("unionId"))
|
||||
},
|
||||
applyOrganizationTypeRule() {
|
||||
if (this.formData.organizationType === "schoolUnion") {
|
||||
this.formData.unionId = ""
|
||||
return
|
||||
}
|
||||
if (this.formData.organizationType === "branchUnion" || this.formData.organizationType === "personal") {
|
||||
const union = this.currentUnion()
|
||||
this.formData.unionId = union.id || ""
|
||||
this.ensureCurrentUnionOption(union)
|
||||
}
|
||||
},
|
||||
ensureCurrentUnionOption(union) {
|
||||
if (!union || !union.id) return
|
||||
const exists = this.unionOptions.some(item => item.id === union.id)
|
||||
if (!exists) {
|
||||
this.unionOptions.push({ id: union.id, name: union.name || "" })
|
||||
}
|
||||
},
|
||||
ensureSelectedUnionOption() {
|
||||
if (!this.formData.unionId) return
|
||||
const exists = this.unionOptions.some(item => item.id === this.formData.unionId)
|
||||
if (!exists) {
|
||||
this.unionOptions.push({ id: this.formData.unionId, name: this.formData.unionName || "" })
|
||||
}
|
||||
},
|
||||
isUnionDisabled() {
|
||||
return ["schoolUnion", "branchUnion", "personal"].includes(this.formData.organizationType)
|
||||
},
|
||||
yearChange(year) {
|
||||
this.formData.settingId = ""
|
||||
this.loadSettings(year, true)
|
||||
},
|
||||
loadSettings(year, autoSelectLatest) {
|
||||
this.$axios.post(loc() + "/settingOptions", { year }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.settingOptions = res.data || []
|
||||
if (autoSelectLatest && !this.formData.settingId && this.settingOptions.length > 0) {
|
||||
this.formData.settingId = this.settingOptions[0].id
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadUnions() {
|
||||
this.$axios.post(loc() + "/unionOptions").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.unionOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
loadOrganizationTypes() {
|
||||
this.$axios.post(loc() + "/organizationTypeOptions").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.organizationTypeOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
emptyBatchForm() {
|
||||
return {
|
||||
id: this.currentMatter.id || "",
|
||||
lineId: "",
|
||||
signupStartTime: "",
|
||||
signupEndTime: "",
|
||||
travelStartTime: "",
|
||||
travelEndTime: "",
|
||||
contactName: "",
|
||||
contactPhone: "",
|
||||
minGroupPeople: null,
|
||||
maxGroupPeople: null,
|
||||
estimatedCost: ""
|
||||
}
|
||||
},
|
||||
openBatchForm(row) {
|
||||
this.currentMatter = row
|
||||
this.loadLines(row.year)
|
||||
this.batchTitle = "选择线路"
|
||||
this.batchForm = this.emptyBatchForm()
|
||||
this.$axios.post(loc() + "/lineConfig", { matterId: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.batchForm = Object.assign(this.emptyBatchForm(), res.data || {})
|
||||
this.batchForm.travelStartTime = this.dateOnly(this.batchForm.travelStartTime)
|
||||
this.batchForm.travelEndTime = this.dateOnly(this.batchForm.travelEndTime)
|
||||
this.applySelectedLineDefaults(false)
|
||||
if (!this.batchForm.minGroupPeople || !this.batchForm.maxGroupPeople) {
|
||||
this.loadSettingPeople((data) => {
|
||||
this.batchForm.minGroupPeople = data.minGroupPeople || null
|
||||
this.batchForm.maxGroupPeople = data.maxGroupPeople || null
|
||||
})
|
||||
}
|
||||
this.batchFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.applySelectedLineDefaults(true)
|
||||
this.$refs.batchFormRef && this.$refs.batchFormRef.clearValidate()
|
||||
})
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLines(year) {
|
||||
this.$axios.post(loc() + "/lineOptions", { year }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineOptions = res.data || []
|
||||
this.applySelectedLineDefaults(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
lineOptionLabel(item) {
|
||||
if (!item) return ""
|
||||
const lineName = this.lineField(item, "lineName") || ""
|
||||
const lineType = this.lineField(item, "lineType") || ""
|
||||
return lineType ? lineName + "(" + lineType + ")" : lineName
|
||||
},
|
||||
lineChange() {
|
||||
this.applySelectedLineDefaults(true)
|
||||
},
|
||||
travelStartChange() {
|
||||
this.fillTravelEndByLot()
|
||||
},
|
||||
travelEndChange() {
|
||||
this.validateTravelFields()
|
||||
},
|
||||
selectedLine() {
|
||||
return (this.lineOptions || []).find((item) => this.lineField(item, "id") === this.batchForm.lineId) || null
|
||||
},
|
||||
applySelectedLineDefaults(recalculateEnd) {
|
||||
const line = this.selectedLine()
|
||||
if (!line) return
|
||||
const activityCost = this.lineField(line, "activityCost")
|
||||
if (activityCost !== null && activityCost !== undefined && activityCost !== "") {
|
||||
this.batchForm.estimatedCost = activityCost
|
||||
}
|
||||
if (recalculateEnd) {
|
||||
this.fillTravelEndByLot()
|
||||
}
|
||||
},
|
||||
fillTravelEndByLot() {
|
||||
const line = this.selectedLine()
|
||||
const days = this.lineLotDays(line)
|
||||
if (!this.batchForm.travelStartTime || !days) return
|
||||
this.$set(this.batchForm, "travelEndTime", moment(this.batchForm.travelStartTime).add(days - 1, "days").format("YYYY-MM-DD"))
|
||||
this.validateTravelFields()
|
||||
},
|
||||
lineLotDays(line) {
|
||||
if (!line) return null
|
||||
const lotDays = this.lineField(line, "lotDays") || this.lineField(line, "lotValue")
|
||||
if (!/^\d+$/.test(String(lotDays || ""))) return null
|
||||
const days = parseInt(lotDays, 10)
|
||||
return days > 0 ? days : null
|
||||
},
|
||||
lineField(line, field) {
|
||||
if (!line) return null
|
||||
if (line[field] !== undefined) return line[field]
|
||||
const lowerField = field.toLowerCase()
|
||||
const matchedKey = Object.keys(line).find((key) => key.toLowerCase() === lowerField)
|
||||
return matchedKey ? line[matchedKey] : null
|
||||
},
|
||||
validateTravelFields() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.batchFormRef) {
|
||||
this.$refs.batchFormRef.validateField(["travelStartTime", "travelEndTime"])
|
||||
}
|
||||
})
|
||||
},
|
||||
dateOnly(value) {
|
||||
if (!value) return ""
|
||||
return String(value).substring(0, 10)
|
||||
},
|
||||
loadSettingPeople(callback) {
|
||||
this.$axios.post(loc() + "/settingPeople", { matterId: this.currentMatter.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
callback && callback(data)
|
||||
}
|
||||
})
|
||||
},
|
||||
peopleChange() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.batchFormRef) {
|
||||
this.$refs.batchFormRef.validateField(["minGroupPeople", "maxGroupPeople"])
|
||||
}
|
||||
})
|
||||
},
|
||||
submitBatch() {
|
||||
this.$refs.batchFormRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.batchSubDis = true
|
||||
this.$axios.post(loc() + "/lineConfigDoSubmit", this.batchForm).then((res) => {
|
||||
this.batchSubDis = false
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.batchFormVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "保存失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.batchSubDis = false
|
||||
})
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.subDis = true
|
||||
this.$axios.post(loc() + "/doSubmit", this.formData).then((res) => {
|
||||
this.subDis = false
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.dialogVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "保存失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.subDis = false
|
||||
})
|
||||
})
|
||||
},
|
||||
toggleEnabled(row) {
|
||||
this.$axios.post(loc() + "/doSubmit", row).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("状态已更新")
|
||||
} else {
|
||||
row.enabled = !row.enabled
|
||||
this.$message.warning(res.msg || "状态更新失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
row.enabled = !row.enabled
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$set(row, "deleteLoading", true)
|
||||
this.$axios.post(loc() + "/deleteInfo", { id: row.id }).then((res) => {
|
||||
this.$set(row, "deleteLoading", false)
|
||||
if (res.code !== 0) {
|
||||
this.$message.warning(res.msg || "删除检查失败")
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
const signupCount = Number(data.signupCount || 0)
|
||||
const matterName = row.matterName || ""
|
||||
if (!data.canDelete) {
|
||||
this.$alert("事项【" + matterName + "】已有 " + signupCount + " 人报名,不能删除。", "删除提醒", {
|
||||
confirmButtonText: "知道了",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$confirm("事项【" + matterName + "】暂无人员报名,确认删除吗?", "删除提醒", {
|
||||
confirmButtonText: "确认删除",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
confirmButtonClass: "el-button--danger"
|
||||
}).then(() => {
|
||||
this.$set(row, "deleteLoading", true)
|
||||
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((deleteRes) => {
|
||||
this.$set(row, "deleteLoading", false)
|
||||
if (deleteRes.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(deleteRes.msg || "删除失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$set(row, "deleteLoading", false)
|
||||
})
|
||||
}).catch(() => {})
|
||||
}).catch(() => {
|
||||
this.$set(row, "deleteLoading", false)
|
||||
})
|
||||
},
|
||||
destroyForm() {
|
||||
this.formData = {}
|
||||
this.viewMode = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadSettings(this.pageForm.year)
|
||||
this.loadUnions()
|
||||
this.loadOrganizationTypes()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<el-empty description="${title!'功能建设中'}">
|
||||
<template slot="description">
|
||||
<span>${title!'功能建设中'}正在分阶段建设中</span>
|
||||
</template>
|
||||
</el-empty>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app"
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,432 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="创建年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择创建年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="线路名称">
|
||||
<el-input
|
||||
v-model="pageForm.lineName"
|
||||
clearable
|
||||
placeholder="请输入线路名称"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="时间标段">
|
||||
<el-select v-model="pageForm.lotId" clearable filterable placeholder="请选择时间标段" style="width: 100%">
|
||||
<el-option v-for="item in lotOptions" :key="item.id" :label="item.lotName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="线路列表">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="el-icon-plus"></i>
|
||||
新增
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
:default-sort="{prop: 'enabled', order: 'descending'}"
|
||||
:row-class-name="tableRowClassName"
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="创建年度" prop="year" width="120" sortable="custom" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="线路编号" prop="lineCode" width="140" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路名称" prop="lineName" min-width="220" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="时间标段" prop="lotName" width="140" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路类型" prop="lineType" width="140" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="创建人" prop="creatorName" width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="是否对外开放" prop="openFlag" width="160" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-switch
|
||||
v-model="row.openFlag"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="toggleOpenFlag(row)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="激活状态" prop="enabled" width="160" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-switch
|
||||
v-model="row.enabled"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
@change="toggleEnabled(row)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="260" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="72%"
|
||||
@closed="destroyEditor">
|
||||
<el-form :model="formData" :rules="formRules" label-width="110px" ref="form" :disabled="viewMode">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="创建年度" prop="year">
|
||||
<el-date-picker
|
||||
v-model="formData.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择创建年度"
|
||||
style="width: 100%"
|
||||
@change="yearChange">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="旅行社名称" prop="travelAgencyId">
|
||||
<el-select v-model="formData.travelAgencyId" clearable filterable placeholder="请选择旅行社" style="width: 100%">
|
||||
<el-option v-for="item in travelAgencyOptions" :key="item.id" :label="item.agencyName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="线路名称" prop="lineName">
|
||||
<el-input v-model="formData.lineName" maxlength="30" show-word-limit placeholder="请输入线路名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="创建人">
|
||||
<el-input v-model="formData.creatorName" readonly placeholder="当前登录人"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所在单位">
|
||||
<el-input v-model="formData.unitName" readonly placeholder="当前登录人所在单位"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="线路编号" prop="lineCode">
|
||||
<el-input v-model="formData.lineCode" maxlength="50" placeholder="建议年度加序号,如202601"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="激活状态" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio :label="true">启用</el-radio>
|
||||
<el-radio :label="false">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否对外开放" prop="openFlag">
|
||||
<el-radio-group v-model="formData.openFlag">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="直系亲属线路" prop="directFamilyUnitLine">
|
||||
<el-radio-group v-model="formData.directFamilyUnitLine">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="线路类型" prop="lineType">
|
||||
<el-select v-model="formData.lineType" clearable placeholder="请选择线路类型" style="width: 100%">
|
||||
<el-option v-for="item in lineTypeOptions" :key="item.code" :label="item.name" :value="item.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="时间标段" prop="lotId">
|
||||
<el-select v-model="formData.lotId" clearable filterable placeholder="请选择时间标段" style="width: 100%">
|
||||
<el-option v-for="item in lotOptions" :key="item.id" :label="item.lotName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="线路内容" prop="lineContent">
|
||||
<text-editor v-model="formData.lineContent"></text-editor>
|
||||
</el-form-item>
|
||||
<el-form-item label="移动端缩略图" prop="mobileThumb">
|
||||
<!-- 与旅行社管理保持一致:移动端缩略图使用单图上传并保存URL。 -->
|
||||
<file-upload
|
||||
style="--upload-width: 200px;--upload-height:108px"
|
||||
:upload_number="1"
|
||||
:upload_size="20971520"
|
||||
:value.sync="formData.mobileThumb"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
<div class="el-upload__tip">支持jpg、jpeg、png格式,大小不超过20MB。</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button v-if="!viewMode" type="primary" :loading="submitLoading" @click="doSubmit">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.el-table .direct-family-row > td,
|
||||
.el-table .direct-family-row > td .cell {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
return {
|
||||
title: "",
|
||||
dialogVisible: false,
|
||||
viewMode: false,
|
||||
submitLoading: false,
|
||||
travelAgencyOptions: [],
|
||||
lotOptions: [],
|
||||
lineTypeOptions: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "enabled",
|
||||
pageOrderBy: "descending",
|
||||
year: currentYear,
|
||||
lineName: "",
|
||||
lotId: ""
|
||||
},
|
||||
formData: {},
|
||||
formRules: {
|
||||
year: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
travelAgencyId: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
lineName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
lineCode: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
lineType: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
lotId: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
mobileThumb: [{ required: true, message: "请上传移动端缩略图", trigger: ["blur", "change"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.year = moment().format("YYYY")
|
||||
this.pageForm.lineName = ""
|
||||
this.pageForm.lotId = ""
|
||||
this.loadOptions(this.pageForm.year)
|
||||
this.doSearch()
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return this.isDirectFamilyLine(row) ? "direct-family-row" : ""
|
||||
},
|
||||
isDirectFamilyLine(row) {
|
||||
if (!row) return false
|
||||
return row.directFamilyUnitLine === true
|
||||
|| row.directFamilyUnitLine === 1
|
||||
|| row.directFamilyUnitLine === "1"
|
||||
},
|
||||
emptyForm() {
|
||||
const user = (this.$store && this.$store.state && this.$store.state.user) || {}
|
||||
return {
|
||||
year: moment().format("YYYY"),
|
||||
travelAgencyId: "",
|
||||
creatorUserId: user.id || "",
|
||||
creatorName: user.username || "",
|
||||
unitId: user.unit?.id || "",
|
||||
unitName: user.unit?.name || "",
|
||||
lineName: "",
|
||||
lineCode: "",
|
||||
enabled: true,
|
||||
openFlag: true,
|
||||
directFamilyUnitLine: false,
|
||||
lineType: "",
|
||||
lotId: "",
|
||||
lineContent: "",
|
||||
mobileThumb: ""
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.title = "新增线路信息"
|
||||
this.viewMode = false
|
||||
this.formData = this.emptyForm()
|
||||
this.loadOptions(this.formData.year)
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
},
|
||||
openEdit(row) {
|
||||
this.title = "编辑线路信息"
|
||||
this.viewMode = false
|
||||
this.loadDetail(row.id)
|
||||
},
|
||||
openView(row) {
|
||||
this.title = "查看线路信息"
|
||||
this.viewMode = true
|
||||
this.loadDetail(row.id)
|
||||
},
|
||||
loadDetail(id) {
|
||||
this.$axios.post(loc() + "/detail", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = Object.assign(this.emptyForm(), res.data || {})
|
||||
this.formData.year = this.formData.year ? String(this.formData.year) : ""
|
||||
this.loadOptions(this.formData.year)
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
yearChange(year) {
|
||||
this.formData.travelAgencyId = ""
|
||||
this.formData.lotId = ""
|
||||
this.loadOptions(year)
|
||||
},
|
||||
loadOptions(year) {
|
||||
this.loadTravelAgencies(year)
|
||||
this.loadLots(year)
|
||||
this.loadLineTypes()
|
||||
},
|
||||
loadTravelAgencies(year) {
|
||||
this.$axios.post(loc() + "/travelAgencyOptions", { year }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.travelAgencyOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLots(year) {
|
||||
this.$axios.post(loc() + "/lotOptions", { year }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lotOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineTypes() {
|
||||
this.$axios.post(loc() + "/lineTypeOptions").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineTypeOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.submitLoading = true
|
||||
this.$axios.post(loc() + "/doSubmit", this.formData).then((res) => {
|
||||
this.submitLoading = false
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.dialogVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "保存失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
toggleEnabled(row) {
|
||||
this.$axios.post(loc() + "/doSubmit", row).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("状态已更新")
|
||||
} else {
|
||||
row.enabled = !row.enabled
|
||||
this.$message.warning(res.msg || "状态更新失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
row.enabled = !row.enabled
|
||||
})
|
||||
},
|
||||
toggleOpenFlag(row) {
|
||||
this.$axios.post(loc() + "/doSubmit", row).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("对外开放状态已更新")
|
||||
} else {
|
||||
row.openFlag = !row.openFlag
|
||||
this.$message.warning(res.msg || "对外开放状态更新失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
row.openFlag = !row.openFlag
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定删除【" + row.lineName + "】吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "删除失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
this.formData = {}
|
||||
this.viewMode = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadOptions(this.pageForm.year)
|
||||
this.pageData()
|
||||
},
|
||||
watch: {
|
||||
"pageForm.year"(year) {
|
||||
this.pageForm.lotId = ""
|
||||
this.loadOptions(year)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="开始年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.startYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择开始年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="结束年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.endYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择结束年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="pageForm.keyword"
|
||||
clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路">
|
||||
<el-select v-model="pageForm.lineId" clearable filterable placeholder="请选择线路" style="width: 100%">
|
||||
<el-option v-for="item in lineOptions" :key="item.lineId" :label="item.lineName" :value="item.lineId"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="出行时段">
|
||||
<el-select v-model="pageForm.travelPeriod" clearable filterable placeholder="请选择出行时段" style="width: 100%">
|
||||
<el-option v-for="item in travelPeriodOptions" :key="item.travelPeriod" :label="item.travelPeriod" :value="item.travelPeriod"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路类型">
|
||||
<el-select v-model="pageForm.lineType" clearable filterable placeholder="请选择线路类型" style="width: 100%">
|
||||
<el-option v-for="item in lineTypeOptions" :key="item.lineType" :label="item.lineType" :value="item.lineType"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="校工会审核">
|
||||
<el-radio-group class="mr5" size="small" v-model="pageForm.audit" @change="changeAudit">
|
||||
<el-radio-button :label="true">已审核</el-radio-button>
|
||||
<el-radio-button :label="false">未审核</el-radio-button>
|
||||
</el-radio-group>
|
||||
</table-tool>
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="工号" prop="jobNo" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="姓名" prop="userName" width="120" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名线路" prop="lineName" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="出行时段" prop="travelPeriod" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路类型" prop="lineType" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名时间" prop="signupTime" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="携带家属" prop="familyCount" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="Number(row.familyCount || 0) > 0 ? 'success' : 'info'">{{ row.familyCount || 0 }}人</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报销超出费用" prop="overCostReimbursed" width="140" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.overCostReimbursed ? 'success' : 'info'">{{ row.overCostReimbursed ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前节点" prop="curTaskName" width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="流程状态" prop="instanceState" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button v-if="row.taskState === 10" size="mini" type="primary" @click="openApproval(row)">审核</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<template #edit>
|
||||
<tour-approval-info ref="infoRef">
|
||||
<div v-if="showApprovalForm">
|
||||
<div class="process-title">
|
||||
{{ formData.taskName }}
|
||||
</div>
|
||||
<el-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
label-width="0"
|
||||
label-suffix=":"
|
||||
class="flow-task-form">
|
||||
<el-form-item
|
||||
label="审批意见"
|
||||
prop="tf_opinion"
|
||||
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
|
||||
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
|
||||
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到发起人</el-button>
|
||||
<el-button @click="handleTaskAction(2)" size="small" type="danger">拒绝申请</el-button>
|
||||
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意申请</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</tour-approval-info>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#app .search .search-item {
|
||||
width: calc((100% - 150px) / 4);
|
||||
}
|
||||
#app .search .search-query {
|
||||
margin-left: auto;
|
||||
}
|
||||
@media screen and (max-width: 1350px) {
|
||||
#app .search .search-item {
|
||||
width: calc((100% - 100px) / 3);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1200px) {
|
||||
#app .search .search-item {
|
||||
width: calc((100% - 50px) / 2);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 992px) {
|
||||
#app .search .search-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.tour-approval-section {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.tour-approval-table {
|
||||
width: 100%;
|
||||
}
|
||||
.tour-approval-empty {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const TOUR_APPROVAL_INFO = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<div class="process-title">
|
||||
报名信息
|
||||
<el-link type="primary" @click="openChart">点击查看流程图</el-link>
|
||||
</div>
|
||||
<el-descriptions :column="3" border class="tour-approval-section">
|
||||
<el-descriptions-item label="工号">{{ detail.jobNo || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ detail.userName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ detail.gender || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="身份证号">{{ detail.idCard || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ detail.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在工会">{{ detail.unionName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名时间">{{ detail.signupTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名线路">{{ detail.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行时段">{{ detail.travelPeriod || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路类型">{{ detail.lineType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名酒店">{{ detail.hotelName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="旅行社">{{ detail.travelAgencyName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否携带家属">
|
||||
{{ familyText() }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="报销超出费用">{{ detail.overCostReimbursed ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="意向拼床人">{{ detail.intendedRoommate || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床型">{{ detail.bedType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床位信息">{{ detail.bedInfo || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div v-if="isDirectFamilyLine()" class="tour-approval-section">
|
||||
<div class="process-title">直系亲属线路</div>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="亲属姓名">{{ directRelative.relativeName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ directRelative.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="亲属关系">{{ directRelative.relationshipName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路名称">{{ directRelative.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行开始日期">{{ directRelative.travelStartTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行结束日期">{{ directRelative.travelEndTime || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="showFamilySection()" class="tour-approval-section">
|
||||
<div class="process-title">亲属信息</div>
|
||||
<el-table :data="familyData" border size="mini" empty-text="暂无亲属信息" class="tour-approval-table">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="家属姓名" prop="familyName" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="性别" prop="gender" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="身份证号码" prop="idCard" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="年龄" prop="age" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="关系" prop="relationship" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床型" prop="bedType" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床位" prop="bedInfo" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="意向拼床人" prop="intendedRoommate" min-width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template v-for="task in doneTasks">
|
||||
<div class="mt10">
|
||||
<div class="process-title">{{ task.displayName }}</div>
|
||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-if="task.ext.isFirstTaskNode">
|
||||
<el-descriptions-item label="申请用户">
|
||||
{{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
|
||||
<el-descriptions-item label="办理用户">
|
||||
{{ task.taskFormData.userName }}({{ task.taskFormData.loginName }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理意见" :span="3">
|
||||
{{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<snaker-chart ref="snakerChartRef"></snaker-chart>
|
||||
</div>
|
||||
`,
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
return {
|
||||
row: {},
|
||||
detail: {},
|
||||
familyData: [],
|
||||
directRelative: {},
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
directFamilyUnitLine: false,
|
||||
doneTasks: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.row = row || {}
|
||||
this.detail = {}
|
||||
this.familyData = []
|
||||
this.directRelative = {}
|
||||
this.allowFamily = false
|
||||
this.fillBedInfo = true
|
||||
this.directFamilyUnitLine = false
|
||||
this.doneTasks = []
|
||||
this.info()
|
||||
this.getDoneTasks()
|
||||
},
|
||||
info() {
|
||||
this.$axios.post(loc() + "/detail", { id: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.detail = data.ledger || {}
|
||||
this.detail.travelPeriod = data.travelPeriod || this.row.travelPeriod || ""
|
||||
this.familyData = data.families || []
|
||||
this.directRelative = data.directRelative || {}
|
||||
this.allowFamily = this.toBoolean(data.allowFamily)
|
||||
this.fillBedInfo = data.fillBedInfo === undefined || data.fillBedInfo === null || this.toBoolean(data.fillBedInfo)
|
||||
this.directFamilyUnitLine = this.toBoolean(data.directFamilyUnitLine)
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1"
|
||||
},
|
||||
isDirectFamilyLine() {
|
||||
return this.directFamilyUnitLine
|
||||
|| this.toBoolean(this.detail.directFamilyUnitLine)
|
||||
|| (this.directRelative && this.directRelative.id)
|
||||
},
|
||||
hasFamily() {
|
||||
return this.toBoolean(this.detail.hasFamily) || this.familyData.length > 0
|
||||
},
|
||||
showFamilySection() {
|
||||
return this.allowFamily && this.hasFamily() && !this.isDirectFamilyLine()
|
||||
},
|
||||
familyText() {
|
||||
if (this.isDirectFamilyLine()) {
|
||||
return "否"
|
||||
}
|
||||
if (!this.allowFamily) {
|
||||
return "否"
|
||||
}
|
||||
return this.hasFamily() ? "是" : "否"
|
||||
},
|
||||
getDoneTasks() {
|
||||
this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
openChart() {
|
||||
if (!this.row.instanceProcessDefineId || !this.row.instanceId) {
|
||||
this.$message.warning("暂无流程图信息")
|
||||
return
|
||||
}
|
||||
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
return {
|
||||
unionOptions: [],
|
||||
lineOptions: [],
|
||||
travelPeriodOptions: [],
|
||||
lineTypeOptions: [],
|
||||
showApprovalForm: false,
|
||||
formData: {
|
||||
tf_opinion: ""
|
||||
},
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
audit: false,
|
||||
startYear: currentYear,
|
||||
endYear: currentYear,
|
||||
keyword: "",
|
||||
unionId: "",
|
||||
lineId: "",
|
||||
travelPeriod: "",
|
||||
lineType: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.startYear = moment().format("YYYY")
|
||||
this.pageForm.endYear = moment().format("YYYY")
|
||||
this.pageForm.keyword = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.loadOptions()
|
||||
this.doSearch()
|
||||
},
|
||||
changeAudit() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.clearCascadeFilters()
|
||||
this.loadOptions()
|
||||
this.pageData()
|
||||
},
|
||||
clearCascadeFilters() {
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.showApprovalForm = false
|
||||
this.formData = {
|
||||
tf_opinion: ""
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
openApproval(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.showApprovalForm = true
|
||||
this.formData = {
|
||||
processTaskId: row.taskId,
|
||||
taskName: row.curTaskName || row.taskName,
|
||||
tf_opinion: ""
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
handleTaskAction(val) {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
const loading = createLoading()
|
||||
this.$axios.post("/flow/common/executeTask", {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: val
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$refs.guava.index()
|
||||
this.$message.success(res.msg || "操作成功")
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "操作失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}).catch(() => {})
|
||||
})
|
||||
},
|
||||
loadOptions() {
|
||||
this.loadUnionOptions()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
},
|
||||
baseOptionParams() {
|
||||
return {
|
||||
audit: this.pageForm.audit,
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
keyword: this.pageForm.keyword,
|
||||
unionId: this.pageForm.unionId,
|
||||
lineId: this.pageForm.lineId,
|
||||
travelPeriod: this.pageForm.travelPeriod,
|
||||
lineType: this.pageForm.lineType
|
||||
}
|
||||
},
|
||||
loadUnionOptions() {
|
||||
this.$axios.post(loc() + "/unionOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.unionOptions = res.data || []
|
||||
if (this.pageForm.unionId && !this.unionOptions.some(item => item.id === this.pageForm.unionId)) {
|
||||
this.pageForm.unionId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineOptions() {
|
||||
this.$axios.post(loc() + "/lineOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineOptions = res.data || []
|
||||
if (this.pageForm.lineId && !this.lineOptions.some(item => item.lineId === this.pageForm.lineId)) {
|
||||
this.pageForm.lineId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadTravelPeriodOptions() {
|
||||
this.$axios.post(loc() + "/travelPeriodOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.travelPeriodOptions = res.data || []
|
||||
if (this.pageForm.travelPeriod && !this.travelPeriodOptions.some(item => item.travelPeriod === this.pageForm.travelPeriod)) {
|
||||
this.pageForm.travelPeriod = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineTypeOptions() {
|
||||
this.$axios.post(loc() + "/lineTypeOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineTypeOptions = res.data || []
|
||||
if (this.pageForm.lineType && !this.lineTypeOptions.some(item => item.lineType === this.pageForm.lineType)) {
|
||||
this.pageForm.lineType = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadOptions()
|
||||
this.pageData()
|
||||
},
|
||||
components: {
|
||||
"tour-approval-info": TOUR_APPROVAL_INFO
|
||||
},
|
||||
watch: {
|
||||
"pageForm.startYear"() {
|
||||
this.clearCascadeFilters()
|
||||
this.loadOptions()
|
||||
},
|
||||
"pageForm.endYear"() {
|
||||
this.clearCascadeFilters()
|
||||
this.loadOptions()
|
||||
},
|
||||
"pageForm.lineId"() {
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
},
|
||||
"pageForm.travelPeriod"() {
|
||||
this.loadLineOptions()
|
||||
this.loadLineTypeOptions()
|
||||
},
|
||||
"pageForm.lineType"() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
},
|
||||
"pageForm.unionId"() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,636 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="配置名称">
|
||||
<el-input
|
||||
v-model="pageForm.configName"
|
||||
clearable
|
||||
placeholder="请输入疗休养配置名称"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="疗休养配置">
|
||||
<el-button type="primary" size="medium" @click="openAdd">
|
||||
<i class="el-icon-plus"></i>
|
||||
新建
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
class="vi-table"
|
||||
ref="table"
|
||||
row-key="id"
|
||||
v-loading="tableLoading"
|
||||
@sort-change="pageOrder"
|
||||
>
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="年度" prop="year" width="100" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="疗休养配置名称" prop="configName" min-width="260" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="周期开始年度" prop="cycleStartYear" width="130" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="最少成团人数" prop="minGroupPeople" width="120" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="最多成团人数" prop="maxGroupPeople" width="120" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="是否携带家属" prop="allowFamily" width="120" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" type="success" v-if="row.allowFamily">允许</el-tag>
|
||||
<el-tag size="mini" type="info" v-else>不允许</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否启用" prop="enabled" width="100" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" type="success" v-if="row.enabled">启用</el-tag>
|
||||
<el-tag size="mini" type="info" v-else>停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="64.8%"
|
||||
@closed="destroyEditor">
|
||||
<el-form :model="formData" :rules="formRules" label-width="120px" ref="form">
|
||||
<div v-if="title === '新建疗休养配置'" class="tour-setting-inherit">
|
||||
<el-button type="primary" plain size="medium" icon="el-icon-copy-document" :loading="inheritLoading" @click="inheritPreviousYearInfo">延用上一年信息</el-button>
|
||||
</div>
|
||||
<el-tabs v-model="activeTab" type="card">
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年度" prop="year">
|
||||
<el-date-picker
|
||||
v-model="formData.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="配置名称" prop="configName">
|
||||
<el-input v-model="formData.configName" maxlength="100" placeholder="请输入配置名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="疗休养类型" prop="tourType">
|
||||
<el-select v-model="formData.tourType" clearable allow-create filterable placeholder="请选择或输入类型" style="width: 100%">
|
||||
<el-option label="省内疗休养" value="省内疗休养"></el-option>
|
||||
<el-option label="省外疗休养" value="省外疗休养"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="可参加人员" prop="activityGroupId">
|
||||
<div class="tour-user-scope">
|
||||
<el-select
|
||||
v-model="formData.activityGroupId"
|
||||
placeholder="请选择可参加人员"
|
||||
filterable
|
||||
clearable
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in activityGroupList"
|
||||
:key="item.groupId"
|
||||
:label="item.groupName"
|
||||
:value="item.groupId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="openUserScope">设置</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最少成团人数" prop="minGroupPeople">
|
||||
<el-input-number v-model="formData.minGroupPeople" :controls="false" :min="0" :precision="0" placeholder="请输入最少成团人数" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最多成团人数" prop="maxGroupPeople">
|
||||
<el-input-number v-model="formData.maxGroupPeople" :controls="false" :min="0" :precision="0" placeholder="请输入最多成团人数" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div class="tour-setting-basic-divider"></div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="省外几年去一次" prop="outProvinceYears">
|
||||
<el-input-number v-model="formData.outProvinceYears" :controls="false" :min="0" :precision="0" placeholder="请输入省外间隔年限" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="省外人数占比" prop="outProvinceRatio">
|
||||
<el-input-number v-model="formData.outProvinceRatio" :controls="false" :min="0" :max="100" :precision="2" placeholder="请输入省外人数占比" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="省外占比类型" prop="outProvinceRatioType">
|
||||
<el-select v-model="formData.outProvinceRatioType" placeholder="请选择省外占比类型" style="width: 100%" @change="outProvinceRatioTypeChange">
|
||||
<el-option label="当年报名人数" value="当年报名人数"></el-option>
|
||||
<el-option label="可参加教职工人数" value="可参加教职工人数"></el-option>
|
||||
<el-option label="固定人数" value="固定人数"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="formData.outProvinceRatioType === '固定人数'" :span="12">
|
||||
<el-form-item label="固定人数" prop="outProvinceFixedPeople">
|
||||
<el-input-number v-model="formData.outProvinceFixedPeople" :controls="false" :min="0" :precision="0" placeholder="请输入固定人数" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div class="tour-setting-basic-divider"></div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="周期开始年度" prop="cycleStartYear">
|
||||
<el-date-picker
|
||||
v-model="formData.cycleStartYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
clearable
|
||||
placeholder="请选择周期开始年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="周期结束年度" prop="cycleEndYear">
|
||||
<el-date-picker
|
||||
v-model="formData.cycleEndYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
clearable
|
||||
placeholder="请选择周期结束年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="周期内总费用" prop="cycleTotalCost">
|
||||
<el-input-number v-model="formData.cycleTotalCost" :controls="false" :min="0" :precision="0" placeholder="请输入周期内总费用" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="周期允许次数" prop="cycleAllowedTimes">
|
||||
<el-input-number v-model="formData.cycleAllowedTimes" :controls="false" :min="0" :precision="0" placeholder="请输入周期允许次数" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<div class="tour-setting-basic-divider"></div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="允许携带家属" prop="allowFamily">
|
||||
<el-switch v-model="formData.allowFamily" active-text="允许" inactive-text="不允许"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="填报床位信息" prop="fillBedInfo">
|
||||
<el-switch v-model="formData.fillBedInfo" active-text="是" inactive-text="否"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否启用" prop="enabled">
|
||||
<el-switch v-model="formData.enabled" active-text="启用" inactive-text="停用"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="标段管理" name="lots">
|
||||
<div style="text-align: right; margin-bottom: 10px;">
|
||||
<el-button type="primary" size="medium" icon="el-icon-plus" @click="addLot">增加一条</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="formData.lots"
|
||||
border
|
||||
class="vi-table"
|
||||
empty-text="暂无标段"
|
||||
style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="标段名称" min-width="180" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.lotName" maxlength="50" placeholder="请输入标段名称"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标段值" min-width="160" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.lotValue" maxlength="10" placeholder="请输入整数" @input="integerInput(row, 'lotValue', $event)"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标段费用" min-width="150" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.activityCost" maxlength="10" placeholder="请输入整数" @input="integerInput(row, 'activityCost', $event)"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="允许超出报销" width="140" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-checkbox v-model="row.allowOverReimbursement"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" align="center" header-align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="deleteLot(scope.$index, scope.row)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="服务须知" name="notice">
|
||||
<el-form-item prop="serviceNotice" label-width="0">
|
||||
<text-editor v-model="formData.serviceNotice"></text-editor>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="doSubmit">确认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<drawer-user-scope
|
||||
ref="drawerUserScope"
|
||||
:group_id.sync="formData.activityGroupId"
|
||||
@group_change="handleActivityGroupChange">
|
||||
</drawer-user-scope>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
// 前端先做成团人数区间校验,后端也会再兜底校验一次。
|
||||
const checkGroupPeople = (rule, value, callback) => {
|
||||
if (this.formData.minGroupPeople !== null && this.formData.maxGroupPeople !== null
|
||||
&& this.formData.minGroupPeople > this.formData.maxGroupPeople) {
|
||||
callback(new Error("最少成团人数不能大于最多成团人数"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const checkCycleYear = (rule, value, callback) => {
|
||||
const startYear = parseInt(this.formData.cycleStartYear, 10)
|
||||
const endYear = parseInt(this.formData.cycleEndYear, 10)
|
||||
if (!isNaN(startYear) && !isNaN(endYear) && startYear > endYear) {
|
||||
callback(new Error("周期开始年度不能大于周期结束年度"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const checkOutProvinceFixedPeople = (rule, value, callback) => {
|
||||
if (this.formData.outProvinceRatioType === "固定人数"
|
||||
&& (value === null || value === undefined || value === "" || value < 0)) {
|
||||
callback(new Error("固定人数必须大于等于0"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
title: "",
|
||||
dialogVisible: false,
|
||||
activeTab: "basic",
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
year: "",
|
||||
configName: ""
|
||||
},
|
||||
activityGroupList: [],
|
||||
formData: {},
|
||||
lotDeleteList: [],
|
||||
inheritLoading: false,
|
||||
formRules: {
|
||||
year: [{required: true, message: "必填", trigger: ["blur", "change"]}],
|
||||
configName: [{required: true, message: "必填", trigger: ["blur", "change"]}],
|
||||
minGroupPeople: [{validator: checkGroupPeople, trigger: ["blur", "change"]}],
|
||||
maxGroupPeople: [{validator: checkGroupPeople, trigger: ["blur", "change"]}],
|
||||
outProvinceRatioType: [{required: true, message: "必填", trigger: ["blur", "change"]}],
|
||||
outProvinceFixedPeople: [{validator: checkOutProvinceFixedPeople, trigger: ["blur", "change"]}],
|
||||
cycleStartYear: [{validator: checkCycleYear, trigger: ["blur", "change"]}],
|
||||
cycleEndYear: [{validator: checkCycleYear, trigger: ["blur", "change"]}]
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"drawer-user-scope": httpVueLoader("/components/module/activity/DrawerUserScope.vue")
|
||||
},
|
||||
methods: {
|
||||
getActivityGroup() {
|
||||
return this.$axios.post("/platform/activity/basic/scope/getActivityUserScopeGroup").then((res) => {
|
||||
const data = res.data
|
||||
this.activityGroupList = (data || []).map((item) => {
|
||||
return Object.assign({}, item, {
|
||||
groupId: item.groupId === null || item.groupId === undefined ? "" : String(item.groupId)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
openUserScope() {
|
||||
if (this.$refs.drawerUserScope) {
|
||||
this.$refs.drawerUserScope.groupId = this.formData.activityGroupId || ""
|
||||
this.$refs.drawerUserScope.userScopeDialog = true
|
||||
}
|
||||
},
|
||||
async handleActivityGroupChange() {
|
||||
if (this.formData.activityGroupId !== null && this.formData.activityGroupId !== undefined) {
|
||||
this.formData.activityGroupId = String(this.formData.activityGroupId)
|
||||
}
|
||||
await this.getActivityGroup()
|
||||
},
|
||||
resetSearch() {
|
||||
this.pageForm.year = ""
|
||||
this.pageForm.configName = ""
|
||||
this.doSearch()
|
||||
},
|
||||
defaultFormData() {
|
||||
return {
|
||||
year: moment().format("YYYY"),
|
||||
configName: "",
|
||||
tourType: "",
|
||||
activityGroupId: "",
|
||||
sortNo: 0,
|
||||
minGroupPeople: 0,
|
||||
maxGroupPeople: 0,
|
||||
outProvinceYears: 0,
|
||||
outProvinceRatio: 0,
|
||||
outProvinceRatioType: "当年报名人数",
|
||||
outProvinceFixedPeople: 0,
|
||||
cycleStartYear: "",
|
||||
cycleEndYear: "",
|
||||
cycleTotalCost: null,
|
||||
cycleAllowedTimes: null,
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
enabled: true,
|
||||
lots: [],
|
||||
serviceNotice: ""
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.title = "新建疗休养配置"
|
||||
this.lotDeleteList = []
|
||||
this.activeTab = "basic"
|
||||
// 新建时给出默认年度和开关值,减少校工会管理员录入成本。
|
||||
this.formData = this.defaultFormData()
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
},
|
||||
inheritPreviousYearInfo() {
|
||||
const currentYear = parseInt(this.formData.year, 10)
|
||||
if (isNaN(currentYear)) {
|
||||
this.$message.warning("请先选择年度")
|
||||
return
|
||||
}
|
||||
this.inheritLoading = true
|
||||
this.$axios.post(loc() + "/previousYearInfo", {year: currentYear}).then((res) => {
|
||||
this.inheritLoading = false
|
||||
if (res.code !== 0) {
|
||||
this.$message.warning(res.msg || "未找到上一年度配置")
|
||||
return
|
||||
}
|
||||
const previous = res.data || {}
|
||||
const inheritedLots = (previous.lots || []).map(item => {
|
||||
return {
|
||||
lotName: item.lotName || "",
|
||||
lotValue: item.lotValue || "",
|
||||
activityCost: item.activityCost,
|
||||
allowOverReimbursement: !!item.allowOverReimbursement
|
||||
}
|
||||
})
|
||||
this.formData = Object.assign(this.defaultFormData(), previous, {
|
||||
year: String(currentYear),
|
||||
cycleStartYear: previous.cycleStartYear ? String(previous.cycleStartYear) : "",
|
||||
cycleEndYear: previous.cycleEndYear ? String(previous.cycleEndYear) : "",
|
||||
lots: inheritedLots
|
||||
})
|
||||
delete this.formData.id
|
||||
delete this.formData.createdAt
|
||||
delete this.formData.createdBy
|
||||
delete this.formData.updatedAt
|
||||
delete this.formData.updatedBy
|
||||
delete this.formData.delFlag
|
||||
if (!this.formData.outProvinceRatioType) {
|
||||
this.$set(this.formData, "outProvinceRatioType", "当年报名人数")
|
||||
}
|
||||
if (this.formData.outProvinceFixedPeople === null || this.formData.outProvinceFixedPeople === undefined) {
|
||||
this.$set(this.formData, "outProvinceFixedPeople", 0)
|
||||
}
|
||||
if (this.formData.fillBedInfo === null || this.formData.fillBedInfo === undefined) {
|
||||
this.$set(this.formData, "fillBedInfo", true)
|
||||
}
|
||||
this.lotDeleteList = []
|
||||
this.$message.success("已延用上一年配置信息")
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
}).catch(() => {
|
||||
this.inheritLoading = false
|
||||
})
|
||||
},
|
||||
openEdit(row) {
|
||||
this.title = "编辑疗休养配置"
|
||||
this.lotDeleteList = []
|
||||
this.activeTab = "basic"
|
||||
this.$axios.post(loc() + "/detail", {id: row.id}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = Object.assign({
|
||||
activityGroupId: "",
|
||||
outProvinceRatioType: "当年报名人数",
|
||||
outProvinceFixedPeople: 0,
|
||||
cycleStartYear: "",
|
||||
cycleEndYear: "",
|
||||
cycleTotalCost: null,
|
||||
cycleAllowedTimes: null,
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
enabled: true,
|
||||
lots: [],
|
||||
serviceNotice: ""
|
||||
}, res.data || {})
|
||||
this.formData.year = this.formData.year ? String(this.formData.year) : ""
|
||||
if (!this.formData.outProvinceRatioType) {
|
||||
this.$set(this.formData, "outProvinceRatioType", "当年报名人数")
|
||||
}
|
||||
if (this.formData.outProvinceFixedPeople === null || this.formData.outProvinceFixedPeople === undefined) {
|
||||
this.$set(this.formData, "outProvinceFixedPeople", 0)
|
||||
}
|
||||
if (this.formData.fillBedInfo === null || this.formData.fillBedInfo === undefined) {
|
||||
this.$set(this.formData, "fillBedInfo", true)
|
||||
}
|
||||
this.formData.cycleStartYear = this.formData.cycleStartYear ? String(this.formData.cycleStartYear) : ""
|
||||
this.formData.cycleEndYear = this.formData.cycleEndYear ? String(this.formData.cycleEndYear) : ""
|
||||
this.formData.lots = (this.formData.lots || []).map(item => Object.assign({}, item, {
|
||||
allowOverReimbursement: !!item.allowOverReimbursement
|
||||
}))
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return
|
||||
if (!this.validateLots()) return
|
||||
this.submitLoading = true
|
||||
// Nutz 对子表集合按字符串化 JSON 绑定更稳定,和体检项目维护的提交方式保持一致。
|
||||
const submitData = JSON.parse(JSON.stringify(this.formData))
|
||||
if (submitData.outProvinceRatioType !== "固定人数") {
|
||||
submitData.outProvinceFixedPeople = 0
|
||||
}
|
||||
submitData.lots = JSON.stringify(this.formData.lots || [])
|
||||
submitData.lotDeleteList = JSON.stringify(this.lotDeleteList)
|
||||
this.$axios.post(loc() + "/doSubmit", submitData).then((res) => {
|
||||
this.submitLoading = false
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.dialogVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "保存失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
addLot() {
|
||||
if (!this.formData.lots) {
|
||||
this.$set(this.formData, "lots", [])
|
||||
}
|
||||
this.formData.lots.push({
|
||||
lotName: "",
|
||||
lotValue: "",
|
||||
activityCost: null,
|
||||
allowOverReimbursement: false
|
||||
})
|
||||
},
|
||||
integerInput(row, field, value) {
|
||||
const nextValue = String(value || "").replace(/[^\d]/g, "")
|
||||
this.$set(row, field, nextValue)
|
||||
},
|
||||
outProvinceRatioTypeChange(value) {
|
||||
if (value !== "固定人数") {
|
||||
this.$set(this.formData, "outProvinceFixedPeople", 0)
|
||||
}
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.validateField("outProvinceFixedPeople"))
|
||||
},
|
||||
validateLots() {
|
||||
const lots = this.formData.lots || []
|
||||
for (let i = 0; i < lots.length; i++) {
|
||||
const row = lots[i]
|
||||
const hasContent = row && (row.lotName || row.lotValue || row.activityCost !== null && row.activityCost !== undefined && row.activityCost !== "")
|
||||
if (!hasContent) continue
|
||||
if (!/^\d+$/.test(String(row.lotValue || ""))) {
|
||||
this.$message.warning("第" + (i + 1) + "行标段值必须为整数")
|
||||
return false
|
||||
}
|
||||
if (!/^\d+$/.test(String(row.activityCost === null || row.activityCost === undefined ? "" : row.activityCost))) {
|
||||
this.$message.warning("第" + (i + 1) + "行标段费用必须为整数")
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
deleteLot(index, row) {
|
||||
this.$confirm("确定删除该标段吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
if (row && row.id) {
|
||||
this.lotDeleteList.push(row.id)
|
||||
}
|
||||
this.formData.lots.splice(index, 1)
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定删除【" + row.configName + "】吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/doDelete", {id: row.id}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "删除失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
this.formData = {}
|
||||
this.lotDeleteList = []
|
||||
this.inheritLoading = false
|
||||
this.activeTab = "basic"
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getActivityGroup()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.tour-user-scope {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tour-user-scope .el-select {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tour-setting-inherit {
|
||||
margin-bottom: -40px;
|
||||
padding-right: 96px;
|
||||
position: relative;
|
||||
text-align: right;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tour-setting-basic-divider {
|
||||
border-top: 1px dashed #dcdfe6;
|
||||
margin: 2px 0 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,338 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="旅行社名称">
|
||||
<el-input
|
||||
v-model="pageForm.agencyName"
|
||||
clearable
|
||||
placeholder="请输入旅行社名称"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="联系人">
|
||||
<el-input
|
||||
v-model="pageForm.contactName"
|
||||
clearable
|
||||
placeholder="请输入联系人"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="联系电话">
|
||||
<el-input
|
||||
v-model="pageForm.contactPhone"
|
||||
clearable
|
||||
placeholder="请输入联系电话"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="旅行社列表">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="el-icon-plus"></i>
|
||||
新增
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="年度" prop="year" width="110" sortable="custom" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="旅行社编号" prop="agencyCode" width="120" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="旅行社名称" prop="agencyName" min-width="220" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="联系人" prop="contactName" width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="联系电话" prop="contactPhone" width="150" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="激活状态" prop="enabled" width="160" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-switch
|
||||
v-model="row.enabled"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
@change="toggleEnabled(row)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="260" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="72%"
|
||||
@closed="destroyForm">
|
||||
<el-form :model="formData" :rules="formRules" label-width="110px" ref="form" :disabled="viewMode">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年度" prop="year">
|
||||
<el-date-picker
|
||||
v-model="formData.year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="旅行社名称" prop="agencyName">
|
||||
<el-input v-model="formData.agencyName" maxlength="30" show-word-limit placeholder="请输入旅行社名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="旅行社编号" prop="agencyCode">
|
||||
<el-input v-model="formData.agencyCode" maxlength="50" placeholder="请输入旅行社编号"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="激活状态" prop="enabled">
|
||||
<el-radio-group v-model="formData.enabled">
|
||||
<el-radio :label="true">启用</el-radio>
|
||||
<el-radio :label="false">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人" prop="contactName">
|
||||
<el-input v-model="formData.contactName" maxlength="10" show-word-limit placeholder="请输入联系人"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人手机" prop="contactPhone">
|
||||
<el-input v-model="formData.contactPhone" maxlength="30" placeholder="请输入联系人手机"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="formData.email" maxlength="100" placeholder="请输入邮箱"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" type="textarea" maxlength="100" :rows="3" show-word-limit placeholder="请输入备注"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="移动端缩略图" prop="mobileThumb">
|
||||
<!-- 复用课程管理的封面上传逻辑:单图上传,保存URL,供移动端列表展示使用。 -->
|
||||
<file-upload
|
||||
style="--upload-width: 200px;--upload-height:108px"
|
||||
:upload_number="1"
|
||||
:upload_size="20971520"
|
||||
:value.sync="formData.mobileThumb"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
<div class="el-upload__tip">支持jpg、jpeg、png格式,大小不超过20MB。</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button v-if="!viewMode" type="primary" :loading="subDis" @click="doSubmit">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
const validateMobile = (rule, value, callback) => {
|
||||
const mobileReg = /^1[3-9]\d{9}$/
|
||||
if (!value) {
|
||||
callback(new Error("必填"))
|
||||
} else if (!mobileReg.test(value)) {
|
||||
callback(new Error("手机号格式不正确"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
const emailReg = /^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$/
|
||||
if (!value) {
|
||||
callback(new Error("必填"))
|
||||
} else if (!emailReg.test(value)) {
|
||||
callback(new Error("邮箱格式不正确"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
title: "",
|
||||
dialogVisible: false,
|
||||
viewMode: false,
|
||||
subDis: false,
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
year: currentYear,
|
||||
agencyName: "",
|
||||
contactName: "",
|
||||
contactPhone: ""
|
||||
},
|
||||
formData: {},
|
||||
formRules: {
|
||||
year: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
agencyName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
agencyCode: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
contactName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
contactPhone: [{ validator: validateMobile, trigger: ["blur", "change"] }],
|
||||
email: [{ validator: validateEmail, trigger: ["blur", "change"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.year = moment().format("YYYY")
|
||||
this.pageForm.agencyName = ""
|
||||
this.pageForm.contactName = ""
|
||||
this.pageForm.contactPhone = ""
|
||||
this.doSearch()
|
||||
},
|
||||
emptyForm() {
|
||||
return {
|
||||
year: moment().format("YYYY"),
|
||||
agencyName: "",
|
||||
agencyCode: "",
|
||||
contactName: "",
|
||||
contactPhone: "",
|
||||
email: "",
|
||||
remark: "",
|
||||
mobileThumb: "",
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.title = "新增旅行社信息"
|
||||
this.viewMode = false
|
||||
this.formData = this.emptyForm()
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
},
|
||||
openEdit(row) {
|
||||
this.title = "编辑旅行社信息"
|
||||
this.viewMode = false
|
||||
this.loadDetail(row.id)
|
||||
},
|
||||
openView(row) {
|
||||
this.title = "查看旅行社信息"
|
||||
this.viewMode = true
|
||||
this.loadDetail(row.id)
|
||||
},
|
||||
loadDetail(id) {
|
||||
this.$axios.post(loc() + "/detail", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = Object.assign(this.emptyForm(), res.data || {})
|
||||
this.formData.year = this.formData.year ? String(this.formData.year) : ""
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.subDis = true
|
||||
this.$axios.post(loc() + "/doSubmit", this.formData).then((res) => {
|
||||
this.subDis = false
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.dialogVisible = false
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "保存失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.subDis = false
|
||||
})
|
||||
})
|
||||
},
|
||||
toggleEnabled(row) {
|
||||
this.$axios.post(loc() + "/doSubmit", row).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("状态已更新")
|
||||
} else {
|
||||
row.enabled = !row.enabled
|
||||
this.$message.warning(res.msg || "状态更新失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
row.enabled = !row.enabled
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定删除【" + row.agencyName + "】吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "删除失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
destroyForm() {
|
||||
this.formData = {}
|
||||
this.viewMode = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,547 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="开始年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.startYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择开始年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="结束年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.endYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择结束年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="pageForm.keyword"
|
||||
clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路">
|
||||
<el-select v-model="pageForm.lineId" clearable filterable placeholder="请选择线路" style="width: 100%">
|
||||
<el-option v-for="item in lineOptions" :key="item.lineId" :label="item.lineName" :value="item.lineId"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="出行时段">
|
||||
<el-select v-model="pageForm.travelPeriod" clearable filterable placeholder="请选择出行时段" style="width: 100%">
|
||||
<el-option v-for="item in travelPeriodOptions" :key="item.travelPeriod" :label="item.travelPeriod" :value="item.travelPeriod"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路类型">
|
||||
<el-select v-model="pageForm.lineType" clearable filterable placeholder="请选择线路类型" style="width: 100%">
|
||||
<el-option v-for="item in lineTypeOptions" :key="item.lineType" :label="item.lineType" :value="item.lineType"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="分工会审核">
|
||||
<el-radio-group class="mr5" size="small" v-model="pageForm.audit" @change="changeAudit">
|
||||
<el-radio-button :label="true">已审核</el-radio-button>
|
||||
<el-radio-button :label="false">未审核</el-radio-button>
|
||||
</el-radio-group>
|
||||
</table-tool>
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="工号" prop="jobNo" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="姓名" prop="userName" width="120" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名线路" prop="lineName" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="出行时段" prop="travelPeriod" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路类型" prop="lineType" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名时间" prop="signupTime" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="携带家属" prop="familyCount" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="Number(row.familyCount || 0) > 0 ? 'success' : 'info'">{{ row.familyCount || 0 }}人</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报销超出费用" prop="overCostReimbursed" width="140" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.overCostReimbursed ? 'success' : 'info'">{{ row.overCostReimbursed ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前节点" prop="curTaskName" width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="流程状态" prop="instanceState" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button v-if="row.taskState === 10" size="mini" type="primary" @click="openApproval(row)">审核</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<template #edit>
|
||||
<tour-approval-info ref="infoRef">
|
||||
<div v-if="showApprovalForm">
|
||||
<div class="process-title">
|
||||
{{ formData.taskName }}
|
||||
</div>
|
||||
<el-form
|
||||
:model="formData"
|
||||
ref="formRef"
|
||||
label-width="0"
|
||||
label-suffix=":"
|
||||
class="flow-task-form">
|
||||
<el-form-item
|
||||
label="审批意见"
|
||||
prop="tf_opinion"
|
||||
:rules="[{required:true,message:'必填',trigger:['change','blur']}]">
|
||||
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
|
||||
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到发起人</el-button>
|
||||
<el-button @click="handleTaskAction(2)" size="small" type="danger">拒绝申请</el-button>
|
||||
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意申请</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</tour-approval-info>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#app .search .search-item {
|
||||
width: calc((100% - 150px) / 4);
|
||||
}
|
||||
#app .search .search-query {
|
||||
margin-left: auto;
|
||||
}
|
||||
@media screen and (max-width: 1350px) {
|
||||
#app .search .search-item {
|
||||
width: calc((100% - 100px) / 3);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1200px) {
|
||||
#app .search .search-item {
|
||||
width: calc((100% - 50px) / 2);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 992px) {
|
||||
#app .search .search-item {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.tour-approval-section {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.tour-approval-table {
|
||||
width: 100%;
|
||||
}
|
||||
.tour-approval-empty {
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const TOUR_APPROVAL_INFO = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<div class="process-title">
|
||||
报名信息
|
||||
<el-link type="primary" @click="openChart">点击查看流程图</el-link>
|
||||
</div>
|
||||
<el-descriptions :column="3" border class="tour-approval-section">
|
||||
<el-descriptions-item label="工号">{{ detail.jobNo || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ detail.userName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ detail.gender || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="身份证号">{{ detail.idCard || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ detail.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在工会">{{ detail.unionName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名时间">{{ detail.signupTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名线路">{{ detail.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行时段">{{ detail.travelPeriod || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路类型">{{ detail.lineType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名酒店">{{ detail.hotelName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="旅行社">{{ detail.travelAgencyName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否携带家属">
|
||||
{{ familyText() }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="报销超出费用">{{ detail.overCostReimbursed ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="意向拼床人">{{ detail.intendedRoommate || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床型">{{ detail.bedType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床位信息">{{ detail.bedInfo || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div v-if="isDirectFamilyLine()" class="tour-approval-section">
|
||||
<div class="process-title">直系亲属线路</div>
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="亲属姓名">{{ directRelative.relativeName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ directRelative.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="亲属关系">{{ directRelative.relationshipName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路名称">{{ directRelative.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行开始日期">{{ directRelative.travelStartTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行结束日期">{{ directRelative.travelEndTime || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="showFamilySection()" class="tour-approval-section">
|
||||
<div class="process-title">亲属信息</div>
|
||||
<el-table :data="familyData" border size="mini" empty-text="暂无亲属信息" class="tour-approval-table">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="家属姓名" prop="familyName" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="性别" prop="gender" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="身份证号码" prop="idCard" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="年龄" prop="age" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="关系" prop="relationship" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床型" prop="bedType" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床位" prop="bedInfo" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="意向拼床人" prop="intendedRoommate" min-width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template v-for="task in doneTasks">
|
||||
<div class="mt10">
|
||||
<div class="process-title">{{ task.displayName }}</div>
|
||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-if="task.ext.isFirstTaskNode">
|
||||
<el-descriptions-item label="申请用户">
|
||||
{{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
|
||||
<el-descriptions-item label="办理用户">
|
||||
{{ task.taskFormData.userName }}({{ task.taskFormData.loginName }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理意见" :span="3">
|
||||
{{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<snaker-chart ref="snakerChartRef"></snaker-chart>
|
||||
</div>
|
||||
`,
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
return {
|
||||
row: {},
|
||||
detail: {},
|
||||
familyData: [],
|
||||
directRelative: {},
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
directFamilyUnitLine: false,
|
||||
doneTasks: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.row = row || {}
|
||||
this.detail = {}
|
||||
this.familyData = []
|
||||
this.directRelative = {}
|
||||
this.allowFamily = false
|
||||
this.fillBedInfo = true
|
||||
this.directFamilyUnitLine = false
|
||||
this.doneTasks = []
|
||||
this.info()
|
||||
this.getDoneTasks()
|
||||
},
|
||||
info() {
|
||||
this.$axios.post(loc() + "/detail", { id: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.detail = data.ledger || {}
|
||||
this.detail.travelPeriod = data.travelPeriod || this.row.travelPeriod || ""
|
||||
this.familyData = data.families || []
|
||||
this.directRelative = data.directRelative || {}
|
||||
this.allowFamily = this.toBoolean(data.allowFamily)
|
||||
this.fillBedInfo = data.fillBedInfo === undefined || data.fillBedInfo === null || this.toBoolean(data.fillBedInfo)
|
||||
this.directFamilyUnitLine = this.toBoolean(data.directFamilyUnitLine)
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1"
|
||||
},
|
||||
isDirectFamilyLine() {
|
||||
return this.directFamilyUnitLine
|
||||
|| this.toBoolean(this.detail.directFamilyUnitLine)
|
||||
|| (this.directRelative && this.directRelative.id)
|
||||
},
|
||||
hasFamily() {
|
||||
return this.toBoolean(this.detail.hasFamily) || this.familyData.length > 0
|
||||
},
|
||||
showFamilySection() {
|
||||
return this.allowFamily && this.hasFamily() && !this.isDirectFamilyLine()
|
||||
},
|
||||
familyText() {
|
||||
if (this.isDirectFamilyLine()) {
|
||||
return "否"
|
||||
}
|
||||
if (!this.allowFamily) {
|
||||
return "否"
|
||||
}
|
||||
return this.hasFamily() ? "是" : "否"
|
||||
},
|
||||
getDoneTasks() {
|
||||
this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
openChart() {
|
||||
if (!this.row.instanceProcessDefineId || !this.row.instanceId) {
|
||||
this.$message.warning("暂无流程图信息")
|
||||
return
|
||||
}
|
||||
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
return {
|
||||
unionOptions: [],
|
||||
lineOptions: [],
|
||||
travelPeriodOptions: [],
|
||||
lineTypeOptions: [],
|
||||
showApprovalForm: false,
|
||||
formData: {
|
||||
tf_opinion: ""
|
||||
},
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
audit: false,
|
||||
startYear: currentYear,
|
||||
endYear: currentYear,
|
||||
keyword: "",
|
||||
unionId: "",
|
||||
lineId: "",
|
||||
travelPeriod: "",
|
||||
lineType: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.startYear = moment().format("YYYY")
|
||||
this.pageForm.endYear = moment().format("YYYY")
|
||||
this.pageForm.keyword = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.loadOptions()
|
||||
this.doSearch()
|
||||
},
|
||||
changeAudit() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.clearCascadeFilters()
|
||||
this.loadOptions()
|
||||
this.pageData()
|
||||
},
|
||||
clearCascadeFilters() {
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.showApprovalForm = false
|
||||
this.formData = {
|
||||
tf_opinion: ""
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
openApproval(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.showApprovalForm = true
|
||||
this.formData = {
|
||||
processTaskId: row.taskId,
|
||||
taskName: row.curTaskName || row.taskName,
|
||||
tf_opinion: ""
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
handleTaskAction(val) {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
const loading = createLoading()
|
||||
this.$axios.post("/flow/common/executeTask", {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: val
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$refs.guava.index()
|
||||
this.$message.success(res.msg || "操作成功")
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "操作失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}).catch(() => {})
|
||||
})
|
||||
},
|
||||
loadOptions() {
|
||||
this.loadUnionOptions()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
},
|
||||
baseOptionParams() {
|
||||
return {
|
||||
audit: this.pageForm.audit,
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
keyword: this.pageForm.keyword,
|
||||
unionId: this.pageForm.unionId,
|
||||
lineId: this.pageForm.lineId,
|
||||
travelPeriod: this.pageForm.travelPeriod,
|
||||
lineType: this.pageForm.lineType
|
||||
}
|
||||
},
|
||||
loadUnionOptions() {
|
||||
this.$axios.post(loc() + "/unionOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.unionOptions = res.data || []
|
||||
if (this.pageForm.unionId && !this.unionOptions.some(item => item.id === this.pageForm.unionId)) {
|
||||
this.pageForm.unionId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineOptions() {
|
||||
this.$axios.post(loc() + "/lineOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineOptions = res.data || []
|
||||
if (this.pageForm.lineId && !this.lineOptions.some(item => item.lineId === this.pageForm.lineId)) {
|
||||
this.pageForm.lineId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadTravelPeriodOptions() {
|
||||
this.$axios.post(loc() + "/travelPeriodOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.travelPeriodOptions = res.data || []
|
||||
if (this.pageForm.travelPeriod && !this.travelPeriodOptions.some(item => item.travelPeriod === this.pageForm.travelPeriod)) {
|
||||
this.pageForm.travelPeriod = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineTypeOptions() {
|
||||
this.$axios.post(loc() + "/lineTypeOptions", this.baseOptionParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineTypeOptions = res.data || []
|
||||
if (this.pageForm.lineType && !this.lineTypeOptions.some(item => item.lineType === this.pageForm.lineType)) {
|
||||
this.pageForm.lineType = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadOptions()
|
||||
this.pageData()
|
||||
},
|
||||
components: {
|
||||
"tour-approval-info": TOUR_APPROVAL_INFO
|
||||
},
|
||||
watch: {
|
||||
"pageForm.startYear"() {
|
||||
this.clearCascadeFilters()
|
||||
this.loadOptions()
|
||||
},
|
||||
"pageForm.endYear"() {
|
||||
this.clearCascadeFilters()
|
||||
this.loadOptions()
|
||||
},
|
||||
"pageForm.lineId"() {
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
},
|
||||
"pageForm.travelPeriod"() {
|
||||
this.loadLineOptions()
|
||||
this.loadLineTypeOptions()
|
||||
},
|
||||
"pageForm.lineType"() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
},
|
||||
"pageForm.unionId"() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,609 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="开始年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.startYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择开始年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="结束年度">
|
||||
<el-date-picker
|
||||
v-model="pageForm.endYear"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择结束年度"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="pageForm.keyword"
|
||||
clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路">
|
||||
<el-select v-model="pageForm.lineId" clearable filterable placeholder="请选择线路" style="width: 100%">
|
||||
<el-option v-for="item in lineOptions" :key="item.lineId" :label="item.lineName" :value="item.lineId"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="出行时段">
|
||||
<el-select v-model="pageForm.travelPeriod" clearable filterable placeholder="请选择出行时段" style="width: 100%">
|
||||
<el-option v-for="item in travelPeriodOptions" :key="item.travelPeriod" :label="item.travelPeriod" :value="item.travelPeriod"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="线路类型">
|
||||
<el-select v-model="pageForm.lineType" clearable filterable placeholder="请选择线路类型" style="width: 100%">
|
||||
<el-option v-for="item in lineTypeOptions" :key="item.lineType" :label="item.lineType" :value="item.lineType"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query" style="margin-left: auto;">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<div class="tour-ledger-toolbar">
|
||||
<div class="tour-ledger-toolbar-title">
|
||||
<table-tool :app="this" label="分工会台账"></table-tool>
|
||||
</div>
|
||||
<div class="tour-ledger-scope">
|
||||
<el-button
|
||||
class="tour-scope-btn"
|
||||
size="medium"
|
||||
:class="{'is-active': pageForm.scopeType === 'ownUnionJoined'}"
|
||||
@click="setScopeType('ownUnionJoined')">
|
||||
本工会参加疗休养教职工({{ summaryStats.ownUnionJoinedStaffCount || 0 }})
|
||||
</el-button>
|
||||
<el-button
|
||||
class="tour-scope-btn"
|
||||
size="medium"
|
||||
:class="{'is-active': pageForm.scopeType === 'organizedLineJoined'}"
|
||||
@click="setScopeType('organizedLineJoined')">
|
||||
参加本工会组织线路人员(含其他工会及家属)({{ summaryStats.organizedLineJoinedTotalCount || 0 }})
|
||||
</el-button>
|
||||
<el-button
|
||||
class="tour-scope-btn"
|
||||
size="medium"
|
||||
:class="{'is-active': pageForm.overCostOnly}"
|
||||
@click="setOverCostOnly">
|
||||
超出费用由单位承担
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="pageForm.overCostOnly"
|
||||
type="primary"
|
||||
size="medium"
|
||||
icon="el-icon-download"
|
||||
@click="downloadOverCostSummary">
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="80" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="工号" prop="jobNo" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="姓名" prop="userName" width="120" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名线路" prop="lineName" min-width="220" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="出行时段" prop="travelPeriod" min-width="260" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="线路类型" prop="lineType" width="130" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="报名时间" prop="signupTime" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="携带家属" prop="familyCount" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="Number(row.familyCount || 0) > 0 ? 'success' : 'info'">{{ row.familyCount || 0 }}人</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否参加" prop="joined" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.joined ? 'success' : 'info'">{{ row.joined ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="报销超出费用" prop="overCostReimbursed" width="140" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.overCostReimbursed ? 'success' : 'info'">{{ row.overCostReimbursed ? '是' : '否' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openView(row)">查看</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog
|
||||
title="台账详情"
|
||||
:visible.sync="detailVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="72%">
|
||||
<div class="tour-ledger-section">
|
||||
<div class="tour-ledger-title">教职工信息</div>
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="工号">{{ detail.jobNo || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ detail.userName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ detail.gender || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="身份证号">{{ detail.idCard || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ detail.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在工会">{{ detail.unionName || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div class="tour-ledger-section mt10">
|
||||
<div class="tour-ledger-title">
|
||||
报名信息
|
||||
<el-link v-if="detailHasWorkflow()" type="primary" @click="openChart">点击查看流程图</el-link>
|
||||
</div>
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="报名时间">{{ detail.signupTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名线路">{{ detail.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路类型">{{ detail.lineType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名酒店">{{ detail.hotelName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报名旅行社">{{ detail.travelAgencyName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行时间">{{ detail.travelPeriod || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床型">{{ detail.bedType || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="床位信息">{{ detail.bedInfo || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="fillBedInfo" label="意向拼床人">{{ detail.intendedRoommate || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否携带家属">{{ familyText() }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否参加">{{ detail.joined ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="是否报销">{{ detail.reimbursed ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报销超出费用">{{ detail.overCostReimbursed ? '是' : '否' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="isDirectFamilyLine()" class="tour-ledger-section mt10">
|
||||
<div class="tour-ledger-title">直系亲属线路</div>
|
||||
<el-descriptions :column="3" border size="medium">
|
||||
<el-descriptions-item label="亲属姓名">{{ directRelative.relativeName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{ directRelative.unitName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="亲属关系">{{ directRelative.relationshipName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="线路名称">{{ directRelative.lineName || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行开始日期">{{ directRelative.travelStartTime || '' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出行结束日期">{{ directRelative.travelEndTime || '' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
|
||||
<div v-if="showFamilySection()" class="tour-ledger-section mt10">
|
||||
<div class="tour-ledger-title">家属信息</div>
|
||||
<el-table :data="familyData" border :size="tableSize" empty-text="暂无家属信息">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="家属姓名" prop="familyName" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="性别" prop="gender" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="身份证号码" prop="idCard" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="年龄" prop="age" width="90" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="关系" prop="relationship" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床型" prop="bedType" width="110" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="床位" prop="bedInfo" min-width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column v-if="fillBedInfo" label="意向拼床人" prop="intendedRoommate" min-width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<template v-if="detailHasWorkflow()" v-for="task in doneTasks">
|
||||
<div class="tour-ledger-section mt10" :key="task.id">
|
||||
<div class="tour-ledger-title">{{ task.displayName }}</div>
|
||||
<el-descriptions border :column="3" v-if="task.ext && task.ext.isFirstTaskNode">
|
||||
<el-descriptions-item label="申请用户">
|
||||
{{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions border :column="3" v-else>
|
||||
<el-descriptions-item label="办理用户">
|
||||
{{ (task.taskFormData && task.taskFormData.userName) || '' }}({{ (task.taskFormData && task.taskFormData.loginName) || '' }})
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理意见" :span="3">
|
||||
{{ (task.taskFormData && (task.taskFormData.opinion || task.taskFormData.tf_opinion)) || '' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<snaker-chart ref="snakerChartRef"></snaker-chart>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="detailVisible = false">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.tour-ledger-section {
|
||||
padding: 0 2px;
|
||||
}
|
||||
.tour-ledger-title {
|
||||
border-left: 4px solid #0079c2;
|
||||
color: #0079c2;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
margin-bottom: 14px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.tour-ledger-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: 24px;
|
||||
margin-bottom: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tour-ledger-toolbar-title {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.tour-ledger-scope {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
flex-wrap: nowrap;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tour-ledger-scope .el-button {
|
||||
margin: 0;
|
||||
}
|
||||
.tour-ledger-scope .el-button + .el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn {
|
||||
background: #ecf5ff;
|
||||
border-color: #b3d8ff;
|
||||
border-radius: 4px;
|
||||
color: #0079c2;
|
||||
font-weight: 600;
|
||||
height: 34px;
|
||||
line-height: 1;
|
||||
padding: 8px 18px;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn:hover,
|
||||
.tour-ledger-scope .tour-scope-btn:focus {
|
||||
background: #d9ecff;
|
||||
border-color: #66b1ff;
|
||||
color: #006bb0;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn.is-active {
|
||||
background: #0079c2;
|
||||
border-color: #0079c2;
|
||||
box-shadow: 0 2px 6px rgba(0, 121, 194, 0.24);
|
||||
color: #fff;
|
||||
}
|
||||
.tour-ledger-scope .tour-scope-btn.is-active:hover,
|
||||
.tour-ledger-scope .tour-scope-btn.is-active:focus {
|
||||
background: #006bb0;
|
||||
border-color: #006bb0;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
const currentYear = moment().format("YYYY")
|
||||
return {
|
||||
detailVisible: false,
|
||||
unionOptions: [],
|
||||
lineOptions: [],
|
||||
travelPeriodOptions: [],
|
||||
lineTypeOptions: [],
|
||||
summaryStats: {},
|
||||
detail: {},
|
||||
familyData: [],
|
||||
directRelative: {},
|
||||
directFamilyUnitLine: false,
|
||||
fillBedInfo: true,
|
||||
detailRow: {},
|
||||
doneTasks: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
startYear: currentYear,
|
||||
endYear: currentYear,
|
||||
keyword: "",
|
||||
unionId: "",
|
||||
lineId: "",
|
||||
travelPeriod: "",
|
||||
lineType: "",
|
||||
scopeType: "",
|
||||
overCostOnly: false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1" || value === "true"
|
||||
},
|
||||
isDirectFamilyLine() {
|
||||
return this.directFamilyUnitLine
|
||||
|| this.toBoolean(this.detail && this.detail.directFamilyUnitLine)
|
||||
|| !!(this.directRelative && this.directRelative.id)
|
||||
},
|
||||
detailHasWorkflow() {
|
||||
return !!(this.detailRow && this.detailRow.instanceId)
|
||||
},
|
||||
hasFamily() {
|
||||
return this.toBoolean(this.detail && this.detail.hasFamily) || this.familyData.length > 0
|
||||
},
|
||||
familyText() {
|
||||
if (this.isDirectFamilyLine()) {
|
||||
return "否"
|
||||
}
|
||||
if (!this.hasFamily()) {
|
||||
return "否"
|
||||
}
|
||||
const count = Number(this.familyData.length || 0)
|
||||
return count > 0 ? count + "人" : "是"
|
||||
},
|
||||
showFamilySection() {
|
||||
return this.hasFamily() && !this.isDirectFamilyLine()
|
||||
},
|
||||
loadDoneTasks() {
|
||||
this.$axios.post("/flow/common/doneTasks", { bizId: this.detailRow.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
openChart() {
|
||||
if (!this.detailRow.instanceProcessDefineId || !this.detailRow.instanceId) {
|
||||
this.$message.warning("暂无流程图信息")
|
||||
return
|
||||
}
|
||||
this.$refs.snakerChartRef.onOpenFull(this.detailRow.instanceProcessDefineId, this.detailRow.instanceId)
|
||||
},
|
||||
resetSearch() {
|
||||
this.pageForm.startYear = moment().format("YYYY")
|
||||
this.pageForm.endYear = moment().format("YYYY")
|
||||
this.pageForm.keyword = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.pageForm.scopeType = ""
|
||||
this.pageForm.overCostOnly = false
|
||||
this.loadUnionOptions()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
this.doSearch()
|
||||
},
|
||||
openView(row) {
|
||||
this.detailRow = row || {}
|
||||
this.detail = {}
|
||||
this.familyData = []
|
||||
this.directRelative = {}
|
||||
this.directFamilyUnitLine = false
|
||||
this.fillBedInfo = true
|
||||
this.doneTasks = []
|
||||
this.$axios.post(loc() + "/detail", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.detail = data.ledger || {}
|
||||
this.detail.travelPeriod = data.travelPeriod || ""
|
||||
this.familyData = data.families || []
|
||||
this.directRelative = data.directRelative || {}
|
||||
this.directFamilyUnitLine = this.toBoolean(data.directFamilyUnitLine)
|
||||
this.fillBedInfo = data.fillBedInfo === undefined || data.fillBedInfo === null || this.toBoolean(data.fillBedInfo)
|
||||
this.detailVisible = true
|
||||
if (this.detailHasWorkflow()) {
|
||||
this.loadDoneTasks()
|
||||
}
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定删除【" + row.userName + "】的报名台账吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("删除成功")
|
||||
this.pageData()
|
||||
this.loadSummaryStats()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "删除失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
loadUnionOptions() {
|
||||
this.$axios.post(loc() + "/unionOptions", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.unionOptions = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineOptions() {
|
||||
this.$axios.post(loc() + "/lineOptions", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
travelPeriod: this.pageForm.travelPeriod,
|
||||
lineType: this.pageForm.lineType,
|
||||
unionId: this.pageForm.unionId,
|
||||
keyword: this.pageForm.keyword,
|
||||
scopeType: this.pageForm.scopeType
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineOptions = res.data || []
|
||||
if (this.pageForm.lineId && !this.lineOptions.some(item => item.lineId === this.pageForm.lineId)) {
|
||||
this.pageForm.lineId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadTravelPeriodOptions() {
|
||||
this.$axios.post(loc() + "/travelPeriodOptions", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
lineId: this.pageForm.lineId,
|
||||
lineType: this.pageForm.lineType,
|
||||
unionId: this.pageForm.unionId,
|
||||
keyword: this.pageForm.keyword,
|
||||
scopeType: this.pageForm.scopeType
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.travelPeriodOptions = res.data || []
|
||||
if (this.pageForm.travelPeriod && !this.travelPeriodOptions.some(item => item.travelPeriod === this.pageForm.travelPeriod)) {
|
||||
this.pageForm.travelPeriod = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineTypeOptions() {
|
||||
this.$axios.post(loc() + "/lineTypeOptions", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
lineId: this.pageForm.lineId,
|
||||
travelPeriod: this.pageForm.travelPeriod,
|
||||
unionId: this.pageForm.unionId,
|
||||
keyword: this.pageForm.keyword,
|
||||
scopeType: this.pageForm.scopeType
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.lineTypeOptions = res.data || []
|
||||
if (this.pageForm.lineType && !this.lineTypeOptions.some(item => item.lineType === this.pageForm.lineType)) {
|
||||
this.pageForm.lineType = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadSummaryStats() {
|
||||
this.$axios.post(loc() + "/summaryStats", {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
unionId: this.pageForm.unionId,
|
||||
lineId: this.pageForm.lineId,
|
||||
travelPeriod: this.pageForm.travelPeriod,
|
||||
lineType: this.pageForm.lineType
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.summaryStats = res.data || {}
|
||||
}
|
||||
})
|
||||
},
|
||||
setScopeType(scopeType) {
|
||||
this.pageForm.scopeType = this.pageForm.scopeType === scopeType ? "" : scopeType
|
||||
this.pageForm.pageNumber = 1
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.pageData()
|
||||
},
|
||||
setOverCostOnly() {
|
||||
this.pageForm.overCostOnly = !this.pageForm.overCostOnly
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
downloadOverCostSummary() {
|
||||
this.$downLoad(loc() + "/exportOverCostSummary", this.pageForm)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadUnionOptions()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
this.pageData()
|
||||
},
|
||||
watch: {
|
||||
"pageForm.startYear"() {
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.scopeType = ""
|
||||
this.loadUnionOptions()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
},
|
||||
"pageForm.endYear"() {
|
||||
this.pageForm.lineId = ""
|
||||
this.pageForm.travelPeriod = ""
|
||||
this.pageForm.lineType = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.scopeType = ""
|
||||
this.loadUnionOptions()
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
},
|
||||
"pageForm.lineId"() {
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
},
|
||||
"pageForm.travelPeriod"() {
|
||||
this.loadLineOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
},
|
||||
"pageForm.lineType"() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadSummaryStats()
|
||||
},
|
||||
"pageForm.unionId"() {
|
||||
this.loadLineOptions()
|
||||
this.loadTravelPeriodOptions()
|
||||
this.loadLineTypeOptions()
|
||||
this.loadSummaryStats()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,530 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称/地址">
|
||||
<el-input placeholder="请输入名称或地址" clearable v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="场地类型">
|
||||
<el-select v-model="pageForm.type" clearable filterable placeholder="请选择场地类型" @change="doSearch">
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20">
|
||||
<table-tool label="场地列表"></table-tool>
|
||||
<div v-if="tableData.length > 0" class="site-card-grid">
|
||||
<div v-for="row in tableData" :key="row.id" class="site-card">
|
||||
<div class="site-card__media">
|
||||
<el-image
|
||||
v-if="row.sitePhoto"
|
||||
:preview-src-list="[row.sitePhoto]"
|
||||
:src="row.sitePhoto"
|
||||
fit="cover"
|
||||
class="site-card__image">
|
||||
</el-image>
|
||||
<div v-else class="site-card__image site-card__image--placeholder">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
<span>暂无场地照片</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="site-card__content">
|
||||
<div class="site-card__header">
|
||||
<div class="site-card__title-wrap">
|
||||
<div class="site-card__title" :title="row.name">{{ row.name || '--' }}</div>
|
||||
<div class="site-card__meta">
|
||||
<i class="el-icon-location-outline"></i>
|
||||
<span :title="row.address">{{ row.address || '暂无场地地址' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="site-card__actions">
|
||||
<el-tag size="mini" effect="dark" type="success">{{ row.reserveTimeTypeName || '分段预约' }}</el-tag>
|
||||
<el-button size="mini" plain @click="onViewSite(row)">场地详情</el-button>
|
||||
<el-button size="mini" type="primary" @click="onApply(row)">预约</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="site-card__summary">
|
||||
<span>场地类型:{{ row.typeName || '--' }}</span>
|
||||
<span>容纳人数:{{ row.maxNum || 0 }}人</span>
|
||||
<span>联系人:{{ row.contactName || '--' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="site-card__timeline">
|
||||
<div class="site-card__timeline-title">今日预约情况</div>
|
||||
<div class="timeline-bar">
|
||||
<span
|
||||
v-for="(segment, index) in row.timelineSegments || []"
|
||||
:key="row.id + '-segment-' + index"
|
||||
:class="['timeline-bar__segment', 'timeline-bar__segment--' + (segment.status || 'closed')]">
|
||||
</span>
|
||||
</div>
|
||||
<div class="timeline-scale">
|
||||
<span v-for="hour in hourMarks" :key="row.id + '-hour-' + hour">{{ hour }}</span>
|
||||
</div>
|
||||
<div class="timeline-legend">
|
||||
<span class="timeline-legend__item">
|
||||
<i class="timeline-legend__dot timeline-legend__dot--available"></i>未预约
|
||||
</span>
|
||||
<span class="timeline-legend__item">
|
||||
<i class="timeline-legend__dot timeline-legend__dot--reserved"></i>已预约
|
||||
</span>
|
||||
<span class="timeline-legend__item">
|
||||
<i class="timeline-legend__dot timeline-legend__dot--closed"></i>未开放
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无可预约场地"></el-empty>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<site-apply ref="applyRef" @refresh="refresh"></site-apply>
|
||||
</template>
|
||||
<template #view>
|
||||
<info ref="infoRef"></info>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../manage/info.js'){}#-->
|
||||
<!--#include('apply.js'){}#-->
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
"info": siteInfo,
|
||||
"site-apply": apply,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
typeOptions: [],
|
||||
hourMarks: ['0', '2', '4', '6', '8', '10', '12', '14', '16', '18', '20', '22']
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.doSearch()
|
||||
this.$refs.guava.index()
|
||||
},
|
||||
onApply(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.applyRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onViewSite(row) {
|
||||
this.$refs.guava.view(() => {
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
querySiteType() {
|
||||
this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
|
||||
this.typeOptions = res.data
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post(loc() + '/pageData', this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.querySiteType()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.site-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.site-card {
|
||||
display: flex;
|
||||
min-height: 240px;
|
||||
padding: 18px;
|
||||
border: 1px solid #e8edf5;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f8fbff 100%);
|
||||
box-shadow: 0 10px 30px rgba(21, 66, 120, 0.06);
|
||||
}
|
||||
|
||||
.site-card__media {
|
||||
flex: 0 0 180px;
|
||||
margin-right: 18px;
|
||||
}
|
||||
|
||||
.site-card__image {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.site-card__image--placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8c9bb1;
|
||||
background: linear-gradient(135deg, #eef4fb 0%, #dde8f6 100%);
|
||||
font-size: 14px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.site-card__image--placeholder i {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.site-card__content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.site-card__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.site-card__title-wrap {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.site-card__title {
|
||||
overflow: hidden;
|
||||
color: #1f2d3d;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.site-card__meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
color: #7f8ea3;
|
||||
font-size: 14px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.site-card__meta span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.site-card__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.site-card__summary {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 16px;
|
||||
color: #4f6277;
|
||||
font-size: 13px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.site-card__timeline {
|
||||
margin-top: auto;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.site-card__timeline-title {
|
||||
margin-bottom: 10px;
|
||||
color: #25364d;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.timeline-bar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(48, minmax(0, 1fr));
|
||||
gap: 2px;
|
||||
align-items: center;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.timeline-bar__segment {
|
||||
height: 8px;
|
||||
border-radius: 999px;
|
||||
background: #e5ebf3;
|
||||
}
|
||||
|
||||
.timeline-bar__segment--available {
|
||||
background: #32b56c;
|
||||
}
|
||||
|
||||
.timeline-bar__segment--reserved {
|
||||
background: #9aa6b2;
|
||||
}
|
||||
|
||||
.timeline-bar__segment--closed {
|
||||
background: #e9edf3;
|
||||
}
|
||||
|
||||
.timeline-scale {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
||||
margin-top: 10px;
|
||||
color: #607389;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.timeline-scale span {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.timeline-scale span:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.timeline-legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 12px;
|
||||
color: #607389;
|
||||
font-size: 13px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.timeline-legend__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.timeline-legend__dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.timeline-legend__dot--available {
|
||||
background: #32b56c;
|
||||
}
|
||||
|
||||
.timeline-legend__dot--reserved {
|
||||
background: #9aa6b2;
|
||||
}
|
||||
|
||||
.timeline-legend__dot--closed {
|
||||
background: #e9edf3;
|
||||
border: 1px solid #d6dde8;
|
||||
}
|
||||
|
||||
.apply-visual-panel {
|
||||
margin-bottom: 18px;
|
||||
padding: 18px;
|
||||
border: 1px solid #e8edf5;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(180deg, #ffffff 0%, #f6faff 100%);
|
||||
}
|
||||
|
||||
.apply-visual-panel__toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.apply-visual-panel__title {
|
||||
color: #1f2d3d;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.apply-visual-panel__subtitle {
|
||||
margin-top: 6px;
|
||||
color: #708399;
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.apply-visual-panel__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.apply-visual-panel__legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 14px;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.apply-legend__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: #607389;
|
||||
font-size: 13px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.apply-legend__dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.apply-legend__dot--available {
|
||||
background: #32b56c;
|
||||
}
|
||||
|
||||
.apply-legend__dot--reserved {
|
||||
background: #9aa6b2;
|
||||
}
|
||||
|
||||
.apply-legend__dot--selected {
|
||||
background: #2f7df6;
|
||||
}
|
||||
|
||||
.apply-legend__dot--closed {
|
||||
background: #e9edf3;
|
||||
border: 1px solid #d6dde8;
|
||||
}
|
||||
|
||||
.apply-block-group {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.apply-block-group__title {
|
||||
margin-bottom: 10px;
|
||||
color: #1f2d3d;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.apply-block-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.apply-block {
|
||||
min-height: 40px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 10px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.apply-block--available {
|
||||
color: #0f5132;
|
||||
background: #d9f6e5;
|
||||
border-color: #87d7a7;
|
||||
}
|
||||
|
||||
.apply-block--reserved {
|
||||
color: #4b5663;
|
||||
background: #e1e6ec;
|
||||
border-color: #c5ced8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.apply-block--closed {
|
||||
color: #8b97a6;
|
||||
background: #f3f5f8;
|
||||
border-color: #e2e8f0;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.apply-block--selected {
|
||||
color: #ffffff;
|
||||
background: #2f7df6;
|
||||
border-color: #2f7df6;
|
||||
box-shadow: 0 6px 16px rgba(47, 125, 246, 0.22);
|
||||
}
|
||||
|
||||
.apply-block:not(.apply-block--reserved):not(.apply-block--closed):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 16px rgba(30, 72, 124, 0.12);
|
||||
}
|
||||
|
||||
.apply-visual-panel__summary {
|
||||
margin-top: 16px;
|
||||
padding: 12px 14px;
|
||||
color: #1f4e8c;
|
||||
font-size: 13px;
|
||||
background: #eef5ff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1400px) {
|
||||
.site-card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.site-card {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.site-card__media {
|
||||
flex: none;
|
||||
margin-right: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.site-card__image {
|
||||
width: 100%;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
.site-card__header {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.site-card__actions {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.apply-visual-panel__toolbar {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,80 @@
|
||||
const siteCugApplyInfo = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<div class="process-title">
|
||||
申请信息
|
||||
<el-link type="primary" @click="openChart">点击查看流程图</el-link>
|
||||
</div>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="预约场地">{{ viewData.siteName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约人">{{ viewData.applyUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约人工号">{{ viewData.applyLoginName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属单位">{{ viewData.applyUnitName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约类型">{{ reserveTypeLabel }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约主体">{{ reserveTargetName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">{{ viewData.reserveStartTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">{{ viewData.reserveEndTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ viewData.applyMobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约人数">{{ viewData.joinCount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约事由" :span="2">{{ viewData.applyCause }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template v-for="task in doneTasks">
|
||||
<div class="mt10">
|
||||
<div class="process-title">{{ task.displayName }}</div>
|
||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-if="task.ext.isFirstTaskNode">
|
||||
<el-descriptions-item label="申请用户">{{ task.ext.initiatorName }}({{task.ext.initiatorAccount}})</el-descriptions-item>
|
||||
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
|
||||
<el-descriptions-item label="办理用户">{{ task.taskFormData.userName }}({{task.taskFormData.loginName}})</el-descriptions-item>
|
||||
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="办理意见">{{ task.taskFormData.opinion }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
<snaker-chart ref="snakerChartRef"></snaker-chart>
|
||||
</div>
|
||||
`,
|
||||
store,
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
return {
|
||||
viewData: {},
|
||||
doneTasks: [],
|
||||
row: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
reserveTypeLabel() {
|
||||
return this.viewData.reserveType === 'club' ? '协会预约' : '分工会预约'
|
||||
},
|
||||
reserveTargetName() {
|
||||
return this.viewData.reserveType === 'club' ? (this.viewData.clubName || '-') : (this.viewData.applyUnionName || '-')
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.row = row
|
||||
this.viewData = row
|
||||
this.getDoneTasks()
|
||||
},
|
||||
getDoneTasks() {
|
||||
this.$axios.post('/flow/common/doneTasks', {bizId: this.row.id}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
openChart() {
|
||||
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,740 @@
|
||||
const basicForm = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="110px" label-position="left">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="场地基本信息" name="basicInfo">
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="创建人" prop="createUserName">
|
||||
<el-input disabled type="text" v-model="formData.createUserName" maxlength="20" placeholder="请输入创建人"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="sortNum" label="排序编号">
|
||||
<el-input v-model="formData.sortNum" placeholder="请输入排序编号" type="number"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="场地名称" prop="name">
|
||||
<el-input type="text" v-model="formData.name" maxlength="50" placeholder="请输入场地名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="场地地址" prop="address">
|
||||
<el-input type="text" v-model="formData.address" maxlength="100" placeholder="请输入场地地址"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系人" prop="contactName">
|
||||
<el-input type="text" v-model="formData.contactName" maxlength="20" placeholder="请输入联系人"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input type="text" v-model="formData.contactPhone" maxlength="20" placeholder="请输入联系电话"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="容纳人数" prop="maxNum">
|
||||
<el-input v-model="formData.maxNum" placeholder="请输入容纳人数" type="number"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="场地类型" prop="typeId">
|
||||
<el-select v-model="formData.typeId" clearable filterable placeholder="请选择场地类型" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in typeList"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="场地介绍" prop="introduce">
|
||||
<text-editor v-model="formData.introduce"></text-editor>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="场地照片" prop="sitePhoto">
|
||||
<file-upload
|
||||
:value.sync="formData.sitePhoto"
|
||||
:upload_number="1"
|
||||
:upload_size="1024 * 1024 * 10"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
upload_result_category="interval"
|
||||
complete_result>
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="设置场地" name="siteSetting">
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="state" label="开启状态">
|
||||
<el-radio-group v-model="formData.state" size="medium">
|
||||
<el-radio-button :label="true">开启</el-radio-button>
|
||||
<el-radio-button :label="false">禁用</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="sexLimit" label="性别限制">
|
||||
<el-radio-group v-model="formData.sexLimit" size="medium">
|
||||
<el-radio-button :label="0">不限制</el-radio-button>
|
||||
<el-radio-button :label="1">男</el-radio-button>
|
||||
<el-radio-button :label="2">女</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="reserveTimeType" label="预约时间段类型">
|
||||
<el-radio-group v-model="formData.reserveTimeType" @change="handleReserveTimeTypeChange" size="medium">
|
||||
<el-radio-button :label="1">分段预约</el-radio-button>
|
||||
<el-radio-button :label="2">全天候预约</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="filterHolidays" label="排除节假日">
|
||||
<el-radio-group v-model="formData.filterHolidays" size="medium">
|
||||
<el-radio-button :label="true">是</el-radio-button>
|
||||
<el-radio-button :label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="禁用时间">
|
||||
<el-button type="primary" @click="openSetUpTime" size="small">点击设置禁用时间</el-button>
|
||||
<span style="color: #c64120">您已设置{{ formData.notApplyTimeList ? formData.notApplyTimeList.length : 0 }}个禁用时间</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">场次信息</el-divider>
|
||||
|
||||
<div v-if="formData.reserveTimeType === 1">
|
||||
<div class="left-span-label">分段预约:可设置一个或多个场次,预约人按拆分后的场次进行预约。</div>
|
||||
<el-table :data="formData.openHours" border size="mini">
|
||||
<el-table-column type="index" label="序号" align="center" header-align="center" width="80"></el-table-column>
|
||||
<el-table-column prop="startTime" label="开始时间" align="center" header-align="center">
|
||||
<template v-slot="{ row }">
|
||||
<el-time-select
|
||||
style="width: 100%"
|
||||
placeholder="开始时间"
|
||||
v-model="row.startTime"
|
||||
@change="checkSegmentedOpenHoursOverlap"
|
||||
:picker-options="{ start: '00:00', step: '00:30', end: '23:59' }">
|
||||
</el-time-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="endTime" label="结束时间" align="center" header-align="center">
|
||||
<template v-slot="{ row }">
|
||||
<el-time-select
|
||||
style="width: 100%"
|
||||
placeholder="结束时间"
|
||||
v-model="row.endTime"
|
||||
@change="checkSegmentedOpenHoursOverlap"
|
||||
:picker-options="{ start: '00:00', step: '00:30', end: '23:59' }">
|
||||
</el-time-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="timeUnit" label="预约时间单位(分钟)" align="center" header-align="center">
|
||||
<template v-slot="{ row }">
|
||||
<el-input-number style="width: 100%" v-model="row.timeUnit" :min="1" :step="1" :precision="0" placeholder="请输入预约时间单位"></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" header-align="center" width="130">
|
||||
<template slot="header">
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="addOpenHour">添加场次</el-button>
|
||||
</template>
|
||||
<template v-slot="scope">
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="formData.openHours.length <= 1"
|
||||
@click="formData.openHours.splice(scope.$index, 1)">
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="left-span-label">全天候预约:设置可预约的开始时间、结束时间和预约时间单位(分钟),预约人按设定单位选择连续时段。</div>
|
||||
<el-row :gutter="20" type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间">
|
||||
<el-time-select
|
||||
style="width: 100%"
|
||||
placeholder="开始时间"
|
||||
v-model="fullDayOpenHour.startTime"
|
||||
:picker-options="{ start: '00:00', step: '00:30', end: '23:59' }">
|
||||
</el-time-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间">
|
||||
<el-time-select
|
||||
style="width: 100%"
|
||||
placeholder="结束时间"
|
||||
v-model="fullDayOpenHour.endTime"
|
||||
:picker-options="{ start: '00:00', step: '00:30', end: '23:59' }">
|
||||
</el-time-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="预约时间单位(分钟)">
|
||||
<el-input-number
|
||||
style="width: 100%"
|
||||
v-model="fullDayOpenHour.timeUnit"
|
||||
:min="1"
|
||||
:step="1"
|
||||
:precision="0"
|
||||
placeholder="请输入预约时间单位">
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<div class="mt10" style="color: #909399; font-size: 13px;">
|
||||
暂存:保存当前填写内容并留在本页继续编辑。正式提交:保存后返回列表页。
|
||||
</div>
|
||||
<el-row class="mt10" justify="end" type="flex">
|
||||
<el-button @click="$emit('refresh')">取消</el-button>
|
||||
<el-button @click="onTempSave" plain type="warning">暂存并继续编辑</el-button>
|
||||
<el-button @click="onSubmit" type="primary">正式提交</el-button>
|
||||
</el-row>
|
||||
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="setUpTimeDialog" title="设置时间">
|
||||
<div class="left-span-label">选择日期</div>
|
||||
<el-date-picker
|
||||
@change="setUpDateChange"
|
||||
placeholder="请选择一个或多个日期"
|
||||
style="width: 100%"
|
||||
type="dates"
|
||||
v-model="formData.setUpDate"
|
||||
value-format="yyyy-MM-dd">
|
||||
</el-date-picker>
|
||||
<div class="left-span-label mt20">设置禁用时间</div>
|
||||
<el-row>
|
||||
<el-time-select
|
||||
:picker-options="{
|
||||
start: '00:00',
|
||||
step: '00:05',
|
||||
end: '23:55'
|
||||
}"
|
||||
placeholder="开始时间"
|
||||
v-model="timeOneKeySet.startTime">
|
||||
</el-time-select>
|
||||
<el-time-select
|
||||
:picker-options="{
|
||||
start: '00:00',
|
||||
step: '00:05',
|
||||
end: '23:55'
|
||||
}"
|
||||
placeholder="结束时间"
|
||||
v-model="timeOneKeySet.endTime">
|
||||
</el-time-select>
|
||||
<el-button @click="oneKeySetStartEndTime" type="primary">一键设置开始/结束时间</el-button>
|
||||
</el-row>
|
||||
<el-table :data="formData.notApplyTimeList" class="mt10" max-height="520px" size="mini" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column label="日期" width="200">
|
||||
<template v-slot="{row}">
|
||||
<i class="el-icon-time"></i>
|
||||
{{$moment(row.date).format('YYYY-MM-DD')}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间">
|
||||
<template v-slot="{row}">
|
||||
<el-time-select
|
||||
size="mini"
|
||||
:picker-options="{
|
||||
start: '00:00',
|
||||
step: '00:05',
|
||||
end: '23:55'
|
||||
}"
|
||||
v-model="row.startTime">
|
||||
</el-time-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间">
|
||||
<template v-slot="{row}">
|
||||
<el-time-select
|
||||
size="mini"
|
||||
:picker-options="{
|
||||
start: '00:00',
|
||||
step: '00:05',
|
||||
end: '23:55'
|
||||
}"
|
||||
v-model="row.endTime">
|
||||
</el-time-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template v-slot="{row, $index}">
|
||||
<el-button
|
||||
size="mini"
|
||||
@click="formData.notApplyTimeList.splice($index,0,{date:row.date,startTime:'',endTime:''})"
|
||||
type="primary">
|
||||
新增同天时段
|
||||
</el-button>
|
||||
<el-button size="mini" @click="removeSetUpTableRow(row,$index)" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="mt20" justify="end" type="flex">
|
||||
<el-button @click="setUpTimeDialog = false">取消</el-button>
|
||||
<el-button @click="doConfirmSetUpCourse" type="primary">确定</el-button>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
typeList: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: [],
|
||||
}
|
||||
},
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'basicInfo',
|
||||
segmentedOpenHoursCache: [{ startTime: '', endTime: '', timeUnit: 30 }],
|
||||
fullDayOpenHourCache: { startTime: '', endTime: '', timeUnit: 30 },
|
||||
formData: {
|
||||
state: true,
|
||||
sexLimit: 0,
|
||||
filterHolidays: false,
|
||||
reserveTimeType: 1,
|
||||
openHours: [{ startTime: '', endTime: '', timeUnit: 30 }],
|
||||
segmentedOpenHours: [{ startTime: '', endTime: '', timeUnit: 30 }],
|
||||
fullDayOpenHour: { startTime: '', endTime: '', timeUnit: 30 },
|
||||
notApplyTimeList: [],
|
||||
createUserName: this.$store.state.user.username,
|
||||
createUserId: this.$store.state.user.id,
|
||||
},
|
||||
formRules: {
|
||||
createUserName: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
sortNum: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
name: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
address: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
contactName: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
contactPhone: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
maxNum: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
typeId: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
sexLimit: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
filterHolidays: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
state: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
reserveTimeType: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
},
|
||||
setUpTimeDialog: false,
|
||||
timeOneKeySet: {
|
||||
startTime: "",
|
||||
endTime: ""
|
||||
},
|
||||
multipleSelection: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
fullDayOpenHour() {
|
||||
if (!Array.isArray(this.formData.openHours)) {
|
||||
this.$set(this.formData, 'openHours', [])
|
||||
}
|
||||
if (this.formData.openHours.length === 0) {
|
||||
this.formData.openHours.push({ startTime: '', endTime: '', timeUnit: 30 })
|
||||
}
|
||||
if (!this.formData.openHours[0].timeUnit || this.formData.openHours[0].timeUnit <= 0) {
|
||||
this.$set(this.formData.openHours[0], 'timeUnit', 30)
|
||||
}
|
||||
return this.formData.openHours[0]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
normalizeTimeUnit(value, defaultValue = 30) {
|
||||
const num = Number(value)
|
||||
if (!Number.isFinite(num) || num <= 0) {
|
||||
return defaultValue
|
||||
}
|
||||
return Math.round(num)
|
||||
},
|
||||
syncOpenHoursCache() {
|
||||
if (this.formData.reserveTimeType === 1) {
|
||||
this.segmentedOpenHoursCache = Array.isArray(this.formData.openHours) && this.formData.openHours.length > 0
|
||||
? clone(this.formData.openHours).map(item => ({
|
||||
startTime: item.startTime || '',
|
||||
endTime: item.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(item.timeUnit),
|
||||
}))
|
||||
: [{ startTime: '', endTime: '', timeUnit: 30 }]
|
||||
this.formData.segmentedOpenHours = clone(this.segmentedOpenHoursCache)
|
||||
} else {
|
||||
const first = (this.formData.openHours && this.formData.openHours[0]) || {}
|
||||
this.fullDayOpenHourCache = {
|
||||
startTime: first.startTime || '',
|
||||
endTime: first.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(first.timeUnit),
|
||||
}
|
||||
this.formData.fullDayOpenHour = clone(this.fullDayOpenHourCache)
|
||||
}
|
||||
},
|
||||
buildSubmitFormData() {
|
||||
this.syncOpenHoursCache()
|
||||
const submitData = clone(this.formData)
|
||||
submitData.segmentedOpenHours = clone(this.segmentedOpenHoursCache || [])
|
||||
submitData.fullDayOpenHour = clone(this.fullDayOpenHourCache || { startTime: '', endTime: '', timeUnit: 30 })
|
||||
if (submitData.reserveTimeType === 1) {
|
||||
submitData.openHours = clone(this.segmentedOpenHoursCache || [])
|
||||
} else {
|
||||
submitData.openHours = [clone(this.fullDayOpenHourCache || { startTime: '', endTime: '', timeUnit: 60 })]
|
||||
}
|
||||
return submitData
|
||||
},
|
||||
initOpenHoursCache() {
|
||||
const segmentedOpenHours = Array.isArray(this.formData.segmentedOpenHours) && this.formData.segmentedOpenHours.length > 0
|
||||
? clone(this.formData.segmentedOpenHours)
|
||||
: (Array.isArray(this.formData.openHours) ? clone(this.formData.openHours) : [])
|
||||
const fullDayOpenHour = this.formData.fullDayOpenHour && Object.keys(this.formData.fullDayOpenHour).length > 0
|
||||
? clone(this.formData.fullDayOpenHour)
|
||||
: ((Array.isArray(this.formData.openHours) && this.formData.openHours.length > 0) ? clone(this.formData.openHours[0]) : {})
|
||||
if (this.formData.reserveTimeType === 2) {
|
||||
this.fullDayOpenHourCache = {
|
||||
startTime: fullDayOpenHour.startTime || '',
|
||||
endTime: fullDayOpenHour.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(fullDayOpenHour.timeUnit),
|
||||
}
|
||||
this.segmentedOpenHoursCache = segmentedOpenHours && segmentedOpenHours.length > 0
|
||||
? segmentedOpenHours.map(item => ({
|
||||
startTime: item.startTime || '',
|
||||
endTime: item.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(item.timeUnit),
|
||||
}))
|
||||
: [{ startTime: '', endTime: '', timeUnit: 30 }]
|
||||
this.formData.openHours = [clone(this.fullDayOpenHourCache)]
|
||||
} else {
|
||||
this.segmentedOpenHoursCache = segmentedOpenHours.length > 0
|
||||
? segmentedOpenHours.map(item => ({
|
||||
startTime: item.startTime || '',
|
||||
endTime: item.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(item.timeUnit),
|
||||
}))
|
||||
: [{ startTime: '', endTime: '', timeUnit: 30 }]
|
||||
const first = this.segmentedOpenHoursCache[0] || {}
|
||||
this.fullDayOpenHourCache = {
|
||||
startTime: fullDayOpenHour.startTime || first.startTime || '',
|
||||
endTime: fullDayOpenHour.endTime || first.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(fullDayOpenHour.timeUnit || first.timeUnit),
|
||||
}
|
||||
this.formData.openHours = clone(this.segmentedOpenHoursCache)
|
||||
}
|
||||
this.formData.segmentedOpenHours = clone(this.segmentedOpenHoursCache)
|
||||
this.formData.fullDayOpenHour = clone(this.fullDayOpenHourCache)
|
||||
},
|
||||
addOpenHour() {
|
||||
if (!Array.isArray(this.formData.openHours)) {
|
||||
this.$set(this.formData, 'openHours', [])
|
||||
}
|
||||
this.formData.openHours.push({ startTime: '', endTime: '', timeUnit: 30 })
|
||||
this.segmentedOpenHoursCache = clone(this.formData.openHours)
|
||||
},
|
||||
hasSegmentedOpenHoursOverlap(openHours) {
|
||||
const validHours = (openHours || [])
|
||||
.map((item, index) => ({ ...item, index }))
|
||||
.filter(item => item.startTime && item.endTime && item.startTime < item.endTime)
|
||||
.sort((a, b) => a.startTime.localeCompare(b.startTime))
|
||||
for (let i = 1; i < validHours.length; i++) {
|
||||
const prev = validHours[i - 1]
|
||||
const current = validHours[i]
|
||||
if (current.startTime < prev.endTime) {
|
||||
return {
|
||||
overlap: true,
|
||||
prevIndex: prev.index,
|
||||
currentIndex: current.index,
|
||||
}
|
||||
}
|
||||
}
|
||||
return { overlap: false }
|
||||
},
|
||||
checkSegmentedOpenHoursOverlap() {
|
||||
if (this.formData.reserveTimeType !== 1) {
|
||||
return false
|
||||
}
|
||||
const result = this.hasSegmentedOpenHoursOverlap(this.formData.openHours)
|
||||
if (result.overlap) {
|
||||
this.$message.warning('第' + (result.prevIndex + 1) + '条和第' + (result.currentIndex + 1) + '条场次时间有重叠,请调整后再保存')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
handleReserveTimeTypeChange(val) {
|
||||
if (!Array.isArray(this.formData.openHours)) {
|
||||
this.$set(this.formData, 'openHours', [])
|
||||
}
|
||||
if (val === 1) {
|
||||
const currentFullDay = this.formData.openHours[0] || this.fullDayOpenHourCache || {}
|
||||
this.fullDayOpenHourCache = {
|
||||
startTime: currentFullDay.startTime || '',
|
||||
endTime: currentFullDay.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(currentFullDay.timeUnit),
|
||||
}
|
||||
if (!Array.isArray(this.segmentedOpenHoursCache) || this.segmentedOpenHoursCache.length === 0) {
|
||||
this.segmentedOpenHoursCache = [{
|
||||
startTime: this.fullDayOpenHourCache.startTime || '',
|
||||
endTime: this.fullDayOpenHourCache.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(this.fullDayOpenHourCache.timeUnit),
|
||||
}]
|
||||
}
|
||||
this.formData.openHours = clone(this.segmentedOpenHoursCache)
|
||||
} else if (val === 2) {
|
||||
this.segmentedOpenHoursCache = Array.isArray(this.formData.openHours) && this.formData.openHours.length > 0
|
||||
? clone(this.formData.openHours)
|
||||
: this.segmentedOpenHoursCache
|
||||
const first = this.fullDayOpenHourCache && (this.fullDayOpenHourCache.startTime || this.fullDayOpenHourCache.endTime)
|
||||
? this.fullDayOpenHourCache
|
||||
: (this.segmentedOpenHoursCache[0] || {})
|
||||
this.fullDayOpenHourCache = {
|
||||
startTime: first.startTime || '',
|
||||
endTime: first.endTime || '',
|
||||
timeUnit: this.normalizeTimeUnit(first.timeUnit || this.fullDayOpenHourCache.timeUnit),
|
||||
}
|
||||
this.formData.openHours = [clone(this.fullDayOpenHourCache)]
|
||||
}
|
||||
},
|
||||
validateOpenHours() {
|
||||
const openHours = this.formData.openHours || []
|
||||
if (this.formData.reserveTimeType === 1) {
|
||||
if (openHours.length === 0) {
|
||||
this.$message.warning('请至少添加一条场次信息')
|
||||
return false
|
||||
}
|
||||
for (let i = 0; i < openHours.length; i++) {
|
||||
const item = openHours[i] || {}
|
||||
if (!item.startTime) {
|
||||
this.$message.warning('第' + (i + 1) + '条场次信息中,开始时间必填')
|
||||
return false
|
||||
}
|
||||
if (!item.endTime) {
|
||||
this.$message.warning('第' + (i + 1) + '条场次信息中,结束时间必填')
|
||||
return false
|
||||
}
|
||||
if (item.startTime >= item.endTime) {
|
||||
this.$message.warning('第' + (i + 1) + '条场次信息中,开始时间必须早于结束时间')
|
||||
return false
|
||||
}
|
||||
if (!item.timeUnit || item.timeUnit <= 0) {
|
||||
this.$message.warning('第' + (i + 1) + '条场次信息中,预约时间单位必须大于0分钟')
|
||||
return false
|
||||
}
|
||||
}
|
||||
const overlapResult = this.hasSegmentedOpenHoursOverlap(openHours)
|
||||
if (overlapResult.overlap) {
|
||||
this.$message.warning('第' + (overlapResult.prevIndex + 1) + '条和第' + (overlapResult.currentIndex + 1) + '条场次时间有重叠,请调整后再保存')
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
const item = openHours[0] || {}
|
||||
if (!item.startTime || !item.endTime) {
|
||||
this.$message.warning('请设置全天候预约的开始时间和结束时间')
|
||||
return false
|
||||
}
|
||||
if (item.startTime >= item.endTime) {
|
||||
this.$message.warning('全天候预约的开始时间必须早于结束时间')
|
||||
return false
|
||||
}
|
||||
if (!item.timeUnit || item.timeUnit <= 0) {
|
||||
this.$message.warning('全天候预约的预约时间单位必须大于0分钟')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
doConfirmSetUpCourse() {
|
||||
const timeList = this.formData.notApplyTimeList
|
||||
if (timeList && timeList.length > 0) {
|
||||
const valid = timeList.every(v => v.date && v.startTime && v.endTime && (v.startTime < v.endTime))
|
||||
if (!valid) {
|
||||
this.$message.warning("时间不完整或者有误")
|
||||
return
|
||||
}
|
||||
}
|
||||
this.setUpTimeDialog = false
|
||||
},
|
||||
removeSetUpTableRow(row, index) {
|
||||
this.formData.notApplyTimeList.splice(index, 1)
|
||||
const dateArray = this.formData.notApplyTimeList.map(v => this.$moment(v.date).format("YYYY-MM-DD"))
|
||||
const setUpDate = this.formData.setUpDate || []
|
||||
this.formData.setUpDate = setUpDate.filter(v => {
|
||||
return dateArray.includes(this.$moment(v).format("YYYY-MM-DD"))
|
||||
})
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val
|
||||
},
|
||||
oneKeySetStartEndTime() {
|
||||
if (this.multipleSelection.length === 0) {
|
||||
this.$message.warning('请在下面表格多选框中选择需要一键设置的时间')
|
||||
return
|
||||
}
|
||||
const { startTime, endTime } = this.timeOneKeySet
|
||||
this.formData.notApplyTimeList.forEach(v => {
|
||||
const selected = this.multipleSelection.find(o => o.date === v.date && o.startTime === v.startTime && o.endTime === v.endTime)
|
||||
if (selected) {
|
||||
this.$set(v, "startTime", startTime)
|
||||
this.$set(v, "endTime", endTime)
|
||||
}
|
||||
})
|
||||
this.$forceUpdate()
|
||||
},
|
||||
setUpDateChange(val) {
|
||||
if (!val) {
|
||||
this.formData.notApplyTimeList = []
|
||||
return
|
||||
}
|
||||
if (this.formData.notApplyTimeList === undefined) {
|
||||
this.$set(this.formData, "notApplyTimeList", [])
|
||||
}
|
||||
const dateSet = new Set(this.formData.notApplyTimeList.map(v => this.$moment(v.date).format("YYYY-MM-DD")))
|
||||
val.forEach(v => {
|
||||
if (!dateSet.has(v)) {
|
||||
this.formData.notApplyTimeList.push({
|
||||
date: v, startTime: "", endTime: ""
|
||||
})
|
||||
}
|
||||
})
|
||||
this.formData.notApplyTimeList = this.formData.notApplyTimeList.filter(v => {
|
||||
return val.includes(this.$moment(v.date).format("YYYY-MM-DD"))
|
||||
})
|
||||
this.formData.notApplyTimeList.sort((a, b) => {
|
||||
return Date.parse(a.date) - Date.parse(b.date)
|
||||
})
|
||||
},
|
||||
openSetUpTime() {
|
||||
if (this.formData.notApplyTimeList) {
|
||||
this.$set(this.formData, 'setUpDate', this.formData.notApplyTimeList.map(o => o.date))
|
||||
}
|
||||
this.activeTab = 'siteSetting'
|
||||
this.setUpTimeDialog = true
|
||||
},
|
||||
onOpen(row) {
|
||||
this.activeTab = 'basicInfo'
|
||||
if (row && row.id) {
|
||||
this.formData = clone(row)
|
||||
if (!Array.isArray(this.formData.notApplyTimeList)) {
|
||||
this.$set(this.formData, 'notApplyTimeList', [])
|
||||
}
|
||||
if (!this.formData.reserveTimeType) {
|
||||
this.$set(this.formData, 'reserveTimeType', 1)
|
||||
}
|
||||
if (!Array.isArray(this.formData.openHours) || this.formData.openHours.length === 0) {
|
||||
this.$set(this.formData, 'openHours', this.formData.reserveTimeType === 2 ? [{ startTime: '', endTime: '', timeUnit: 30 }] : [{ startTime: '', endTime: '', timeUnit: 30 }])
|
||||
}
|
||||
if (!Array.isArray(this.formData.segmentedOpenHours) || this.formData.segmentedOpenHours.length === 0) {
|
||||
this.$set(this.formData, 'segmentedOpenHours', [{ startTime: '', endTime: '', timeUnit: 30 }])
|
||||
}
|
||||
if (!this.formData.fullDayOpenHour) {
|
||||
this.$set(this.formData, 'fullDayOpenHour', { startTime: '', endTime: '', timeUnit: 30 })
|
||||
}
|
||||
this.initOpenHoursCache()
|
||||
} else {
|
||||
this.formData = {
|
||||
state: true,
|
||||
sexLimit: 0,
|
||||
filterHolidays: false,
|
||||
reserveTimeType: 1,
|
||||
openHours: [{ startTime: '', endTime: '', timeUnit: 30 }],
|
||||
segmentedOpenHours: [{ startTime: '', endTime: '', timeUnit: 30 }],
|
||||
fullDayOpenHour: { startTime: '', endTime: '', timeUnit: 30 },
|
||||
notApplyTimeList: [],
|
||||
createUserName: this.$store.state.user.username,
|
||||
createUserId: this.$store.state.user.id,
|
||||
}
|
||||
this.segmentedOpenHoursCache = [{ startTime: '', endTime: '', timeUnit: 30 }]
|
||||
this.fullDayOpenHourCache = { startTime: '', endTime: '', timeUnit: 30 }
|
||||
}
|
||||
},
|
||||
async doSave(stayOnPage) {
|
||||
if (!this.validateOpenHours()) {
|
||||
this.activeTab = 'siteSetting'
|
||||
return
|
||||
}
|
||||
const submitData = this.buildSubmitFormData()
|
||||
const resp = await this.$axios.post("/platform/siteCug/manage/submit", {data: JSON.stringify(submitData)})
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(stayOnPage ? '暂存成功,您可以继续编辑当前内容' : '正式提交成功,已返回列表页')
|
||||
if (resp.data) {
|
||||
this.formData = clone(resp.data)
|
||||
if (!Array.isArray(this.formData.notApplyTimeList)) {
|
||||
this.$set(this.formData, 'notApplyTimeList', [])
|
||||
}
|
||||
if (!this.formData.reserveTimeType) {
|
||||
this.$set(this.formData, 'reserveTimeType', 1)
|
||||
}
|
||||
if (!Array.isArray(this.formData.openHours) || this.formData.openHours.length === 0) {
|
||||
this.$set(this.formData, 'openHours', this.formData.reserveTimeType === 2 ? [{ startTime: '', endTime: '', timeUnit: 30 }] : [{ startTime: '', endTime: '', timeUnit: 30 }])
|
||||
}
|
||||
if (!Array.isArray(this.formData.segmentedOpenHours) || this.formData.segmentedOpenHours.length === 0) {
|
||||
this.$set(this.formData, 'segmentedOpenHours', [{ startTime: '', endTime: '', timeUnit: 30 }])
|
||||
}
|
||||
if (!this.formData.fullDayOpenHour) {
|
||||
this.$set(this.formData, 'fullDayOpenHour', { startTime: '', endTime: '', timeUnit: 30 })
|
||||
}
|
||||
this.initOpenHoursCache()
|
||||
}
|
||||
if (!stayOnPage) {
|
||||
this.$emit('refresh')
|
||||
}
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
onTempSave() {
|
||||
this.$refs.formRef.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.$confirm("确认暂存当前内容,并继续留在本页编辑吗?", "暂存确认", {
|
||||
confirmButtonText: "确认暂存",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
await this.doSave(true)
|
||||
}).catch(() => {})
|
||||
}
|
||||
})
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$confirm("确认正式提交当前内容吗?提交成功后将返回列表页。", "正式提交确认", {
|
||||
confirmButtonText: "确认提交",
|
||||
cancelButtonText: "取消",
|
||||
type: "primary"
|
||||
}).then(async () => {
|
||||
await this.doSave(false)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,184 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称/地址">
|
||||
<el-input placeholder="请输入名称或地址" clearable v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="场地类型">
|
||||
<el-select v-model="pageForm.type" clearable filterable placeholder="请选择场地类型" @change="doSearch">
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20">
|
||||
<table-tool label="场地列表">
|
||||
<el-button type="primary" size="small" @click="onAdd">
|
||||
<i class="ti-plus"></i>
|
||||
新增场地
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" :size="tableSize">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||
<el-table-column
|
||||
v-for="column in tableColumns"
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:width="column.width"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template v-slot="{ row }" v-if="column.prop === 'state'">
|
||||
<el-switch
|
||||
@change="switchChange(row)"
|
||||
v-model="row.state"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'reserveTimeType'">
|
||||
<span v-if="row.reserveTimeType === 2">全天候预约</span>
|
||||
<span v-else>分段预约</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'sexLimit'">
|
||||
<span v-if="row.sexLimit === 0">不限制</span>
|
||||
<span v-else-if="row.sexLimit === 1">男</span>
|
||||
<span v-else-if="row.sexLimit === 2">女</span>
|
||||
<span v-else>--</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="230">
|
||||
<template v-slot="{ row }">
|
||||
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
|
||||
<el-button @click="onEdit(row)" size="mini" type="primary">编辑</el-button>
|
||||
<el-button @click="onDelete(row)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<basic-form :type-list="typeOptions" ref="basicFormRef" @refresh="refresh"></basic-form>
|
||||
</template>
|
||||
<template #view>
|
||||
<info ref="infoRef"></info>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('basicForm.js'){}#-->
|
||||
<!--#include('info.js'){}#-->
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
"basic-form": basicForm,
|
||||
"info": siteInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
typeOptions: [],
|
||||
tableColumns: [
|
||||
{prop: 'createUserName', label: '创建人', sortable: 'custom'},
|
||||
{prop: 'name', label: '场地名称', sortable: 'custom'},
|
||||
{prop: 'address', label: '场地地址', sortable: 'custom'},
|
||||
{prop: 'contactName', label: '联系人', sortable: 'custom'},
|
||||
{prop: 'contactPhone', label: '联系电话', sortable: 'custom'},
|
||||
{prop: 'maxNum', label: '容纳人数', sortable: 'custom'},
|
||||
{prop: 'typeName', label: '场地类型'},
|
||||
{prop: 'reserveTimeType', label: '预约时间段类型', sortable: 'custom'},
|
||||
{prop: 'sexLimit', label: '性别限制', sortable: 'custom'},
|
||||
{prop: 'state', label: '开启状态', sortable: 'custom'},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.doSearch()
|
||||
this.$refs.guava.index()
|
||||
},
|
||||
onAdd() {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.basicFormRef.onOpen()
|
||||
})
|
||||
},
|
||||
onEdit(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.basicFormRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onView(row) {
|
||||
this.$refs.guava.view(() => {
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm("您确定要删除吗? 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
this.$axios.post("/platform/siteCug/manage/delete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
switchChange(row) {
|
||||
this.$axios.post("/platform/siteCug/manage/submit", row).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
},
|
||||
querySiteType() {
|
||||
this.$axios.post("/platform/siteCug/function/type/queryFunctionType").then((res) => {
|
||||
this.typeOptions = res.data
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post(loc() + "/pageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.querySiteType()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,107 @@
|
||||
const siteInfo = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="场地基本信息" name="basicInfo">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="创建人">{{ viewData.createUserName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="排序编号">{{ viewData.sortNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="场地名称">{{ viewData.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="场地地址">{{ viewData.address }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系人">{{ viewData.contactName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ viewData.contactPhone }}</el-descriptions-item>
|
||||
<el-descriptions-item label="容纳人数">{{ viewData.maxNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="场地类型">{{ viewData.typeName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="场地介绍" :span="2">
|
||||
<div v-if="viewData.introduce" v-html="viewData.introduce"></div>
|
||||
<div v-else>暂无场地介绍</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="场地照片" :span="2">
|
||||
<el-image
|
||||
v-if="viewData.sitePhoto"
|
||||
:preview-src-list="[viewData.sitePhoto]"
|
||||
:src="viewData.sitePhoto"
|
||||
fit="cover"
|
||||
style="width: 160px; height: 160px; border-radius: 4px;">
|
||||
</el-image>
|
||||
<div v-else>暂无场地照片</div>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="设置场地" name="siteSetting">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="开启状态">
|
||||
<span v-if="viewData.state">开启</span>
|
||||
<span v-else>禁用</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="性别限制">
|
||||
<span v-if="viewData.sexLimit === 0">不限制</span>
|
||||
<span v-else-if="viewData.sexLimit === 1">男</span>
|
||||
<span v-else-if="viewData.sexLimit === 2">女</span>
|
||||
<span v-else>--</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="排除节假日">
|
||||
<span v-if="viewData.filterHolidays">是</span>
|
||||
<span v-else>否</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="预约时间段类型">
|
||||
<span v-if="viewData.reserveTimeType === 1">分段预约</span>
|
||||
<span v-else-if="viewData.reserveTimeType === 2">全天候预约</span>
|
||||
<span v-else>--</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="禁用时间" :span="2">
|
||||
<el-table v-if="viewData.notApplyTimeList && viewData.notApplyTimeList.length > 0"
|
||||
:data="viewData.notApplyTimeList" max-height="300" size="mini">
|
||||
<el-table-column prop="date" label="日期"></el-table-column>
|
||||
<el-table-column prop="startTime" label="开始时间"></el-table-column>
|
||||
<el-table-column prop="endTime" label="结束时间"></el-table-column>
|
||||
</el-table>
|
||||
<span v-else>暂无禁用时间</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="场次信息" :span="2">
|
||||
<div v-if="viewData.reserveTimeType === 1">
|
||||
<el-table v-if="viewData.openHours && viewData.openHours.length > 0"
|
||||
:data="viewData.openHours" max-height="300" size="mini">
|
||||
<el-table-column type="index" label="序号" width="80"></el-table-column>
|
||||
<el-table-column prop="startTime" label="开始时间"></el-table-column>
|
||||
<el-table-column prop="endTime" label="结束时间"></el-table-column>
|
||||
<el-table-column prop="timeUnit" label="预约时间单位(分钟)"></el-table-column>
|
||||
</el-table>
|
||||
<span v-else>暂无场次信息</span>
|
||||
</div>
|
||||
<div v-else-if="viewData.reserveTimeType === 2">
|
||||
<div v-if="viewData.openHours && viewData.openHours.length > 0">
|
||||
<div><span>开始时间:</span><span>{{ viewData.openHours[0].startTime || '--' }}</span></div>
|
||||
<div><span>结束时间:</span><span>{{ viewData.openHours[0].endTime || '--' }}</span></div>
|
||||
<div><span>预约时间单位(分钟):</span><span>{{ viewData.openHours[0].timeUnit || '--' }}</span></div>
|
||||
</div>
|
||||
<span v-else>暂无场次信息</span>
|
||||
</div>
|
||||
<span v-else>暂无场次信息</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'basicInfo',
|
||||
viewData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.activeTab = 'basicInfo'
|
||||
this.viewData = row
|
||||
},
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
.el-descriptions-item__label {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
max-width: 200px;
|
||||
}
|
||||
`
|
||||
};
|
||||
@@ -0,0 +1,250 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.mine-batch-toggle {
|
||||
padding: 0;
|
||||
color: #409EFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.mine-batch-empty {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.mine-batch-child-row {
|
||||
background: #fafcff;
|
||||
}
|
||||
|
||||
.mine-batch-child-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #7a8a9a;
|
||||
}
|
||||
|
||||
.mine-batch-child-label::before {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 1px;
|
||||
background: #c8d3df;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="姓名/工号">
|
||||
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="活动场地">
|
||||
<el-select v-model="pageForm.siteId" @change="doSearch" style="width: 100%;" placeholder="请选择活动场地" filterable clearable>
|
||||
<el-option v-for="item in siteOptions" :value="item.id" :key="item.id" :label="item.name"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="场地类型">
|
||||
<el-select v-model="pageForm.siteType" @change="doSearch" style="width: 100%;" placeholder="请选择场地类型" filterable clearable>
|
||||
<el-option v-for="item in typeOptions" :value="item.id" :key="item.id" :label="item.name"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool label="申请列表"></table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" :row-class-name="tableRowClassName" style="width: 100%;">
|
||||
<el-table-column label="" width="54" align="center" header-align="center">
|
||||
<template v-slot="{ row }">
|
||||
<el-button
|
||||
v-if="row._hasFoldChildren"
|
||||
class="mine-batch-toggle"
|
||||
type="text"
|
||||
@click="toggleBatchGroup(row)">
|
||||
<i :class="row._expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right'"></i>
|
||||
</el-button>
|
||||
<span v-else class="mine-batch-empty"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="index" width="50" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column
|
||||
v-for="column in tableColumns"
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:width="column.width"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template v-slot="{ row }" v-if="column.prop === 'siteName'">
|
||||
<span v-if="row._isBatchChild" class="mine-batch-child-label">{{ row.siteName }}</span>
|
||||
<span v-else>{{ row.siteName }}</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'instanceState'">
|
||||
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'yearlyBatch'">
|
||||
<span>{{ normalizeBatchFlag(row) ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="180">
|
||||
<template v-slot="{ row }">
|
||||
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
|
||||
<el-button v-if="row.canCancel && !row._isBatchChild" @click="onDelete(row)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<info ref="infoRef"></info>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../common/applyInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
'info': siteCugApplyInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
typeOptions: [],
|
||||
siteOptions: [],
|
||||
rawTableData: [],
|
||||
expandedBatchKeys: {},
|
||||
tableColumns: [
|
||||
{prop: 'siteName', label: '场地名称', sortable: 'custom'},
|
||||
{prop: 'applyUserName', label: '预约人', sortable: 'custom'},
|
||||
{prop: 'applyLoginName', label: '工号', sortable: 'custom'},
|
||||
{prop: 'yearlyBatch', label: '是否批量预约', sortable: 'custom', width: 130},
|
||||
{prop: 'reserveTargetName', label: '预约单位', sortable: 'custom'},
|
||||
{prop: 'reserveStartTime', label: '开始时间', sortable: 'custom'},
|
||||
{prop: 'reserveEndTime', label: '结束时间', sortable: 'custom'},
|
||||
{prop: 'applyMobile', label: '联系方式', sortable: 'custom'},
|
||||
{prop: 'taskName', label: '当前节点', sortable: 'custom'},
|
||||
{prop: 'instanceState', label: '流程状态', sortable: 'custom', width: 120},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
normalizeBatchFlag(row) {
|
||||
return row && (row.yearlyBatch === 1 || row.yearlyBatch === '1' || row.yearlyBatch === true)
|
||||
},
|
||||
getBatchGroupKey(row) {
|
||||
if (!this.normalizeBatchFlag(row)) {
|
||||
return 'single_' + row.id
|
||||
}
|
||||
const batchNo = row.backOption || row.id
|
||||
return 'batch_' + batchNo + '_' + (row.applyUserName || '') + '_' + (row.siteName || '')
|
||||
},
|
||||
buildTableData(rows) {
|
||||
const groups = new Map()
|
||||
;(rows || []).forEach(row => {
|
||||
const key = this.getBatchGroupKey(row)
|
||||
if (!groups.has(key)) {
|
||||
groups.set(key, [])
|
||||
}
|
||||
groups.get(key).push({...row})
|
||||
})
|
||||
const displayRows = []
|
||||
groups.forEach((groupRows, key) => {
|
||||
const sortedRows = groupRows.slice().sort((a, b) => {
|
||||
return (b.reserveStartTime || '').localeCompare(a.reserveStartTime || '')
|
||||
})
|
||||
const parent = {...sortedRows[0]}
|
||||
const children = sortedRows.slice(1).map(item => ({
|
||||
...item,
|
||||
_isBatchChild: true,
|
||||
_groupKey: key,
|
||||
_hasFoldChildren: false,
|
||||
}))
|
||||
parent._groupKey = key
|
||||
parent._isBatchChild = false
|
||||
parent._hasFoldChildren = children.length > 0
|
||||
parent._expanded = !!this.expandedBatchKeys[key]
|
||||
parent._batchCount = sortedRows.length
|
||||
parent._foldedRecords = children
|
||||
displayRows.push(parent)
|
||||
if (parent._expanded) {
|
||||
displayRows.push(...children)
|
||||
}
|
||||
})
|
||||
return displayRows
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return row && row._isBatchChild ? 'mine-batch-child-row' : ''
|
||||
},
|
||||
toggleBatchGroup(row) {
|
||||
if (!row || !row._groupKey || !row._hasFoldChildren) {
|
||||
return
|
||||
}
|
||||
this.$set(this.expandedBatchKeys, row._groupKey, !this.expandedBatchKeys[row._groupKey])
|
||||
this.tableData = this.buildTableData(this.rawTableData)
|
||||
},
|
||||
onView(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onDelete(row) {
|
||||
const message = this.normalizeBatchFlag(row)
|
||||
? ('该记录属于“预约本年”批量预约,确认后将一并删除同批次的 ' + (row.batchDeleteCount || row._batchCount || 0) + ' 条预约记录,是否继续?')
|
||||
: '您确定要删除吗?'
|
||||
this.$confirm(message, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$axios.post('/platform/siteCug/mine/delete', { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
querySiteType() {
|
||||
this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
|
||||
this.typeOptions = res.data
|
||||
})
|
||||
},
|
||||
querySites() {
|
||||
this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
|
||||
this.siteOptions = res.data
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post(loc() + '/pageData', this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.rawTableData = Array.isArray(res.data.list) ? res.data.list : []
|
||||
this.tableData = this.buildTableData(this.rawTableData)
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.querySiteType()
|
||||
this.querySites()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,317 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.record-batch-toggle {
|
||||
padding: 0;
|
||||
color: #409EFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.record-batch-empty {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.record-batch-child-row {
|
||||
background: #fafcff;
|
||||
}
|
||||
|
||||
.record-batch-child-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #7a8a9a;
|
||||
}
|
||||
|
||||
.record-batch-child-label::before {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 1px;
|
||||
background: #c8d3df;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="pageForm.searchKeyword"
|
||||
placeholder="请输入姓名或工号"
|
||||
clearable
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="活动场地">
|
||||
<el-select
|
||||
v-model="pageForm.siteId"
|
||||
@change="doSearch"
|
||||
style="width: 100%"
|
||||
placeholder="请选择活动场地"
|
||||
filterable
|
||||
clearable>
|
||||
<el-option
|
||||
v-for="item in siteOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="场地类型">
|
||||
<el-select
|
||||
v-model="pageForm.siteType"
|
||||
@change="doSearch"
|
||||
style="width: 100%"
|
||||
placeholder="请选择场地类型"
|
||||
filterable
|
||||
clearable>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="预约单位">
|
||||
<el-input
|
||||
v-model="pageForm.reserveTargetKeyword"
|
||||
placeholder="请输入分工会或协会名称"
|
||||
clearable
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="预约开始时间">
|
||||
<el-date-picker
|
||||
v-model="pageForm.reserveTimeStart"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择预约开始时间"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
@change="doSearch">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="预约结束时间">
|
||||
<el-date-picker
|
||||
v-model="pageForm.reserveTimeEnd"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
placeholder="请选择预约结束时间"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
@change="doSearch">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool label="申请列表">
|
||||
<el-button type="primary" size="small" @click="doExport">导出表格</el-button>
|
||||
</table-tool>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:row-class-name="tableRowClassName"
|
||||
@sort-change="pageOrder"
|
||||
style="width: 100%">
|
||||
<el-table-column label="" width="54" align="center" header-align="center">
|
||||
<template v-slot="{ row }">
|
||||
<el-button
|
||||
v-if="row._hasFoldChildren"
|
||||
class="record-batch-toggle"
|
||||
type="text"
|
||||
@click="toggleBatchGroup(row)">
|
||||
<i :class="row._expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right'"></i>
|
||||
</el-button>
|
||||
<span v-else class="record-batch-empty"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="index" width="50" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column
|
||||
v-for="column in tableColumns"
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:width="column.width"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot="{ row }" v-if="column.prop === 'siteName'">
|
||||
<span v-if="row._isBatchChild" class="record-batch-child-label">{{ row.siteName }}</span>
|
||||
<span v-else>{{ row.siteName }}</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'reserveTargetName'">
|
||||
<span>{{ row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-') }}</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'yearlyBatch'">
|
||||
<span>{{ normalizeBatchFlag(row) ? '是' : '否' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="180">
|
||||
<template v-slot="{ row }">
|
||||
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
|
||||
<el-button @click="onDelete(row.id)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<info ref="infoRef"></info>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../common/applyInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
info: siteCugApplyInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
audit: false,
|
||||
siteType: '',
|
||||
siteId: '',
|
||||
reserveType: '',
|
||||
reserveTargetKeyword: '',
|
||||
reserveTimeStart: '',
|
||||
reserveTimeEnd: '',
|
||||
},
|
||||
typeOptions: [],
|
||||
siteOptions: [],
|
||||
rawTableData: [],
|
||||
expandedBatchKeys: {},
|
||||
tableColumns: [
|
||||
{ prop: 'siteName', label: '场地名称', sortable: 'custom' },
|
||||
{ prop: 'applyUserName', label: '预约人', sortable: 'custom' },
|
||||
{ prop: 'applyLoginName', label: '工号', sortable: 'custom' },
|
||||
{ prop: 'yearlyBatch', label: '是否批量预约', sortable: 'custom', width: 130 },
|
||||
{ prop: 'reserveTargetName', label: '预约单位', sortable: 'custom' },
|
||||
{ prop: 'reserveStartTime', label: '开始时间', sortable: 'custom' },
|
||||
{ prop: 'reserveEndTime', label: '结束时间', sortable: 'custom' },
|
||||
{ prop: 'applyMobile', label: '联系方式', sortable: 'custom' },
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
normalizeBatchFlag(row) {
|
||||
return row && (row.yearlyBatch === 1 || row.yearlyBatch === '1' || row.yearlyBatch === true)
|
||||
},
|
||||
getBatchGroupKey(row) {
|
||||
if (!this.normalizeBatchFlag(row)) {
|
||||
return 'single_' + row.id
|
||||
}
|
||||
const batchNo = row.backOption || row.id
|
||||
return 'batch_' + batchNo + '_' + (row.applyUserName || '') + '_' + (row.siteName || '')
|
||||
},
|
||||
buildTableData(rows) {
|
||||
const groups = new Map()
|
||||
;(rows || []).forEach(row => {
|
||||
const key = this.getBatchGroupKey(row)
|
||||
if (!groups.has(key)) {
|
||||
groups.set(key, [])
|
||||
}
|
||||
groups.get(key).push({ ...row })
|
||||
})
|
||||
const displayRows = []
|
||||
groups.forEach((groupRows, key) => {
|
||||
const sortedRows = groupRows.slice().sort((a, b) => {
|
||||
return (b.reserveStartTime || '').localeCompare(a.reserveStartTime || '')
|
||||
})
|
||||
const parent = { ...sortedRows[0] }
|
||||
const children = sortedRows.slice(1).map(item => ({
|
||||
...item,
|
||||
_isBatchChild: true,
|
||||
_groupKey: key,
|
||||
_hasFoldChildren: false,
|
||||
}))
|
||||
parent._groupKey = key
|
||||
parent._isBatchChild = false
|
||||
parent._hasFoldChildren = children.length > 0
|
||||
parent._expanded = !!this.expandedBatchKeys[key]
|
||||
parent._batchCount = sortedRows.length
|
||||
parent._foldedRecords = children
|
||||
displayRows.push(parent)
|
||||
if (parent._expanded) {
|
||||
displayRows.push(...children)
|
||||
}
|
||||
})
|
||||
return displayRows
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return row && row._isBatchChild ? 'record-batch-child-row' : ''
|
||||
},
|
||||
toggleBatchGroup(row) {
|
||||
if (!row || !row._groupKey || !row._hasFoldChildren) {
|
||||
return
|
||||
}
|
||||
this.$set(this.expandedBatchKeys, row._groupKey, !this.expandedBatchKeys[row._groupKey])
|
||||
this.tableData = this.buildTableData(this.rawTableData)
|
||||
},
|
||||
onView(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onDelete(id) {
|
||||
this.$confirm('您确定要删除吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
this.$axios.post('/platform/siteCug/record/delete', { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
doExport() {
|
||||
this.$downLoad('/platform/siteCug/record/doExport', this.pageForm)
|
||||
},
|
||||
querySiteType() {
|
||||
this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
|
||||
this.typeOptions = res.data
|
||||
})
|
||||
},
|
||||
querySites() {
|
||||
this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
|
||||
this.siteOptions = res.data
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post(loc() + '/pageData', this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.rawTableData = Array.isArray(res.data.list) ? res.data.list : []
|
||||
this.tableData = this.buildTableData(this.rawTableData)
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.querySiteType()
|
||||
this.querySites()
|
||||
this.pageData()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.audit-batch-toggle {
|
||||
padding: 0;
|
||||
color: #409EFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.audit-batch-empty {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.audit-batch-child-row {
|
||||
background: #fafcff;
|
||||
}
|
||||
|
||||
.audit-batch-child-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #7a8a9a;
|
||||
}
|
||||
|
||||
.audit-batch-child-label::before {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 1px;
|
||||
background: #c8d3df;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
<guava ref="guava">
|
||||
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="姓名/工号">
|
||||
<el-input
|
||||
v-model="pageForm.searchKeyword"
|
||||
placeholder="请输入姓名或工号"
|
||||
clearable
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="活动场地">
|
||||
<el-select
|
||||
v-model="pageForm.siteId"
|
||||
@change="doSearch"
|
||||
style="width: 100%"
|
||||
placeholder="请选择活动场地"
|
||||
filterable
|
||||
clearable>
|
||||
<el-option
|
||||
v-for="item in siteOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="场地类型">
|
||||
<el-select
|
||||
v-model="pageForm.siteType"
|
||||
@change="doSearch"
|
||||
style="width: 100%"
|
||||
placeholder="请选择场地类型"
|
||||
filterable
|
||||
clearable>
|
||||
<el-option
|
||||
v-for="item in typeOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool label="申请列表">
|
||||
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
|
||||
<el-radio-button :label="true">已审核</el-radio-button>
|
||||
<el-radio-button :label="false">未审核</el-radio-button>
|
||||
</el-radio-group>
|
||||
</table-tool>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:row-class-name="tableRowClassName"
|
||||
@sort-change="pageOrder"
|
||||
style="width: 100%">
|
||||
<el-table-column label="" width="54" align="center" header-align="center">
|
||||
<template v-slot="{ row }">
|
||||
<el-button
|
||||
v-if="row._hasFoldChildren"
|
||||
class="audit-batch-toggle"
|
||||
type="text"
|
||||
@click="toggleBatchGroup(row)">
|
||||
<i :class="row._expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right'"></i>
|
||||
</el-button>
|
||||
<span v-else class="audit-batch-empty"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column type="index" width="50" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column
|
||||
v-for="column in tableColumns"
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:width="column.width"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot="{ row }" v-if="column.prop === 'siteName'">
|
||||
<span v-if="row._isBatchChild" class="audit-batch-child-label">{{ row.siteName }}</span>
|
||||
<span v-else>{{ row.siteName }}</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'reserveTargetName'">
|
||||
<span>{{ row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-') }}</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'yearlyBatch'">
|
||||
<span>{{ normalizeBatchFlag(row) ? '是' : '否' }}</span>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'instanceState'">
|
||||
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="220">
|
||||
<template v-slot="{ row }">
|
||||
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
|
||||
<el-button v-if="!row._isBatchChild && row.taskState === 10" @click="onAudit(row)" size="mini" type="primary">审核</el-button>
|
||||
<el-button v-if="!row._isBatchChild && canRevoke(row)" :loading="revokeLoading" :disabled="revokeLoading" @click="onRevoke(row)" size="mini" type="danger">撤回</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<template #edit>
|
||||
<info ref="infoRef">
|
||||
<div v-if="showApprovalForm">
|
||||
<div class="process-title">
|
||||
{{ formData.taskName }}
|
||||
</div>
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" class="flow-task-form">
|
||||
<el-form-item
|
||||
label="审批意见"
|
||||
prop="tf_opinion"
|
||||
:rules="[{ required: true, message: '必填', trigger: ['change', 'blur'] }]">
|
||||
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
|
||||
<el-button @click="handleTaskAction(6)" :loading="auditLoading" :disabled="auditLoading" size="small" type="info">退回到发起人</el-button>
|
||||
<el-button @click="handleTaskAction(2)" :loading="auditLoading" :disabled="auditLoading" size="small" type="danger">拒绝申请</el-button>
|
||||
<el-button @click="handleTaskAction(1)" :loading="auditLoading" :disabled="auditLoading" size="small" type="primary">同意申请</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
</info>
|
||||
</template>
|
||||
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../common/applyInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
info: siteCugApplyInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
approval: false,
|
||||
},
|
||||
rawTableData: [],
|
||||
expandedBatchKeys: {},
|
||||
tableColumns: [
|
||||
{ prop: 'siteName', label: '场地名称', sortable: 'custom' },
|
||||
{ prop: 'applyUserName', label: '预约人', sortable: 'custom' },
|
||||
{ prop: 'applyLoginName', label: '工号', sortable: 'custom' },
|
||||
{ prop: 'yearlyBatch', label: '是否批量预约', sortable: 'custom', width: 130 },
|
||||
{ prop: 'reserveTargetName', label: '预约单位', sortable: 'custom' },
|
||||
{ prop: 'reserveStartTime', label: '开始时间', sortable: 'custom' },
|
||||
{ prop: 'reserveEndTime', label: '结束时间', sortable: 'custom' },
|
||||
{ prop: 'applyMobile', label: '联系方式', sortable: 'custom' },
|
||||
{ prop: 'curTaskName', label: '当前节点', sortable: 'custom' },
|
||||
{ prop: 'instanceState', label: '流程状态', sortable: 'custom', width: 120 },
|
||||
],
|
||||
typeOptions: [],
|
||||
siteOptions: [],
|
||||
formData: {},
|
||||
formRules: {},
|
||||
showApprovalForm: false,
|
||||
auditLoading: false,
|
||||
revokeLoading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
normalizeBatchFlag(row) {
|
||||
return row && (row.yearlyBatch === 1 || row.yearlyBatch === '1' || row.yearlyBatch === true)
|
||||
},
|
||||
getBatchGroupKey(row) {
|
||||
if (!this.normalizeBatchFlag(row)) {
|
||||
return 'single_' + row.id
|
||||
}
|
||||
const batchNo = row.backOption || row.id
|
||||
return 'batch_' + batchNo + '_' + (row.applyUserName || '') + '_' + (row.siteName || '')
|
||||
},
|
||||
buildTableData(rows) {
|
||||
const groups = new Map()
|
||||
;(rows || []).forEach(row => {
|
||||
const key = this.getBatchGroupKey(row)
|
||||
if (!groups.has(key)) {
|
||||
groups.set(key, [])
|
||||
}
|
||||
groups.get(key).push({ ...row })
|
||||
})
|
||||
const displayRows = []
|
||||
groups.forEach((groupRows, key) => {
|
||||
const sortedRows = groupRows.slice().sort((a, b) => {
|
||||
return (b.reserveStartTime || '').localeCompare(a.reserveStartTime || '')
|
||||
})
|
||||
const parent = { ...sortedRows[0] }
|
||||
const children = sortedRows.slice(1).map(item => ({
|
||||
...item,
|
||||
_isBatchChild: true,
|
||||
_groupKey: key,
|
||||
_hasFoldChildren: false,
|
||||
}))
|
||||
parent._groupKey = key
|
||||
parent._isBatchChild = false
|
||||
parent._hasFoldChildren = children.length > 0
|
||||
parent._expanded = !!this.expandedBatchKeys[key]
|
||||
parent._batchCount = sortedRows.length
|
||||
parent._foldedRecords = children
|
||||
displayRows.push(parent)
|
||||
if (parent._expanded) {
|
||||
displayRows.push(...children)
|
||||
}
|
||||
})
|
||||
return displayRows
|
||||
},
|
||||
tableRowClassName({ row }) {
|
||||
return row && row._isBatchChild ? 'audit-batch-child-row' : ''
|
||||
},
|
||||
toggleBatchGroup(row) {
|
||||
if (!row || !row._groupKey || !row._hasFoldChildren) {
|
||||
return
|
||||
}
|
||||
this.$set(this.expandedBatchKeys, row._groupKey, !this.expandedBatchKeys[row._groupKey])
|
||||
this.tableData = this.buildTableData(this.rawTableData)
|
||||
},
|
||||
canRevoke(row) {
|
||||
return Number(row.instanceState) === 20
|
||||
},
|
||||
onView(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.showApprovalForm = false
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onAudit(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.showApprovalForm = true
|
||||
this.formData = {
|
||||
processTaskId: row.taskId,
|
||||
applyId: row.id,
|
||||
taskName: row.curTaskName,
|
||||
tf_opinion: '',
|
||||
yearlyBatch: row.yearlyBatch,
|
||||
batchAuditCount: row.batchAuditCount || row._batchCount,
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
handleTaskAction(val) {
|
||||
if (this.auditLoading) {
|
||||
return
|
||||
}
|
||||
const message = this.formData.yearlyBatch
|
||||
? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?')
|
||||
: '您确定要提交吗?'
|
||||
this.$confirm(message, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(() => {
|
||||
this.auditLoading = true
|
||||
this.$axios.post('/platform/siteCug/schoolUnionAudit/executeTask', {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: val,
|
||||
}),
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$refs.guava.index()
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
}).finally(() => {
|
||||
this.auditLoading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
onRevoke(row) {
|
||||
if (!this.canRevoke(row) || this.revokeLoading) {
|
||||
return
|
||||
}
|
||||
const message = this.normalizeBatchFlag(row)
|
||||
? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || row._batchCount || 0) + ' 条审核任务,是否继续?')
|
||||
: '您确定要撤回吗?'
|
||||
this.$confirm(message, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info',
|
||||
}).then(() => {
|
||||
this.revokeLoading = true
|
||||
this.$axios.post('/platform/siteCug/schoolUnionAudit/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
}).finally(() => {
|
||||
this.revokeLoading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
querySiteType() {
|
||||
this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
|
||||
this.typeOptions = res.data
|
||||
})
|
||||
},
|
||||
querySites() {
|
||||
this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
|
||||
this.siteOptions = res.data
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post(loc() + '/pageData', this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.rawTableData = Array.isArray(res.data.list) ? res.data.list : []
|
||||
this.tableData = this.buildTableData(this.rawTableData)
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.querySiteType()
|
||||
this.querySites()
|
||||
this.pageData()
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,68 @@
|
||||
const basicForm = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="110px" label-position="left">
|
||||
<el-form-item label="类型编码" prop="code">
|
||||
<el-input type="text" v-model="formData.code" maxlength="50"
|
||||
placeholder="请输入类型编码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型名称" prop="name">
|
||||
<el-input type="text" v-model="formData.name" maxlength="50"
|
||||
placeholder="请输入类型名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否启用" prop="enable">
|
||||
<el-switch
|
||||
v-model="formData.enable"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row class="mt10" justify="end" type="flex">
|
||||
<el-button @click="$emit('refresh')">取消</el-button>
|
||||
<el-button @click="onSubmit" type="primary">提交</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
enable: true
|
||||
},
|
||||
formRules: {
|
||||
name: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
code: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
enable: [{required: true, message: '必填', trigger: ['blur', 'change']}],
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
if (row && row.id) {
|
||||
this.formData = clone(row)
|
||||
}
|
||||
},
|
||||
onSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post("/platform/siteCug/function/type/submit", this.formData)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.$emit('refresh')
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称/编码">
|
||||
<el-input placeholder="请输入名称或编码" clearable v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20">
|
||||
<table-tool label="功能类型列表">
|
||||
<el-button type="primary" size="small" @click="onAdd">
|
||||
<i class="ti-plus"></i>
|
||||
新增类型
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" :size="tableSize">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||
<el-table-column
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:key="column.prop"
|
||||
:width="column.width"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns"
|
||||
>
|
||||
<template v-slot="{ row }" v-if="column.prop === 'enable'">
|
||||
<el-switch
|
||||
@change="switchChange(row)"
|
||||
v-model="row.enable"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="230">
|
||||
<template v-slot="{ row }">
|
||||
<el-button @click="onEdit(row)" size="mini" type="primary">编辑</el-button>
|
||||
<el-button @click="onDelete(row)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<basic-form ref="basicFormRef" @refresh="refresh"></basic-form>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('basicForm.js'){}#-->
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
"basic-form": basicForm,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableColumns: [
|
||||
{prop: 'code', label: '类型编码'},
|
||||
{prop: 'name', label: '类型名称'},
|
||||
{prop: 'enable', label: '是否启用'},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.doSearch()
|
||||
this.$refs.guava.index()
|
||||
},
|
||||
onAdd() {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.basicFormRef.onOpen()
|
||||
})
|
||||
},
|
||||
onEdit(row) {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.basicFormRef.onOpen(row)
|
||||
})
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm("您确定要删除吗, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
this.$axios.post("/platform/siteCug/function/type/delete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
switchChange(row) {
|
||||
this.$axios.post("/platform/siteCug/function/type/submit", row).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post(loc() + "/pageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,979 @@
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
noticeCollapsed: true,
|
||||
siteId: GetQueryString('siteId'),
|
||||
row: {},
|
||||
siteLoaded: false,
|
||||
clubOptions: [],
|
||||
yearlyReserve: false,
|
||||
scheduleDate: '',
|
||||
availabilityLoading: false,
|
||||
activeTimeGroupKey: '',
|
||||
selectedBlockKeys: [],
|
||||
timeLimitConfig: {
|
||||
filterHolidays: false,
|
||||
holidayList: [],
|
||||
notApplyTimeList: [],
|
||||
},
|
||||
availabilityData: {
|
||||
date: '',
|
||||
reserveTimeType: 1,
|
||||
reserveTimeTypeName: '分段预约',
|
||||
blocks: [],
|
||||
},
|
||||
formData: {
|
||||
siteId: '',
|
||||
applyUserId: '',
|
||||
applyUserName: '',
|
||||
applySex: '',
|
||||
applyLoginName: '',
|
||||
applyUnitId: '',
|
||||
applyUnitName: '',
|
||||
reserveType: '',
|
||||
applyUnionId: '',
|
||||
applyUnionName: '',
|
||||
clubId: '',
|
||||
clubName: '',
|
||||
applyMobile: '',
|
||||
reserveStartTime: '',
|
||||
reserveEndTime: '',
|
||||
yearlyReserveEndDate: '',
|
||||
joinCount: 1,
|
||||
applyCause: '',
|
||||
},
|
||||
showReserveTypePicker: false,
|
||||
showClubPicker: false,
|
||||
showScheduleDatePicker: false,
|
||||
showAvailabilityPopup: false,
|
||||
showYearlyEndDatePicker: false,
|
||||
schedulePickerDate: new Date(),
|
||||
yearlyReservePickerDate: new Date(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
canApplyUnion() {
|
||||
return this.$auth.hasPermission('siteCug.apply.union')
|
||||
},
|
||||
canApplyClub() {
|
||||
return this.$auth.hasPermission('siteCug.apply.club')
|
||||
},
|
||||
availableReserveTypes() {
|
||||
const result = []
|
||||
if (this.canApplyUnion) {
|
||||
result.push('union')
|
||||
}
|
||||
if (this.canApplyClub) {
|
||||
result.push('club')
|
||||
}
|
||||
return result
|
||||
},
|
||||
availableReserveTypeOptions() {
|
||||
const result = []
|
||||
if (this.canApplyUnion) {
|
||||
result.push({ value: 'union', label: '分工会预约' })
|
||||
}
|
||||
if (this.canApplyClub) {
|
||||
result.push({ value: 'club', label: '协会预约' })
|
||||
}
|
||||
return result
|
||||
},
|
||||
reserveTypeColumns() {
|
||||
return this.availableReserveTypeOptions.map(item => item.label)
|
||||
},
|
||||
reserveTypeText() {
|
||||
const current = this.availableReserveTypeOptions.find(item => item.value === this.formData.reserveType)
|
||||
return current ? current.label : ''
|
||||
},
|
||||
clubColumns() {
|
||||
return this.clubOptions.map(item => item.clubName)
|
||||
},
|
||||
availabilityBlocks() {
|
||||
return Array.isArray(this.availabilityData.blocks) ? this.availabilityData.blocks : []
|
||||
},
|
||||
isSegmentedMode() {
|
||||
return Number(this.availabilityData.reserveTimeType || this.row.reserveTimeType || 1) === 1
|
||||
},
|
||||
reserveModeText() {
|
||||
return this.availabilityData.reserveTimeTypeName || (this.isSegmentedMode ? '分段预约' : '全天候预约')
|
||||
},
|
||||
timeGroupTabs() {
|
||||
const result = []
|
||||
const used = {}
|
||||
this.availabilityBlocks.forEach((block) => {
|
||||
const key = this.getBlockGroupKey(block)
|
||||
if (!key || used[key]) {
|
||||
return
|
||||
}
|
||||
used[key] = true
|
||||
result.push({
|
||||
key: key,
|
||||
label: this.getBlockGroupLabel(block),
|
||||
})
|
||||
})
|
||||
return result
|
||||
},
|
||||
displayedAvailabilityBlocks() {
|
||||
if (!this.activeTimeGroupKey) {
|
||||
return this.availabilityBlocks
|
||||
}
|
||||
return this.availabilityBlocks.filter(block => this.getBlockGroupKey(block) === this.activeTimeGroupKey)
|
||||
},
|
||||
selectionSummary() {
|
||||
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
|
||||
return ''
|
||||
}
|
||||
return this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
|
||||
},
|
||||
scheduleMinDate() {
|
||||
return this.$moment().startOf('day').toDate()
|
||||
},
|
||||
scheduleMaxDate() {
|
||||
return this.$moment().add(365, 'day').endOf('day').toDate()
|
||||
},
|
||||
yearlyReserveMinDate() {
|
||||
const base = this.formData.reserveStartTime
|
||||
? this.$moment(this.formData.reserveStartTime)
|
||||
: (this.scheduleDate ? this.$moment(this.scheduleDate, 'YYYY-MM-DD') : this.$moment())
|
||||
return base.startOf('day').toDate()
|
||||
},
|
||||
yearlyReserveMaxDate() {
|
||||
return this.$moment().add(2, 'year').endOf('year').toDate()
|
||||
},
|
||||
yearlyReserveDates() {
|
||||
if (!this.yearlyReserve || !this.formData.reserveStartTime || !this.formData.reserveEndTime || !this.formData.yearlyReserveEndDate) {
|
||||
return []
|
||||
}
|
||||
const start = this.$moment(this.formData.reserveStartTime)
|
||||
const end = this.$moment(this.formData.reserveEndTime)
|
||||
const reserveEndDate = this.$moment(this.formData.yearlyReserveEndDate, 'YYYY-MM-DD')
|
||||
if (!start.isValid() || !end.isValid() || !end.isAfter(start) || !reserveEndDate.isValid() || reserveEndDate.isBefore(start, 'day')) {
|
||||
return []
|
||||
}
|
||||
const result = []
|
||||
const current = start.clone().startOf('day')
|
||||
const reserveEndDay = reserveEndDate.clone().startOf('day')
|
||||
const targetWeekDay = start.day()
|
||||
const startClock = start.format('HH:mm:ss')
|
||||
const endClock = end.format('HH:mm:ss')
|
||||
while (current.isSameOrBefore(reserveEndDay, 'day')) {
|
||||
if (current.day() === targetWeekDay) {
|
||||
const day = current.format('YYYY-MM-DD')
|
||||
const startTime = day + ' ' + startClock
|
||||
const endTime = day + ' ' + endClock
|
||||
if (this.validateTimeLimit(false, false, startTime, endTime)) {
|
||||
result.push(day)
|
||||
}
|
||||
}
|
||||
current.add(1, 'day')
|
||||
}
|
||||
return result
|
||||
},
|
||||
batchReserveSummary() {
|
||||
if (!this.yearlyReserve) {
|
||||
return ''
|
||||
}
|
||||
if (!this.formData.yearlyReserveEndDate) {
|
||||
return '请选择批量预约截止日期'
|
||||
}
|
||||
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
|
||||
return '请先选择预约时间段,再设置批量预约截止日期'
|
||||
}
|
||||
if (!this.yearlyReserveDates.length) {
|
||||
return '当前截止日期内没有可批量预约的同星期时段'
|
||||
}
|
||||
return '将从 ' + this.yearlyReserveDates[0] + ' 开始,自动预约到 ' + this.formData.yearlyReserveEndDate
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
yearlyReserve(val) {
|
||||
if (val) {
|
||||
this.syncYearlyReserveEndDate(true)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
getDefaultReserveType() {
|
||||
return this.availableReserveTypes[0] || ''
|
||||
},
|
||||
buildDefaultFormData() {
|
||||
const user = this.$store.state.user || {}
|
||||
const reserveType = this.getDefaultReserveType()
|
||||
return {
|
||||
siteId: this.row.id || '',
|
||||
applyUserId: user.id || '',
|
||||
applyUserName: user.username || '',
|
||||
applySex: user.sex || '',
|
||||
applyLoginName: user.loginname || '',
|
||||
applyUnitId: user.unit ? user.unit.id : '',
|
||||
applyUnitName: user.unit ? user.unit.name : '',
|
||||
reserveType: reserveType,
|
||||
applyUnionId: reserveType === 'union' && user.union ? user.union.id : '',
|
||||
applyUnionName: reserveType === 'union' && user.union ? user.union.name : '',
|
||||
clubId: '',
|
||||
clubName: '',
|
||||
applyMobile: user.mobile || '',
|
||||
reserveStartTime: '',
|
||||
reserveEndTime: '',
|
||||
yearlyReserveEndDate: '',
|
||||
joinCount: 1,
|
||||
applyCause: '',
|
||||
}
|
||||
},
|
||||
isBlockedDay(momentValue) {
|
||||
return this.isDisabledDate(momentValue.toDate())
|
||||
},
|
||||
getInitialScheduleDate() {
|
||||
let current = this.$moment().startOf('day')
|
||||
for (let i = 0; i < 90; i++) {
|
||||
if (!this.isBlockedDay(current)) {
|
||||
return current.format('YYYY-MM-DD')
|
||||
}
|
||||
current = current.add(1, 'day')
|
||||
}
|
||||
return this.$moment().add(1, 'day').format('YYYY-MM-DD')
|
||||
},
|
||||
getDefaultYearlyReserveEndDate() {
|
||||
const base = this.formData.reserveStartTime
|
||||
? this.$moment(this.formData.reserveStartTime)
|
||||
: (this.scheduleDate ? this.$moment(this.scheduleDate, 'YYYY-MM-DD') : this.$moment())
|
||||
if (!base.isValid()) {
|
||||
return this.$moment().endOf('month').format('YYYY-MM-DD')
|
||||
}
|
||||
return base.clone().endOf('month').format('YYYY-MM-DD')
|
||||
},
|
||||
syncYearlyReserveEndDate(force = false) {
|
||||
const defaultDate = this.getDefaultYearlyReserveEndDate()
|
||||
if (force || !this.formData.yearlyReserveEndDate) {
|
||||
this.$set(this.formData, 'yearlyReserveEndDate', defaultDate)
|
||||
return
|
||||
}
|
||||
if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) {
|
||||
this.$set(this.formData, 'yearlyReserveEndDate', defaultDate)
|
||||
}
|
||||
},
|
||||
openReserveTypePicker() {
|
||||
if (!this.reserveTypeColumns.length) {
|
||||
this.$toast('当前没有可用的预约类型')
|
||||
return
|
||||
}
|
||||
this.showReserveTypePicker = true
|
||||
},
|
||||
async onReserveTypeConfirm(value, index) {
|
||||
this.showReserveTypePicker = false
|
||||
const option = this.availableReserveTypeOptions[index]
|
||||
if (!option) {
|
||||
return
|
||||
}
|
||||
this.$set(this.formData, 'reserveType', option.value)
|
||||
await this.reserveTypeChange(option.value)
|
||||
},
|
||||
async reserveTypeChange(value) {
|
||||
const user = this.$store.state.user || {}
|
||||
if (!this.availableReserveTypes.includes(value)) {
|
||||
this.formData.reserveType = this.getDefaultReserveType()
|
||||
return
|
||||
}
|
||||
if (value === 'union') {
|
||||
this.formData.applyUnionId = user.union ? user.union.id : ''
|
||||
this.formData.applyUnionName = user.union ? user.union.name : ''
|
||||
this.formData.clubId = ''
|
||||
this.formData.clubName = ''
|
||||
return
|
||||
}
|
||||
if (value === 'club') {
|
||||
this.formData.applyUnionId = ''
|
||||
this.formData.applyUnionName = ''
|
||||
this.formData.clubId = ''
|
||||
this.formData.clubName = ''
|
||||
await this.loadManagedClubs()
|
||||
}
|
||||
},
|
||||
async loadManagedClubs() {
|
||||
try {
|
||||
const res = await this.$axios.post('/platform/club/examine/apply/getClubsByRole')
|
||||
this.clubOptions = Array.isArray(res.data) ? res.data : []
|
||||
this.syncManagedClubSelection()
|
||||
if (!this.clubOptions.length) {
|
||||
this.$toast('您当前没有可预约的协会管理权限')
|
||||
}
|
||||
} catch (e) {
|
||||
this.clubOptions = []
|
||||
this.formData.clubId = ''
|
||||
this.formData.clubName = ''
|
||||
this.$toast('协会列表加载失败,请稍后重试')
|
||||
}
|
||||
},
|
||||
syncManagedClubSelection() {
|
||||
if (!this.clubOptions.length) {
|
||||
this.formData.clubId = ''
|
||||
this.formData.clubName = ''
|
||||
return
|
||||
}
|
||||
const currentClub = this.clubOptions.find(item => item.id === this.formData.clubId)
|
||||
if (currentClub) {
|
||||
this.formData.clubName = currentClub.clubName || ''
|
||||
return
|
||||
}
|
||||
if (this.clubOptions.length === 1) {
|
||||
this.formData.clubId = this.clubOptions[0].id
|
||||
this.formData.clubName = this.clubOptions[0].clubName || ''
|
||||
return
|
||||
}
|
||||
this.formData.clubId = ''
|
||||
this.formData.clubName = ''
|
||||
},
|
||||
async openClubPicker() {
|
||||
if (this.formData.reserveType !== 'club') {
|
||||
this.$toast('请先选择协会预约')
|
||||
return
|
||||
}
|
||||
if (!this.clubOptions.length) {
|
||||
await this.loadManagedClubs()
|
||||
}
|
||||
if (!this.clubOptions.length) {
|
||||
return
|
||||
}
|
||||
if (this.clubOptions.length === 1 && this.formData.clubId) {
|
||||
return
|
||||
}
|
||||
this.showClubPicker = true
|
||||
},
|
||||
onClubConfirm(value, index) {
|
||||
this.showClubPicker = false
|
||||
const club = this.clubOptions[index]
|
||||
this.formData.clubId = club ? club.id : ''
|
||||
this.formData.clubName = club ? club.clubName : value
|
||||
},
|
||||
openScheduleDatePicker() {
|
||||
const target = this.scheduleDate
|
||||
? this.$moment(this.scheduleDate, 'YYYY-MM-DD')
|
||||
: this.$moment(this.getInitialScheduleDate(), 'YYYY-MM-DD')
|
||||
this.schedulePickerDate = target.isValid() ? target.toDate() : new Date()
|
||||
this.showScheduleDatePicker = true
|
||||
},
|
||||
async onScheduleDateConfirm(value) {
|
||||
this.showScheduleDatePicker = false
|
||||
const target = this.$moment(value)
|
||||
if (!target.isValid()) {
|
||||
this.$toast('请选择正确的预约日期')
|
||||
return
|
||||
}
|
||||
if (this.isBlockedDay(target)) {
|
||||
this.$toast('该日期不可预约,请重新选择')
|
||||
return
|
||||
}
|
||||
const day = target.format('YYYY-MM-DD')
|
||||
this.schedulePickerDate = target.toDate()
|
||||
this.scheduleDate = day
|
||||
this.clearSelection(true)
|
||||
await this.queryAvailability(day)
|
||||
this.showAvailabilityPopup = true
|
||||
},
|
||||
async openAvailabilityPopup() {
|
||||
if (!this.scheduleDate) {
|
||||
this.$toast('请先选择预约日期')
|
||||
this.openScheduleDatePicker()
|
||||
return
|
||||
}
|
||||
if (!this.availabilityData.date || this.availabilityData.date !== this.scheduleDate) {
|
||||
await this.queryAvailability(this.scheduleDate)
|
||||
}
|
||||
this.showAvailabilityPopup = true
|
||||
},
|
||||
async queryAvailability(date = this.scheduleDate) {
|
||||
const targetDate = date || this.scheduleDate
|
||||
if (!targetDate || !this.row.id) {
|
||||
this.availabilityData = {
|
||||
date: targetDate || '',
|
||||
reserveTimeType: Number(this.row.reserveTimeType || 1),
|
||||
reserveTimeTypeName: Number(this.row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约',
|
||||
blocks: [],
|
||||
}
|
||||
this.resetActiveTimeGroup()
|
||||
return
|
||||
}
|
||||
this.availabilityLoading = true
|
||||
try {
|
||||
const res = await this.$axios.post('/platform/siteCug/apply/availability', {
|
||||
siteId: this.row.id,
|
||||
date: targetDate,
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
this.availabilityData = {
|
||||
date: res.data.date || targetDate,
|
||||
reserveTimeType: Number(res.data.reserveTimeType || this.row.reserveTimeType || 1),
|
||||
reserveTimeTypeName: res.data.reserveTimeTypeName || (Number(res.data.reserveTimeType || this.row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约'),
|
||||
blocks: Array.isArray(res.data.blocks) ? res.data.blocks : [],
|
||||
}
|
||||
} else {
|
||||
this.availabilityData = {
|
||||
date: targetDate,
|
||||
reserveTimeType: Number(this.row.reserveTimeType || 1),
|
||||
reserveTimeTypeName: Number(this.row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约',
|
||||
blocks: [],
|
||||
}
|
||||
this.$toast((res && res.msg) || '预约时段加载失败')
|
||||
}
|
||||
} catch (e) {
|
||||
this.availabilityData = {
|
||||
date: targetDate,
|
||||
reserveTimeType: Number(this.row.reserveTimeType || 1),
|
||||
reserveTimeTypeName: Number(this.row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约',
|
||||
blocks: [],
|
||||
}
|
||||
this.$toast('预约时段加载失败,请稍后重试')
|
||||
} finally {
|
||||
this.availabilityLoading = false
|
||||
this.resetActiveTimeGroup()
|
||||
this.syncSelectionFromFields()
|
||||
}
|
||||
},
|
||||
resetActiveTimeGroup() {
|
||||
const selectedBlock = this.availabilityBlocks.find(block => this.selectedBlockKeys.includes(block.key))
|
||||
if (selectedBlock) {
|
||||
this.activeTimeGroupKey = this.getBlockGroupKey(selectedBlock)
|
||||
return
|
||||
}
|
||||
this.activeTimeGroupKey = this.timeGroupTabs.length ? this.timeGroupTabs[0].key : ''
|
||||
},
|
||||
getBlockGroupKey(block) {
|
||||
if (!block) {
|
||||
return ''
|
||||
}
|
||||
if (this.isSegmentedMode) {
|
||||
const groupIndex = block.groupIndex === undefined || block.groupIndex === null ? 0 : block.groupIndex
|
||||
return 'group_' + groupIndex
|
||||
}
|
||||
const hour = this.$moment(block.startDateTime).hour()
|
||||
return hour < 12 ? 'am' : 'pm'
|
||||
},
|
||||
getBlockGroupLabel(block) {
|
||||
if (!block) {
|
||||
return ''
|
||||
}
|
||||
if (this.isSegmentedMode) {
|
||||
const groupLabel = block.groupLabel || '场次'
|
||||
return groupLabel.split(' / ')[0]
|
||||
}
|
||||
const hour = this.$moment(block.startDateTime).hour()
|
||||
return hour < 12 ? '上午' : '下午'
|
||||
},
|
||||
getSelectedIndices() {
|
||||
return this.availabilityBlocks
|
||||
.map((block, index) => this.selectedBlockKeys.includes(block.key) ? index : -1)
|
||||
.filter(index => index > -1)
|
||||
},
|
||||
getBlockClass(block) {
|
||||
const result = ['slot-block']
|
||||
if (this.selectedBlockKeys.includes(block.key)) {
|
||||
result.push('slot-block--selected')
|
||||
return result
|
||||
}
|
||||
if (block.status === 'available') {
|
||||
result.push('slot-block--available')
|
||||
return result
|
||||
}
|
||||
if (block.status === 'reserved') {
|
||||
result.push('slot-block--reserved')
|
||||
return result
|
||||
}
|
||||
result.push('slot-block--closed')
|
||||
return result
|
||||
},
|
||||
onAvailabilityBlockClick(block) {
|
||||
if (!block || !block.key) {
|
||||
return
|
||||
}
|
||||
if (block.status !== 'available' && !this.selectedBlockKeys.includes(block.key)) {
|
||||
this.$toast(block.status === 'reserved' ? '该时段已被预约,请选择其他时段' : '该时段当前不可预约')
|
||||
return
|
||||
}
|
||||
const index = this.availabilityBlocks.findIndex(item => item.key === block.key)
|
||||
if (index < 0) {
|
||||
return
|
||||
}
|
||||
const selectedIndices = this.getSelectedIndices()
|
||||
if (selectedIndices.length) {
|
||||
const currentGroupKey = this.getBlockGroupKey(this.availabilityBlocks[selectedIndices[0]])
|
||||
const targetGroupKey = this.getBlockGroupKey(block)
|
||||
if (currentGroupKey && targetGroupKey && currentGroupKey !== targetGroupKey) {
|
||||
this.applySelectionRange(index, index)
|
||||
return
|
||||
}
|
||||
}
|
||||
this.toggleContinuousSelection(index)
|
||||
},
|
||||
toggleContinuousSelection(index) {
|
||||
const selectedIndices = this.getSelectedIndices()
|
||||
if (!selectedIndices.length) {
|
||||
this.applySelectionRange(index, index)
|
||||
return
|
||||
}
|
||||
const selectedStart = selectedIndices[0]
|
||||
const selectedEnd = selectedIndices[selectedIndices.length - 1]
|
||||
if (selectedIndices.includes(index)) {
|
||||
if (selectedIndices.length === 1) {
|
||||
this.clearSelection(true)
|
||||
return
|
||||
}
|
||||
if (index === selectedStart) {
|
||||
this.applySelectionRange(selectedStart + 1, selectedEnd)
|
||||
return
|
||||
}
|
||||
if (index === selectedEnd) {
|
||||
this.applySelectionRange(selectedStart, selectedEnd - 1)
|
||||
return
|
||||
}
|
||||
this.applySelectionRange(index, index)
|
||||
return
|
||||
}
|
||||
const nextStart = Math.min(index, selectedStart)
|
||||
const nextEnd = Math.max(index, selectedEnd)
|
||||
if (!this.canSelectRange(nextStart, nextEnd)) {
|
||||
this.$toast(this.isSegmentedMode
|
||||
? '分段预约只能连续选择一个或多个时段,不能跨间隔选择'
|
||||
: '全天候预约只能连续选择一个或多个时段,不能跨间隔选择')
|
||||
return
|
||||
}
|
||||
this.applySelectionRange(nextStart, nextEnd)
|
||||
},
|
||||
canSelectRange(startIndex, endIndex) {
|
||||
if (startIndex < 0 || endIndex >= this.availabilityBlocks.length || startIndex > endIndex) {
|
||||
return false
|
||||
}
|
||||
const firstBlock = this.availabilityBlocks[startIndex]
|
||||
const groupKey = this.getBlockGroupKey(firstBlock)
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
const block = this.availabilityBlocks[i]
|
||||
if (!block || block.status !== 'available') {
|
||||
return false
|
||||
}
|
||||
if (this.getBlockGroupKey(block) !== groupKey) {
|
||||
return false
|
||||
}
|
||||
if (i > startIndex) {
|
||||
const prev = this.availabilityBlocks[i - 1]
|
||||
if (!prev || prev.endDateTime !== block.startDateTime) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
applySelectionRange(startIndex, endIndex) {
|
||||
const selected = this.availabilityBlocks.slice(startIndex, endIndex + 1)
|
||||
this.selectedBlockKeys = selected.map(item => item.key)
|
||||
if (!selected.length) {
|
||||
return
|
||||
}
|
||||
this.formData.reserveStartTime = selected[0].startDateTime
|
||||
this.formData.reserveEndTime = selected[selected.length - 1].endDateTime
|
||||
this.scheduleDate = selected[0].startDateTime.slice(0, 10)
|
||||
this.schedulePickerDate = this.$moment(this.scheduleDate, 'YYYY-MM-DD').toDate()
|
||||
this.activeTimeGroupKey = this.getBlockGroupKey(selected[0])
|
||||
this.syncYearlyReserveEndDate()
|
||||
},
|
||||
clearSelection(clearTimeFields = false) {
|
||||
this.selectedBlockKeys = []
|
||||
if (clearTimeFields) {
|
||||
this.formData.reserveStartTime = ''
|
||||
this.formData.reserveEndTime = ''
|
||||
}
|
||||
},
|
||||
confirmAvailabilitySelection() {
|
||||
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime || !this.selectedBlockKeys.length) {
|
||||
this.$toast('请选择一个或多个连续时段')
|
||||
return
|
||||
}
|
||||
if (!this.validateGraphSelection()) {
|
||||
return
|
||||
}
|
||||
this.showAvailabilityPopup = false
|
||||
},
|
||||
findRangeIndicesByFields(startTime, endTime) {
|
||||
if (!startTime || !endTime) {
|
||||
return null
|
||||
}
|
||||
for (let i = 0; i < this.availabilityBlocks.length; i++) {
|
||||
const block = this.availabilityBlocks[i]
|
||||
if (block.status !== 'available' || block.startDateTime !== startTime) {
|
||||
continue
|
||||
}
|
||||
let currentEnd = block.endDateTime
|
||||
if (currentEnd === endTime) {
|
||||
return [i, i]
|
||||
}
|
||||
for (let j = i + 1; j < this.availabilityBlocks.length; j++) {
|
||||
const prev = this.availabilityBlocks[j - 1]
|
||||
const next = this.availabilityBlocks[j]
|
||||
if (next.status !== 'available' || prev.endDateTime !== next.startDateTime) {
|
||||
break
|
||||
}
|
||||
if (this.getBlockGroupKey(prev) !== this.getBlockGroupKey(next)) {
|
||||
break
|
||||
}
|
||||
currentEnd = next.endDateTime
|
||||
if (currentEnd === endTime) {
|
||||
return [i, j]
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
syncSelectionFromFields() {
|
||||
const startTime = this.formData.reserveStartTime
|
||||
const endTime = this.formData.reserveEndTime
|
||||
if (!startTime || !endTime) {
|
||||
this.selectedBlockKeys = []
|
||||
this.resetActiveTimeGroup()
|
||||
return
|
||||
}
|
||||
if (startTime.slice(0, 10) !== this.scheduleDate || endTime.slice(0, 10) !== this.scheduleDate) {
|
||||
this.selectedBlockKeys = []
|
||||
this.resetActiveTimeGroup()
|
||||
return
|
||||
}
|
||||
const range = this.findRangeIndicesByFields(startTime, endTime)
|
||||
if (!range) {
|
||||
this.selectedBlockKeys = []
|
||||
this.resetActiveTimeGroup()
|
||||
return
|
||||
}
|
||||
const selected = this.availabilityBlocks.slice(range[0], range[1] + 1)
|
||||
this.selectedBlockKeys = selected.map(item => item.key)
|
||||
if (selected.length) {
|
||||
this.activeTimeGroupKey = this.getBlockGroupKey(selected[0])
|
||||
}
|
||||
},
|
||||
validateGraphSelection(showMessage = true, startTime = this.formData.reserveStartTime, endTime = this.formData.reserveEndTime, targetDay = this.scheduleDate) {
|
||||
if (!startTime || !endTime) {
|
||||
return true
|
||||
}
|
||||
if (!targetDay || startTime.slice(0, 10) !== targetDay || endTime.slice(0, 10) !== targetDay) {
|
||||
if (showMessage) {
|
||||
this.$toast('预约时间必须与当前选择的预约日期一致')
|
||||
}
|
||||
return false
|
||||
}
|
||||
const range = this.findRangeIndicesByFields(startTime, endTime)
|
||||
if (range) {
|
||||
return true
|
||||
}
|
||||
if (showMessage) {
|
||||
this.$toast(this.isSegmentedMode
|
||||
? '分段预约请按场次标签选择一个或多个连续时段'
|
||||
: '全天候预约请按上午或下午标签选择一个或多个连续时段')
|
||||
}
|
||||
return false
|
||||
},
|
||||
async normalizeReserveTypeData() {
|
||||
const user = this.$store.state.user || {}
|
||||
if (!this.availableReserveTypes.includes(this.formData.reserveType)) {
|
||||
this.formData.reserveType = this.getDefaultReserveType()
|
||||
}
|
||||
if (this.formData.reserveType === 'union') {
|
||||
this.formData.applyUnionId = user.union ? user.union.id : ''
|
||||
this.formData.applyUnionName = user.union ? user.union.name : ''
|
||||
this.formData.clubId = ''
|
||||
this.formData.clubName = ''
|
||||
return true
|
||||
}
|
||||
if (this.formData.reserveType === 'club') {
|
||||
if (!this.clubOptions.length) {
|
||||
await this.loadManagedClubs()
|
||||
}
|
||||
const club = this.clubOptions.find(item => item.id === this.formData.clubId)
|
||||
this.formData.clubName = club ? club.clubName : ''
|
||||
this.formData.applyUnionId = ''
|
||||
this.formData.applyUnionName = ''
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
normalizeTime(timeStr) {
|
||||
if (!timeStr) {
|
||||
return '00:00:00'
|
||||
}
|
||||
return timeStr.length === 5 ? timeStr + ':00' : timeStr
|
||||
},
|
||||
isDisabledDate(time) {
|
||||
const day = this.$moment(time).format('YYYY-MM-DD')
|
||||
const today = this.$moment().startOf('day')
|
||||
const currentDay = this.$moment(day)
|
||||
if (currentDay.isBefore(today)) {
|
||||
return true
|
||||
}
|
||||
if (currentDay.day() === 0 || currentDay.day() === 6) {
|
||||
return true
|
||||
}
|
||||
return this.timeLimitConfig.filterHolidays && this.timeLimitConfig.holidayList.includes(day)
|
||||
},
|
||||
getDisabledRangesByDay(dayStr) {
|
||||
const list = Array.isArray(this.timeLimitConfig.notApplyTimeList) ? this.timeLimitConfig.notApplyTimeList : []
|
||||
return list
|
||||
.filter(item => item && item.date && this.$moment(item.date).format('YYYY-MM-DD') === dayStr)
|
||||
.sort((a, b) => this.normalizeTime(a.startTime).localeCompare(this.normalizeTime(b.startTime)))
|
||||
},
|
||||
isDateTimeBlocked(dateTimeStr) {
|
||||
if (!dateTimeStr) {
|
||||
return false
|
||||
}
|
||||
const target = this.$moment(dateTimeStr)
|
||||
const dayStr = target.format('YYYY-MM-DD')
|
||||
if (target.day() === 0 || target.day() === 6) {
|
||||
return true
|
||||
}
|
||||
if (this.timeLimitConfig.filterHolidays && this.timeLimitConfig.holidayList.includes(dayStr)) {
|
||||
return true
|
||||
}
|
||||
const disabledRanges = this.getDisabledRangesByDay(dayStr)
|
||||
return disabledRanges.some(item => {
|
||||
const start = this.$moment(dayStr + ' ' + this.normalizeTime(item.startTime))
|
||||
const end = this.$moment(dayStr + ' ' + this.normalizeTime(item.endTime))
|
||||
return target.isSameOrAfter(start) && target.isBefore(end)
|
||||
})
|
||||
},
|
||||
hasHolidayInRange(start, end) {
|
||||
const current = start.clone().startOf('day')
|
||||
const endDay = end.clone().startOf('day')
|
||||
while (current.isSameOrBefore(endDay)) {
|
||||
if (current.day() === 0 || current.day() === 6) {
|
||||
return true
|
||||
}
|
||||
if (this.timeLimitConfig.filterHolidays && this.timeLimitConfig.holidayList.includes(current.format('YYYY-MM-DD'))) {
|
||||
return true
|
||||
}
|
||||
current.add(1, 'day')
|
||||
}
|
||||
return false
|
||||
},
|
||||
overlapsDisabledRange(start, end) {
|
||||
const list = Array.isArray(this.timeLimitConfig.notApplyTimeList) ? this.timeLimitConfig.notApplyTimeList : []
|
||||
return list.some(item => {
|
||||
if (!item || !item.date || !item.startTime || !item.endTime) {
|
||||
return false
|
||||
}
|
||||
const rangeStart = this.$moment(this.$moment(item.date).format('YYYY-MM-DD') + ' ' + this.normalizeTime(item.startTime))
|
||||
const rangeEnd = this.$moment(this.$moment(item.date).format('YYYY-MM-DD') + ' ' + this.normalizeTime(item.endTime))
|
||||
return start.isBefore(rangeEnd) && end.isAfter(rangeStart)
|
||||
})
|
||||
},
|
||||
showSubmitError(message) {
|
||||
return this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: message || '提交失败',
|
||||
}).catch(() => {})
|
||||
},
|
||||
validateTimeLimit(showMessage = true, useDialog = false, reserveStartTime = this.formData.reserveStartTime, reserveEndTime = this.formData.reserveEndTime) {
|
||||
if (!reserveStartTime || !reserveEndTime) {
|
||||
return true
|
||||
}
|
||||
const start = this.$moment(reserveStartTime)
|
||||
const end = this.$moment(reserveEndTime)
|
||||
const showError = (message) => {
|
||||
if (!showMessage) {
|
||||
return
|
||||
}
|
||||
if (useDialog) {
|
||||
this.showSubmitError(message)
|
||||
return
|
||||
}
|
||||
this.$toast(message)
|
||||
}
|
||||
if (!start.isValid() || !end.isValid()) {
|
||||
showError('请选择正确的预约时间')
|
||||
return false
|
||||
}
|
||||
if (!end.isAfter(start)) {
|
||||
showError('预约结束时间必须晚于开始时间')
|
||||
return false
|
||||
}
|
||||
if (start.format('YYYY-MM-DD') !== end.format('YYYY-MM-DD')) {
|
||||
showError('预约时间必须在同一天内')
|
||||
return false
|
||||
}
|
||||
if (this.isDateTimeBlocked(reserveStartTime) || this.isDateTimeBlocked(reserveEndTime)) {
|
||||
showError('预约时间不能选择周末、节假日或禁用时间')
|
||||
return false
|
||||
}
|
||||
if (this.hasHolidayInRange(start, end)) {
|
||||
showError('预约时间范围内包含周末或节假日,请重新选择')
|
||||
return false
|
||||
}
|
||||
if (this.overlapsDisabledRange(start, end)) {
|
||||
showError('预约时间范围与禁用时间冲突,请重新选择')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
openYearlyEndDatePicker() {
|
||||
if (!this.yearlyReserve) {
|
||||
return
|
||||
}
|
||||
this.syncYearlyReserveEndDate()
|
||||
const current = this.formData.yearlyReserveEndDate
|
||||
? this.$moment(this.formData.yearlyReserveEndDate, 'YYYY-MM-DD')
|
||||
: this.$moment(this.getDefaultYearlyReserveEndDate(), 'YYYY-MM-DD')
|
||||
this.yearlyReservePickerDate = current.isValid() ? current.toDate() : new Date()
|
||||
this.showYearlyEndDatePicker = true
|
||||
},
|
||||
onYearlyReserveEndDateConfirm(value) {
|
||||
this.showYearlyEndDatePicker = false
|
||||
this.yearlyReservePickerDate = value
|
||||
this.$set(this.formData, 'yearlyReserveEndDate', this.$moment(value).format('YYYY-MM-DD'))
|
||||
},
|
||||
validateYearlyReserve() {
|
||||
if (!this.yearlyReserve) {
|
||||
return true
|
||||
}
|
||||
if (!this.formData.yearlyReserveEndDate) {
|
||||
this.$toast('请选择批量预约截止日期')
|
||||
return false
|
||||
}
|
||||
if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) {
|
||||
this.$toast('批量预约截止日期不能早于开始日期')
|
||||
return false
|
||||
}
|
||||
if (!this.yearlyReserveDates.length) {
|
||||
this.$toast('当前截止日期内没有可批量预约的同星期时段')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
buildSubmitApi() {
|
||||
return this.yearlyReserve ? '/platform/siteCug/apply/submitYearly' : '/platform/siteCug/apply/submit'
|
||||
},
|
||||
buildSubmitConfirmMessage() {
|
||||
const baseText = '您已选择 ' + this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
|
||||
if (!this.yearlyReserve) {
|
||||
return baseText + ',是否确定选择并提交预约?'
|
||||
}
|
||||
return baseText + '。系统将继续预约到 ' + this.formData.yearlyReserveEndDate + ',是否确定选择并提交预约?'
|
||||
},
|
||||
async onSubmit() {
|
||||
this.$refs.formRef.validate().then(async () => {
|
||||
if (!this.formData.reserveType) {
|
||||
await this.showSubmitError('请选择预约类型')
|
||||
return
|
||||
}
|
||||
await this.normalizeReserveTypeData()
|
||||
if (this.formData.reserveType === 'union' && !this.formData.applyUnionId) {
|
||||
await this.showSubmitError('当前用户未关联分工会,不能发起分工会预约')
|
||||
return
|
||||
}
|
||||
if (this.formData.reserveType === 'club' && !this.formData.clubId) {
|
||||
await this.showSubmitError('请选择您管理的协会')
|
||||
return
|
||||
}
|
||||
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
|
||||
await this.showSubmitError('请选择预约时间段')
|
||||
return
|
||||
}
|
||||
const start = this.$moment(this.formData.reserveStartTime)
|
||||
const end = this.$moment(this.formData.reserveEndTime)
|
||||
if (!start.isAfter(this.$moment())) {
|
||||
await this.showSubmitError('预约开始时间必须晚于当前时间')
|
||||
return
|
||||
}
|
||||
if (!end.isAfter(start)) {
|
||||
await this.showSubmitError('预约结束时间必须晚于开始时间')
|
||||
return
|
||||
}
|
||||
if (!this.validateGraphSelection()) {
|
||||
return
|
||||
}
|
||||
if (!this.validateTimeLimit(true, true)) {
|
||||
return
|
||||
}
|
||||
if (!this.validateYearlyReserve()) {
|
||||
return
|
||||
}
|
||||
this.$dialog.confirm({
|
||||
title: '提示',
|
||||
message: this.buildSubmitConfirmMessage(),
|
||||
}).then(() => {
|
||||
const loading = this.$toast.loading({
|
||||
message: '提交中...',
|
||||
forbidClick: true,
|
||||
overlay: true,
|
||||
duration: 0,
|
||||
})
|
||||
this.$axios.post(this.buildSubmitApi(), {
|
||||
data: JSON.stringify(this.formData),
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$toast.success(res.msg || '提交成功')
|
||||
this.$pjaxReplace('/platform/siteCug/apply/h5')
|
||||
} else {
|
||||
this.showSubmitError(res.msg || '提交失败')
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.showSubmitError((err && err.msg) || '提交失败')
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}).catch(() => {})
|
||||
}).catch(() => {})
|
||||
},
|
||||
async queryTimeLimitConfig() {
|
||||
const fallback = {
|
||||
filterHolidays: !!this.row.filterHolidays,
|
||||
holidayList: [],
|
||||
notApplyTimeList: Array.isArray(this.row.notApplyTimeList) ? this.row.notApplyTimeList : [],
|
||||
}
|
||||
try {
|
||||
const res = await this.$axios.post('/platform/siteCug/apply/timeLimitConfig', {
|
||||
siteId: this.row.id,
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
this.timeLimitConfig = {
|
||||
filterHolidays: !!res.data.filterHolidays,
|
||||
holidayList: Array.isArray(res.data.holidayList) ? res.data.holidayList : [],
|
||||
notApplyTimeList: Array.isArray(res.data.notApplyTimeList) ? res.data.notApplyTimeList : [],
|
||||
}
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
this.timeLimitConfig = fallback
|
||||
},
|
||||
async init() {
|
||||
if (!this.siteId) {
|
||||
this.$toast('未获取到场地信息')
|
||||
this.historyBack()
|
||||
return
|
||||
}
|
||||
let siteRes
|
||||
try {
|
||||
siteRes = await this.$axios.post('/platform/siteCug/manage/info', { id: this.siteId })
|
||||
} catch (e) {
|
||||
this.$toast('场地信息加载失败')
|
||||
this.historyBack()
|
||||
return
|
||||
}
|
||||
if (siteRes.code !== 0 || !siteRes.data) {
|
||||
this.$toast(siteRes.msg || '场地信息加载失败')
|
||||
this.historyBack()
|
||||
return
|
||||
}
|
||||
this.row = siteRes.data
|
||||
this.formData = this.buildDefaultFormData()
|
||||
this.syncYearlyReserveEndDate(true)
|
||||
await this.queryTimeLimitConfig()
|
||||
if (this.formData.reserveType) {
|
||||
await this.reserveTypeChange(this.formData.reserveType)
|
||||
}
|
||||
this.scheduleDate = this.getInitialScheduleDate()
|
||||
this.schedulePickerDate = this.$moment(this.scheduleDate, 'YYYY-MM-DD').toDate()
|
||||
await this.queryAvailability(this.scheduleDate)
|
||||
this.siteLoaded = true
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,583 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
padding: 12px 12px 96px;
|
||||
background: #f5f7fb;
|
||||
min-height: calc(100vh - 46px);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.panel-card {
|
||||
margin-bottom: 12px;
|
||||
overflow: hidden;
|
||||
border-radius: 22px;
|
||||
background: #ffffff;
|
||||
box-shadow:
|
||||
0 2px 6px rgba(15, 23, 42, 0.04),
|
||||
0 12px 28px rgba(15, 23, 42, 0.08),
|
||||
0 0 0 1px rgba(226, 232, 240, 0.95);
|
||||
}
|
||||
|
||||
.notice-card__header {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
border: none;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #0f172a;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.notice-card__icon {
|
||||
color: #64748b;
|
||||
font-size: 16px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.notice-card__icon--expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.notice-card__body {
|
||||
padding: 0 16px 14px;
|
||||
border-top: 1px solid #eef2f7;
|
||||
}
|
||||
|
||||
.notice-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.notice-item {
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.notice-item__label {
|
||||
color: #334155;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.notice-item__value {
|
||||
margin-top: 4px;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.disabled-time-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.disabled-time-item {
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
color: #475569;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.section-tip {
|
||||
padding: 0 16px 14px;
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.selection-summary {
|
||||
margin: 0 16px 14px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(37, 99, 235, 0.08);
|
||||
color: #2563eb;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.batch-panel {
|
||||
padding: 0 16px 14px;
|
||||
}
|
||||
|
||||
.batch-summary {
|
||||
margin-top: 10px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 12px;
|
||||
background: rgba(37, 99, 235, 0.08);
|
||||
color: #2563eb;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.availability-popup {
|
||||
height: 82vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.availability-popup__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #eef2f7;
|
||||
}
|
||||
|
||||
.availability-popup__title {
|
||||
color: #0f172a;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.availability-popup__subtitle {
|
||||
margin-top: 4px;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.availability-popup__close {
|
||||
color: #94a3b8;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.availability-popup__tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 12px 16px 0;
|
||||
overflow-x: auto;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.availability-tab {
|
||||
min-width: 72px;
|
||||
height: 34px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 999px;
|
||||
background: #ffffff;
|
||||
color: #475569;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.availability-tab--active {
|
||||
border-color: #2563eb;
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 8px 16px rgba(37, 99, 235, 0.22);
|
||||
}
|
||||
|
||||
.availability-popup__legend {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
padding: 12px 16px 0;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.availability-legend__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.availability-legend__dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.availability-legend__dot--available {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
.availability-legend__dot--reserved {
|
||||
background: #9ca3af;
|
||||
}
|
||||
|
||||
.availability-legend__dot--selected {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.availability-legend__dot--closed {
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.availability-popup__body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.slot-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.slot-block {
|
||||
min-height: 48px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #dbe4ee;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
color: #475569;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.slot-block--available {
|
||||
border-color: #86efac;
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.slot-block--reserved {
|
||||
background: #e5e7eb;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.slot-block--closed {
|
||||
background: #f8fafc;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.slot-block--selected {
|
||||
border-color: #2563eb;
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 10px 20px rgba(37, 99, 235, 0.24);
|
||||
}
|
||||
|
||||
.availability-popup__footer {
|
||||
padding: 12px 16px calc(env(safe-area-inset-bottom) + 12px);
|
||||
background: #ffffff;
|
||||
border-top: 1px solid #eef2f7;
|
||||
}
|
||||
|
||||
.availability-popup__summary {
|
||||
margin-bottom: 12px;
|
||||
color: #475569;
|
||||
font-size: 12px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.availability-popup__actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.availability-popup__actions .van-button {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.footer-actions {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 10px 12px calc(env(safe-area-inset-bottom) + 10px);
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 -8px 24px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.footer-actions .van-button {
|
||||
flex: 1;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
/deep/ .panel-card .van-cell-group {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/deep/ .panel-card .van-cell-group__title {
|
||||
padding: 16px 16px 8px;
|
||||
margin: 0;
|
||||
color: #0f172a;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/deep/ .panel-card .van-cell {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
/deep/ .panel-card .van-field__label,
|
||||
/deep/ .panel-card .van-cell__title {
|
||||
color: #334155;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/deep/ .direction-column-cell .van-cell__value {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="场馆申请" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<div class="page-container" v-if="siteLoaded">
|
||||
<div class="panel-card">
|
||||
<button type="button" class="notice-card__header" @click="noticeCollapsed = !noticeCollapsed">
|
||||
<span>预约须知</span>
|
||||
<van-icon :class="['notice-card__icon', noticeCollapsed ? '' : 'notice-card__icon--expanded']" name="arrow-down"></van-icon>
|
||||
</button>
|
||||
<div v-if="!noticeCollapsed" class="notice-card__body">
|
||||
<div class="notice-list">
|
||||
<div class="notice-item">
|
||||
<div class="notice-item__label">可预约日期</div>
|
||||
<div class="notice-item__value">仅限工作日</div>
|
||||
</div>
|
||||
<div class="notice-item">
|
||||
<div class="notice-item__label">节假日限制</div>
|
||||
<div class="notice-item__value">{{ timeLimitConfig.filterHolidays ? '节假日不可预约' : '不排除节假日' }}</div>
|
||||
</div>
|
||||
<div class="notice-item">
|
||||
<div class="notice-item__label">禁用时段</div>
|
||||
<div class="notice-item__value">
|
||||
<div v-if="timeLimitConfig.notApplyTimeList.length" class="disabled-time-list">
|
||||
<div
|
||||
class="disabled-time-item"
|
||||
v-for="(item, index) in timeLimitConfig.notApplyTimeList"
|
||||
:key="item.date + item.startTime + item.endTime + index">
|
||||
{{ item.date }} {{ item.startTime }} - {{ item.endTime }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>暂无禁用时段</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-form ref="formRef" class="form-container" :show-error-message="false">
|
||||
<div class="panel-card">
|
||||
<van-cell-group title="申请信息">
|
||||
<van-field
|
||||
:value="reserveTypeText"
|
||||
label="预约类型"
|
||||
name="reserveType"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
required
|
||||
placeholder="请选择预约类型"
|
||||
@click="openReserveTypePicker"
|
||||
:rules="[{ required: true, message: '请选择预约类型' }]">
|
||||
</van-field>
|
||||
<van-field v-model="formData.applyUserName" label="预约人" name="applyUserName" readonly
|
||||
:rules="[{ required: true, message: '请确认预约人' }]"></van-field>
|
||||
<van-field v-model="formData.applyLoginName" label="工号" name="applyLoginName" readonly
|
||||
:rules="[{ required: true, message: '请确认工号' }]"></van-field>
|
||||
<van-field v-model="formData.applyUnitName" label="所属单位" name="applyUnitName" readonly
|
||||
:rules="[{ required: true, message: '请确认所属单位' }]"></van-field>
|
||||
<van-field
|
||||
v-if="formData.reserveType === 'union'"
|
||||
v-model="formData.applyUnionName"
|
||||
label="分工会"
|
||||
name="applyUnionName"
|
||||
readonly
|
||||
placeholder="自动读取当前登录人的分工会">
|
||||
</van-field>
|
||||
<van-field
|
||||
v-if="formData.reserveType === 'club'"
|
||||
v-model="formData.clubName"
|
||||
label="协会"
|
||||
name="clubName"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
required
|
||||
placeholder="请选择您管理的协会"
|
||||
@click="openClubPicker"
|
||||
:rules="[{ required: true, message: '请选择您管理的协会' }]">
|
||||
</van-field>
|
||||
<van-field v-model="formData.applyMobile" label="联系电话" name="applyMobile" type="tel" maxlength="11"
|
||||
required placeholder="请输入联系电话"
|
||||
:rules="[{ required: true, message: '请输入联系电话' }]"></van-field>
|
||||
<van-field
|
||||
:value="scheduleDate"
|
||||
label="预约日期"
|
||||
name="scheduleDate"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
required
|
||||
placeholder="请选择预约日期"
|
||||
@click="openScheduleDatePicker">
|
||||
</van-field>
|
||||
<van-field
|
||||
:value="selectionSummary || ''"
|
||||
label="预约时段"
|
||||
name="timeSelection"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
required
|
||||
:placeholder="scheduleDate ? '请选择预约时段' : '请先选择预约日期'"
|
||||
@click="openAvailabilityPopup">
|
||||
</van-field>
|
||||
<div v-if="selectionSummary" class="selection-summary">当前已选:{{ selectionSummary }}</div>
|
||||
<van-cell title="批量预约">
|
||||
<template #default>
|
||||
<van-switch v-model="yearlyReserve" size="22px"></van-switch>
|
||||
</template>
|
||||
</van-cell>
|
||||
<div v-if="yearlyReserve" class="batch-panel">
|
||||
<div class="section-tip">将自动预约截止日期内每周同一时段,默认到本月底。</div>
|
||||
<van-field
|
||||
:value="formData.yearlyReserveEndDate"
|
||||
label="截至日期"
|
||||
name="yearlyReserveEndDate"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
required
|
||||
placeholder="请选择批量预约截止日期"
|
||||
@click="openYearlyEndDatePicker">
|
||||
</van-field>
|
||||
<div v-if="batchReserveSummary" class="batch-summary">{{ batchReserveSummary }}</div>
|
||||
</div>
|
||||
<van-field v-model="formData.applyCause" label="预约事由" name="applyCause" required rows="4" autosize
|
||||
type="textarea" maxlength="1000" show-word-limit placeholder="请输入预约事由"
|
||||
:rules="[{ required: true, message: '请输入预约事由' }]"></van-field>
|
||||
</van-cell-group>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
|
||||
<div class="footer-actions" v-if="siteLoaded">
|
||||
<van-button plain round type="info" @click="historyBack">取消</van-button>
|
||||
<van-button round type="primary" color="#246fb4" @click="onSubmit">提交预约</van-button>
|
||||
</div>
|
||||
|
||||
<van-popup v-model="showReserveTypePicker" position="bottom" round>
|
||||
<van-picker show-toolbar :columns="reserveTypeColumns" @confirm="onReserveTypeConfirm"
|
||||
@cancel="showReserveTypePicker = false"></van-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="showClubPicker" position="bottom" round>
|
||||
<van-picker show-toolbar :columns="clubColumns" @confirm="onClubConfirm"
|
||||
@cancel="showClubPicker = false"></van-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="showScheduleDatePicker" position="bottom" round>
|
||||
<van-datetime-picker
|
||||
v-model="schedulePickerDate"
|
||||
type="date"
|
||||
title="选择预约日期"
|
||||
:min-date="scheduleMinDate"
|
||||
:max-date="scheduleMaxDate"
|
||||
@confirm="onScheduleDateConfirm"
|
||||
@cancel="showScheduleDatePicker = false">
|
||||
</van-datetime-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="showAvailabilityPopup" position="bottom" round class="availability-popup">
|
||||
<div class="availability-popup__header">
|
||||
<div>
|
||||
<div class="availability-popup__title">选择预约时段</div>
|
||||
<div class="availability-popup__subtitle">{{ scheduleDate || '请选择预约日期' }} {{ reserveModeText }}</div>
|
||||
</div>
|
||||
<van-icon class="availability-popup__close" name="cross" @click="showAvailabilityPopup = false"></van-icon>
|
||||
</div>
|
||||
<div v-if="timeGroupTabs.length" class="availability-popup__tabs">
|
||||
<button
|
||||
v-for="tab in timeGroupTabs"
|
||||
:key="tab.key"
|
||||
type="button"
|
||||
:class="['availability-tab', activeTimeGroupKey === tab.key ? 'availability-tab--active' : '']"
|
||||
@click="activeTimeGroupKey = tab.key">
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="availability-popup__legend">
|
||||
<span class="availability-legend__item"><i class="availability-legend__dot availability-legend__dot--available"></i>可预约</span>
|
||||
<span class="availability-legend__item"><i class="availability-legend__dot availability-legend__dot--reserved"></i>已预约</span>
|
||||
<span class="availability-legend__item"><i class="availability-legend__dot availability-legend__dot--selected"></i>已选择</span>
|
||||
<span class="availability-legend__item"><i class="availability-legend__dot availability-legend__dot--closed"></i>不可预约</span>
|
||||
</div>
|
||||
<div class="availability-popup__body">
|
||||
<van-loading v-if="availabilityLoading" vertical>时段加载中</van-loading>
|
||||
<van-empty v-else-if="!availabilityBlocks.length" description="当前日期暂无可选时段"></van-empty>
|
||||
<van-empty v-else-if="!displayedAvailabilityBlocks.length" description="当前标签暂无可选时段"></van-empty>
|
||||
<div v-else class="slot-grid">
|
||||
<button
|
||||
v-for="block in displayedAvailabilityBlocks"
|
||||
:key="block.key"
|
||||
type="button"
|
||||
:class="getBlockClass(block)"
|
||||
@click="onAvailabilityBlockClick(block)">
|
||||
<span>{{ block.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="availability-popup__footer">
|
||||
<div class="availability-popup__summary">{{ selectionSummary || '请选择一个或多个连续时段,不可跨越间隔选择。' }}</div>
|
||||
<div class="availability-popup__actions">
|
||||
<van-button plain round type="default" @click="clearSelection(true)">清空</van-button>
|
||||
<van-button round type="primary" color="#246fb4" @click="confirmAvailabilitySelection">确定</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="showYearlyEndDatePicker" position="bottom" round>
|
||||
<van-datetime-picker
|
||||
v-model="yearlyReservePickerDate"
|
||||
type="date"
|
||||
title="选择批量预约截止日期"
|
||||
:min-date="yearlyReserveMinDate"
|
||||
:max-date="yearlyReserveMaxDate"
|
||||
@confirm="onYearlyReserveEndDateConfirm"
|
||||
@cancel="showYearlyEndDatePicker = false">
|
||||
</van-datetime-picker>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('apply.js'){}#-->
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,474 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.site-page {
|
||||
background: #f5f7fb;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrap {
|
||||
padding: 10px 10px 8px;
|
||||
background: #f5f7fb;
|
||||
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.search-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.search-panel {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.search-panel /deep/ .van-search {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.search-panel /deep/ .van-search__content {
|
||||
height: 42px;
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.toolbar-btn,
|
||||
.toolbar-icon-btn {
|
||||
height: 42px;
|
||||
border: none;
|
||||
border-radius: 14px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.08);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #2563eb;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
min-width: 62px;
|
||||
padding: 0 14px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.toolbar-icon-btn {
|
||||
width: 42px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.selected-type-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 14px;
|
||||
background: rgba(37, 99, 235, 0.08);
|
||||
color: #2563eb;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.selected-type-text {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-right: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.selected-type-clear {
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: #2563eb;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.type-popup {
|
||||
width: calc(100vw - 48px);
|
||||
max-width: 320px;
|
||||
padding: 16px 16px 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.type-popup__header {
|
||||
color: #111827;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.type-popup__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.type-popup__item {
|
||||
min-width: calc(50% - 5px);
|
||||
min-height: 38px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #dbe4f0;
|
||||
border-radius: 12px;
|
||||
background: #ffffff;
|
||||
color: #475569;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.2;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.type-popup__item--active {
|
||||
border-color: #2563eb;
|
||||
background: rgba(37, 99, 235, 0.08);
|
||||
color: #2563eb;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.site-card-header {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.site-thumb {
|
||||
width: 92px;
|
||||
height: 92px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, #e5eefb 0%, #f5f9ff 100%);
|
||||
box-shadow: inset 0 0 0 1px rgba(37, 99, 235, 0.06);
|
||||
}
|
||||
|
||||
.site-thumb img,
|
||||
.site-thumb .van-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.site-thumb-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #94a3b8;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.site-head-main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.site-name {
|
||||
color: #111827;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.site-address {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-top: 8px;
|
||||
color: #6b7280;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.site-address i {
|
||||
color: #9ca3af;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.site-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.site-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 24px;
|
||||
padding: 0 8px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #93c5fd;
|
||||
color: #2563eb;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.site-tag--solid {
|
||||
border-color: #2563eb;
|
||||
background: #2563eb;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.site-tag--muted {
|
||||
border-color: #cbd5e1;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.site-timeline {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.timeline-bar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(48, minmax(0, 1fr));
|
||||
gap: 1px;
|
||||
align-items: center;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
.timeline-bar__segment {
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.timeline-bar__segment--available {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
.timeline-bar__segment--reserved {
|
||||
background: #9ca3af;
|
||||
}
|
||||
|
||||
.timeline-scale {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
padding: 0 2px;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/deep/ .table-list-container {
|
||||
margin-top: 8px;
|
||||
padding: 0 8px 12px;
|
||||
}
|
||||
|
||||
/deep/ .table-list-container .table-list-item {
|
||||
margin-bottom: 12px;
|
||||
padding: 14px;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
border-radius: 22px;
|
||||
background: #ffffff;
|
||||
box-shadow:
|
||||
0 2px 6px rgba(15, 23, 42, 0.05),
|
||||
0 10px 24px rgba(15, 23, 42, 0.08),
|
||||
0 0 0 1px rgba(226, 232, 240, 0.9);
|
||||
}
|
||||
|
||||
/deep/ .table-list-container .table-list-item .item-actions {
|
||||
margin-top: 12px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #eef2f7;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="site-page">
|
||||
<van-nav-bar title="场馆预约" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<van-sticky offset-top="46px">
|
||||
<div class="toolbar-wrap">
|
||||
<div class="search-toolbar">
|
||||
<div class="search-panel">
|
||||
<van-search
|
||||
v-model="pageForm.searchKeyword"
|
||||
:show-action="false"
|
||||
:reverse-color="false"
|
||||
input-align="left"
|
||||
placeholder="请输入名称或地址搜索"
|
||||
@search="doSearch"
|
||||
></van-search>
|
||||
</div>
|
||||
<button type="button" class="toolbar-btn" @click="doSearch">搜索</button>
|
||||
<button type="button" class="toolbar-icon-btn" @click="typePopupVisible = true">
|
||||
<van-icon name="apps-o"></van-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="selectedTypeText" class="selected-type-bar">
|
||||
<div class="selected-type-text">已选场地类型:{{ selectedTypeText }}</div>
|
||||
<button type="button" class="selected-type-clear" @click="selectType(null)">清除</button>
|
||||
</div>
|
||||
</div>
|
||||
</van-sticky>
|
||||
|
||||
<table-list api="/platform/siteCug/apply/pageData" :page_form.sync="pageForm" ref="tableListRef" @ready="onReady">
|
||||
<template #header="{ row }">
|
||||
<div class="site-card-header">
|
||||
<div class="site-thumb" @click="previewSitePhoto(row)">
|
||||
<van-image v-if="row.sitePhoto" :src="row.sitePhoto" fit="cover"></van-image>
|
||||
<div v-else class="site-thumb-empty">暂无场地照片</div>
|
||||
</div>
|
||||
<div class="site-head-main">
|
||||
<div class="site-name">{{ row.name || '-' }}</div>
|
||||
<div class="site-address">
|
||||
<i class="fa fa-map-marker"></i>
|
||||
<span>{{ row.address || '-' }}</span>
|
||||
</div>
|
||||
<div class="site-tags">
|
||||
<span class="site-tag site-tag--solid">{{ row.typeName || '活动场地' }}</span>
|
||||
<span class="site-tag">容{{ row.maxNum || '-' }}</span>
|
||||
<span :class="['site-tag', row.state ? '' : 'site-tag--muted']">{{ row.state ? '已开启' : '未开启' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot="{ row }">
|
||||
<div class="site-timeline">
|
||||
<div class="timeline-bar">
|
||||
<span
|
||||
v-for="(segment, index) in row.timelineSegments || []"
|
||||
:key="row.id + '-segment-' + index"
|
||||
:class="['timeline-bar__segment', 'timeline-bar__segment--' + (segment.status || 'closed')]">
|
||||
</span>
|
||||
</div>
|
||||
<div class="timeline-scale">
|
||||
<span v-for="mark in timeMarks" :key="row.id + '-mark-' + mark">{{ mark }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
</div>
|
||||
<div class="action-btn" @click="onApply(row)">
|
||||
<i class="fa fa-edit"></i>
|
||||
<span>预约</span>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
|
||||
<van-popup v-model="typePopupVisible" round class="type-popup">
|
||||
<div class="type-popup__header">选择场地类型</div>
|
||||
<div class="type-popup__list">
|
||||
<button
|
||||
v-for="item in typeFilterOptions"
|
||||
:key="String(item.value)"
|
||||
type="button"
|
||||
:class="['type-popup__item', pageForm.type === item.value ? 'type-popup__item--active' : '']"
|
||||
@click="selectType(item.value)">
|
||||
<span>{{ item.text }}</span>
|
||||
<van-icon v-if="pageForm.type === item.value" name="success"></van-icon>
|
||||
</button>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<info ref="infoRef"></info>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../common/siteInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
components: {
|
||||
info: siteInfo,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: '',
|
||||
type: null,
|
||||
},
|
||||
typeFilterOptions: [],
|
||||
typePopupVisible: false,
|
||||
timeMarks: ['0', '2', '4', '6', '8', '10', '12', '14', '16', '18', '20', '22'],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedTypeText() {
|
||||
const current = this.typeFilterOptions.find((item) => item.value === this.pageForm.type)
|
||||
return current && current.value !== null ? current.text : ''
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
previewSitePhoto(row) {
|
||||
if (!row || !row.sitePhoto) {
|
||||
return
|
||||
}
|
||||
this.vant.ImagePreview([row.sitePhoto])
|
||||
},
|
||||
onView(row) {
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
onApply(row) {
|
||||
this.$pjaxReplace('/platform/siteCug/apply/form/h5?siteId=' + row.id)
|
||||
},
|
||||
async onReady() {
|
||||
const typeList = await this.querySiteType()
|
||||
this.typeFilterOptions = [
|
||||
{
|
||||
text: '全部类型',
|
||||
value: null,
|
||||
}
|
||||
].concat(typeList.map((item) => ({ text: item.name, value: item.id })))
|
||||
if (this.typeFilterOptions.length > 0) {
|
||||
this.pageForm.type = this.typeFilterOptions[0].value
|
||||
this.doSearch()
|
||||
}
|
||||
},
|
||||
async querySiteType() {
|
||||
const res = await this.$axios.post('/platform/siteCug/function/type/queryFunctionType')
|
||||
return Array.isArray(res.data) ? res.data : []
|
||||
},
|
||||
selectType(value) {
|
||||
this.pageForm.type = value
|
||||
this.typePopupVisible = false
|
||||
this.doSearch()
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,80 @@
|
||||
const siteCugApplyInfoH5 = {
|
||||
template:
|
||||
/*language=HTML*/
|
||||
`
|
||||
<van-action-sheet v-model="visible" :title="sheetTitle">
|
||||
<div class="detail-container">
|
||||
<van-cell-group title="基本信息">
|
||||
<van-cell title="预约场地">{{ viewData.siteName }}</van-cell>
|
||||
<van-cell title="预约人">{{ viewData.applyUserName }}</van-cell>
|
||||
<van-cell title="预约人工号">{{ viewData.applyLoginName }}</van-cell>
|
||||
<van-cell title="所属单位">{{ viewData.applyUnitName }}</van-cell>
|
||||
<van-cell title="预约类型">{{ reserveTypeLabel }}</van-cell>
|
||||
<van-cell title="预约主体">{{ reserveTargetName }}</van-cell>
|
||||
<van-cell title="开始时间">{{ viewData.reserveStartTime }}</van-cell>
|
||||
<van-cell title="结束时间">{{ viewData.reserveEndTime }}</van-cell>
|
||||
<van-cell title="联系电话">{{ viewData.applyMobile }}</van-cell>
|
||||
<van-cell title="预约事由" class="direction-column-cell">{{ viewData.applyCause }}</van-cell>
|
||||
</van-cell-group>
|
||||
<template v-for="task in doneTasks">
|
||||
<van-cell-group :title="task.displayName" v-if="task.ext.isFirstTaskNode" :key="task.id + '-first'">
|
||||
<van-cell title="申请人">{{ task.ext.initiatorName }}({{task.ext.initiatorAccount}})</van-cell>
|
||||
<van-cell title="申请时间">{{ task.finishTime }}</van-cell>
|
||||
<van-cell title="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<van-cell-group :title="task.displayName" v-else :key="task.id + '-done'">
|
||||
<van-cell title="办理人">{{ task.taskFormData.userName }}({{task.taskFormData.loginName}})</van-cell>
|
||||
<van-cell title="办理时间">{{ task.finishTime }}</van-cell>
|
||||
<van-cell title="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>
|
||||
</van-cell>
|
||||
<van-cell title="办理意见" class="direction-column-cell">
|
||||
<div v-html="task.taskFormData.opinion || task.taskFormData.tf_opinion || '-' "></div>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</template>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</van-action-sheet>
|
||||
`,
|
||||
dicts: ['PROCESS_TASK_SUBMIT_TYPE'],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
viewData: {},
|
||||
doneTasks: [],
|
||||
row: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
reserveTypeLabel() {
|
||||
return this.viewData.reserveType === 'club' ? '协会预约' : '分工会预约'
|
||||
},
|
||||
reserveTargetName() {
|
||||
return this.viewData.reserveType === 'club' ? (this.viewData.clubName || '-') : (this.viewData.applyUnionName || '-')
|
||||
},
|
||||
sheetTitle() {
|
||||
return this.visible && this.$slots.default ? '审核预约' : '预约详情'
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.row = row
|
||||
this.visible = true
|
||||
this.viewData = row
|
||||
this.getDoneTasks()
|
||||
},
|
||||
onClose() {
|
||||
this.visible = false
|
||||
},
|
||||
getDoneTasks() {
|
||||
this.$axios.post('/flow/common/doneTasks', { bizId: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
const siteInfo = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<van-action-sheet v-model="visible" title="场馆信息">
|
||||
<div class="detail-container">
|
||||
<van-cell-group title="基本信息">
|
||||
<van-cell title="创建人" :value="viewData.createUserName || '-' "></van-cell>
|
||||
<van-cell title="场馆名称" :value="viewData.name || '-' "></van-cell>
|
||||
<van-cell title="场地地址" class="direction-column-cell">
|
||||
<template #default>
|
||||
{{ viewData.address || '-' }}
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="联系人" :value="viewData.contactName || '-' "></van-cell>
|
||||
<van-cell title="联系电话" :value="viewData.contactPhone || '-' "></van-cell>
|
||||
<van-cell title="排序编号" :value="viewData.sortNum || '-' "></van-cell>
|
||||
<van-cell title="容纳人数" :value="viewData.maxNum || '-' "></van-cell>
|
||||
<van-cell title="场地类型" :value="viewData.typeName || '-' "></van-cell>
|
||||
<van-cell title="性别限制">
|
||||
<template #default>
|
||||
<span v-if="viewData.sexLimit === 1">男</span>
|
||||
<span v-else-if="viewData.sexLimit === 2">女</span>
|
||||
<span v-else>不限制</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="开启状态">
|
||||
<template #default>
|
||||
<span v-if="viewData.state">开启</span>
|
||||
<span v-else>禁用</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="排除节假日">
|
||||
<template #default>
|
||||
<span v-if="viewData.filterHolidays">是</span>
|
||||
<span v-else>否</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="场地介绍" class="direction-column-cell">
|
||||
<template #default>
|
||||
<div v-if="viewData.introduce" v-html="viewData.introduce"></div>
|
||||
<div v-else>暂无场地介绍</div>
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
<van-cell-group title="禁用时间" v-if="viewData.notApplyTimeList && viewData.notApplyTimeList.length > 0">
|
||||
<table class="table-class">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>日期</th>
|
||||
<th>开始时间</th>
|
||||
<th>结束时间</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in viewData.notApplyTimeList" :key="item.date + item.startTime + item.endTime + index">
|
||||
<td>{{ item.date }}</td>
|
||||
<td>{{ item.startTime }}</td>
|
||||
<td>{{ item.endTime }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</van-cell-group>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
</div>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
viewData: {},
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.viewData = row
|
||||
this.visible = true
|
||||
},
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
/deep/ .van-action-sheet {
|
||||
border-top-left-radius: 24px;
|
||||
border-top-right-radius: 24px;
|
||||
overflow: hidden;
|
||||
background: #f8fafc;
|
||||
}
|
||||
/deep/ .van-action-sheet__header {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
background: #ffffff;
|
||||
}
|
||||
/deep/ .detail-container {
|
||||
padding: 10px 12px 18px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
/deep/ .detail-container .van-cell-group {
|
||||
margin-bottom: 12px;
|
||||
overflow: hidden;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
/deep/ .detail-container .van-cell-group__title {
|
||||
padding: 12px 16px 8px;
|
||||
color: #334155;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
/deep/ .detail-container .van-cell {
|
||||
min-height: 52px;
|
||||
}
|
||||
/deep/ .direction-column-cell .van-cell__value {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
/deep/ .table-class {
|
||||
width: 100%;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
line-height: 1.8rem;
|
||||
font-size: 13px;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
background: #ffffff;
|
||||
}
|
||||
/deep/ .table-class th {
|
||||
padding: 10px 8px;
|
||||
background-color: #f1f5f9;
|
||||
border: 1px solid #dbe4ee;
|
||||
color: #334155;
|
||||
font-weight: 600;
|
||||
}
|
||||
/deep/ .table-class tr {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #dbe4ee;
|
||||
}
|
||||
/deep/ .table-class td {
|
||||
padding: 10px 8px;
|
||||
border: 1px solid #dbe4ee;
|
||||
color: #475569;
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<van-nav-bar title="我的预约" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<van-sticky offset-top="46px">
|
||||
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
|
||||
<van-dropdown-item v-model="pageForm.siteType" :options="typeOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
|
||||
<van-dropdown-item v-model="pageForm.siteId" :options="siteOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
|
||||
</van-dropdown-menu>
|
||||
</van-sticky>
|
||||
|
||||
<table-list api="/platform/siteCug/mine/pageData" :page_form.sync="pageForm" ref="tableListRef" title="siteName" @ready="onReady">
|
||||
<template v-slot="{index,row}">
|
||||
<table-column label="预约人">{{row.applyUserName || '-'}}</table-column>
|
||||
<table-column label="预约类型">{{row.reserveType === 'club' ? '协会预约' : '分工会预约'}}</table-column>
|
||||
<table-column label="开始时间">{{row.reserveStartTime}}</table-column>
|
||||
<table-column label="结束时间">{{row.reserveEndTime}}</table-column>
|
||||
<table-column label="当前节点">{{row.taskName || '-'}}</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
</div>
|
||||
<div class="action-btn delete" @click="onDelete(row)" v-if="row.canCancel">
|
||||
<i class="fa fa-trash"></i>
|
||||
<span>取消预约</span>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
|
||||
<info ref="infoRef"></info>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../common/applyInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
components: {
|
||||
info: siteCugApplyInfoH5,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
siteType: null,
|
||||
siteId: null,
|
||||
},
|
||||
typeOptions: [
|
||||
{
|
||||
text: '全部类型',
|
||||
value: null,
|
||||
}
|
||||
],
|
||||
siteOptions: [
|
||||
{
|
||||
text: '全部场地',
|
||||
value: null,
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async onReady() {
|
||||
await this.querySiteType()
|
||||
await this.querySites()
|
||||
this.doSearch()
|
||||
},
|
||||
onView(row) {
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$dialog.confirm({
|
||||
title: '提示',
|
||||
message: row.yearlyBatch
|
||||
? ('该记录属于“预约本年”批量预约,确认后将一并删除同批次的 ' + (row.batchDeleteCount || 0) + ' 条预约记录,是否继续?')
|
||||
: '您确定要取消该预约吗?',
|
||||
}).then(() => {
|
||||
this.$axios.post('/platform/siteCug/mine/delete', { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$toast.success(res.msg || '取消成功')
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: res.msg || '取消失败',
|
||||
}).catch(() => {})
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: (err && err.msg) || '取消失败',
|
||||
}).catch(() => {})
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
querySiteType() {
|
||||
return this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
this.typeOptions = [
|
||||
{
|
||||
text: '全部类型',
|
||||
value: null,
|
||||
}
|
||||
].concat(list.map((item) => ({
|
||||
text: item.name,
|
||||
value: item.id,
|
||||
})))
|
||||
})
|
||||
},
|
||||
querySites() {
|
||||
return this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
this.siteOptions = [
|
||||
{
|
||||
text: '全部场地',
|
||||
value: null,
|
||||
}
|
||||
].concat(list.map((item) => ({
|
||||
text: item.name,
|
||||
value: item.id,
|
||||
})))
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
+335
@@ -0,0 +1,335 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.site-cug-audit-page {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f4f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.site-cug-audit-sticky {
|
||||
flex-shrink: 0;
|
||||
background: #f4f6f8;
|
||||
}
|
||||
.site-cug-audit-list-scroll {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 18px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.site-cug-audit-search /deep/ .van-search__content,
|
||||
.site-cug-audit-search .van-search__content {
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
.site-cug-card-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.site-cug-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
background: #eef6ff;
|
||||
color: #0b75bd;
|
||||
font-size: 12px;
|
||||
}
|
||||
.site-cug-tag.gray {
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
}
|
||||
.site-cug-tag.warn {
|
||||
background: #fff3e5;
|
||||
color: #d97706;
|
||||
}
|
||||
.action-btn.disabled {
|
||||
opacity: 0.45;
|
||||
pointer-events: none;
|
||||
}
|
||||
.action-btn.loading {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.action-btn .van-loading {
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="site-cug-audit-page" v-cloak>
|
||||
<van-nav-bar title="校工会审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<van-sticky offset-top="46px" class="site-cug-audit-sticky">
|
||||
<van-search
|
||||
class="site-cug-audit-search"
|
||||
v-model="pageForm.searchKeyword"
|
||||
show-action
|
||||
clearable
|
||||
input-align="left"
|
||||
placeholder="请输入姓名/工号/场地搜索"
|
||||
@search="doSearch"
|
||||
@clear="doSearch">
|
||||
<template #action>
|
||||
<div @click="doSearch">搜索</div>
|
||||
</template>
|
||||
</van-search>
|
||||
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
|
||||
<van-dropdown-item v-model="pageForm.siteType" :options="typeOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
|
||||
<van-dropdown-item v-model="pageForm.siteId" :options="siteOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
|
||||
</van-dropdown-menu>
|
||||
<van-tabs v-model="pageForm.approvalText" @change="onApprovalTabChange">
|
||||
<van-tab title="已审核" name="1"></van-tab>
|
||||
<van-tab title="未审核" name="0"></van-tab>
|
||||
</van-tabs>
|
||||
</van-sticky>
|
||||
|
||||
<div class="site-cug-audit-list-scroll">
|
||||
<table-list api="/platform/siteCug/schoolUnionAudit/pageData" :page_form.sync="pageForm" ref="tableListRef" title="applyUserName" @ready="onReady">
|
||||
<template v-slot="{index,row}">
|
||||
<div class="site-cug-card-tags">
|
||||
<span v-if="row.applyLoginName" class="site-cug-tag">{{ row.applyLoginName }}</span>
|
||||
<span class="site-cug-tag gray">{{ reserveTypeText(row) }}</span>
|
||||
<span v-if="row.yearlyBatch" class="site-cug-tag warn">预约本年</span>
|
||||
</div>
|
||||
<table-column label="场地名称">{{row.siteName}}</table-column>
|
||||
<table-column label="预约主体">{{row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-')}}</table-column>
|
||||
<table-column label="所属单位">{{row.applyUnitName}}</table-column>
|
||||
<table-column label="开始时间">{{row.reserveStartTime}}</table-column>
|
||||
<table-column label="结束时间">{{row.reserveEndTime}}</table-column>
|
||||
<table-column label="当前节点">{{row.curTaskName || '-'}}</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
</div>
|
||||
<div class="action-btn" v-if="row.taskState === 10" @click="onAudit(row)">
|
||||
<i class="fa fa-edit"></i>
|
||||
<span>审核</span>
|
||||
</div>
|
||||
<div class="action-btn delete" v-if="canRevoke(row)" :class="{ loading: revokeLoading }" @click="onRevoke(row)">
|
||||
<van-loading v-if="revokeLoading" size="14px" color="#fff"></van-loading>
|
||||
<i v-else class="fa fa-reply"></i>
|
||||
<span>撤回</span>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
|
||||
<info ref="infoRef">
|
||||
<div v-if="showApprovalForm">
|
||||
<div class="process-title">{{formData.taskName}}</div>
|
||||
<van-form ref="formRef">
|
||||
<van-field
|
||||
v-model="formData.tf_opinion"
|
||||
name="tf_opinion"
|
||||
label="审批意见"
|
||||
placeholder="请输入审批意见"
|
||||
:rules="[{ required: true, message: '请填写审批意见' }]"
|
||||
required
|
||||
maxlength="100"
|
||||
show-word-limit
|
||||
></van-field>
|
||||
</van-form>
|
||||
<div style="display:flex;justify-content:space-between;column-gap:10px;padding:10px;">
|
||||
<van-button type="info" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(6)">退回</van-button>
|
||||
<van-button type="danger" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(2)">不同意</van-button>
|
||||
<van-button type="primary" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(1)">同意</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</info>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('../common/applyInfo.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
components: {
|
||||
info: siteCugApplyInfoH5,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: '',
|
||||
approvalText: '0',
|
||||
approval: false,
|
||||
siteType: null,
|
||||
siteId: null,
|
||||
},
|
||||
typeOptions: [
|
||||
{
|
||||
text: '全部类型',
|
||||
value: null,
|
||||
}
|
||||
],
|
||||
siteOptions: [
|
||||
{
|
||||
text: '全部场地',
|
||||
value: null,
|
||||
}
|
||||
],
|
||||
formData: {},
|
||||
showApprovalForm: false,
|
||||
auditLoading: false,
|
||||
revokeLoading: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
async onReady() {
|
||||
await this.querySiteType()
|
||||
await this.querySites()
|
||||
this.doSearch()
|
||||
},
|
||||
onApprovalTabChange(val) {
|
||||
this.pageForm.approval = val === '1'
|
||||
this.doSearch()
|
||||
},
|
||||
canRevoke(row) {
|
||||
return Number(row.instanceState) === 20
|
||||
},
|
||||
reserveTypeText(row) {
|
||||
return row && row.reserveType === 'club' ? '协会预约' : '分工会预约'
|
||||
},
|
||||
onView(row) {
|
||||
this.showApprovalForm = false
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
onAudit(row) {
|
||||
this.showApprovalForm = true
|
||||
this.formData = {
|
||||
processTaskId: row.taskId,
|
||||
applyId: row.id,
|
||||
taskName: row.curTaskName,
|
||||
tf_opinion: '',
|
||||
yearlyBatch: row.yearlyBatch,
|
||||
batchAuditCount: row.batchAuditCount,
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
async handleTaskAction(submitType) {
|
||||
if (this.auditLoading) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
await this.$refs.formRef.validate()
|
||||
this.$dialog.confirm({
|
||||
title: '提示',
|
||||
message: this.formData.yearlyBatch
|
||||
? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?')
|
||||
: '您确定要提交吗?',
|
||||
}).then(() => {
|
||||
this.auditLoading = true
|
||||
this.$axios.post('/platform/siteCug/schoolUnionAudit/executeTask', {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: submitType,
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$refs.infoRef.onClose()
|
||||
this.$toast.success(res.msg || '提交成功')
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: res.msg || '提交失败',
|
||||
}).catch(() => {})
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: (err && err.msg) || '提交失败',
|
||||
}).catch(() => {})
|
||||
}).finally(() => {
|
||||
this.auditLoading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
onRevoke(row) {
|
||||
if (!this.canRevoke(row) || this.revokeLoading) {
|
||||
return
|
||||
}
|
||||
this.$dialog.confirm({
|
||||
title: '提示',
|
||||
message: row.yearlyBatch
|
||||
? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || 0) + ' 条审核任务,是否继续?')
|
||||
: '您确定要撤回吗?',
|
||||
}).then(() => {
|
||||
this.revokeLoading = true
|
||||
this.$axios.post('/platform/siteCug/schoolUnionAudit/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$toast.success(res.msg || '撤回成功')
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: res.msg || '撤回失败',
|
||||
}).catch(() => {})
|
||||
}
|
||||
}).catch((err) => {
|
||||
this.$dialog.alert({
|
||||
title: '提示',
|
||||
message: (err && err.msg) || '撤回失败',
|
||||
}).catch(() => {})
|
||||
}).finally(() => {
|
||||
this.revokeLoading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
querySiteType() {
|
||||
return this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
this.typeOptions = [
|
||||
{
|
||||
text: '全部类型',
|
||||
value: null,
|
||||
}
|
||||
].concat(list.map((item) => ({
|
||||
text: item.name,
|
||||
value: item.id,
|
||||
})))
|
||||
})
|
||||
},
|
||||
querySites() {
|
||||
return this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
|
||||
const list = Array.isArray(res.data) ? res.data : []
|
||||
this.siteOptions = [
|
||||
{
|
||||
text: '全部场地',
|
||||
value: null,
|
||||
}
|
||||
].concat(list.map((item) => ({
|
||||
text: item.name,
|
||||
value: item.id,
|
||||
})))
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
File diff suppressed because it is too large
Load Diff
+478
@@ -0,0 +1,478 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.tour-approval-page {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f4f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-approval-sticky {
|
||||
flex-shrink: 0;
|
||||
background: #f4f6f8;
|
||||
}
|
||||
|
||||
.tour-approval-list-scroll {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 18px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-approval-search /deep/ .van-search__content,
|
||||
.tour-approval-search .van-search__content {
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-card-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tour-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
background: #eef6ff;
|
||||
color: #0b75bd;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tour-tag.gray {
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.tour-tag.warn {
|
||||
background: #fff3e5;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.tour-approval-sheet {
|
||||
max-height: 86vh;
|
||||
overflow-y: auto;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
background: #f4f6f8;
|
||||
}
|
||||
|
||||
.tour-section {
|
||||
margin: 10px 12px;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-section-title {
|
||||
position: relative;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #edf0f4;
|
||||
color: #0b75bd;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.tour-section-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 0;
|
||||
width: 30px;
|
||||
height: 4px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
background: #0b75bd;
|
||||
}
|
||||
|
||||
.tour-approval-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 10px 12px 14px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-process-opinion {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="tour-approval-page" v-cloak>
|
||||
<van-nav-bar title="校工会审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<van-sticky offset-top="46px" class="tour-approval-sticky">
|
||||
<van-search
|
||||
class="tour-approval-search"
|
||||
v-model="pageForm.keyword"
|
||||
placeholder="请输入工号/姓名/线路搜索"
|
||||
show-action
|
||||
clearable
|
||||
input-align="left"
|
||||
@search="doSearch"
|
||||
@clear="doSearch">
|
||||
<template #action>
|
||||
<div @click="doSearch">搜索</div>
|
||||
</template>
|
||||
</van-search>
|
||||
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
|
||||
<van-dropdown-item :options="auditOptions" @change="doSearch" v-model="pageForm.audit"></van-dropdown-item>
|
||||
<van-dropdown-item :options="unionOptions" @change="doSearch" v-model="pageForm.unionId"></van-dropdown-item>
|
||||
<van-dropdown-item :options="lineTypeOptions" @change="doSearch" v-model="pageForm.lineType"></van-dropdown-item>
|
||||
</van-dropdown-menu>
|
||||
</van-sticky>
|
||||
|
||||
<div class="tour-approval-list-scroll">
|
||||
<table-list api="/platform/tour/schoolUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName" @ready="onReady">
|
||||
<template v-slot="{index,row}">
|
||||
<div class="tour-card-tags">
|
||||
<span v-if="row.jobNo" class="tour-tag">{{ row.jobNo }}</span>
|
||||
<span v-if="row.lineType" class="tour-tag gray">{{ row.lineType }}</span>
|
||||
<span v-if="row.overCostReimbursed" class="tour-tag warn">报销超出费用</span>
|
||||
</div>
|
||||
<table-column label="线路名称">{{ row.lineName || '' }}</table-column>
|
||||
<table-column label="出行时段">{{ row.travelPeriod || '' }}</table-column>
|
||||
<table-column label="报销超出费用">{{ row.overCostReimbursed ? '是' : '否' }}</table-column>
|
||||
<table-column label="当前节点">{{ row.curTaskName || '' }}</table-column>
|
||||
<table-column label="流程状态">
|
||||
<enum-tag
|
||||
v-if="row.instanceState !== null && row.instanceState !== undefined && row.instanceState !== ''"
|
||||
:value="row.instanceState"
|
||||
name="ProcessInstanceStateEnum"
|
||||
label_key="message"
|
||||
size="small">
|
||||
</enum-tag>
|
||||
</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
</div>
|
||||
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
|
||||
<i class="fa fa-check"></i>
|
||||
<span>审核</span>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
|
||||
<tour-info ref="infoRef">
|
||||
<div v-if="showApprovalForm" class="tour-section">
|
||||
<div class="tour-section-title">{{ formData.taskName || '审核' }}</div>
|
||||
<van-form ref="formRef">
|
||||
<van-field
|
||||
v-model="formData.tf_opinion"
|
||||
name="tf_opinion"
|
||||
label="审批意见"
|
||||
placeholder="请输入审批意见"
|
||||
:rules="[{ required: true, message: '请填写审批意见' }]"
|
||||
required
|
||||
type="textarea"
|
||||
rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit>
|
||||
</van-field>
|
||||
</van-form>
|
||||
<div class="tour-approval-actions">
|
||||
<van-button type="danger" block @click="handleTaskAction(6)">退回</van-button>
|
||||
<van-button type="danger" block @click="handleTaskAction(2)">拒绝</van-button>
|
||||
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</tour-info>
|
||||
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const TOUR_INFO = {
|
||||
template: /*language=HTML*/ `
|
||||
<van-action-sheet v-model="visible" title="报名详情">
|
||||
<div class="tour-approval-sheet">
|
||||
<div class="tour-section">
|
||||
<div class="tour-section-title">教职工信息</div>
|
||||
<van-cell title="工号" :value="detail.jobNo || ''"></van-cell>
|
||||
<van-cell title="姓名" :value="detail.userName || ''"></van-cell>
|
||||
<van-cell title="性别" :value="detail.gender || ''"></van-cell>
|
||||
<van-cell title="身份证号" :value="detail.idCard || ''"></van-cell>
|
||||
<van-cell title="所在单位" :value="detail.unitName || ''"></van-cell>
|
||||
<van-cell title="所在工会" :value="detail.unionName || ''"></van-cell>
|
||||
</div>
|
||||
|
||||
<div class="tour-section">
|
||||
<div class="tour-section-title">报名信息</div>
|
||||
<van-cell title="报名线路" :value="detail.lineName || ''"></van-cell>
|
||||
<van-cell title="线路类型" :value="detail.lineType || ''"></van-cell>
|
||||
<van-cell title="出行时段" :value="detail.travelPeriod || ''"></van-cell>
|
||||
<van-cell title="报名时间" :value="detail.signupTime || ''"></van-cell>
|
||||
<van-cell title="报名酒店" :value="detail.hotelName || ''"></van-cell>
|
||||
<van-cell title="旅行社" :value="detail.travelAgencyName || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" title="床型" :value="detail.bedType || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" title="床位信息" :value="detail.bedInfo || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" title="意向拼床人" :value="detail.intendedRoommate || ''"></van-cell>
|
||||
<van-cell title="携带家属" :value="familyText()"></van-cell>
|
||||
<van-cell title="报销超出费用" :value="detail.overCostReimbursed ? '是' : '否'"></van-cell>
|
||||
</div>
|
||||
|
||||
<div v-if="isDirectFamilyLine()" class="tour-section">
|
||||
<div class="tour-section-title">直系亲属线路</div>
|
||||
<van-cell title="亲属姓名" :value="directRelative.relativeName || ''"></van-cell>
|
||||
<van-cell title="所在单位" :value="directRelative.unitName || ''"></van-cell>
|
||||
<van-cell title="亲属关系" :value="directRelative.relationshipName || ''"></van-cell>
|
||||
<van-cell title="线路名称" :value="directRelative.lineName || ''"></van-cell>
|
||||
<van-cell title="出行开始" :value="directRelative.travelStartTime || ''"></van-cell>
|
||||
<van-cell title="出行结束" :value="directRelative.travelEndTime || ''"></van-cell>
|
||||
</div>
|
||||
|
||||
<div v-if="showFamilySection()" class="tour-section">
|
||||
<div class="tour-section-title">家属信息</div>
|
||||
<template v-for="(item,index) in familyData">
|
||||
<van-cell :key="'title_' + index" :title="'家属' + (index + 1)"></van-cell>
|
||||
<van-cell :key="'name_' + index" title="姓名" :value="item.familyName || ''"></van-cell>
|
||||
<van-cell :key="'gender_' + index" title="性别" :value="item.gender || ''"></van-cell>
|
||||
<van-cell :key="'id_' + index" title="身份证号" :value="item.idCard || ''"></van-cell>
|
||||
<van-cell :key="'rel_' + index" title="关系" :value="item.relationship || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" :key="'bed_' + index" title="床型" :value="item.bedType || ''"></van-cell>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="doneTasks.length > 0" class="tour-section">
|
||||
<div class="tour-section-title">流程记录</div>
|
||||
<template v-for="task in doneTasks">
|
||||
<van-cell-group :key="task.id" :title="task.displayName || task.taskName">
|
||||
<van-cell title="办理时间" :value="task.finishTime || ''"></van-cell>
|
||||
<van-cell title="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag>
|
||||
</van-cell>
|
||||
<van-cell title="办理意见" v-if="task.taskFormData" class="direction-column-cell">
|
||||
<div class="tour-process-opinion">{{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}</div>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
`,
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
row: {},
|
||||
detail: {},
|
||||
familyData: [],
|
||||
directRelative: {},
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
directFamilyUnitLine: false,
|
||||
doneTasks: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.row = row || {}
|
||||
this.visible = true
|
||||
this.detail = {}
|
||||
this.familyData = []
|
||||
this.directRelative = {}
|
||||
this.allowFamily = false
|
||||
this.fillBedInfo = true
|
||||
this.directFamilyUnitLine = false
|
||||
this.doneTasks = []
|
||||
this.info()
|
||||
this.getDoneTasks()
|
||||
},
|
||||
onClose() {
|
||||
this.visible = false
|
||||
},
|
||||
info() {
|
||||
this.$axios.post("/platform/tour/schoolUnionApproval/detail", { id: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.detail = data.ledger || {}
|
||||
this.detail.travelPeriod = data.travelPeriod || this.row.travelPeriod || ""
|
||||
this.familyData = data.families || []
|
||||
this.directRelative = data.directRelative || {}
|
||||
this.allowFamily = this.toBoolean(data.allowFamily)
|
||||
this.fillBedInfo = data.fillBedInfo === undefined || data.fillBedInfo === null || this.toBoolean(data.fillBedInfo)
|
||||
this.directFamilyUnitLine = this.toBoolean(data.directFamilyUnitLine)
|
||||
} else {
|
||||
vant.Toast(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
getDoneTasks() {
|
||||
this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1" || value === "true" || value === "TRUE"
|
||||
},
|
||||
isDirectFamilyLine() {
|
||||
return this.directFamilyUnitLine || this.toBoolean(this.detail.directFamilyUnitLine) || !!(this.directRelative && this.directRelative.id)
|
||||
},
|
||||
hasFamily() {
|
||||
return this.toBoolean(this.detail.hasFamily) || this.familyData.length > 0
|
||||
},
|
||||
showFamilySection() {
|
||||
return this.allowFamily && this.hasFamily() && !this.isDirectFamilyLine()
|
||||
},
|
||||
familyText() {
|
||||
if (this.isDirectFamilyLine() || !this.allowFamily) {
|
||||
return "否"
|
||||
}
|
||||
return this.hasFamily() ? "是" : "否"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
components: {
|
||||
"tour-info": TOUR_INFO
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
audit: false,
|
||||
keyword: "",
|
||||
unionId: "",
|
||||
lineType: ""
|
||||
},
|
||||
auditOptions: [
|
||||
{ text: "未审核", value: false },
|
||||
{ text: "已审核", value: true }
|
||||
],
|
||||
unionOptions: [
|
||||
{ text: "全部工会", value: "" }
|
||||
],
|
||||
lineTypeOptions: [
|
||||
{ text: "全部线路类型", value: "" }
|
||||
],
|
||||
formData: {},
|
||||
showApprovalForm: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
onReady() {
|
||||
this.doSearch()
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(async () => {
|
||||
await Promise.all([this.loadUnionOptions(), this.loadLineTypeOptions()])
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
loadUnionOptions() {
|
||||
return this.$axios.post("/platform/tour/schoolUnionApproval/unionOptions", {
|
||||
audit: this.pageForm.audit,
|
||||
keyword: this.pageForm.keyword,
|
||||
lineType: this.pageForm.lineType
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const options = (res.data || []).map(item => ({
|
||||
text: item.name,
|
||||
value: item.id
|
||||
})).filter(item => item.value)
|
||||
this.unionOptions = [{ text: "全部工会", value: "" }].concat(options)
|
||||
if (this.pageForm.unionId && !this.unionOptions.some(item => item.value === this.pageForm.unionId)) {
|
||||
this.pageForm.unionId = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadLineTypeOptions() {
|
||||
return this.$axios.post("/platform/tour/schoolUnionApproval/lineTypeOptions", {
|
||||
audit: this.pageForm.audit,
|
||||
keyword: this.pageForm.keyword,
|
||||
unionId: this.pageForm.unionId
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const options = (res.data || []).map(item => ({
|
||||
text: item.lineType,
|
||||
value: item.lineType
|
||||
})).filter(item => item.value)
|
||||
this.lineTypeOptions = [{ text: "全部线路类型", value: "" }].concat(options)
|
||||
if (this.pageForm.lineType && !this.lineTypeOptions.some(item => item.value === this.pageForm.lineType)) {
|
||||
this.pageForm.lineType = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
onView(row) {
|
||||
this.showApprovalForm = false
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
onApproval(row) {
|
||||
this.showApprovalForm = true
|
||||
this.formData = {
|
||||
processTaskId: row.taskId,
|
||||
taskName: row.curTaskName || row.taskName,
|
||||
tf_opinion: ""
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
async handleTaskAction(val) {
|
||||
try {
|
||||
await this.$refs.formRef.validate()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
this.$dialog.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要提交吗?"
|
||||
}).then(() => {
|
||||
const loading = this.$toast.loading({
|
||||
message: "加载中...",
|
||||
forbidClick: true,
|
||||
overlay: true,
|
||||
duration: 0
|
||||
})
|
||||
this.$axios.post("/flow/common/executeTask", {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: val
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$refs.infoRef.onClose()
|
||||
this.$toast.success(res.msg || "操作成功")
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$toast(res.msg || "操作失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,556 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.tour-apply-page {
|
||||
min-height: 100vh;
|
||||
padding: 10px 14px 78px;
|
||||
background: #f4f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-apply-card {
|
||||
margin-bottom: 14px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.tour-apply-title {
|
||||
position: relative;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #edf0f4;
|
||||
color: #0b75bd;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.tour-apply-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 0;
|
||||
width: 30px;
|
||||
height: 4px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
background: #0b75bd;
|
||||
}
|
||||
|
||||
.tour-card-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tour-card-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tour-family-tab {
|
||||
margin: 10px 12px 0;
|
||||
height: 30px;
|
||||
border-radius: 5px;
|
||||
background: #1684d2;
|
||||
color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tour-field-tip {
|
||||
padding: 0 16px 12px;
|
||||
color: #ee2f2f;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tour-over-cost /deep/ .van-field__label,
|
||||
.tour-over-cost /deep/ .van-field__label span,
|
||||
.tour-over-cost /deep/ .van-radio__label {
|
||||
color: #ee2f2f;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tour-submit-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 20;
|
||||
padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
|
||||
border-top: 1px solid #edf0f4;
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-apply-empty {
|
||||
padding: 24px 0;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
<van-nav-bar title="疗休养报名" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<div class="tour-apply-page">
|
||||
<van-loading v-if="pageLoading" size="24px" vertical>加载中...</van-loading>
|
||||
|
||||
<template v-else>
|
||||
<div class="tour-apply-card">
|
||||
<div class="tour-apply-title">教职工信息</div>
|
||||
<van-field label="姓名" readonly v-model="signupForm.userName"></van-field>
|
||||
<van-field label="工号" readonly v-model="signupForm.jobNo"></van-field>
|
||||
<van-field label="身份证号码" v-model="signupForm.idCard" maxlength="30" placeholder="请填写身份证号码"></van-field>
|
||||
<van-field label="手机号" v-model="signupForm.mobile" type="tel" maxlength="30" placeholder="请填写手机号"></van-field>
|
||||
<van-field label="所属工会" readonly v-model="signupForm.unionName"></van-field>
|
||||
</div>
|
||||
|
||||
<div class="tour-apply-card">
|
||||
<div class="tour-apply-title">报名信息</div>
|
||||
<van-field label="报名线路" readonly v-model="signupForm.lineName"></van-field>
|
||||
<van-field label="线路类型" readonly v-model="signupForm.lineType"></van-field>
|
||||
<van-field v-if="signupForm.travelPeriod" label="出行时间" readonly v-model="signupForm.travelPeriod"></van-field>
|
||||
<van-field label="报名酒店" v-model="signupForm.hotelName" maxlength="100" placeholder="请输入报名酒店"></van-field>
|
||||
<van-field
|
||||
v-if="fillBedInfo"
|
||||
label="床型"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
placeholder="请选择房型"
|
||||
v-model="signupForm.bedType"
|
||||
@click="openBedPicker('self')">
|
||||
</van-field>
|
||||
<van-field v-if="fillBedInfo" label="床位信息" v-model="signupForm.bedInfo" maxlength="100" placeholder="请输入床位数量"></van-field>
|
||||
<van-field v-if="fillBedInfo" label="意向拼床人" v-model="signupForm.intendedRoommate" maxlength="100" placeholder="请输入意向拼床人"></van-field>
|
||||
<van-field v-if="canApplyOverReimbursement(signupForm)" class="tour-over-cost" label="报销超出费用">
|
||||
<template #input>
|
||||
<van-radio-group v-model="signupForm.overCostReimbursed" direction="horizontal">
|
||||
<van-radio :name="true">是</van-radio>
|
||||
<van-radio :name="false">否</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<div v-if="canApplyOverReimbursement(signupForm) && signupForm.overCostReimbursed" class="tour-field-tip">
|
||||
提醒:您选择了报销超出费用,会影响到下一年度的疗休养,请认真思考。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isDirectFamilyLine(signupForm)" class="tour-apply-card">
|
||||
<div class="tour-apply-title">直系亲属线路</div>
|
||||
<van-field label="亲属姓名" v-model="directRelativeForm.relativeName" maxlength="100" placeholder="请输入亲属姓名"></van-field>
|
||||
<van-field label="所在单位" v-model="directRelativeForm.unitName" maxlength="100" placeholder="请输入所在单位"></van-field>
|
||||
<van-field
|
||||
label="亲属关系"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
placeholder="请选择亲属关系"
|
||||
v-model="directRelativeForm.relationshipName"
|
||||
@click="openDirectRelativePicker">
|
||||
</van-field>
|
||||
<van-field label="线路名称" v-model="directRelativeForm.lineName" maxlength="100" placeholder="请输入线路名称"></van-field>
|
||||
<van-field label="出行开始" v-model="directRelativeForm.travelStartTime" type="date" placeholder="请选择出行开始日期"></van-field>
|
||||
<van-field label="出行结束" v-model="directRelativeForm.travelEndTime" type="date" placeholder="请选择出行结束日期"></van-field>
|
||||
</div>
|
||||
|
||||
<div class="tour-apply-card">
|
||||
<div class="tour-apply-title">
|
||||
<div class="tour-card-title-row">
|
||||
<span>亲属信息</span>
|
||||
<div class="tour-card-actions">
|
||||
<van-button size="small" type="default" :disabled="familyData.length === 0" @click="removeFamily">删除亲属</van-button>
|
||||
<van-button size="small" type="info" :disabled="!allowFamily || isDirectFamilyLine(signupForm)" @click="addFamily">添加亲属</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!allowFamily || isDirectFamilyLine(signupForm)" class="tour-apply-empty">
|
||||
当前线路不支持填写亲属
|
||||
</div>
|
||||
<template v-else-if="familyData.length > 0">
|
||||
<div class="tour-family-tab">亲属{{ familyActive + 1 }}</div>
|
||||
<van-field label="姓名" v-model="currentFamily.familyName" maxlength="100" placeholder="请填写姓名"></van-field>
|
||||
<van-field label="年龄" v-model.number="currentFamily.age" type="digit" placeholder="请填写年龄"></van-field>
|
||||
<van-field label="性别">
|
||||
<template #input>
|
||||
<van-radio-group v-model="currentFamily.gender" direction="horizontal">
|
||||
<van-radio name="男">男</van-radio>
|
||||
<van-radio name="女">女</van-radio>
|
||||
</van-radio-group>
|
||||
</template>
|
||||
</van-field>
|
||||
<van-field label="身份证号码" v-model="currentFamily.idCard" maxlength="18" placeholder="请填写身份证号码" @blur="normalizeFamilyIdCard(currentFamily)"></van-field>
|
||||
<van-field label="手机号" v-model="currentFamily.mobile" maxlength="30" placeholder="请填写手机号"></van-field>
|
||||
<van-field
|
||||
label="与本人关系"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
placeholder="请选择关系"
|
||||
v-model="currentFamily.relationship"
|
||||
@click="openRelationshipPicker">
|
||||
</van-field>
|
||||
<van-field
|
||||
v-if="fillBedInfo"
|
||||
label="床型"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
placeholder="请选择房型"
|
||||
v-model="currentFamily.bedType"
|
||||
@click="openBedPicker('family')">
|
||||
</van-field>
|
||||
<van-field v-if="fillBedInfo" label="床位" v-model="currentFamily.bedInfo" maxlength="100" placeholder="请输入床位数量"></van-field>
|
||||
<van-field v-if="fillBedInfo" label="意向拼床人" v-model="currentFamily.intendedRoommate" maxlength="100" placeholder="请输入意向拼床人"></van-field>
|
||||
<div v-if="fillBedInfo" class="tour-field-tip">提醒:如果跟报名人员同一房间,无须选择!</div>
|
||||
</template>
|
||||
<div v-else class="tour-apply-empty">暂无亲属,请按需添加</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="tour-submit-bar">
|
||||
<van-button type="info" block round :loading="submitLoading" @click="submitSignup">提交报名</van-button>
|
||||
</div>
|
||||
|
||||
<van-popup v-model="bedPickerVisible" position="bottom">
|
||||
<van-picker show-toolbar :columns="bedTypeColumns" @confirm="confirmBedType" @cancel="bedPickerVisible=false"></van-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="relationshipPickerVisible" position="bottom">
|
||||
<van-picker show-toolbar :columns="relationshipColumns" @confirm="confirmRelationship" @cancel="relationshipPickerVisible=false"></van-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model="directRelativePickerVisible" position="bottom">
|
||||
<van-picker show-toolbar :columns="directRelativeColumns" @confirm="confirmDirectRelative" @cancel="directRelativePickerVisible=false"></van-picker>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
pageLoading: true,
|
||||
submitLoading: false,
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
signupForm: {},
|
||||
directRelativeForm: {},
|
||||
familyData: [],
|
||||
familyActive: 0,
|
||||
bedTypeOptions: [],
|
||||
familyRelationshipOptions: [],
|
||||
directRelativeOptions: [],
|
||||
bedPickerVisible: false,
|
||||
relationshipPickerVisible: false,
|
||||
directRelativePickerVisible: false,
|
||||
bedPickerTarget: "self",
|
||||
matterId: GetQueryString("matterId") || ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentFamily() {
|
||||
return this.familyData[this.familyActive] || {}
|
||||
},
|
||||
bedTypeColumns() {
|
||||
return this.bedTypeOptions.map(item => item.name || item.code).filter(Boolean)
|
||||
},
|
||||
relationshipColumns() {
|
||||
return this.familyRelationshipOptions.map(item => item.name || item.code).filter(Boolean)
|
||||
},
|
||||
directRelativeColumns() {
|
||||
return this.directRelativeOptions.map(item => ({ text: item.name || item.code, item }))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (!this.matterId) {
|
||||
vant.Dialog.alert({ title: "提示", message: "报名事项参数缺失", confirmButtonColor: "#1867b0" })
|
||||
this.pageLoading = false
|
||||
return
|
||||
}
|
||||
Promise.all([this.loadOptions(), this.loadSignupDetail()]).finally(() => {
|
||||
this.pageLoading = false
|
||||
})
|
||||
},
|
||||
loadOptions() {
|
||||
return Promise.all([
|
||||
this.$axios.post("/platform/tour/signup/bedTypeOptions").then(res => {
|
||||
if (res.code === 0) this.bedTypeOptions = res.data || []
|
||||
}),
|
||||
this.$axios.post("/platform/tour/signup/familyRelationshipOptions").then(res => {
|
||||
if (res.code === 0) this.familyRelationshipOptions = res.data || []
|
||||
}),
|
||||
this.$axios.post("/platform/tour/signup/directRelativeOptions").then(res => {
|
||||
if (res.code === 0) this.directRelativeOptions = res.data || []
|
||||
})
|
||||
])
|
||||
},
|
||||
loadSignupDetail() {
|
||||
return this.$axios.post("/platform/tour/signup/signupDetail", { matterId: this.matterId }).then((res) => {
|
||||
if (res.code !== 0) {
|
||||
vant.Dialog.alert({
|
||||
title: "提示",
|
||||
message: res.msg || "报名信息加载失败",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
const matter = data.matter || {}
|
||||
const staff = data.staff || {}
|
||||
const ledger = data.ledger || {}
|
||||
const directRelative = data.directRelative || {}
|
||||
this.allowFamily = matter.allowFamily === true || matter.allowFamily === 1 || matter.allowFamily === "1"
|
||||
this.fillBedInfo = matter.fillBedInfo === undefined || matter.fillBedInfo === null || matter.fillBedInfo === true || matter.fillBedInfo === 1 || matter.fillBedInfo === "1"
|
||||
this.signupForm = Object.assign({
|
||||
id: "",
|
||||
year: matter.year,
|
||||
jobNo: "",
|
||||
userName: "",
|
||||
gender: "",
|
||||
idCard: "",
|
||||
mobile: "",
|
||||
unitId: "",
|
||||
unitName: "",
|
||||
unionId: "",
|
||||
unionName: "",
|
||||
signupTime: this.$moment ? this.$moment().format("YYYY-MM-DD HH:mm:ss") : "",
|
||||
matterId: matter.matterId || "",
|
||||
lineId: matter.lineId || "",
|
||||
lineName: matter.lineName || "",
|
||||
lineType: matter.lineType || "",
|
||||
directFamilyUnitLine: matter.directFamilyUnitLine,
|
||||
hotelName: "",
|
||||
travelAgencyId: matter.travelAgencyId || "",
|
||||
travelAgencyName: matter.travelAgencyName || "",
|
||||
travelPeriod: matter.travelPeriod || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || "",
|
||||
allowOverReimbursement: matter.allowOverReimbursement,
|
||||
overCostReimbursed: false,
|
||||
hasFamily: false,
|
||||
intendedRoommate: "",
|
||||
bedType: "",
|
||||
bedInfo: ""
|
||||
}, staff, ledger, {
|
||||
year: matter.year,
|
||||
matterId: matter.matterId || ledger.matterId || "",
|
||||
lineId: matter.lineId || ledger.lineId || "",
|
||||
lineName: matter.lineName || ledger.lineName || "",
|
||||
lineType: matter.lineType || ledger.lineType || "",
|
||||
directFamilyUnitLine: matter.directFamilyUnitLine,
|
||||
travelAgencyId: matter.travelAgencyId || ledger.travelAgencyId || "",
|
||||
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
|
||||
travelPeriod: matter.travelPeriod || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || "",
|
||||
allowOverReimbursement: matter.allowOverReimbursement,
|
||||
overCostReimbursed: ledger.overCostReimbursed === true || ledger.overCostReimbursed === 1 || ledger.overCostReimbursed === "1",
|
||||
hasFamily: ledger.hasFamily === true || ledger.hasFamily === 1 || ledger.hasFamily === "1"
|
||||
})
|
||||
this.directRelativeForm = Object.assign(this.emptyDirectRelative(), {
|
||||
lineId: matter.lineId || "",
|
||||
lineName: matter.lineName || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || ""
|
||||
}, directRelative)
|
||||
this.familyData = (data.families || []).map(item => Object.assign(this.emptyFamily(), item))
|
||||
if (this.familyData.length > 0) {
|
||||
this.signupForm.hasFamily = true
|
||||
}
|
||||
if (!this.allowFamily || this.isDirectFamilyLine(this.signupForm)) {
|
||||
this.familyData = []
|
||||
this.signupForm.hasFamily = false
|
||||
}
|
||||
})
|
||||
},
|
||||
emptyFamily() {
|
||||
return {
|
||||
familyName: "",
|
||||
gender: "",
|
||||
idCard: "",
|
||||
mobile: "",
|
||||
age: null,
|
||||
bedType: "",
|
||||
bedInfo: "",
|
||||
intendedRoommate: "",
|
||||
staffJobNo: this.signupForm.jobNo || "",
|
||||
staffName: this.signupForm.userName || "",
|
||||
relationship: ""
|
||||
}
|
||||
},
|
||||
emptyDirectRelative() {
|
||||
return {
|
||||
relativeName: "",
|
||||
unitName: "",
|
||||
relationshipCode: "",
|
||||
relationshipName: "",
|
||||
lineId: this.signupForm.lineId || "",
|
||||
lineName: this.signupForm.lineName || "",
|
||||
travelStartTime: this.signupForm.travelStartTime || "",
|
||||
travelEndTime: this.signupForm.travelEndTime || ""
|
||||
}
|
||||
},
|
||||
isDirectFamilyLine(row) {
|
||||
return row && (row.directFamilyUnitLine === true || row.directFamilyUnitLine === 1 || row.directFamilyUnitLine === "1")
|
||||
},
|
||||
canApplyOverReimbursement(row) {
|
||||
return row && (row.allowOverReimbursement === true || row.allowOverReimbursement === 1 || row.allowOverReimbursement === "1")
|
||||
},
|
||||
addFamily() {
|
||||
if (!this.allowFamily || this.isDirectFamilyLine(this.signupForm)) return
|
||||
this.familyData.push(this.emptyFamily())
|
||||
this.familyActive = this.familyData.length - 1
|
||||
this.signupForm.hasFamily = true
|
||||
},
|
||||
removeFamily() {
|
||||
if (this.familyData.length === 0) return
|
||||
this.familyData.splice(this.familyActive, 1)
|
||||
this.familyActive = Math.max(0, this.familyActive - 1)
|
||||
this.signupForm.hasFamily = this.familyData.length > 0
|
||||
},
|
||||
openBedPicker(target) {
|
||||
if (this.bedTypeColumns.length === 0) {
|
||||
vant.Toast("暂无床型选项")
|
||||
return
|
||||
}
|
||||
this.bedPickerTarget = target
|
||||
this.bedPickerVisible = true
|
||||
},
|
||||
confirmBedType(value) {
|
||||
if (this.bedPickerTarget === "family") {
|
||||
this.currentFamily.bedType = value
|
||||
} else {
|
||||
this.signupForm.bedType = value
|
||||
}
|
||||
this.bedPickerVisible = false
|
||||
},
|
||||
openRelationshipPicker() {
|
||||
if (this.relationshipColumns.length === 0) {
|
||||
vant.Toast("暂无关系选项")
|
||||
return
|
||||
}
|
||||
this.relationshipPickerVisible = true
|
||||
},
|
||||
confirmRelationship(value) {
|
||||
this.currentFamily.relationship = value
|
||||
this.relationshipPickerVisible = false
|
||||
},
|
||||
openDirectRelativePicker() {
|
||||
if (this.directRelativeColumns.length === 0) {
|
||||
vant.Toast("暂无亲属关系选项")
|
||||
return
|
||||
}
|
||||
this.directRelativePickerVisible = true
|
||||
},
|
||||
confirmDirectRelative(value) {
|
||||
const item = value && value.item ? value.item : {}
|
||||
this.directRelativeForm.relationshipCode = item.code || ""
|
||||
this.directRelativeForm.relationshipName = item.name || value.text || ""
|
||||
this.directRelativePickerVisible = false
|
||||
},
|
||||
normalizeFamilyIdCard(row) {
|
||||
if (!row || !row.idCard) return
|
||||
row.idCard = String(row.idCard).trim().toUpperCase()
|
||||
},
|
||||
isValidIdCard(value) {
|
||||
if (!value) return false
|
||||
const idCard = String(value).trim().toUpperCase()
|
||||
return /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$/.test(idCard)
|
||||
},
|
||||
validateFamilies() {
|
||||
if (!this.allowFamily || !this.signupForm.hasFamily) return true
|
||||
if (this.familyData.length === 0) {
|
||||
vant.Toast("请添加亲属信息")
|
||||
return false
|
||||
}
|
||||
const invalid = this.familyData.some(item => !item.familyName || !item.gender || !item.idCard || !item.relationship)
|
||||
if (invalid) {
|
||||
vant.Toast("请完善亲属姓名、性别、身份证号码和关系")
|
||||
return false
|
||||
}
|
||||
const invalidIdCard = this.familyData.some(item => !this.isValidIdCard(item.idCard))
|
||||
if (invalidIdCard) {
|
||||
vant.Toast("请输入正确的亲属身份证号码")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
validateDirectRelative() {
|
||||
if (!this.isDirectFamilyLine(this.signupForm)) return true
|
||||
if (!this.directRelativeForm.relativeName) {
|
||||
vant.Toast("请输入亲属姓名")
|
||||
return false
|
||||
}
|
||||
if (!this.directRelativeForm.unitName) {
|
||||
vant.Toast("请输入亲属所在单位")
|
||||
return false
|
||||
}
|
||||
if (!this.directRelativeForm.relationshipCode) {
|
||||
vant.Toast("请选择亲属关系")
|
||||
return false
|
||||
}
|
||||
if (!this.directRelativeForm.lineName) {
|
||||
vant.Toast("请输入直系亲属线路名称")
|
||||
return false
|
||||
}
|
||||
if (!this.directRelativeForm.travelStartTime || !this.directRelativeForm.travelEndTime) {
|
||||
vant.Toast("请选择直系亲属出行时间")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
submitSignup() {
|
||||
if (this.pageLoading || this.submitLoading) return
|
||||
if (!this.validateFamilies()) return
|
||||
if (!this.validateDirectRelative()) return
|
||||
const families = this.allowFamily && this.signupForm.hasFamily ? this.familyData : []
|
||||
const form = Object.assign({}, this.signupForm, {
|
||||
families: JSON.stringify(families),
|
||||
directRelative: this.isDirectFamilyLine(this.signupForm) ? JSON.stringify(this.directRelativeForm) : ""
|
||||
})
|
||||
this.submitLoading = true
|
||||
this.$axios.post("/platform/tour/signup/doSignup", form).then((res) => {
|
||||
this.submitLoading = false
|
||||
if (res.code === 0) {
|
||||
vant.Dialog.alert({
|
||||
title: "提示",
|
||||
message: res.msg || "报名成功",
|
||||
confirmButtonColor: "#1867b0"
|
||||
}).then(() => {
|
||||
window.location.href = "/platform/tour/signup/h5/signup"
|
||||
})
|
||||
} else {
|
||||
vant.Dialog.alert({
|
||||
title: "提示",
|
||||
message: res.msg || "报名失败",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitLoading = false
|
||||
vant.Toast("报名提交失败")
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,210 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.tour-signup-h5 {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fb;
|
||||
padding-bottom: 80px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-signup-banner {
|
||||
position: relative;
|
||||
height: 193px;
|
||||
overflow: hidden;
|
||||
background: #0f74bc;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.tour-signup-swipe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tour-signup-swipe img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 193px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.tour-signup-banner::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, rgba(5, 83, 143, 0.72) 0%, rgba(5, 83, 143, 0.32) 48%, rgba(5, 83, 143, 0.08) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tour-signup-banner__text {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
bottom: 20px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tour-signup-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.tour-signup-subtitle {
|
||||
margin-top: 8px;
|
||||
color: rgba(255, 255, 255, 0.86);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tour-signup-card {
|
||||
margin: 10px 12px 0;
|
||||
padding: 14px 14px 16px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.tour-signup-card__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #eef2f7;
|
||||
}
|
||||
|
||||
.tour-signup-card__title {
|
||||
color: #111827;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.tour-signup-card__year {
|
||||
flex-shrink: 0;
|
||||
color: #0f74bc;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tour-signup-notice {
|
||||
margin-top: 14px;
|
||||
color: #334155;
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tour-signup-notice /deep/ img,
|
||||
.tour-signup-notice /deep/ video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tour-signup-notice /deep/ #pdf-container {
|
||||
min-height: 560px;
|
||||
}
|
||||
|
||||
.tour-signup-empty {
|
||||
padding: 34px 0 26px;
|
||||
}
|
||||
|
||||
.tour-signup-footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
box-shadow: 0 -6px 18px rgba(15, 23, 42, 0.08);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="tour-signup-h5">
|
||||
<van-nav-bar title="疗休养报名" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<div class="tour-signup-banner">
|
||||
<van-swipe class="tour-signup-swipe" :autoplay="3500" indicator-color="white">
|
||||
<van-swipe-item v-for="item in bannerList" :key="item">
|
||||
<img :src="item" alt="疗休养报名">
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
<div class="tour-signup-banner__text">
|
||||
<div class="tour-signup-title">疗休养报名</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tour-signup-card">
|
||||
<div class="tour-signup-card__header">
|
||||
<div class="tour-signup-card__title">服务须知</div>
|
||||
<div class="tour-signup-card__year">{{ setting.year || currentYear }}年度</div>
|
||||
</div>
|
||||
|
||||
<van-skeleton title :row="8" :loading="loading">
|
||||
<div v-if="setting.serviceNotice" class="tour-signup-notice">
|
||||
<pdf-preview :key="serviceNoticeKey" :content="setting.serviceNotice"></pdf-preview>
|
||||
</div>
|
||||
<van-empty v-else class="tour-signup-empty" description="暂无服务须知"></van-empty>
|
||||
</van-skeleton>
|
||||
</div>
|
||||
|
||||
<div class="tour-signup-footer">
|
||||
<van-button block type="info" color="#0f74bc" round @click="confirmRead">我已阅读</van-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
components: {
|
||||
"pdf-preview": httpVueLoader("/components/plugins/sysFilePreview/PdfIndex.vue?v=" + new Date().getTime())
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
currentYear: new Date().getFullYear(),
|
||||
serviceNoticeKey: "",
|
||||
bannerList: [
|
||||
"/assets/platform/images/tour/tour-h5-banner-1.jpg",
|
||||
"/assets/platform/images/tour/tour-h5-banner-2.jpg"
|
||||
],
|
||||
setting: {
|
||||
year: "",
|
||||
configName: "",
|
||||
serviceNotice: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadServiceNotice() {
|
||||
this.loading = true
|
||||
this.$axios.post("/platform/tour/signup/h5/serviceNotice").then((res) => {
|
||||
this.loading = false
|
||||
if (res.code === 0) {
|
||||
this.setting = Object.assign({}, this.setting, res.data || {})
|
||||
this.serviceNoticeKey = (this.setting.year || this.currentYear) + "_" + new Date().getTime()
|
||||
} else {
|
||||
vant.Toast(res.msg || "服务须知加载失败")
|
||||
}
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
confirmRead() {
|
||||
window.location.href = "/platform/tour/signup/h5/signup"
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadServiceNotice()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,559 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
#app {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tour-detail-page {
|
||||
height: calc(100vh - 46px);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px 10px 70px;
|
||||
background: #f4f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-detail-header {
|
||||
padding: 14px 12px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
.tour-detail-title {
|
||||
color: #0f172a;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tour-detail-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.tour-line-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
padding: 0 7px;
|
||||
border-radius: 4px;
|
||||
background: #0b75bd;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.tour-detail-meta {
|
||||
margin-top: 10px;
|
||||
color: #334155;
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tour-detail-meta-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tour-detail-meta-label {
|
||||
width: 72px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #64748b;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tour-detail-meta-value {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tour-detail-section {
|
||||
margin-top: 10px;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 12px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-detail-section-title {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #0b75bd;
|
||||
color: #0f172a;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.tour-detail-content /deep/ img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.tour-detail-section-scroll {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.tour-pdf-preview {
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
.tour-pdf-status {
|
||||
padding: 16px 0;
|
||||
color: #64748b;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tour-pdf-toolbar {
|
||||
margin-bottom: 8px;
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.tour-pdf-pages {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.tour-pdf-page {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
background: #f8fafc;
|
||||
box-shadow: 0 1px 4px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.tour-pdf-page canvas {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tour-detail-empty {
|
||||
padding: 18px 0;
|
||||
color: #94a3b8;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tour-detail-footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
padding: 9px 12px calc(9px + env(safe-area-inset-bottom));
|
||||
border-top: 1px solid #edf0f4;
|
||||
background: #ffffff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
<van-nav-bar title="线路详情" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<div class="tour-detail-page">
|
||||
<van-loading v-if="detailLoading" size="24px" vertical>加载中...</van-loading>
|
||||
<template v-else>
|
||||
<div class="tour-detail-header">
|
||||
<div class="tour-detail-title">{{ lineDetail.lineName || signupDetail.lineName || '线路详情' }}</div>
|
||||
<div class="tour-detail-tags">
|
||||
<span v-if="lineDetail.lotName || signupDetail.lotName" class="tour-line-tag">{{ lineDetail.lotName || signupDetail.lotName }}</span>
|
||||
<span v-if="lineDetail.lineType || signupDetail.lineType" class="tour-line-tag">{{ lineDetail.lineType || signupDetail.lineType }}</span>
|
||||
<span v-if="signupDetail.unionName" class="tour-line-tag">{{ signupDetail.unionName }}</span>
|
||||
</div>
|
||||
<div class="tour-detail-meta">
|
||||
<div v-if="travelPeriod(signupDetail)" class="tour-detail-meta-row">
|
||||
<span class="tour-detail-meta-label"><span>出行时间</span><span>:</span></span>
|
||||
<span class="tour-detail-meta-value">{{ travelPeriod(signupDetail) }}</span>
|
||||
</div>
|
||||
<div class="tour-detail-meta-row">
|
||||
<span class="tour-detail-meta-label"><span>已报人数</span><span>:</span></span>
|
||||
<span class="tour-detail-meta-value">{{ signupDetail.signupCount || 0 }}(家属{{ signupDetail.familyCount || 0 }}人)</span>
|
||||
</div>
|
||||
<div class="tour-detail-meta-row">
|
||||
<span class="tour-detail-meta-label"><span>联系人</span><span>:</span></span>
|
||||
<span class="tour-detail-meta-value">{{ signupDetail.contactName || '暂无' }}</span>
|
||||
</div>
|
||||
<div class="tour-detail-meta-row">
|
||||
<span class="tour-detail-meta-label"><span>联系方式</span><span>:</span></span>
|
||||
<span class="tour-detail-meta-value">{{ lineDetail.contactPhone || signupDetail.contactPhone || '暂无' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tour-detail-section">
|
||||
<div class="tour-detail-section-title">线路介绍</div>
|
||||
<div class="tour-detail-section-scroll">
|
||||
<div v-if="lineDetail.lineContent && lineContentHref" class="tour-pdf-preview">
|
||||
<div v-if="pdfPageCount" class="tour-pdf-toolbar">
|
||||
已加载 {{ pdfRenderedPages }} / {{ pdfPageCount }} 页
|
||||
</div>
|
||||
<div ref="pdfPreviewContainer" class="tour-pdf-pages"></div>
|
||||
<div v-if="pdfLoading && pdfRenderedPages === 0" class="tour-pdf-status">
|
||||
<van-loading size="22px" vertical>正在加载第一页...</van-loading>
|
||||
</div>
|
||||
<div v-else-if="pdfLoading" class="tour-pdf-status">
|
||||
正在继续加载后续页面...
|
||||
</div>
|
||||
<div v-if="pdfError" class="tour-pdf-status">{{ pdfError }}</div>
|
||||
</div>
|
||||
<div v-else-if="lineDetail.lineContent" class="tour-detail-content" v-html="lineDetail.lineContent">
|
||||
</div>
|
||||
<div v-else class="tour-detail-empty">暂无线路介绍</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="tour-detail-footer">
|
||||
<van-button :type="detailActionType()" block round :disabled="detailActionDisabled()" @click="handleDetailAction">
|
||||
{{ detailActionText() }}
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
detailLoading: true,
|
||||
lineDetail: {},
|
||||
signupDetail: {},
|
||||
lineContentHref: "",
|
||||
pdfDoc: null,
|
||||
pdfLoadingTask: null,
|
||||
pdfLoading: false,
|
||||
pdfError: "",
|
||||
pdfPageCount: 0,
|
||||
pdfRenderedPages: 0,
|
||||
pdfRenderToken: 0,
|
||||
matterId: GetQueryString("matterId") || "",
|
||||
lineId: GetQueryString("lineId") || ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
travelPeriod(row) {
|
||||
if (!row) {
|
||||
return ""
|
||||
}
|
||||
if (row.travelStartTime && row.travelEndTime) {
|
||||
return row.travelStartTime + " 至 " + row.travelEndTime
|
||||
}
|
||||
return row.travelPeriod || row.travelStartTime || row.travelEndTime || ""
|
||||
},
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1" || value === "true" || value === "TRUE"
|
||||
},
|
||||
isSigned() {
|
||||
return !!(this.signupDetail && this.signupDetail.signed)
|
||||
},
|
||||
isApprovalSignup() {
|
||||
return this.isSigned() && !!this.signupDetail.instanceId
|
||||
},
|
||||
canRevokeSignup() {
|
||||
return this.isApprovalSignup() && this.toBoolean(this.signupDetail.canRevoke) && !!this.signupDetail.startTaskId
|
||||
},
|
||||
isFinishedSignup() {
|
||||
return this.isApprovalSignup() && Number(this.signupDetail.instanceState) === 20
|
||||
},
|
||||
isRejectedSignup() {
|
||||
return this.isApprovalSignup() && Number(this.signupDetail.instanceState) === 45
|
||||
},
|
||||
canModifySignup() {
|
||||
if (!this.isSigned()) {
|
||||
return true
|
||||
}
|
||||
if (!this.isApprovalSignup()) {
|
||||
return true
|
||||
}
|
||||
return !this.isFinishedSignup() && !this.isRejectedSignup() && this.signupDetail.taskKey === "startTask"
|
||||
},
|
||||
detailActionText() {
|
||||
if (this.isSigned()) {
|
||||
if (this.canRevokeSignup()) {
|
||||
return "撤回"
|
||||
}
|
||||
if (this.isFinishedSignup()) {
|
||||
return "已完成"
|
||||
}
|
||||
if (this.isRejectedSignup()) {
|
||||
return "已拒绝"
|
||||
}
|
||||
return this.canModifySignup() ? "修改报名" : "审核中"
|
||||
}
|
||||
return "我要报名"
|
||||
},
|
||||
detailActionType() {
|
||||
return this.canRevokeSignup() ? "danger" : "info"
|
||||
},
|
||||
detailActionDisabled() {
|
||||
return this.isSigned() && this.isApprovalSignup() && (this.isFinishedSignup() || this.isRejectedSignup() || (!this.canRevokeSignup() && !this.canModifySignup()))
|
||||
},
|
||||
handleDetailAction() {
|
||||
if (this.detailActionDisabled()) {
|
||||
return
|
||||
}
|
||||
if (this.canRevokeSignup()) {
|
||||
this.revokeSignup()
|
||||
return
|
||||
}
|
||||
this.onApply()
|
||||
},
|
||||
parseLineContentHref(content) {
|
||||
if (!content) {
|
||||
return ""
|
||||
}
|
||||
try {
|
||||
const parser = new DOMParser()
|
||||
const doc = parser.parseFromString(content, "text/html")
|
||||
const link = doc.querySelector("a[href]")
|
||||
return link ? link.getAttribute("href") : ""
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
},
|
||||
getPdfLib() {
|
||||
return window.pdfjsLib || window["pdfjs-dist/build/pdf"]
|
||||
},
|
||||
clearPdfPreview() {
|
||||
this.pdfRenderToken += 1
|
||||
this.pdfDoc = null
|
||||
this.pdfLoading = false
|
||||
this.pdfError = ""
|
||||
this.pdfPageCount = 0
|
||||
this.pdfRenderedPages = 0
|
||||
if (this.pdfLoadingTask && this.pdfLoadingTask.destroy) {
|
||||
try {
|
||||
this.pdfLoadingTask.destroy()
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
this.pdfLoadingTask = null
|
||||
if (this.$refs.pdfPreviewContainer) {
|
||||
this.$refs.pdfPreviewContainer.innerHTML = ""
|
||||
}
|
||||
},
|
||||
renderLinePdf() {
|
||||
this.clearPdfPreview()
|
||||
if (!this.lineContentHref) {
|
||||
return
|
||||
}
|
||||
const pdfLib = this.getPdfLib()
|
||||
if (!pdfLib || !pdfLib.getDocument) {
|
||||
this.pdfError = "PDF预览组件加载失败"
|
||||
return
|
||||
}
|
||||
if (pdfLib.GlobalWorkerOptions) {
|
||||
pdfLib.GlobalWorkerOptions.workerSrc = "/assets/platform/plugins/pdfJs/h5/pdf.worker.js"
|
||||
}
|
||||
const token = this.pdfRenderToken
|
||||
this.pdfLoading = true
|
||||
this.pdfLoadingTask = pdfLib.getDocument({
|
||||
url: this.lineContentHref,
|
||||
disableAutoFetch: false,
|
||||
disableStream: false,
|
||||
disableRange: false
|
||||
})
|
||||
this.pdfLoadingTask.promise.then((pdf) => {
|
||||
if (token !== this.pdfRenderToken) {
|
||||
return
|
||||
}
|
||||
this.pdfDoc = pdf
|
||||
this.pdfPageCount = pdf.numPages || 0
|
||||
return this.renderPdfPage(1, token)
|
||||
}).then(() => {
|
||||
if (token !== this.pdfRenderToken) {
|
||||
return
|
||||
}
|
||||
this.renderRestPdfPages(2, token)
|
||||
}).catch(() => {
|
||||
if (token !== this.pdfRenderToken) {
|
||||
return
|
||||
}
|
||||
this.pdfLoading = false
|
||||
this.pdfError = "PDF加载失败,请稍后重试"
|
||||
})
|
||||
},
|
||||
renderPdfPage(pageNumber, token) {
|
||||
if (!this.pdfDoc || token !== this.pdfRenderToken || pageNumber > this.pdfPageCount) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return this.pdfDoc.getPage(pageNumber).then((page) => {
|
||||
if (token !== this.pdfRenderToken || !this.$refs.pdfPreviewContainer) {
|
||||
return
|
||||
}
|
||||
const container = this.$refs.pdfPreviewContainer
|
||||
const baseViewport = page.getViewport({scale: 1})
|
||||
const containerWidth = Math.max(container.clientWidth || 0, window.innerWidth - 44)
|
||||
const cssScale = containerWidth / baseViewport.width
|
||||
const outputScale = Math.min(window.devicePixelRatio || 1, 2)
|
||||
const viewport = page.getViewport({scale: cssScale})
|
||||
const pageEl = document.createElement("div")
|
||||
pageEl.className = "tour-pdf-page"
|
||||
const canvas = document.createElement("canvas")
|
||||
const context = canvas.getContext("2d")
|
||||
canvas.width = Math.floor(viewport.width * outputScale)
|
||||
canvas.height = Math.floor(viewport.height * outputScale)
|
||||
canvas.style.width = Math.floor(viewport.width) + "px"
|
||||
canvas.style.height = Math.floor(viewport.height) + "px"
|
||||
pageEl.appendChild(canvas)
|
||||
container.appendChild(pageEl)
|
||||
const renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport,
|
||||
transform: outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null
|
||||
}
|
||||
return page.render(renderContext).promise.then(() => {
|
||||
if (token === this.pdfRenderToken) {
|
||||
this.pdfRenderedPages = Math.max(this.pdfRenderedPages, pageNumber)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
renderRestPdfPages(pageNumber, token) {
|
||||
if (token !== this.pdfRenderToken || !this.pdfDoc || pageNumber > this.pdfPageCount) {
|
||||
this.pdfLoading = false
|
||||
return
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.renderPdfPage(pageNumber, token).then(() => {
|
||||
this.renderRestPdfPages(pageNumber + 1, token)
|
||||
}).catch(() => {
|
||||
if (token === this.pdfRenderToken) {
|
||||
this.pdfLoading = false
|
||||
this.pdfError = "部分页面加载失败"
|
||||
}
|
||||
})
|
||||
}, 120)
|
||||
},
|
||||
loadDetail() {
|
||||
if (!this.matterId || !this.lineId) {
|
||||
this.detailLoading = false
|
||||
vant.Dialog.alert({
|
||||
title: "提示",
|
||||
message: "线路参数不完整",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
return
|
||||
}
|
||||
Promise.all([
|
||||
this.$axios.post("/platform/tour/signup/lineDetail", { lineId: this.lineId }),
|
||||
this.$axios.post("/platform/tour/signup/signupDetail", { matterId: this.matterId })
|
||||
]).then(([lineRes, signupRes]) => {
|
||||
this.detailLoading = false
|
||||
if (lineRes.code !== 0) {
|
||||
vant.Toast(lineRes.msg || "线路详情加载失败")
|
||||
} else {
|
||||
this.lineDetail = lineRes.data || {}
|
||||
this.lineContentHref = this.parseLineContentHref(this.lineDetail.lineContent)
|
||||
this.$nextTick(() => {
|
||||
this.renderLinePdf()
|
||||
})
|
||||
}
|
||||
if (signupRes.code !== 0) {
|
||||
vant.Toast(signupRes.msg || "报名信息加载失败")
|
||||
} else {
|
||||
const data = signupRes.data || {}
|
||||
const process = data.process || {}
|
||||
this.signupDetail = Object.assign({}, data.matter || {}, {
|
||||
signed: !!(data.ledger && data.ledger.id)
|
||||
}, process)
|
||||
}
|
||||
}).catch(() => {
|
||||
this.detailLoading = false
|
||||
vant.Toast("线路详情加载失败")
|
||||
})
|
||||
},
|
||||
onApply() {
|
||||
if (!this.matterId) {
|
||||
vant.Toast("报名事项信息不完整")
|
||||
return
|
||||
}
|
||||
const goApply = () => {
|
||||
window.location.href = "/platform/tour/signup/h5/apply?matterId=" + encodeURIComponent(this.matterId)
|
||||
}
|
||||
this.$axios.post("/platform/tour/signup/signupEligibilityNotice", { matterId: this.matterId }).then((res) => {
|
||||
if (res.code !== 0) {
|
||||
vant.Dialog.alert({
|
||||
title: "温馨提醒",
|
||||
message: res.msg || "当前不能报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
if (!data.noticeRequired) {
|
||||
goApply()
|
||||
return
|
||||
}
|
||||
if (data.canApply) {
|
||||
vant.Dialog.confirm({
|
||||
title: "温馨提醒",
|
||||
message: data.message || "当前可报名,是否继续报名?",
|
||||
confirmButtonText: "继续报名",
|
||||
cancelButtonText: "稍后报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
}).then(goApply).catch(() => {})
|
||||
} else {
|
||||
vant.Dialog.alert({
|
||||
title: "温馨提醒",
|
||||
message: data.message || "当前不能报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
revokeSignup() {
|
||||
vant.Dialog.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要撤回吗?",
|
||||
confirmButtonColor: "#ee2f2f"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/common/revokeTask", { taskId: this.signupDetail.startTaskId }).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
vant.Toast.success(resp.msg || "撤回成功")
|
||||
this.loadDetail()
|
||||
} else {
|
||||
vant.Dialog.alert({ title: "提示", message: resp.msg || "撤回失败", confirmButtonColor: "#1867b0" })
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadDetail()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearPdfPreview()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,787 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.tour-line-page {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f4f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-scroll {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 18px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-toolbar {
|
||||
padding: 10px 0 8px 16px;
|
||||
background: #f4f6f8;
|
||||
}
|
||||
|
||||
.tour-line-toolbar /deep/ .van-search {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tour-line-toolbar .van-search {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tour-line-toolbar /deep/ .van-search__content {
|
||||
height: 38px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-line-toolbar .van-search__content {
|
||||
height: 38px;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-line-toolbar /deep/ .van-search__action {
|
||||
padding: 0 0 0 6px;
|
||||
}
|
||||
|
||||
.tour-line-toolbar .van-search__action {
|
||||
padding: 0 0 0 6px;
|
||||
}
|
||||
|
||||
.tour-line-search-button {
|
||||
width: 42px;
|
||||
height: 38px;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: #1e88e5;
|
||||
color: #ffffff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tour-line-search-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-union-entry {
|
||||
width: 48px;
|
||||
min-height: 42px;
|
||||
flex-shrink: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: #64748b;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.tour-line-union-entry .van-icon {
|
||||
color: #64748b;
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tour-line-union-entry--active,
|
||||
.tour-line-union-entry--active .van-icon {
|
||||
color: #0b75bd;
|
||||
}
|
||||
|
||||
.tour-line-union-popup {
|
||||
max-height: 68vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tour-line-union-popup__title {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
padding: 14px 16px 10px;
|
||||
background: #ffffff;
|
||||
color: #0f172a;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-union-popup__list {
|
||||
overflow-y: auto;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.tour-line-union-popup__item {
|
||||
min-height: 48px;
|
||||
transition: background-color 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
|
||||
.tour-line-union-popup__item:hover,
|
||||
.tour-line-union-popup__item:active {
|
||||
background: rgba(30, 136, 229, 0.1);
|
||||
}
|
||||
|
||||
.tour-line-union-popup__item:hover /deep/ .van-cell__title,
|
||||
.tour-line-union-popup__item:active /deep/ .van-cell__title {
|
||||
color: #0b75bd;
|
||||
}
|
||||
|
||||
.tour-line-union-popup__item--active /deep/ .van-cell__title {
|
||||
color: #0b75bd;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tour-line-shortcuts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tour-line-shortcut {
|
||||
min-height: 76px;
|
||||
padding: 12px 8px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-shortcut--active {
|
||||
border-color: #0b75bd;
|
||||
background: rgba(11, 117, 189, 0.08);
|
||||
}
|
||||
|
||||
.tour-line-shortcut__icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.tour-line-shortcut__text {
|
||||
min-width: 0;
|
||||
color: #0f172a;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tour-line-shortcut__hint {
|
||||
margin-top: 2px;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.tour-line-list {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.tour-line-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
min-height: 112px;
|
||||
margin: 8px 0;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-thumb {
|
||||
width: 145px;
|
||||
height: 98px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
background: #e5edf6;
|
||||
}
|
||||
|
||||
.tour-line-thumb img,
|
||||
.tour-line-thumb .van-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tour-line-thumb-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8a98a8;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tour-line-main {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tour-line-signed-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
background: #a78bfa;
|
||||
color: #ffffff;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
box-shadow: 0 2px 5px rgba(167, 139, 250, 0.32);
|
||||
}
|
||||
|
||||
.tour-line-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tour-line-title {
|
||||
min-width: 0;
|
||||
color: #0f172a;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tour-line-type-badge {
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
padding: 0 5px;
|
||||
border: 1px solid #ffd6a8;
|
||||
border-radius: 4px;
|
||||
background: #fff3e5;
|
||||
color: #d97706;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
line-height: 17px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-line-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.tour-line-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 20px;
|
||||
padding: 0 7px;
|
||||
border-radius: 4px;
|
||||
background: #39a9ed;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.tour-line-info {
|
||||
margin-top: 5px;
|
||||
color: #111827;
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.tour-line-info span {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.tour-line-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.tour-line-action {
|
||||
min-width: 70px;
|
||||
height: 28px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #1e88e5;
|
||||
border-radius: 14px;
|
||||
background: #1e88e5;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.tour-line-action--danger {
|
||||
border-color: #ee2f2f;
|
||||
background: #ee2f2f;
|
||||
}
|
||||
|
||||
.tour-line-action--disabled {
|
||||
border-color: #d1d5db;
|
||||
background: #e5e7eb;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.tour-line-card {
|
||||
gap: 9px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.tour-line-thumb {
|
||||
width: 126px;
|
||||
height: 92px;
|
||||
}
|
||||
|
||||
.tour-line-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="tour-line-page">
|
||||
<van-nav-bar title="疗休养线路" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<div class="tour-line-toolbar">
|
||||
<div class="tour-line-search-row">
|
||||
<van-search
|
||||
v-model="pageForm.lineName"
|
||||
show-action
|
||||
clearable
|
||||
placeholder="请输入线路名称搜索"
|
||||
@search="doSearch"
|
||||
@clear="doSearch">
|
||||
<template #action>
|
||||
<button type="button" class="tour-line-search-button" @click="doSearch">
|
||||
<van-icon name="search"></van-icon>
|
||||
</button>
|
||||
</template>
|
||||
</van-search>
|
||||
<button
|
||||
type="button"
|
||||
class="tour-line-union-entry"
|
||||
:class="{'tour-line-union-entry--active': !!pageForm.unionId}"
|
||||
@click="openUnionPopup">
|
||||
<van-icon name="friends-o"></van-icon>
|
||||
<span>分工会</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tour-line-shortcuts">
|
||||
<div
|
||||
class="tour-line-shortcut"
|
||||
:class="{'tour-line-shortcut--active': pageForm.lineType === '省内线路' && pageForm.directFamilyUnitLine === null}"
|
||||
@click="toggleLineType('省内线路')">
|
||||
<img class="tour-line-shortcut__icon" src="/assets/platform/images/tour/tour-in-province.png" alt="省内线路">
|
||||
<div class="tour-line-shortcut__text">
|
||||
<div>省内线路</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tour-line-shortcut"
|
||||
:class="{'tour-line-shortcut--active': pageForm.lineType === '省外线路' && pageForm.directFamilyUnitLine === null}"
|
||||
@click="toggleLineType('省外线路')">
|
||||
<img class="tour-line-shortcut__icon" src="/assets/platform/images/tour/tour-out-province.png" alt="省外线路">
|
||||
<div class="tour-line-shortcut__text">
|
||||
<div>省外线路</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tour-line-shortcut"
|
||||
:class="{'tour-line-shortcut--active': pageForm.directFamilyUnitLine === true}"
|
||||
@click="toggleDirectFamilyLine">
|
||||
<img class="tour-line-shortcut__icon" src="/assets/platform/images/tour/tour-direct-family.jpg" alt="直系亲属">
|
||||
<div class="tour-line-shortcut__text">
|
||||
<div>直系亲属</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tour-line-scroll">
|
||||
<van-list
|
||||
v-model="loading"
|
||||
:finished="finished"
|
||||
finished-text="没有更多了"
|
||||
@load="loadData">
|
||||
<div class="tour-line-list">
|
||||
<div class="tour-line-card" v-for="row in list" :key="row.matterId" @click="onLine(row)">
|
||||
<div class="tour-line-thumb">
|
||||
<van-image v-if="thumbUrl(row)" :src="thumbUrl(row)" fit="cover"></van-image>
|
||||
<div v-else class="tour-line-thumb-empty">暂无图片</div>
|
||||
</div>
|
||||
<div class="tour-line-main">
|
||||
<div class="tour-line-title-row">
|
||||
<div class="tour-line-title">{{ row.lineName || '未命名线路' }}</div>
|
||||
<span v-if="lineTypeBadge(row.lineType)" class="tour-line-type-badge">{{ lineTypeBadge(row.lineType) }}</span>
|
||||
<span v-if="isSigned(row)" class="tour-line-signed-icon">
|
||||
<van-icon name="good-job"></van-icon>
|
||||
</span>
|
||||
</div>
|
||||
<div class="tour-line-tags">
|
||||
<span v-if="row.lotName" class="tour-line-tag">{{ row.lotName }}</span>
|
||||
<span v-if="row.unionName" class="tour-line-tag">{{ row.unionName }}</span>
|
||||
</div>
|
||||
<div class="tour-line-info">
|
||||
<div><span>出行开始时间:</span>{{ row.travelStartTime || '暂无' }}</div>
|
||||
<div><span>出行结束时间:</span>{{ row.travelEndTime || '暂无' }}</div>
|
||||
<!-- 临时屏蔽旅行社信息,保留字段和接口便于后续恢复。
|
||||
<div><span>旅行社:</span>{{ row.travelAgencyName || '暂无' }}</div>
|
||||
-->
|
||||
</div>
|
||||
<div class="tour-line-actions">
|
||||
<button
|
||||
v-if="showSignupAction(row)"
|
||||
type="button"
|
||||
class="tour-line-action"
|
||||
:class="{
|
||||
'tour-line-action--danger': canRevokeSignup(row),
|
||||
'tour-line-action--disabled': signupActionDisabled(row)
|
||||
}"
|
||||
:disabled="signupActionDisabled(row)"
|
||||
@click.stop="handleSignupAction(row)">
|
||||
{{ signupActionText(row) }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
|
||||
<van-empty v-if="!loading && list.length === 0" description="暂无线路"></van-empty>
|
||||
</div>
|
||||
|
||||
<van-popup v-model="showUnionPopup" position="bottom" round>
|
||||
<div class="tour-line-union-popup">
|
||||
<div class="tour-line-union-popup__title">选择分工会</div>
|
||||
<div class="tour-line-union-popup__list">
|
||||
<van-cell
|
||||
v-for="item in unionList"
|
||||
:key="item.id || 'all'"
|
||||
clickable
|
||||
class="tour-line-union-popup__item"
|
||||
:class="{'tour-line-union-popup__item--active': pageForm.unionId === item.id}"
|
||||
:title="item.name"
|
||||
@click="selectUnion(item)">
|
||||
<template #right-icon>
|
||||
<van-icon v-if="pageForm.unionId === item.id" name="success" color="#0b75bd"></van-icon>
|
||||
</template>
|
||||
</van-cell>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
loading: false,
|
||||
finished: false,
|
||||
lineChecking: false,
|
||||
showUnionPopup: false,
|
||||
unionList: [
|
||||
{id: "", name: "全部分工会"}
|
||||
],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
year: new Date().getFullYear(),
|
||||
lineName: "",
|
||||
lineType: "",
|
||||
unionId: "",
|
||||
directFamilyUnitLine: null,
|
||||
pageOrderName: "lineName",
|
||||
pageOrderBy: "ascending"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
thumbUrl(row) {
|
||||
return row.lineMobileThumb || row.agencyMobileThumb || ""
|
||||
},
|
||||
lineTypeBadge(lineType) {
|
||||
if (lineType === "省内线路" || lineType === "省内") {
|
||||
return "省内"
|
||||
}
|
||||
if (lineType === "省外线路" || lineType === "省外") {
|
||||
return "省外"
|
||||
}
|
||||
return ""
|
||||
},
|
||||
isSigned(row) {
|
||||
return !!(row && (row.signed === true || row.signed === 1 || row.signed === "1" || row.ledgerId))
|
||||
},
|
||||
isApprovalSignup(row) {
|
||||
return !!(row && this.isSigned(row) && row.instanceId)
|
||||
},
|
||||
canRevokeSignup(row) {
|
||||
return this.isApprovalSignup(row) && this.toBoolean(row.canRevoke) && !!row.startTaskId
|
||||
},
|
||||
isFinishedSignup(row) {
|
||||
return this.isApprovalSignup(row) && Number(row.instanceState) === 20
|
||||
},
|
||||
isRejectedSignup(row) {
|
||||
return this.isApprovalSignup(row) && Number(row.instanceState) === 45
|
||||
},
|
||||
canModifySignup(row) {
|
||||
if (!row || !this.isSigned(row)) {
|
||||
return true
|
||||
}
|
||||
if (!this.isApprovalSignup(row)) {
|
||||
return true
|
||||
}
|
||||
return !this.isFinishedSignup(row) && !this.isRejectedSignup(row) && row.taskKey === "startTask"
|
||||
},
|
||||
signupActionText(row) {
|
||||
if (this.isSigned(row)) {
|
||||
if (this.canRevokeSignup(row)) {
|
||||
return "撤回"
|
||||
}
|
||||
if (this.isRejectedSignup(row)) {
|
||||
return "已拒绝"
|
||||
}
|
||||
return this.canModifySignup(row) ? "修改" : "审核中"
|
||||
}
|
||||
return "报名"
|
||||
},
|
||||
showSignupAction(row) {
|
||||
return this.isSigned(row) && this.canModifySignup(row)
|
||||
},
|
||||
signupActionDisabled(row) {
|
||||
return !!(this.isSigned(row) && this.isApprovalSignup(row) && (this.isRejectedSignup(row) || (!this.canRevokeSignup(row) && !this.canModifySignup(row))))
|
||||
},
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1" || value === "true" || value === "TRUE"
|
||||
},
|
||||
handleSignupAction(row) {
|
||||
if (this.signupActionDisabled(row)) {
|
||||
return
|
||||
}
|
||||
if (this.canRevokeSignup(row)) {
|
||||
this.revokeSignup(row)
|
||||
return
|
||||
}
|
||||
this.goApply(row)
|
||||
},
|
||||
goApply(row) {
|
||||
if (!row || !row.matterId) {
|
||||
vant.Toast("报名事项信息不完整")
|
||||
return
|
||||
}
|
||||
const go = () => {
|
||||
window.location.href = "/platform/tour/signup/h5/apply?matterId=" + encodeURIComponent(row.matterId)
|
||||
}
|
||||
this.$axios.post("/platform/tour/signup/signupEligibilityNotice", { matterId: row.matterId }).then((res) => {
|
||||
if (res.code !== 0) {
|
||||
vant.Dialog.alert({
|
||||
title: "温馨提醒",
|
||||
message: res.msg || "当前不能报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
if (!data.noticeRequired) {
|
||||
go()
|
||||
return
|
||||
}
|
||||
if (data.canApply) {
|
||||
vant.Dialog.confirm({
|
||||
title: "温馨提醒",
|
||||
message: data.message || "当前可报名,是否继续报名?",
|
||||
confirmButtonText: "继续报名",
|
||||
cancelButtonText: "稍后报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
}).then(go).catch(() => {})
|
||||
} else {
|
||||
vant.Dialog.alert({
|
||||
title: "温馨提醒",
|
||||
message: data.message || "当前不能报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
revokeSignup(row) {
|
||||
vant.Dialog.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要撤回吗?",
|
||||
confirmButtonColor: "#ee2f2f"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/common/revokeTask", { taskId: row.startTaskId }).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
vant.Toast.success(resp.msg || "撤回成功")
|
||||
this.doSearch()
|
||||
} else {
|
||||
vant.Dialog.alert({ title: "提示", message: resp.msg || "撤回失败", confirmButtonColor: "#1867b0" })
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.list = []
|
||||
this.finished = false
|
||||
this.loadData()
|
||||
},
|
||||
toggleLineType(lineType) {
|
||||
const active = this.pageForm.lineType === lineType && this.pageForm.directFamilyUnitLine === null
|
||||
this.pageForm.lineType = active ? "" : lineType
|
||||
this.pageForm.directFamilyUnitLine = null
|
||||
this.doSearch()
|
||||
},
|
||||
toggleDirectFamilyLine() {
|
||||
const active = this.pageForm.directFamilyUnitLine === true
|
||||
this.pageForm.directFamilyUnitLine = active ? null : true
|
||||
this.pageForm.lineType = ""
|
||||
this.doSearch()
|
||||
},
|
||||
openUnionPopup() {
|
||||
this.showUnionPopup = true
|
||||
if (this.unionList.length <= 1) {
|
||||
this.loadUnionOptions()
|
||||
}
|
||||
},
|
||||
selectUnion(item) {
|
||||
const unionId = item && item.id ? item.id : ""
|
||||
this.showUnionPopup = false
|
||||
if (this.pageForm.unionId === unionId) {
|
||||
return
|
||||
}
|
||||
this.pageForm.unionId = unionId
|
||||
this.doSearch()
|
||||
},
|
||||
loadUnionOptions() {
|
||||
this.$axios.post("/platform/tour/signup/unionOptions").then((res) => {
|
||||
if (res.code !== 0) {
|
||||
return
|
||||
}
|
||||
const rows = res.data || []
|
||||
this.unionList = [{id: "", name: "全部分工会"}].concat(rows.map((item) => {
|
||||
return {
|
||||
id: item.id || "",
|
||||
name: item.name || "未命名分工会"
|
||||
}
|
||||
}))
|
||||
})
|
||||
},
|
||||
loadData() {
|
||||
if (this.finished) {
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.post("/platform/tour/signup/h5/pageData", this.pageForm).then((res) => {
|
||||
this.loading = false
|
||||
if (res.code !== 0) {
|
||||
vant.Toast(res.msg || "线路加载失败")
|
||||
this.finished = true
|
||||
return
|
||||
}
|
||||
const data = res.data || {}
|
||||
const rows = data.list || []
|
||||
this.pageForm.totalCount = data.totalCount || 0
|
||||
this.list = this.pageForm.pageNumber === 1 ? rows : this.list.concat(rows)
|
||||
if (this.list.length >= this.pageForm.totalCount || rows.length === 0) {
|
||||
this.finished = true
|
||||
} else {
|
||||
this.pageForm.pageNumber += 1
|
||||
}
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
this.finished = true
|
||||
})
|
||||
},
|
||||
onLine(row) {
|
||||
if (!row || !row.lineId || !row.matterId) {
|
||||
vant.Toast("线路信息不完整")
|
||||
return
|
||||
}
|
||||
if (this.lineChecking) {
|
||||
return
|
||||
}
|
||||
this.lineChecking = true
|
||||
axios.post("/platform/tour/signup/signupDetail", $.param({ matterId: row.matterId }), {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
||||
"x-requested-with": "XMLHttpRequest"
|
||||
}
|
||||
}).then((resp) => {
|
||||
this.lineChecking = false
|
||||
const res = resp.data || {}
|
||||
if (res.code !== 0) {
|
||||
vant.Dialog.alert({
|
||||
title: "温馨提醒",
|
||||
message: res.msg || "当前线路暂不能报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
return
|
||||
}
|
||||
window.location.href = "/platform/tour/signup/h5/lineInfo?matterId="
|
||||
+ encodeURIComponent(row.matterId)
|
||||
+ "&lineId="
|
||||
+ encodeURIComponent(row.lineId)
|
||||
}).catch((err) => {
|
||||
this.lineChecking = false
|
||||
vant.Dialog.alert({
|
||||
title: "温馨提醒",
|
||||
message: err && err.msg ? err.msg : "当前线路暂不能报名",
|
||||
confirmButtonColor: "#1867b0"
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadUnionOptions()
|
||||
this.loadData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,453 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style scoped>
|
||||
.tour-approval-page {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #f4f6f8;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-approval-sticky {
|
||||
flex-shrink: 0;
|
||||
background: #f4f6f8;
|
||||
}
|
||||
|
||||
.tour-approval-list-scroll {
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 18px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tour-approval-search /deep/ .van-search__content,
|
||||
.tour-approval-search .van-search__content {
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-card-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tour-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
background: #eef6ff;
|
||||
color: #0b75bd;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tour-tag.gray {
|
||||
background: #f1f5f9;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.tour-tag.warn {
|
||||
background: #fff3e5;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.tour-approval-sheet {
|
||||
max-height: 86vh;
|
||||
overflow-y: auto;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
background: #f4f6f8;
|
||||
}
|
||||
|
||||
.tour-section {
|
||||
margin: 10px 12px;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-section-title {
|
||||
position: relative;
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #edf0f4;
|
||||
color: #0b75bd;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.tour-section-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 0;
|
||||
width: 30px;
|
||||
height: 4px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
background: #0b75bd;
|
||||
}
|
||||
|
||||
.tour-approval-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 10px 12px 14px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.tour-process-opinion {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="tour-approval-page" v-cloak>
|
||||
<van-nav-bar title="分工会审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
|
||||
|
||||
<van-sticky offset-top="46px" class="tour-approval-sticky">
|
||||
<van-search
|
||||
class="tour-approval-search"
|
||||
v-model="pageForm.keyword"
|
||||
placeholder="请输入工号/姓名/线路搜索"
|
||||
show-action
|
||||
clearable
|
||||
input-align="left"
|
||||
@search="doSearch"
|
||||
@clear="doSearch">
|
||||
<template #action>
|
||||
<div @click="doSearch">搜索</div>
|
||||
</template>
|
||||
</van-search>
|
||||
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
|
||||
<van-dropdown-item :options="auditOptions" @change="doSearch" v-model="pageForm.audit"></van-dropdown-item>
|
||||
<van-dropdown-item :options="lineTypeOptions" @change="doSearch" v-model="pageForm.lineType"></van-dropdown-item>
|
||||
</van-dropdown-menu>
|
||||
</van-sticky>
|
||||
|
||||
<div class="tour-approval-list-scroll">
|
||||
<table-list api="/platform/tour/unionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName" @ready="onReady">
|
||||
<template v-slot="{index,row}">
|
||||
<div class="tour-card-tags">
|
||||
<span v-if="row.jobNo" class="tour-tag">{{ row.jobNo }}</span>
|
||||
<span v-if="row.lineType" class="tour-tag gray">{{ row.lineType }}</span>
|
||||
<span v-if="row.overCostReimbursed" class="tour-tag warn">报销超出费用</span>
|
||||
</div>
|
||||
<table-column label="线路名称">{{ row.lineName || '' }}</table-column>
|
||||
<table-column label="出行时段">{{ row.travelPeriod || '' }}</table-column>
|
||||
<table-column label="报销超出费用">{{ row.overCostReimbursed ? '是' : '否' }}</table-column>
|
||||
<table-column label="当前节点">{{ row.curTaskName || '' }}</table-column>
|
||||
<table-column label="流程状态">
|
||||
<enum-tag
|
||||
v-if="row.instanceState !== null && row.instanceState !== undefined && row.instanceState !== ''"
|
||||
:value="row.instanceState"
|
||||
name="ProcessInstanceStateEnum"
|
||||
label_key="message"
|
||||
size="small">
|
||||
</enum-tag>
|
||||
</table-column>
|
||||
</template>
|
||||
<template #actions="{index,row}">
|
||||
<div class="action-btn" @click="onView(row)">
|
||||
<i class="fa fa-eye"></i>
|
||||
<span>查看</span>
|
||||
</div>
|
||||
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
|
||||
<i class="fa fa-check"></i>
|
||||
<span>审核</span>
|
||||
</div>
|
||||
</template>
|
||||
</table-list>
|
||||
</div>
|
||||
|
||||
<tour-info ref="infoRef">
|
||||
<div v-if="showApprovalForm" class="tour-section">
|
||||
<div class="tour-section-title">{{ formData.taskName || '审核' }}</div>
|
||||
<van-form ref="formRef">
|
||||
<van-field
|
||||
v-model="formData.tf_opinion"
|
||||
name="tf_opinion"
|
||||
label="审批意见"
|
||||
placeholder="请输入审批意见"
|
||||
:rules="[{ required: true, message: '请填写审批意见' }]"
|
||||
required
|
||||
type="textarea"
|
||||
rows="3"
|
||||
maxlength="200"
|
||||
show-word-limit>
|
||||
</van-field>
|
||||
</van-form>
|
||||
<div class="tour-approval-actions">
|
||||
<van-button type="danger" block @click="handleTaskAction(6)">退回</van-button>
|
||||
<van-button type="danger" block @click="handleTaskAction(2)">拒绝</van-button>
|
||||
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</tour-info>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const TOUR_INFO = {
|
||||
template: /*language=HTML*/ `
|
||||
<van-action-sheet v-model="visible" title="报名详情">
|
||||
<div class="tour-approval-sheet">
|
||||
<div class="tour-section">
|
||||
<div class="tour-section-title">教职工信息</div>
|
||||
<van-cell title="工号" :value="detail.jobNo || ''"></van-cell>
|
||||
<van-cell title="姓名" :value="detail.userName || ''"></van-cell>
|
||||
<van-cell title="性别" :value="detail.gender || ''"></van-cell>
|
||||
<van-cell title="身份证号" :value="detail.idCard || ''"></van-cell>
|
||||
<van-cell title="所在单位" :value="detail.unitName || ''"></van-cell>
|
||||
<van-cell title="所在工会" :value="detail.unionName || ''"></van-cell>
|
||||
</div>
|
||||
|
||||
<div class="tour-section">
|
||||
<div class="tour-section-title">报名信息</div>
|
||||
<van-cell title="报名线路" :value="detail.lineName || ''"></van-cell>
|
||||
<van-cell title="线路类型" :value="detail.lineType || ''"></van-cell>
|
||||
<van-cell title="出行时段" :value="detail.travelPeriod || ''"></van-cell>
|
||||
<van-cell title="报名时间" :value="detail.signupTime || ''"></van-cell>
|
||||
<van-cell title="报名酒店" :value="detail.hotelName || ''"></van-cell>
|
||||
<van-cell title="旅行社" :value="detail.travelAgencyName || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" title="床型" :value="detail.bedType || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" title="床位信息" :value="detail.bedInfo || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" title="意向拼床人" :value="detail.intendedRoommate || ''"></van-cell>
|
||||
<van-cell title="携带家属" :value="familyText()"></van-cell>
|
||||
<van-cell title="报销超出费用" :value="detail.overCostReimbursed ? '是' : '否'"></van-cell>
|
||||
</div>
|
||||
|
||||
<div v-if="isDirectFamilyLine()" class="tour-section">
|
||||
<div class="tour-section-title">直系亲属线路</div>
|
||||
<van-cell title="亲属姓名" :value="directRelative.relativeName || ''"></van-cell>
|
||||
<van-cell title="所在单位" :value="directRelative.unitName || ''"></van-cell>
|
||||
<van-cell title="亲属关系" :value="directRelative.relationshipName || ''"></van-cell>
|
||||
<van-cell title="线路名称" :value="directRelative.lineName || ''"></van-cell>
|
||||
<van-cell title="出行开始" :value="directRelative.travelStartTime || ''"></van-cell>
|
||||
<van-cell title="出行结束" :value="directRelative.travelEndTime || ''"></van-cell>
|
||||
</div>
|
||||
|
||||
<div v-if="showFamilySection()" class="tour-section">
|
||||
<div class="tour-section-title">家属信息</div>
|
||||
<template v-for="(item,index) in familyData">
|
||||
<van-cell :key="'title_' + index" :title="'家属' + (index + 1)"></van-cell>
|
||||
<van-cell :key="'name_' + index" title="姓名" :value="item.familyName || ''"></van-cell>
|
||||
<van-cell :key="'gender_' + index" title="性别" :value="item.gender || ''"></van-cell>
|
||||
<van-cell :key="'id_' + index" title="身份证号" :value="item.idCard || ''"></van-cell>
|
||||
<van-cell :key="'rel_' + index" title="关系" :value="item.relationship || ''"></van-cell>
|
||||
<van-cell v-if="fillBedInfo" :key="'bed_' + index" title="床型" :value="item.bedType || ''"></van-cell>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-if="doneTasks.length > 0" class="tour-section">
|
||||
<div class="tour-section-title">流程记录</div>
|
||||
<template v-for="task in doneTasks">
|
||||
<van-cell-group :key="task.id" :title="task.displayName || task.taskName">
|
||||
<van-cell title="办理时间" :value="task.finishTime || ''"></van-cell>
|
||||
<van-cell title="办理结果">
|
||||
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag>
|
||||
</van-cell>
|
||||
<van-cell title="办理意见" v-if="task.taskFormData" class="direction-column-cell">
|
||||
<div class="tour-process-opinion">{{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}</div>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
`,
|
||||
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
row: {},
|
||||
detail: {},
|
||||
familyData: [],
|
||||
directRelative: {},
|
||||
allowFamily: false,
|
||||
fillBedInfo: true,
|
||||
directFamilyUnitLine: false,
|
||||
doneTasks: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(row) {
|
||||
this.row = row || {}
|
||||
this.visible = true
|
||||
this.detail = {}
|
||||
this.familyData = []
|
||||
this.directRelative = {}
|
||||
this.allowFamily = false
|
||||
this.fillBedInfo = true
|
||||
this.directFamilyUnitLine = false
|
||||
this.doneTasks = []
|
||||
this.info()
|
||||
this.getDoneTasks()
|
||||
},
|
||||
onClose() {
|
||||
this.visible = false
|
||||
},
|
||||
info() {
|
||||
this.$axios.post("/platform/tour/unionApproval/detail", { id: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.detail = data.ledger || {}
|
||||
this.detail.travelPeriod = data.travelPeriod || this.row.travelPeriod || ""
|
||||
this.familyData = data.families || []
|
||||
this.directRelative = data.directRelative || {}
|
||||
this.allowFamily = this.toBoolean(data.allowFamily)
|
||||
this.fillBedInfo = data.fillBedInfo === undefined || data.fillBedInfo === null || this.toBoolean(data.fillBedInfo)
|
||||
this.directFamilyUnitLine = this.toBoolean(data.directFamilyUnitLine)
|
||||
} else {
|
||||
vant.Toast(res.msg || "查询失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
getDoneTasks() {
|
||||
this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doneTasks = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
toBoolean(value) {
|
||||
return value === true || value === 1 || value === "1" || value === "true" || value === "TRUE"
|
||||
},
|
||||
isDirectFamilyLine() {
|
||||
return this.directFamilyUnitLine || this.toBoolean(this.detail.directFamilyUnitLine) || !!(this.directRelative && this.directRelative.id)
|
||||
},
|
||||
hasFamily() {
|
||||
return this.toBoolean(this.detail.hasFamily) || this.familyData.length > 0
|
||||
},
|
||||
showFamilySection() {
|
||||
return this.allowFamily && this.hasFamily() && !this.isDirectFamilyLine()
|
||||
},
|
||||
familyText() {
|
||||
if (this.isDirectFamilyLine() || !this.allowFamily) {
|
||||
return "否"
|
||||
}
|
||||
return this.hasFamily() ? "是" : "否"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
components: {
|
||||
"tour-info": TOUR_INFO
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "signupTime",
|
||||
pageOrderBy: "descending",
|
||||
audit: false,
|
||||
keyword: "",
|
||||
lineType: ""
|
||||
},
|
||||
auditOptions: [
|
||||
{ text: "未审核", value: false },
|
||||
{ text: "已审核", value: true }
|
||||
],
|
||||
lineTypeOptions: [
|
||||
{ text: "全部线路类型", value: "" }
|
||||
],
|
||||
formData: {},
|
||||
showApprovalForm: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
historyBack,
|
||||
onReady() {
|
||||
this.doSearch()
|
||||
},
|
||||
doSearch() {
|
||||
this.$nextTick(async () => {
|
||||
await this.loadLineTypeOptions()
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.totalCount = 0
|
||||
this.$refs.tableListRef.doSearch()
|
||||
})
|
||||
},
|
||||
loadLineTypeOptions() {
|
||||
return this.$axios.post("/platform/tour/unionApproval/lineTypeOptions", {
|
||||
audit: this.pageForm.audit,
|
||||
keyword: this.pageForm.keyword
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const options = (res.data || []).map(item => ({
|
||||
text: item.lineType,
|
||||
value: item.lineType
|
||||
})).filter(item => item.value)
|
||||
this.lineTypeOptions = [{ text: "全部线路类型", value: "" }].concat(options)
|
||||
if (this.pageForm.lineType && !this.lineTypeOptions.some(item => item.value === this.pageForm.lineType)) {
|
||||
this.pageForm.lineType = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
onView(row) {
|
||||
this.showApprovalForm = false
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
onApproval(row) {
|
||||
this.showApprovalForm = true
|
||||
this.formData = {
|
||||
processTaskId: row.taskId,
|
||||
taskName: row.curTaskName || row.taskName,
|
||||
tf_opinion: ""
|
||||
}
|
||||
this.$refs.infoRef.onOpen(row)
|
||||
},
|
||||
async handleTaskAction(val) {
|
||||
try {
|
||||
await this.$refs.formRef.validate()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
this.$dialog.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要提交吗?"
|
||||
}).then(() => {
|
||||
const loading = this.$toast.loading({
|
||||
message: "加载中...",
|
||||
forbidClick: true,
|
||||
overlay: true,
|
||||
duration: 0
|
||||
})
|
||||
this.$axios.post("/flow/common/executeTask", {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: val
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$refs.infoRef.onClose()
|
||||
this.$toast.success(res.msg || "操作成功")
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$toast(res.msg || "操作失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
Reference in New Issue
Block a user