疗休养1.0版本

This commit is contained in:
2026-06-02 14:28:36 +08:00
parent 532bae8204
commit 67a46212e2
90 changed files with 21639 additions and 3 deletions
@@ -0,0 +1,8 @@
ALTER TABLE activity_user_scope
ADD COLUMN groupType INT(2) NULL COMMENT '分组类型 1.结果分组 2.SQL条件分组' AFTER userId;
ALTER TABLE activity_user_scope
ADD COLUMN groupSql LONGTEXT NULL COMMENT 'SQL条件分组保存的查询SQL' AFTER groupType;
ALTER TABLE activity_user_scope
ADD INDEX INDEX_ACTIVITY_USER_SCOPE_GROUPID_USERID (groupId, userId);
@@ -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;
+253
View File
@@ -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`;
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

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

@@ -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>
<!--#
}
#-->
@@ -0,0 +1,549 @@
<!--#
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: [
"\n",
" <div>\n",
" <div class=\"process-title\">\n",
" 报名信息\n",
" <el-link type=\"primary\" @click=\"openChart\">点击查看流程图</el-link>\n",
" </div>\n",
" <el-descriptions :column=\"3\" border class=\"tour-approval-section\">\n",
" <el-descriptions-item label=\"工号\">{{ detail.jobNo || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"姓名\">{{ detail.userName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"性别\">{{ detail.gender || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"身份证号\">{{ detail.idCard || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"所在单位\">{{ detail.unitName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"所在工会\">{{ detail.unionName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"报名时间\">{{ detail.signupTime || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"报名线路\">{{ detail.lineName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"出行时段\">{{ detail.travelPeriod || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"线路类型\">{{ detail.lineType || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"报名酒店\">{{ detail.hotelName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"旅行社\">{{ detail.travelAgencyName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"是否携带家属\">\n",
" {{ familyText() }}\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"报销超出费用\">{{ detail.overCostReimbursed ? '是' : '否' }}</el-descriptions-item>\n",
" <el-descriptions-item v-if=\"fillBedInfo\" label=\"意向拼床人\">{{ detail.intendedRoommate || '' }}</el-descriptions-item>\n",
" <el-descriptions-item v-if=\"fillBedInfo\" label=\"床型\">{{ detail.bedType || '' }}</el-descriptions-item>\n",
" <el-descriptions-item v-if=\"fillBedInfo\" label=\"床位信息\">{{ detail.bedInfo || '' }}</el-descriptions-item>\n",
" </el-descriptions>\n",
"\n",
" <div v-if=\"isDirectFamilyLine()\" class=\"tour-approval-section\">\n",
" <div class=\"process-title\">直系亲属线路</div>\n",
" <el-descriptions :column=\"3\" border>\n",
" <el-descriptions-item label=\"亲属姓名\">{{ directRelative.relativeName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"所在单位\">{{ directRelative.unitName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"亲属关系\">{{ directRelative.relationshipName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"线路名称\">{{ directRelative.lineName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"出行开始日期\">{{ directRelative.travelStartTime || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"出行结束日期\">{{ directRelative.travelEndTime || '' }}</el-descriptions-item>\n",
" </el-descriptions>\n",
" </div>\n",
"\n",
" <div v-if=\"showFamilySection()\" class=\"tour-approval-section\">\n",
" <div class=\"process-title\">亲属信息</div>\n",
" <el-table :data=\"familyData\" border size=\"mini\" empty-text=\"暂无亲属信息\" class=\"tour-approval-table\">\n",
" <el-table-column label=\"序号\" type=\"index\" width=\"70\" align=\"center\" header-align=\"center\"></el-table-column>\n",
" <el-table-column label=\"家属姓名\" prop=\"familyName\" min-width=\"120\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column label=\"性别\" prop=\"gender\" width=\"90\" align=\"center\" header-align=\"center\"></el-table-column>\n",
" <el-table-column label=\"身份证号码\" prop=\"idCard\" min-width=\"180\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column label=\"年龄\" prop=\"age\" width=\"90\" align=\"center\" header-align=\"center\"></el-table-column>\n",
" <el-table-column label=\"关系\" prop=\"relationship\" width=\"110\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column v-if=\"fillBedInfo\" label=\"床型\" prop=\"bedType\" width=\"110\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column v-if=\"fillBedInfo\" label=\"床位\" prop=\"bedInfo\" min-width=\"120\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column v-if=\"fillBedInfo\" label=\"意向拼床人\" prop=\"intendedRoommate\" min-width=\"130\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" </el-table>\n",
" </div>\n",
"\n",
" <template v-for=\"task in doneTasks\">\n",
" <div class=\"mt10\">\n",
" <div class=\"process-title\">{{ task.displayName }}</div>\n",
" <el-descriptions border class=\"flow-task-form\" :column=\"3\" :key=\"task.id\" v-if=\"task.ext.isFirstTaskNode\">\n",
" <el-descriptions-item label=\"申请用户\">\n",
" {{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"申请时间\">{{ task.finishTime }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"办理结果\">\n",
" <dict-tag :options=\"dict.type.PROCESS_TASK_SUBMIT_TYPE\" :value=\"task.ext.submitType\"></dict-tag>\n",
" </el-descriptions-item>\n",
" </el-descriptions>\n",
"\n",
" <el-descriptions border class=\"flow-task-form\" :column=\"3\" :key=\"task.id\" v-else>\n",
" <el-descriptions-item label=\"办理用户\">\n",
" {{ task.taskFormData.userName }}({{ task.taskFormData.loginName }})\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"办理时间\">{{ task.finishTime }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"办理结果\">\n",
" <dict-tag :options=\"dict.type.PROCESS_TASK_SUBMIT_TYPE\" :value=\"task.ext.submitType\"></dict-tag>\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"办理意见\" :span=\"3\">\n",
" {{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}\n",
" </el-descriptions-item>\n",
" </el-descriptions>\n",
" </div>\n",
" </template>\n",
"\n",
" <slot></slot>\n",
"\n",
" <snaker-chart ref=\"snakerChartRef\"></snaker-chart>\n",
" </div>\n",
" \n"
].join(""),
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,549 @@
<!--#
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: [
"\n",
" <div>\n",
" <div class=\"process-title\">\n",
" 报名信息\n",
" <el-link type=\"primary\" @click=\"openChart\">点击查看流程图</el-link>\n",
" </div>\n",
" <el-descriptions :column=\"3\" border class=\"tour-approval-section\">\n",
" <el-descriptions-item label=\"工号\">{{ detail.jobNo || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"姓名\">{{ detail.userName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"性别\">{{ detail.gender || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"身份证号\">{{ detail.idCard || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"所在单位\">{{ detail.unitName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"所在工会\">{{ detail.unionName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"报名时间\">{{ detail.signupTime || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"报名线路\">{{ detail.lineName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"出行时段\">{{ detail.travelPeriod || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"线路类型\">{{ detail.lineType || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"报名酒店\">{{ detail.hotelName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"旅行社\">{{ detail.travelAgencyName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"是否携带家属\">\n",
" {{ familyText() }}\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"报销超出费用\">{{ detail.overCostReimbursed ? '是' : '否' }}</el-descriptions-item>\n",
" <el-descriptions-item v-if=\"fillBedInfo\" label=\"意向拼床人\">{{ detail.intendedRoommate || '' }}</el-descriptions-item>\n",
" <el-descriptions-item v-if=\"fillBedInfo\" label=\"床型\">{{ detail.bedType || '' }}</el-descriptions-item>\n",
" <el-descriptions-item v-if=\"fillBedInfo\" label=\"床位信息\">{{ detail.bedInfo || '' }}</el-descriptions-item>\n",
" </el-descriptions>\n",
"\n",
" <div v-if=\"isDirectFamilyLine()\" class=\"tour-approval-section\">\n",
" <div class=\"process-title\">直系亲属线路</div>\n",
" <el-descriptions :column=\"3\" border>\n",
" <el-descriptions-item label=\"亲属姓名\">{{ directRelative.relativeName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"所在单位\">{{ directRelative.unitName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"亲属关系\">{{ directRelative.relationshipName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"线路名称\">{{ directRelative.lineName || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"出行开始日期\">{{ directRelative.travelStartTime || '' }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"出行结束日期\">{{ directRelative.travelEndTime || '' }}</el-descriptions-item>\n",
" </el-descriptions>\n",
" </div>\n",
"\n",
" <div v-if=\"showFamilySection()\" class=\"tour-approval-section\">\n",
" <div class=\"process-title\">亲属信息</div>\n",
" <el-table :data=\"familyData\" border size=\"mini\" empty-text=\"暂无亲属信息\" class=\"tour-approval-table\">\n",
" <el-table-column label=\"序号\" type=\"index\" width=\"70\" align=\"center\" header-align=\"center\"></el-table-column>\n",
" <el-table-column label=\"家属姓名\" prop=\"familyName\" min-width=\"120\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column label=\"性别\" prop=\"gender\" width=\"90\" align=\"center\" header-align=\"center\"></el-table-column>\n",
" <el-table-column label=\"身份证号码\" prop=\"idCard\" min-width=\"180\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column label=\"年龄\" prop=\"age\" width=\"90\" align=\"center\" header-align=\"center\"></el-table-column>\n",
" <el-table-column label=\"关系\" prop=\"relationship\" width=\"110\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column v-if=\"fillBedInfo\" label=\"床型\" prop=\"bedType\" width=\"110\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column v-if=\"fillBedInfo\" label=\"床位\" prop=\"bedInfo\" min-width=\"120\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" <el-table-column v-if=\"fillBedInfo\" label=\"意向拼床人\" prop=\"intendedRoommate\" min-width=\"130\" align=\"center\" header-align=\"center\" show-overflow-tooltip></el-table-column>\n",
" </el-table>\n",
" </div>\n",
"\n",
" <template v-for=\"task in doneTasks\">\n",
" <div class=\"mt10\">\n",
" <div class=\"process-title\">{{ task.displayName }}</div>\n",
" <el-descriptions border class=\"flow-task-form\" :column=\"3\" :key=\"task.id\" v-if=\"task.ext.isFirstTaskNode\">\n",
" <el-descriptions-item label=\"申请用户\">\n",
" {{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"申请时间\">{{ task.finishTime }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"办理结果\">\n",
" <dict-tag :options=\"dict.type.PROCESS_TASK_SUBMIT_TYPE\" :value=\"task.ext.submitType\"></dict-tag>\n",
" </el-descriptions-item>\n",
" </el-descriptions>\n",
"\n",
" <el-descriptions border class=\"flow-task-form\" :column=\"3\" :key=\"task.id\" v-else>\n",
" <el-descriptions-item label=\"办理用户\">\n",
" {{ task.taskFormData.userName }}({{ task.taskFormData.loginName }})\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"办理时间\">{{ task.finishTime }}</el-descriptions-item>\n",
" <el-descriptions-item label=\"办理结果\">\n",
" <dict-tag :options=\"dict.type.PROCESS_TASK_SUBMIT_TYPE\" :value=\"task.ext.submitType\"></dict-tag>\n",
" </el-descriptions-item>\n",
" <el-descriptions-item label=\"办理意见\" :span=\"3\">\n",
" {{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}\n",
" </el-descriptions-item>\n",
" </el-descriptions>\n",
" </div>\n",
" </template>\n",
"\n",
" <slot></slot>\n",
"\n",
" <snaker-chart ref=\"snakerChartRef\"></snaker-chart>\n",
" </div>\n",
" \n"
].join(""),
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,480 @@
<!--#
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: [
"\n",
" <van-action-sheet v-model=\"visible\" title=\"报名详情\">\n",
" <div class=\"tour-approval-sheet\">\n",
" <div class=\"tour-section\">\n",
" <div class=\"tour-section-title\">教职工信息</div>\n",
" <van-cell title=\"工号\" :value=\"detail.jobNo || ''\"></van-cell>\n",
" <van-cell title=\"姓名\" :value=\"detail.userName || ''\"></van-cell>\n",
" <van-cell title=\"性别\" :value=\"detail.gender || ''\"></van-cell>\n",
" <van-cell title=\"身份证号\" :value=\"detail.idCard || ''\"></van-cell>\n",
" <van-cell title=\"所在单位\" :value=\"detail.unitName || ''\"></van-cell>\n",
" <van-cell title=\"所在工会\" :value=\"detail.unionName || ''\"></van-cell>\n",
" </div>\n",
"\n",
" <div class=\"tour-section\">\n",
" <div class=\"tour-section-title\">报名信息</div>\n",
" <van-cell title=\"报名线路\" :value=\"detail.lineName || ''\"></van-cell>\n",
" <van-cell title=\"线路类型\" :value=\"detail.lineType || ''\"></van-cell>\n",
" <van-cell title=\"出行时段\" :value=\"detail.travelPeriod || ''\"></van-cell>\n",
" <van-cell title=\"报名时间\" :value=\"detail.signupTime || ''\"></van-cell>\n",
" <van-cell title=\"报名酒店\" :value=\"detail.hotelName || ''\"></van-cell>\n",
" <van-cell title=\"旅行社\" :value=\"detail.travelAgencyName || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" title=\"床型\" :value=\"detail.bedType || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" title=\"床位信息\" :value=\"detail.bedInfo || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" title=\"意向拼床人\" :value=\"detail.intendedRoommate || ''\"></van-cell>\n",
" <van-cell title=\"携带家属\" :value=\"familyText()\"></van-cell>\n",
" <van-cell title=\"报销超出费用\" :value=\"detail.overCostReimbursed ? '是' : '否'\"></van-cell>\n",
" </div>\n",
"\n",
" <div v-if=\"isDirectFamilyLine()\" class=\"tour-section\">\n",
" <div class=\"tour-section-title\">直系亲属线路</div>\n",
" <van-cell title=\"亲属姓名\" :value=\"directRelative.relativeName || ''\"></van-cell>\n",
" <van-cell title=\"所在单位\" :value=\"directRelative.unitName || ''\"></van-cell>\n",
" <van-cell title=\"亲属关系\" :value=\"directRelative.relationshipName || ''\"></van-cell>\n",
" <van-cell title=\"线路名称\" :value=\"directRelative.lineName || ''\"></van-cell>\n",
" <van-cell title=\"出行开始\" :value=\"directRelative.travelStartTime || ''\"></van-cell>\n",
" <van-cell title=\"出行结束\" :value=\"directRelative.travelEndTime || ''\"></van-cell>\n",
" </div>\n",
"\n",
" <div v-if=\"showFamilySection()\" class=\"tour-section\">\n",
" <div class=\"tour-section-title\">家属信息</div>\n",
" <template v-for=\"(item,index) in familyData\">\n",
" <van-cell :key=\"'title_' + index\" :title=\"'家属' + (index + 1)\"></van-cell>\n",
" <van-cell :key=\"'name_' + index\" title=\"姓名\" :value=\"item.familyName || ''\"></van-cell>\n",
" <van-cell :key=\"'gender_' + index\" title=\"性别\" :value=\"item.gender || ''\"></van-cell>\n",
" <van-cell :key=\"'id_' + index\" title=\"身份证号\" :value=\"item.idCard || ''\"></van-cell>\n",
" <van-cell :key=\"'rel_' + index\" title=\"关系\" :value=\"item.relationship || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" :key=\"'bed_' + index\" title=\"床型\" :value=\"item.bedType || ''\"></van-cell>\n",
" </template>\n",
" </div>\n",
"\n",
" <div v-if=\"doneTasks.length > 0\" class=\"tour-section\">\n",
" <div class=\"tour-section-title\">流程记录</div>\n",
" <template v-for=\"task in doneTasks\">\n",
" <van-cell-group :key=\"task.id\" :title=\"task.displayName || task.taskName\">\n",
" <van-cell title=\"办理时间\" :value=\"task.finishTime || ''\"></van-cell>\n",
" <van-cell title=\"办理结果\">\n",
" <dict-tag :options=\"dict.type.PROCESS_TASK_SUBMIT_TYPE\" :value=\"task.ext && task.ext.submitType\"></dict-tag>\n",
" </van-cell>\n",
" <van-cell title=\"办理意见\" v-if=\"task.taskFormData\" class=\"direction-column-cell\">\n",
" <div class=\"tour-process-opinion\">{{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}</div>\n",
" </van-cell>\n",
" </van-cell-group>\n",
" </template>\n",
" </div>\n",
"\n",
" <slot></slot>\n",
" </div>\n",
" </van-action-sheet>\n",
" \n"
].join(""),
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,570 @@
<!--#
layout("/layouts/platform_tour_signup_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('/platform/tour/signup/h5/signup')" 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: {
formatDateTime(date) {
const pad = (value) => String(value).padStart(2, "0")
return date.getFullYear()
+ "-"
+ pad(date.getMonth() + 1)
+ "-"
+ pad(date.getDate())
+ " "
+ pad(date.getHours())
+ ":"
+ pad(date.getMinutes())
+ ":"
+ pad(date.getSeconds())
},
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.formatDateTime(new Date()),
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.replace("/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,403 @@
<!--#
layout("/layouts/platform_tour_signup_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-pdf-preview {
min-height: 560px;
}
.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-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">
<div v-if="serviceNoticePdfHref" 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 v-html="setting.serviceNotice"></div>
</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,
data() {
return {
loading: false,
currentYear: new Date().getFullYear(),
serviceNoticePdfHref: "",
pdfDoc: null,
pdfLoadingTask: null,
pdfLoading: false,
pdfError: "",
pdfPageCount: 0,
pdfRenderedPages: 0,
pdfRenderToken: 0,
bannerList: [
"/assets/platform/images/tour/tour-h5-banner-1.jpg",
"/assets/platform/images/tour/tour-h5-banner-2.jpg"
],
setting: {
year: "",
configName: "",
serviceNotice: ""
}
}
},
methods: {
parseContentHref(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"]
},
loadPdfLib() {
const pdfLib = this.getPdfLib()
if (pdfLib && pdfLib.getDocument) {
return Promise.resolve(pdfLib)
}
return window.loadScriptOnce("/assets/platform/plugins/pdfJs/h5/pdf.js?v=20260524").then(() => {
const loadedPdfLib = this.getPdfLib()
if (!loadedPdfLib || !loadedPdfLib.getDocument) {
throw new Error("pdf lib load failed")
}
return loadedPdfLib
})
},
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 = ""
}
},
renderServiceNoticePdf() {
this.clearPdfPreview()
if (!this.serviceNoticePdfHref) {
return
}
const token = this.pdfRenderToken
this.pdfLoading = true
this.loadPdfLib().then((pdfLib) => {
if (token !== this.pdfRenderToken) {
return null
}
if (pdfLib.GlobalWorkerOptions) {
pdfLib.GlobalWorkerOptions.workerSrc = "/assets/platform/plugins/pdfJs/h5/pdf.worker.js?v=20260524"
}
this.pdfLoadingTask = pdfLib.getDocument({
url: this.serviceNoticePdfHref,
disableAutoFetch: false,
disableStream: false,
disableRange: false
})
return this.pdfLoadingTask.promise
}).then((pdf) => {
if (token !== this.pdfRenderToken || !pdf) {
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)
return page.render({
canvasContext: context,
viewport: viewport,
transform: outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null
}).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)
},
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.serviceNoticePdfHref = this.parseContentHref(this.setting.serviceNotice)
this.$nextTick(() => {
this.renderServiceNoticePdf()
})
} else {
vant.Toast(res.msg || "服务须知加载失败")
}
}).catch(() => {
this.loading = false
})
},
confirmRead() {
window.location.href = "/platform/tour/signup/h5/signup"
}
},
created() {
this.loadServiceNotice()
},
beforeDestroy() {
this.clearPdfPreview()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,587 @@
<!--#
layout("/layouts/platform_tour_signup_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('/platform/tour/signup/h5/signup')" 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"]
},
loadPdfLib() {
const pdfLib = this.getPdfLib()
if (pdfLib && pdfLib.getDocument) {
return Promise.resolve(pdfLib)
}
return window.loadScriptOnce("/assets/platform/plugins/pdfJs/h5/pdf.js?v=20260524").then(() => {
const loadedPdfLib = this.getPdfLib()
if (!loadedPdfLib || !loadedPdfLib.getDocument) {
throw new Error("pdf lib load failed")
}
return loadedPdfLib
})
},
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
}
if (!this.getPdfLib() || !this.getPdfLib().getDocument) {
const token = this.pdfRenderToken
this.pdfLoading = true
this.loadPdfLib().then(() => {
if (token === this.pdfRenderToken) {
this.renderLinePdf()
}
}).catch(() => {
if (token === this.pdfRenderToken) {
this.pdfLoading = false
this.pdfError = "PDF预览组件加载失败"
}
})
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?v=20260524"
}
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,794 @@
<!--#
layout("/layouts/platform_tour_signup_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('/platform/tour/signup/h5')" 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-icon.png?v=20260524" 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" lazy-load>
<template #loading>
<div class="tour-line-thumb-empty">图片加载中</div>
</template>
<template #error>
<div class="tour-line-thumb-empty">暂无图片</div>
</template>
</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,455 @@
<!--#
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: [
"\n",
" <van-action-sheet v-model=\"visible\" title=\"报名详情\">\n",
" <div class=\"tour-approval-sheet\">\n",
" <div class=\"tour-section\">\n",
" <div class=\"tour-section-title\">教职工信息</div>\n",
" <van-cell title=\"工号\" :value=\"detail.jobNo || ''\"></van-cell>\n",
" <van-cell title=\"姓名\" :value=\"detail.userName || ''\"></van-cell>\n",
" <van-cell title=\"性别\" :value=\"detail.gender || ''\"></van-cell>\n",
" <van-cell title=\"身份证号\" :value=\"detail.idCard || ''\"></van-cell>\n",
" <van-cell title=\"所在单位\" :value=\"detail.unitName || ''\"></van-cell>\n",
" <van-cell title=\"所在工会\" :value=\"detail.unionName || ''\"></van-cell>\n",
" </div>\n",
"\n",
" <div class=\"tour-section\">\n",
" <div class=\"tour-section-title\">报名信息</div>\n",
" <van-cell title=\"报名线路\" :value=\"detail.lineName || ''\"></van-cell>\n",
" <van-cell title=\"线路类型\" :value=\"detail.lineType || ''\"></van-cell>\n",
" <van-cell title=\"出行时段\" :value=\"detail.travelPeriod || ''\"></van-cell>\n",
" <van-cell title=\"报名时间\" :value=\"detail.signupTime || ''\"></van-cell>\n",
" <van-cell title=\"报名酒店\" :value=\"detail.hotelName || ''\"></van-cell>\n",
" <van-cell title=\"旅行社\" :value=\"detail.travelAgencyName || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" title=\"床型\" :value=\"detail.bedType || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" title=\"床位信息\" :value=\"detail.bedInfo || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" title=\"意向拼床人\" :value=\"detail.intendedRoommate || ''\"></van-cell>\n",
" <van-cell title=\"携带家属\" :value=\"familyText()\"></van-cell>\n",
" <van-cell title=\"报销超出费用\" :value=\"detail.overCostReimbursed ? '是' : '否'\"></van-cell>\n",
" </div>\n",
"\n",
" <div v-if=\"isDirectFamilyLine()\" class=\"tour-section\">\n",
" <div class=\"tour-section-title\">直系亲属线路</div>\n",
" <van-cell title=\"亲属姓名\" :value=\"directRelative.relativeName || ''\"></van-cell>\n",
" <van-cell title=\"所在单位\" :value=\"directRelative.unitName || ''\"></van-cell>\n",
" <van-cell title=\"亲属关系\" :value=\"directRelative.relationshipName || ''\"></van-cell>\n",
" <van-cell title=\"线路名称\" :value=\"directRelative.lineName || ''\"></van-cell>\n",
" <van-cell title=\"出行开始\" :value=\"directRelative.travelStartTime || ''\"></van-cell>\n",
" <van-cell title=\"出行结束\" :value=\"directRelative.travelEndTime || ''\"></van-cell>\n",
" </div>\n",
"\n",
" <div v-if=\"showFamilySection()\" class=\"tour-section\">\n",
" <div class=\"tour-section-title\">家属信息</div>\n",
" <template v-for=\"(item,index) in familyData\">\n",
" <van-cell :key=\"'title_' + index\" :title=\"'家属' + (index + 1)\"></van-cell>\n",
" <van-cell :key=\"'name_' + index\" title=\"姓名\" :value=\"item.familyName || ''\"></van-cell>\n",
" <van-cell :key=\"'gender_' + index\" title=\"性别\" :value=\"item.gender || ''\"></van-cell>\n",
" <van-cell :key=\"'id_' + index\" title=\"身份证号\" :value=\"item.idCard || ''\"></van-cell>\n",
" <van-cell :key=\"'rel_' + index\" title=\"关系\" :value=\"item.relationship || ''\"></van-cell>\n",
" <van-cell v-if=\"fillBedInfo\" :key=\"'bed_' + index\" title=\"床型\" :value=\"item.bedType || ''\"></van-cell>\n",
" </template>\n",
" </div>\n",
"\n",
" <div v-if=\"doneTasks.length > 0\" class=\"tour-section\">\n",
" <div class=\"tour-section-title\">流程记录</div>\n",
" <template v-for=\"task in doneTasks\">\n",
" <van-cell-group :key=\"task.id\" :title=\"task.displayName || task.taskName\">\n",
" <van-cell title=\"办理时间\" :value=\"task.finishTime || ''\"></van-cell>\n",
" <van-cell title=\"办理结果\">\n",
" <dict-tag :options=\"dict.type.PROCESS_TASK_SUBMIT_TYPE\" :value=\"task.ext && task.ext.submitType\"></dict-tag>\n",
" </van-cell>\n",
" <van-cell title=\"办理意见\" v-if=\"task.taskFormData\" class=\"direction-column-cell\">\n",
" <div class=\"tour-process-opinion\">{{ task.taskFormData.opinion || task.taskFormData.tf_opinion || '' }}</div>\n",
" </van-cell>\n",
" </van-cell-group>\n",
" </template>\n",
" </div>\n",
"\n",
" <slot></slot>\n",
" </div>\n",
" </van-action-sheet>\n",
" \n"
].join(""),
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>
<!--#
}
#-->